summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/core/Kconfig29
-rw-r--r--sound/core/Makefile1
-rw-r--r--sound/core/compress_offload.c25
-rw-r--r--sound/core/hrtimer.c56
-rw-r--r--sound/core/pcm_lib.c4
-rw-r--r--sound/core/pcm_native.c4
-rw-r--r--sound/core/rtctimer.c187
-rw-r--r--sound/core/seq/seq.c2
-rw-r--r--sound/core/timer.c5
-rw-r--r--sound/drivers/dummy.c1
-rw-r--r--sound/firewire/Kconfig1
-rw-r--r--sound/firewire/Makefile3
-rw-r--r--sound/firewire/amdtp-stream-trace.h110
-rw-r--r--sound/firewire/amdtp-stream.c210
-rw-r--r--sound/firewire/amdtp-stream.h37
-rw-r--r--sound/firewire/bebob/bebob.c217
-rw-r--r--sound/firewire/bebob/bebob.h6
-rw-r--r--sound/firewire/bebob/bebob_stream.c101
-rw-r--r--sound/firewire/dice/dice.c41
-rw-r--r--sound/firewire/digi00x/amdtp-dot.c2
-rw-r--r--sound/firewire/digi00x/digi00x-transaction.c7
-rw-r--r--sound/firewire/digi00x/digi00x.c107
-rw-r--r--sound/firewire/digi00x/digi00x.h3
-rw-r--r--sound/firewire/fireworks/fireworks.c168
-rw-r--r--sound/firewire/fireworks/fireworks.h4
-rw-r--r--sound/firewire/fireworks/fireworks_stream.c84
-rw-r--r--sound/firewire/lib.c32
-rw-r--r--sound/firewire/lib.h3
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c3
-rw-r--r--sound/firewire/oxfw/oxfw.c151
-rw-r--r--sound/firewire/oxfw/oxfw.h4
-rw-r--r--sound/firewire/tascam/tascam-stream.c26
-rw-r--r--sound/firewire/tascam/tascam.c118
-rw-r--r--sound/firewire/tascam/tascam.h2
-rw-r--r--sound/hda/ext/hdac_ext_bus.c1
-rw-r--r--sound/hda/hdac_controller.c17
-rw-r--r--sound/hda/hdac_i915.c47
-rw-r--r--sound/hda/hdac_regmap.c4
-rw-r--r--sound/hda/hdmi_chmap.c44
-rw-r--r--sound/isa/wavefront/wavefront_synth.c9
-rw-r--r--sound/oss/waveartist.c8
-rw-r--r--sound/pci/au88x0/au88x0_core.c14
-rw-r--r--sound/pci/au88x0/au88x0_pcm.c5
-rw-r--r--sound/pci/ctxfi/cttimer.c6
-rw-r--r--sound/pci/ens1370.c2
-rw-r--r--sound/pci/hda/Kconfig10
-rw-r--r--sound/pci/hda/hda_generic.c2
-rw-r--r--sound/pci/hda/hda_intel.c11
-rw-r--r--sound/pci/hda/hda_sysfs.c8
-rw-r--r--sound/pci/hda/hda_tegra.c20
-rw-r--r--sound/pci/hda/patch_hdmi.c393
-rw-r--r--sound/pci/hda/patch_realtek.c123
-rw-r--r--sound/pci/hda/thinkpad_helper.c2
-rw-r--r--sound/pci/intel8x0.c20
-rw-r--r--sound/pci/lx6464es/lx_core.c2
-rw-r--r--sound/soc/codecs/Kconfig45
-rw-r--r--sound/soc/codecs/Makefile11
-rw-r--r--sound/soc/codecs/ak4613.c2
-rw-r--r--sound/soc/codecs/ak4642.c3
-rw-r--r--sound/soc/codecs/cx20442.c1
-rw-r--r--sound/soc/codecs/hdac_hdmi.c20
-rw-r--r--sound/soc/codecs/max98371.c441
-rw-r--r--sound/soc/codecs/max98371.h67
-rw-r--r--sound/soc/codecs/rt298.c51
-rw-r--r--sound/soc/codecs/rt298.h2
-rw-r--r--sound/soc/codecs/rt5645.c2
-rw-r--r--sound/soc/codecs/rt5670.c2
-rw-r--r--sound/soc/codecs/rt5677.c41
-rw-r--r--sound/soc/codecs/tas571x.c141
-rw-r--r--sound/soc/codecs/tas571x.h22
-rw-r--r--sound/soc/codecs/tas5720.c620
-rw-r--r--sound/soc/codecs/tas5720.h90
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c10
-rw-r--r--sound/soc/codecs/tlv320aic32x4-i2c.c74
-rw-r--r--sound/soc/codecs/tlv320aic32x4-spi.c76
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c279
-rw-r--r--sound/soc/codecs/tlv320aic32x4.h7
-rw-r--r--sound/soc/codecs/twl6040.c16
-rw-r--r--sound/soc/codecs/wm5100.c16
-rw-r--r--sound/soc/codecs/wm5102.c2
-rw-r--r--sound/soc/codecs/wm5110.c1
-rw-r--r--sound/soc/codecs/wm8903.c17
-rw-r--r--sound/soc/codecs/wm8940.c1
-rw-r--r--sound/soc/codecs/wm8962.c24
-rw-r--r--sound/soc/codecs/wm8962.h6
-rw-r--r--sound/soc/codecs/wm8996.c16
-rw-r--r--sound/soc/davinci/davinci-mcasp.c56
-rw-r--r--sound/soc/davinci/davinci-mcasp.h4
-rw-r--r--sound/soc/fsl/fsl_ssi.c12
-rw-r--r--sound/soc/generic/simple-card.c1
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-compress.c9
-rw-r--r--sound/soc/intel/boards/cht_bsw_max98090_ti.c2
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c2
-rw-r--r--sound/soc/intel/common/sst-firmware.c2
-rw-r--r--sound/soc/intel/skylake/bxt-sst.c1
-rw-r--r--sound/soc/kirkwood/Kconfig1
-rw-r--r--sound/soc/mediatek/Kconfig1
-rw-r--r--sound/soc/mediatek/mt8173-rt5650-rt5676.c27
-rw-r--r--sound/soc/mediatek/mt8173-rt5650.c50
-rw-r--r--sound/soc/mediatek/mtk-afe-pcm.c2
-rw-r--r--sound/soc/omap/mcbsp.c8
-rw-r--r--sound/soc/omap/omap-pcm.c2
-rw-r--r--sound/soc/pxa/brownstone.c1
-rw-r--r--sound/soc/pxa/mioa701_wm9713.c1
-rw-r--r--sound/soc/pxa/mmp-pcm.c1
-rw-r--r--sound/soc/pxa/mmp-sspa.c1
-rw-r--r--sound/soc/pxa/palm27x.c1
-rw-r--r--sound/soc/pxa/pxa-ssp.c1
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c1
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c1
-rw-r--r--sound/soc/qcom/lpass-platform.c8
-rw-r--r--sound/soc/sh/rcar/adg.c8
-rw-r--r--sound/soc/sh/rcar/dma.c12
-rw-r--r--sound/soc/sh/rcar/rsnd.h13
-rw-r--r--sound/soc/sh/rcar/src.c4
-rw-r--r--sound/soc/soc-ac97.c8
-rw-r--r--sound/soc/soc-topology.c48
-rw-r--r--sound/soc/sti/sti_uniperif.c144
-rw-r--r--sound/soc/sti/uniperif.h220
-rw-r--r--sound/soc/sti/uniperif_player.c182
-rw-r--r--sound/soc/sti/uniperif_reader.c229
-rw-r--r--sound/usb/card.c4
-rw-r--r--sound/usb/clock.c4
-rw-r--r--sound/usb/helper.c1
-rw-r--r--sound/usb/midi.c1
-rw-r--r--sound/usb/mixer.c78
-rw-r--r--sound/usb/quirks.c3
-rw-r--r--sound/usb/usbaudio.h1
128 files changed, 4323 insertions, 1441 deletions
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 6d12ca9bcb80..9749f9e8b45c 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -141,35 +141,6 @@ config SND_SEQ_HRTIMER_DEFAULT
Say Y here to use the HR-timer backend as the default sequencer
timer.
-config SND_RTCTIMER
- tristate "RTC Timer support"
- depends on RTC
- select SND_TIMER
- help
- Say Y here to enable RTC timer support for ALSA. ALSA uses
- the RTC timer as a precise timing source and maps the RTC
- timer to ALSA's timer interface. The ALSA sequencer code also
- can use this timing source.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-rtctimer.
-
- Note that this option is exclusive with the new RTC drivers
- (CONFIG_RTC_CLASS) since this requires the old API.
-
-config SND_SEQ_RTCTIMER_DEFAULT
- bool "Use RTC as default sequencer timer"
- depends on SND_RTCTIMER && SND_SEQUENCER
- depends on !SND_SEQ_HRTIMER_DEFAULT
- default y
- help
- Say Y here to use the RTC timer as the default sequencer
- timer. This is strongly recommended because it ensures
- precise MIDI timing even when the system timer runs at less
- than 1000 Hz.
-
- If in doubt, say Y.
-
config SND_DYNAMIC_MINORS
bool "Dynamic device file minor numbers"
help
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 48ab4b8f8279..e85d9dd12c2d 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -37,7 +37,6 @@ obj-$(CONFIG_SND) += snd.o
obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o
obj-$(CONFIG_SND_TIMER) += snd-timer.o
obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o
-obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o
obj-$(CONFIG_SND_PCM) += snd-pcm.o
obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o
obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index a9933c07a6bf..9b3334be9df2 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -288,9 +288,12 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
stream = &data->stream;
mutex_lock(&stream->device->lock);
/* write is allowed when stream is running or has been steup */
- if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
- stream->runtime->state != SNDRV_PCM_STATE_PREPARED &&
- stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_RUNNING:
+ break;
+ default:
mutex_unlock(&stream->device->lock);
return -EBADFD;
}
@@ -391,14 +394,13 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
int retval = 0;
if (snd_BUG_ON(!data))
- return -EFAULT;
+ return POLLERR;
+
stream = &data->stream;
- if (snd_BUG_ON(!stream))
- return -EFAULT;
mutex_lock(&stream->device->lock);
if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
- retval = -EBADFD;
+ retval = snd_compr_get_poll(stream) | POLLERR;
goto out;
}
poll_wait(f, &stream->runtime->sleep, wait);
@@ -421,10 +423,7 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
retval = snd_compr_get_poll(stream);
break;
default:
- if (stream->direction == SND_COMPRESS_PLAYBACK)
- retval = POLLOUT | POLLWRNORM | POLLERR;
- else
- retval = POLLIN | POLLRDNORM | POLLERR;
+ retval = snd_compr_get_poll(stream) | POLLERR;
break;
}
out:
@@ -802,9 +801,9 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
if (snd_BUG_ON(!data))
return -EFAULT;
+
stream = &data->stream;
- if (snd_BUG_ON(!stream))
- return -EFAULT;
+
mutex_lock(&stream->device->lock);
switch (_IOC_NR(cmd)) {
case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c
index 656d9a9032dc..e2f27022b363 100644
--- a/sound/core/hrtimer.c
+++ b/sound/core/hrtimer.c
@@ -38,37 +38,53 @@ static unsigned int resolution;
struct snd_hrtimer {
struct snd_timer *timer;
struct hrtimer hrt;
- atomic_t running;
+ bool in_callback;
};
static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
{
struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt);
struct snd_timer *t = stime->timer;
- unsigned long oruns;
-
- if (!atomic_read(&stime->running))
- return HRTIMER_NORESTART;
-
- oruns = hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution));
- snd_timer_interrupt(stime->timer, t->sticks * oruns);
+ ktime_t delta;
+ unsigned long ticks;
+ enum hrtimer_restart ret = HRTIMER_NORESTART;
+
+ spin_lock(&t->lock);
+ if (!t->running)
+ goto out; /* fast path */
+ stime->in_callback = true;
+ ticks = t->sticks;
+ spin_unlock(&t->lock);
+
+ /* calculate the drift */
+ delta = ktime_sub(hrt->base->get_time(), hrtimer_get_expires(hrt));
+ if (delta.tv64 > 0)
+ ticks += ktime_divns(delta, ticks * resolution);
+
+ snd_timer_interrupt(stime->timer, ticks);
+
+ spin_lock(&t->lock);
+ if (t->running) {
+ hrtimer_add_expires_ns(hrt, t->sticks * resolution);
+ ret = HRTIMER_RESTART;
+ }
- if (!atomic_read(&stime->running))
- return HRTIMER_NORESTART;
- return HRTIMER_RESTART;
+ stime->in_callback = false;
+ out:
+ spin_unlock(&t->lock);
+ return ret;
}
static int snd_hrtimer_open(struct snd_timer *t)
{
struct snd_hrtimer *stime;
- stime = kmalloc(sizeof(*stime), GFP_KERNEL);
+ stime = kzalloc(sizeof(*stime), GFP_KERNEL);
if (!stime)
return -ENOMEM;
hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
stime->timer = t;
stime->hrt.function = snd_hrtimer_callback;
- atomic_set(&stime->running, 0);
t->private_data = stime;
return 0;
}
@@ -78,6 +94,11 @@ static int snd_hrtimer_close(struct snd_timer *t)
struct snd_hrtimer *stime = t->private_data;
if (stime) {
+ spin_lock_irq(&t->lock);
+ t->running = 0; /* just to be sure */
+ stime->in_callback = 1; /* skip start/stop */
+ spin_unlock_irq(&t->lock);
+
hrtimer_cancel(&stime->hrt);
kfree(stime);
t->private_data = NULL;
@@ -89,18 +110,19 @@ static int snd_hrtimer_start(struct snd_timer *t)
{
struct snd_hrtimer *stime = t->private_data;
- atomic_set(&stime->running, 0);
- hrtimer_try_to_cancel(&stime->hrt);
+ if (stime->in_callback)
+ return 0;
hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
HRTIMER_MODE_REL);
- atomic_set(&stime->running, 1);
return 0;
}
static int snd_hrtimer_stop(struct snd_timer *t)
{
struct snd_hrtimer *stime = t->private_data;
- atomic_set(&stime->running, 0);
+
+ if (stime->in_callback)
+ return 0;
hrtimer_try_to_cancel(&stime->hrt);
return 0;
}
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 3a9b66c6e09c..bb1261591a1f 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1886,8 +1886,8 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
snd_timer_interrupt(substream->timer, 1);
#endif
_end:
- snd_pcm_stream_unlock_irqrestore(substream, flags);
kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
+ snd_pcm_stream_unlock_irqrestore(substream, flags);
}
EXPORT_SYMBOL(snd_pcm_period_elapsed);
@@ -2595,6 +2595,8 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
};
int err;
+ if (WARN_ON(pcm->streams[stream].chmap_kctl))
+ return -EBUSY;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 9106d8e2300e..c61fd50f771f 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -3161,7 +3161,7 @@ static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait)
substream = pcm_file->substream;
if (PCM_RUNTIME_CHECK(substream))
- return -ENXIO;
+ return POLLOUT | POLLWRNORM | POLLERR;
runtime = substream->runtime;
poll_wait(file, &runtime->sleep, wait);
@@ -3200,7 +3200,7 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
substream = pcm_file->substream;
if (PCM_RUNTIME_CHECK(substream))
- return -ENXIO;
+ return POLLIN | POLLRDNORM | POLLERR;
runtime = substream->runtime;
poll_wait(file, &runtime->sleep, wait);
diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c
deleted file mode 100644
index f3420d11a12f..000000000000
--- a/sound/core/rtctimer.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * RTC based high-frequency timer
- *
- * Copyright (C) 2000 Takashi Iwai
- * based on rtctimer.c by Steve Ratcliffe
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/log2.h>
-#include <sound/core.h>
-#include <sound/timer.h>
-
-#if IS_ENABLED(CONFIG_RTC)
-
-#include <linux/mc146818rtc.h>
-
-#define RTC_FREQ 1024 /* default frequency */
-#define NANO_SEC 1000000000L /* 10^9 in sec */
-
-/*
- * prototypes
- */
-static int rtctimer_open(struct snd_timer *t);
-static int rtctimer_close(struct snd_timer *t);
-static int rtctimer_start(struct snd_timer *t);
-static int rtctimer_stop(struct snd_timer *t);
-
-
-/*
- * The hardware dependent description for this timer.
- */
-static struct snd_timer_hardware rtc_hw = {
- .flags = SNDRV_TIMER_HW_AUTO |
- SNDRV_TIMER_HW_FIRST |
- SNDRV_TIMER_HW_TASKLET,
- .ticks = 100000000L, /* FIXME: XXX */
- .open = rtctimer_open,
- .close = rtctimer_close,
- .start = rtctimer_start,
- .stop = rtctimer_stop,
-};
-
-static int rtctimer_freq = RTC_FREQ; /* frequency */
-static struct snd_timer *rtctimer;
-static struct tasklet_struct rtc_tasklet;
-static rtc_task_t rtc_task;
-
-
-static int
-rtctimer_open(struct snd_timer *t)
-{
- int err;
-
- err = rtc_register(&rtc_task);
- if (err < 0)
- return err;
- t->private_data = &rtc_task;
- return 0;
-}
-
-static int
-rtctimer_close(struct snd_timer *t)
-{
- rtc_task_t *rtc = t->private_data;
- if (rtc) {
- rtc_unregister(rtc);
- tasklet_kill(&rtc_tasklet);
- t->private_data = NULL;
- }
- return 0;
-}
-
-static int
-rtctimer_start(struct snd_timer *timer)
-{
- rtc_task_t *rtc = timer->private_data;
- if (snd_BUG_ON(!rtc))
- return -EINVAL;
- rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
- rtc_control(rtc, RTC_PIE_ON, 0);
- return 0;
-}
-
-static int
-rtctimer_stop(struct snd_timer *timer)
-{
- rtc_task_t *rtc = timer->private_data;
- if (snd_BUG_ON(!rtc))
- return -EINVAL;
- rtc_control(rtc, RTC_PIE_OFF, 0);
- return 0;
-}
-
-static void rtctimer_tasklet(unsigned long data)
-{
- snd_timer_interrupt((struct snd_timer *)data, 1);
-}
-
-/*
- * interrupt
- */
-static void rtctimer_interrupt(void *private_data)
-{
- tasklet_schedule(private_data);
-}
-
-
-/*
- * ENTRY functions
- */
-static int __init rtctimer_init(void)
-{
- int err;
- struct snd_timer *timer;
-
- if (rtctimer_freq < 2 || rtctimer_freq > 8192 ||
- !is_power_of_2(rtctimer_freq)) {
- pr_err("ALSA: rtctimer: invalid frequency %d\n", rtctimer_freq);
- return -EINVAL;
- }
-
- /* Create a new timer and set up the fields */
- err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer);
- if (err < 0)
- return err;
-
- timer->module = THIS_MODULE;
- strcpy(timer->name, "RTC timer");
- timer->hw = rtc_hw;
- timer->hw.resolution = NANO_SEC / rtctimer_freq;
-
- tasklet_init(&rtc_tasklet, rtctimer_tasklet, (unsigned long)timer);
-
- /* set up RTC callback */
- rtc_task.func = rtctimer_interrupt;
- rtc_task.private_data = &rtc_tasklet;
-
- err = snd_timer_global_register(timer);
- if (err < 0) {
- snd_timer_global_free(timer);
- return err;
- }
- rtctimer = timer; /* remember this */
-
- return 0;
-}
-
-static void __exit rtctimer_exit(void)
-{
- if (rtctimer) {
- snd_timer_global_free(rtctimer);
- rtctimer = NULL;
- }
-}
-
-
-/*
- * exported stuff
- */
-module_init(rtctimer_init)
-module_exit(rtctimer_exit)
-
-module_param(rtctimer_freq, int, 0444);
-MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz");
-
-MODULE_LICENSE("GPL");
-
-MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_RTC));
-
-#endif /* IS_ENABLED(CONFIG_RTC) */
diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c
index 7e0aabb808a6..639544b4fb04 100644
--- a/sound/core/seq/seq.c
+++ b/sound/core/seq/seq.c
@@ -47,8 +47,6 @@ int seq_default_timer_card = -1;
int seq_default_timer_device =
#ifdef CONFIG_SND_SEQ_HRTIMER_DEFAULT
SNDRV_TIMER_GLOBAL_HRTIMER
-#elif defined(CONFIG_SND_SEQ_RTCTIMER_DEFAULT)
- SNDRV_TIMER_GLOBAL_RTC
#else
SNDRV_TIMER_GLOBAL_SYSTEM
#endif
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 6469bedda2f3..e722022d325d 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -37,8 +37,6 @@
#if IS_ENABLED(CONFIG_SND_HRTIMER)
#define DEFAULT_TIMER_LIMIT 4
-#elif IS_ENABLED(CONFIG_SND_RTCTIMER)
-#define DEFAULT_TIMER_LIMIT 2
#else
#define DEFAULT_TIMER_LIMIT 1
#endif
@@ -1225,6 +1223,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
tu->tstamp = *tstamp;
if ((tu->filter & (1 << event)) == 0 || !tu->tread)
return;
+ memset(&r1, 0, sizeof(r1));
r1.event = event;
r1.tstamp = *tstamp;
r1.val = resolution;
@@ -1267,6 +1266,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
}
if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
tu->last_resolution != resolution) {
+ memset(&r1, 0, sizeof(r1));
r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
r1.tstamp = tstamp;
r1.val = resolution;
@@ -1739,6 +1739,7 @@ static int snd_timer_user_params(struct file *file,
if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
if (tu->tread) {
struct snd_timer_tread tread;
+ memset(&tread, 0, sizeof(tread));
tread.event = SNDRV_TIMER_EVENT_EARLY;
tread.tstamp.tv_sec = 0;
tread.tstamp.tv_nsec = 0;
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index c0f8f613f1f1..172dacd925f5 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -420,6 +420,7 @@ static int dummy_hrtimer_stop(struct snd_pcm_substream *substream)
static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm)
{
+ hrtimer_cancel(&dpcm->timer);
tasklet_kill(&dpcm->tasklet);
}
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 2a779c2f63ab..ab894ed1ff67 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -134,6 +134,7 @@ config SND_FIREWIRE_TASCAM
Say Y here to include support for TASCAM.
* FW-1884
* FW-1082
+ * FW-1804
To compile this driver as a module, choose M here: the module
will be called snd-firewire-tascam.
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index 003c09029786..0ee1fb115d88 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -1,3 +1,6 @@
+# To find a header included by define_trace.h.
+CFLAGS_amdtp-stream.o := -I$(src)
+
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
fcp.o cmp.o amdtp-stream.o amdtp-am824.o
snd-isight-objs := isight.o
diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h
new file mode 100644
index 000000000000..9c04faf206b2
--- /dev/null
+++ b/sound/firewire/amdtp-stream-trace.h
@@ -0,0 +1,110 @@
+/*
+ * amdtp-stream-trace.h - tracepoint definitions to dump a part of packet data
+ *
+ * Copyright (c) 2016 Takashi Sakamoto
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM snd_firewire_lib
+
+#if !defined(_AMDTP_STREAM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _AMDTP_STREAM_TRACE_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(in_packet,
+ TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 cip_header[2], unsigned int payload_quadlets, unsigned int index),
+ TP_ARGS(s, cycles, cip_header, payload_quadlets, index),
+ TP_STRUCT__entry(
+ __field(unsigned int, second)
+ __field(unsigned int, cycle)
+ __field(int, channel)
+ __field(int, src)
+ __field(int, dest)
+ __field(u32, cip_header0)
+ __field(u32, cip_header1)
+ __field(unsigned int, payload_quadlets)
+ __field(unsigned int, packet_index)
+ __field(unsigned int, irq)
+ __field(unsigned int, index)
+ ),
+ TP_fast_assign(
+ __entry->second = cycles / CYCLES_PER_SECOND;
+ __entry->cycle = cycles % CYCLES_PER_SECOND;
+ __entry->channel = s->context->channel;
+ __entry->src = fw_parent_device(s->unit)->node_id;
+ __entry->dest = fw_parent_device(s->unit)->card->node_id;
+ __entry->cip_header0 = cip_header[0];
+ __entry->cip_header1 = cip_header[1];
+ __entry->payload_quadlets = payload_quadlets;
+ __entry->packet_index = s->packet_index;
+ __entry->irq = !!in_interrupt();
+ __entry->index = index;
+ ),
+ TP_printk(
+ "%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
+ __entry->second,
+ __entry->cycle,
+ __entry->src,
+ __entry->dest,
+ __entry->channel,
+ __entry->cip_header0,
+ __entry->cip_header1,
+ __entry->payload_quadlets,
+ __entry->packet_index,
+ __entry->irq,
+ __entry->index)
+);
+
+TRACE_EVENT(out_packet,
+ TP_PROTO(const struct amdtp_stream *s, u32 cycles, __be32 *cip_header, unsigned int payload_length, unsigned int index),
+ TP_ARGS(s, cycles, cip_header, payload_length, index),
+ TP_STRUCT__entry(
+ __field(unsigned int, second)
+ __field(unsigned int, cycle)
+ __field(int, channel)
+ __field(int, src)
+ __field(int, dest)
+ __field(u32, cip_header0)
+ __field(u32, cip_header1)
+ __field(unsigned int, payload_quadlets)
+ __field(unsigned int, packet_index)
+ __field(unsigned int, irq)
+ __field(unsigned int, index)
+ ),
+ TP_fast_assign(
+ __entry->second = cycles / CYCLES_PER_SECOND;
+ __entry->cycle = cycles % CYCLES_PER_SECOND;
+ __entry->channel = s->context->channel;
+ __entry->src = fw_parent_device(s->unit)->card->node_id;
+ __entry->dest = fw_parent_device(s->unit)->node_id;
+ __entry->cip_header0 = be32_to_cpu(cip_header[0]);
+ __entry->cip_header1 = be32_to_cpu(cip_header[1]);
+ __entry->payload_quadlets = payload_length / 4;
+ __entry->packet_index = s->packet_index;
+ __entry->irq = !!in_interrupt();
+ __entry->index = index;
+ ),
+ TP_printk(
+ "%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
+ __entry->second,
+ __entry->cycle,
+ __entry->src,
+ __entry->dest,
+ __entry->channel,
+ __entry->cip_header0,
+ __entry->cip_header1,
+ __entry->payload_quadlets,
+ __entry->packet_index,
+ __entry->irq,
+ __entry->index)
+);
+
+#endif
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE amdtp-stream-trace
+#include <trace/define_trace.h>
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index ed2902609a4c..00060c4a9deb 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -19,6 +19,10 @@
#define CYCLES_PER_SECOND 8000
#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
+/* Always support Linux tracing subsystem. */
+#define CREATE_TRACE_POINTS
+#include "amdtp-stream-trace.h"
+
#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */
/* isochronous header parameters */
@@ -87,7 +91,6 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
init_waitqueue_head(&s->callback_wait);
s->callbacked = false;
- s->sync_slave = NULL;
s->fmt = fmt;
s->process_data_blocks = process_data_blocks;
@@ -102,6 +105,10 @@ EXPORT_SYMBOL(amdtp_stream_init);
*/
void amdtp_stream_destroy(struct amdtp_stream *s)
{
+ /* Not initialized. */
+ if (s->protocol == NULL)
+ return;
+
WARN_ON(amdtp_stream_running(s));
kfree(s->protocol);
mutex_destroy(&s->mutex);
@@ -244,7 +251,6 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
tasklet_kill(&s->period_tasklet);
s->pcm_buffer_pointer = 0;
s->pcm_period_pointer = 0;
- s->pointer_flush = true;
}
EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
@@ -349,7 +355,6 @@ static void update_pcm_pointers(struct amdtp_stream *s,
s->pcm_period_pointer += frames;
if (s->pcm_period_pointer >= pcm->runtime->period_size) {
s->pcm_period_pointer -= pcm->runtime->period_size;
- s->pointer_flush = false;
tasklet_hi_schedule(&s->period_tasklet);
}
}
@@ -363,9 +368,8 @@ static void pcm_period_tasklet(unsigned long data)
snd_pcm_period_elapsed(pcm);
}
-static int queue_packet(struct amdtp_stream *s,
- unsigned int header_length,
- unsigned int payload_length, bool skip)
+static int queue_packet(struct amdtp_stream *s, unsigned int header_length,
+ unsigned int payload_length)
{
struct fw_iso_packet p = {0};
int err = 0;
@@ -376,8 +380,10 @@ static int queue_packet(struct amdtp_stream *s,
p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
p.tag = TAG_CIP;
p.header_length = header_length;
- p.payload_length = (!skip) ? payload_length : 0;
- p.skip = skip;
+ if (payload_length > 0)
+ p.payload_length = payload_length;
+ else
+ p.skip = true;
err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
s->buffer.packets[s->packet_index].offset);
if (err < 0) {
@@ -392,27 +398,30 @@ end:
}
static inline int queue_out_packet(struct amdtp_stream *s,
- unsigned int payload_length, bool skip)
+ unsigned int payload_length)
{
- return queue_packet(s, OUT_PACKET_HEADER_SIZE,
- payload_length, skip);
+ return queue_packet(s, OUT_PACKET_HEADER_SIZE, payload_length);
}
static inline int queue_in_packet(struct amdtp_stream *s)
{
return queue_packet(s, IN_PACKET_HEADER_SIZE,
- amdtp_stream_get_max_payload(s), false);
+ amdtp_stream_get_max_payload(s));
}
-static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
- unsigned int syt)
+static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
+ unsigned int index)
{
__be32 *buffer;
+ unsigned int syt;
+ unsigned int data_blocks;
unsigned int payload_length;
unsigned int pcm_frames;
struct snd_pcm_substream *pcm;
buffer = s->buffer.packets[s->packet_index].buffer;
+ syt = calculate_syt(s, cycle);
+ data_blocks = calculate_data_blocks(s, syt);
pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
@@ -424,9 +433,11 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
(syt & CIP_SYT_MASK));
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
-
payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
- if (queue_out_packet(s, payload_length, false) < 0)
+
+ trace_out_packet(s, cycle, buffer, payload_length, index);
+
+ if (queue_out_packet(s, payload_length) < 0)
return -EIO;
pcm = ACCESS_ONCE(s->pcm);
@@ -438,19 +449,24 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
}
static int handle_in_packet(struct amdtp_stream *s,
- unsigned int payload_quadlets, __be32 *buffer,
- unsigned int *data_blocks, unsigned int syt)
+ unsigned int payload_quadlets, unsigned int cycle,
+ unsigned int index)
{
+ __be32 *buffer;
u32 cip_header[2];
- unsigned int fmt, fdf;
+ unsigned int fmt, fdf, syt;
unsigned int data_block_quadlets, data_block_counter, dbc_interval;
+ unsigned int data_blocks;
struct snd_pcm_substream *pcm;
unsigned int pcm_frames;
bool lost;
+ buffer = s->buffer.packets[s->packet_index].buffer;
cip_header[0] = be32_to_cpu(buffer[0]);
cip_header[1] = be32_to_cpu(buffer[1]);
+ trace_in_packet(s, cycle, cip_header, payload_quadlets, index);
+
/*
* This module supports 'Two-quadlet CIP header with SYT field'.
* For convenience, also check FMT field is AM824 or not.
@@ -460,7 +476,7 @@ static int handle_in_packet(struct amdtp_stream *s,
dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
- *data_blocks = 0;
+ data_blocks = 0;
pcm_frames = 0;
goto end;
}
@@ -471,7 +487,7 @@ static int handle_in_packet(struct amdtp_stream *s,
dev_info_ratelimited(&s->unit->device,
"Detect unexpected protocol: %08x %08x\n",
cip_header[0], cip_header[1]);
- *data_blocks = 0;
+ data_blocks = 0;
pcm_frames = 0;
goto end;
}
@@ -480,7 +496,7 @@ static int handle_in_packet(struct amdtp_stream *s,
fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
if (payload_quadlets < 3 ||
(fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
- *data_blocks = 0;
+ data_blocks = 0;
} else {
data_block_quadlets =
(cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
@@ -494,12 +510,12 @@ static int handle_in_packet(struct amdtp_stream *s,
if (s->flags & CIP_WRONG_DBS)
data_block_quadlets = s->data_block_quadlets;
- *data_blocks = (payload_quadlets - 2) / data_block_quadlets;
+ data_blocks = (payload_quadlets - 2) / data_block_quadlets;
}
/* Check data block counter continuity */
data_block_counter = cip_header[0] & CIP_DBC_MASK;
- if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
+ if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
s->data_block_counter != UINT_MAX)
data_block_counter = s->data_block_counter;
@@ -510,10 +526,10 @@ static int handle_in_packet(struct amdtp_stream *s,
} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
lost = data_block_counter != s->data_block_counter;
} else {
- if ((*data_blocks > 0) && (s->tx_dbc_interval > 0))
+ if (data_blocks > 0 && s->tx_dbc_interval > 0)
dbc_interval = s->tx_dbc_interval;
else
- dbc_interval = *data_blocks;
+ dbc_interval = data_blocks;
lost = data_block_counter !=
((s->data_block_counter + dbc_interval) & 0xff);
@@ -526,13 +542,14 @@ static int handle_in_packet(struct amdtp_stream *s,
return -EIO;
}
- pcm_frames = s->process_data_blocks(s, buffer + 2, *data_blocks, &syt);
+ syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
+ pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
if (s->flags & CIP_DBC_IS_END_EVENT)
s->data_block_counter = data_block_counter;
else
s->data_block_counter =
- (data_block_counter + *data_blocks) & 0xff;
+ (data_block_counter + data_blocks) & 0xff;
end:
if (queue_in_packet(s) < 0)
return -EIO;
@@ -544,29 +561,50 @@ end:
return 0;
}
-static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
+/*
+ * In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
+ * the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
+ * it. Thus, via Linux firewire subsystem, we can get the 3 bits for second.
+ */
+static inline u32 compute_cycle_count(u32 tstamp)
+{
+ return (((tstamp >> 13) & 0x07) * 8000) + (tstamp & 0x1fff);
+}
+
+static inline u32 increment_cycle_count(u32 cycle, unsigned int addend)
+{
+ cycle += addend;
+ if (cycle >= 8 * CYCLES_PER_SECOND)
+ cycle -= 8 * CYCLES_PER_SECOND;
+ return cycle;
+}
+
+static inline u32 decrement_cycle_count(u32 cycle, unsigned int subtrahend)
+{
+ if (cycle < subtrahend)
+ cycle += 8 * CYCLES_PER_SECOND;
+ return cycle - subtrahend;
+}
+
+static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
size_t header_length, void *header,
void *private_data)
{
struct amdtp_stream *s = private_data;
- unsigned int i, syt, packets = header_length / 4;
- unsigned int data_blocks;
+ unsigned int i, packets = header_length / 4;
+ u32 cycle;
if (s->packet_index < 0)
return;
- /*
- * Compute the cycle of the last queued packet.
- * (We need only the four lowest bits for the SYT, so we can ignore
- * that bits 0-11 must wrap around at 3072.)
- */
- cycle += QUEUE_LENGTH - packets;
+ cycle = compute_cycle_count(tstamp);
- for (i = 0; i < packets; ++i) {
- syt = calculate_syt(s, ++cycle);
- data_blocks = calculate_data_blocks(s, syt);
+ /* Align to actual cycle count for the last packet. */
+ cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
- if (handle_out_packet(s, data_blocks, syt) < 0) {
+ for (i = 0; i < packets; ++i) {
+ cycle = increment_cycle_count(cycle, 1);
+ if (handle_out_packet(s, cycle, i) < 0) {
s->packet_index = -1;
amdtp_stream_pcm_abort(s);
return;
@@ -576,15 +614,15 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
fw_iso_context_queue_flush(s->context);
}
-static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
+static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
size_t header_length, void *header,
void *private_data)
{
struct amdtp_stream *s = private_data;
- unsigned int p, syt, packets;
+ unsigned int i, packets;
unsigned int payload_quadlets, max_payload_quadlets;
- unsigned int data_blocks;
- __be32 *buffer, *headers = header;
+ __be32 *headers = header;
+ u32 cycle;
if (s->packet_index < 0)
return;
@@ -592,70 +630,44 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
/* The number of packets in buffer */
packets = header_length / IN_PACKET_HEADER_SIZE;
+ cycle = compute_cycle_count(tstamp);
+
+ /* Align to actual cycle count for the last packet. */
+ cycle = decrement_cycle_count(cycle, packets);
+
/* For buffer-over-run prevention. */
max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
- for (p = 0; p < packets; p++) {
- buffer = s->buffer.packets[s->packet_index].buffer;
+ for (i = 0; i < packets; i++) {
+ cycle = increment_cycle_count(cycle, 1);
/* The number of quadlets in this packet */
payload_quadlets =
- (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
+ (be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT) / 4;
if (payload_quadlets > max_payload_quadlets) {
dev_err(&s->unit->device,
"Detect jumbo payload: %02x %02x\n",
payload_quadlets, max_payload_quadlets);
- s->packet_index = -1;
break;
}
- syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
- if (handle_in_packet(s, payload_quadlets, buffer,
- &data_blocks, syt) < 0) {
- s->packet_index = -1;
+ if (handle_in_packet(s, payload_quadlets, cycle, i) < 0)
break;
- }
-
- /* Process sync slave stream */
- if (s->sync_slave && s->sync_slave->callbacked) {
- if (handle_out_packet(s->sync_slave,
- data_blocks, syt) < 0) {
- s->packet_index = -1;
- break;
- }
- }
}
- /* Queueing error or detecting discontinuity */
- if (s->packet_index < 0) {
+ /* Queueing error or detecting invalid payload. */
+ if (i < packets) {
+ s->packet_index = -1;
amdtp_stream_pcm_abort(s);
-
- /* Abort sync slave. */
- if (s->sync_slave) {
- s->sync_slave->packet_index = -1;
- amdtp_stream_pcm_abort(s->sync_slave);
- }
return;
}
- /* when sync to device, flush the packets for slave stream */
- if (s->sync_slave && s->sync_slave->callbacked)
- fw_iso_context_queue_flush(s->sync_slave->context);
-
fw_iso_context_queue_flush(s->context);
}
-/* processing is done by master callback */
-static void slave_stream_callback(struct fw_iso_context *context, u32 cycle,
- size_t header_length, void *header,
- void *private_data)
-{
- return;
-}
-
/* this is executed one time */
static void amdtp_stream_first_callback(struct fw_iso_context *context,
- u32 cycle, size_t header_length,
+ u32 tstamp, size_t header_length,
void *header, void *private_data)
{
struct amdtp_stream *s = private_data;
@@ -669,12 +681,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
if (s->direction == AMDTP_IN_STREAM)
context->callback.sc = in_stream_callback;
- else if (s->flags & CIP_SYNC_TO_DEVICE)
- context->callback.sc = slave_stream_callback;
else
context->callback.sc = out_stream_callback;
- context->callback.sc(context, cycle, header_length, header, s);
+ context->callback.sc(context, tstamp, header_length, header, s);
}
/**
@@ -713,8 +723,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
goto err_unlock;
}
- if (s->direction == AMDTP_IN_STREAM &&
- s->flags & CIP_SKIP_INIT_DBC_CHECK)
+ if (s->direction == AMDTP_IN_STREAM)
s->data_block_counter = UINT_MAX;
else
s->data_block_counter = 0;
@@ -755,7 +764,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
if (s->direction == AMDTP_IN_STREAM)
err = queue_in_packet(s);
else
- err = queue_out_packet(s, 0, true);
+ err = queue_out_packet(s, 0);
if (err < 0)
goto err_context;
} while (s->packet_index > 0);
@@ -794,11 +803,24 @@ EXPORT_SYMBOL(amdtp_stream_start);
*/
unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
{
- /* this optimization is allowed to be racy */
- if (s->pointer_flush && amdtp_stream_running(s))
+ /*
+ * This function is called in software IRQ context of period_tasklet or
+ * process context.
+ *
+ * When the software IRQ context was scheduled by software IRQ context
+ * of IR/IT contexts, queued packets were already handled. Therefore,
+ * no need to flush the queue in buffer anymore.
+ *
+ * When the process context reach here, some packets will be already
+ * queued in the buffer. These packets should be handled immediately
+ * to keep better granularity of PCM pointer.
+ *
+ * Later, the process context will sometimes schedules software IRQ
+ * context of the period_tasklet. Then, no need to flush the queue by
+ * the same reason as described for IR/IT contexts.
+ */
+ if (!in_interrupt() && amdtp_stream_running(s))
fw_iso_context_flush_completions(s->context);
- else
- s->pointer_flush = true;
return ACCESS_ONCE(s->pcm_buffer_pointer);
}
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
index 8775704a3665..c1bc7fad056e 100644
--- a/sound/firewire/amdtp-stream.h
+++ b/sound/firewire/amdtp-stream.h
@@ -17,8 +17,6 @@
* @CIP_BLOCKING: In blocking mode, each packet contains either zero or
* SYT_INTERVAL samples, with these two types alternating so that
* the overall sample rate comes out right.
- * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
- * generated by in packets. Defaultly this driver generates timestamp.
* @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
* @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
* corresponds to the end of event in the packet. Out of IEC 61883.
@@ -26,8 +24,6 @@
* The value of data_block_quadlets is used instead of reported value.
* @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is
* skipped for detecting discontinuity.
- * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first
- * packet is not continuous from an initial value.
* @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
* packet is wrong but the others are correct.
* @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
@@ -37,14 +33,12 @@
enum cip_flags {
CIP_NONBLOCKING = 0x00,
CIP_BLOCKING = 0x01,
- CIP_SYNC_TO_DEVICE = 0x02,
- CIP_EMPTY_WITH_TAG0 = 0x04,
- CIP_DBC_IS_END_EVENT = 0x08,
- CIP_WRONG_DBS = 0x10,
- CIP_SKIP_DBC_ZERO_CHECK = 0x20,
- CIP_SKIP_INIT_DBC_CHECK = 0x40,
- CIP_EMPTY_HAS_WRONG_DBC = 0x80,
- CIP_JUMBO_PAYLOAD = 0x100,
+ CIP_EMPTY_WITH_TAG0 = 0x02,
+ CIP_DBC_IS_END_EVENT = 0x04,
+ CIP_WRONG_DBS = 0x08,
+ CIP_SKIP_DBC_ZERO_CHECK = 0x10,
+ CIP_EMPTY_HAS_WRONG_DBC = 0x20,
+ CIP_JUMBO_PAYLOAD = 0x40,
};
/**
@@ -132,12 +126,10 @@ struct amdtp_stream {
struct tasklet_struct period_tasklet;
unsigned int pcm_buffer_pointer;
unsigned int pcm_period_pointer;
- bool pointer_flush;
/* To wait for first packet. */
bool callbacked;
wait_queue_head_t callback_wait;
- struct amdtp_stream *sync_slave;
/* For backends to process data blocks. */
void *protocol;
@@ -223,23 +215,6 @@ static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
return sfc & 1;
}
-static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
- struct amdtp_stream *master,
- struct amdtp_stream *slave)
-{
- if (sync_mode == CIP_SYNC_TO_DEVICE) {
- master->flags |= CIP_SYNC_TO_DEVICE;
- slave->flags |= CIP_SYNC_TO_DEVICE;
- master->sync_slave = slave;
- } else {
- master->flags &= ~CIP_SYNC_TO_DEVICE;
- slave->flags &= ~CIP_SYNC_TO_DEVICE;
- master->sync_slave = NULL;
- }
-
- slave->sync_slave = NULL;
-}
-
/**
* amdtp_stream_wait_callback - sleep till callbacked or timeout
* @s: the AMDTP stream
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 3e4e0756e3fe..f7e2cbd2a313 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -67,7 +67,7 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
#define MODEL_MAUDIO_PROJECTMIX 0x00010091
static int
-name_device(struct snd_bebob *bebob, unsigned int vendor_id)
+name_device(struct snd_bebob *bebob)
{
struct fw_device *fw_dev = fw_parent_device(bebob->unit);
char vendor[24] = {0};
@@ -126,6 +126,17 @@ end:
return err;
}
+static void bebob_free(struct snd_bebob *bebob)
+{
+ snd_bebob_stream_destroy_duplex(bebob);
+ fw_unit_put(bebob->unit);
+
+ kfree(bebob->maudio_special_quirk);
+
+ mutex_destroy(&bebob->mutex);
+ kfree(bebob);
+}
+
/*
* This module releases the FireWire unit data after all ALSA character devices
* are released by applications. This is for releasing stream data or finishing
@@ -137,18 +148,11 @@ bebob_card_free(struct snd_card *card)
{
struct snd_bebob *bebob = card->private_data;
- snd_bebob_stream_destroy_duplex(bebob);
- fw_unit_put(bebob->unit);
-
- kfree(bebob->maudio_special_quirk);
-
- if (bebob->card_index >= 0) {
- mutex_lock(&devices_mutex);
- clear_bit(bebob->card_index, devices_used);
- mutex_unlock(&devices_mutex);
- }
+ mutex_lock(&devices_mutex);
+ clear_bit(bebob->card_index, devices_used);
+ mutex_unlock(&devices_mutex);
- mutex_destroy(&bebob->mutex);
+ bebob_free(card->private_data);
}
static const struct snd_bebob_spec *
@@ -176,16 +180,17 @@ check_audiophile_booted(struct fw_unit *unit)
return strncmp(name, "FW Audiophile Bootloader", 15) != 0;
}
-static int
-bebob_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
+static void
+do_registration(struct work_struct *work)
{
- struct snd_card *card;
- struct snd_bebob *bebob;
- const struct snd_bebob_spec *spec;
+ struct snd_bebob *bebob =
+ container_of(work, struct snd_bebob, dwork.work);
unsigned int card_index;
int err;
+ if (bebob->registered)
+ return;
+
mutex_lock(&devices_mutex);
for (card_index = 0; card_index < SNDRV_CARDS; card_index++) {
@@ -193,64 +198,39 @@ bebob_probe(struct fw_unit *unit,
break;
}
if (card_index >= SNDRV_CARDS) {
- err = -ENOENT;
- goto end;
+ mutex_unlock(&devices_mutex);
+ return;
}
- if ((entry->vendor_id == VEN_FOCUSRITE) &&
- (entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH))
- spec = get_saffire_spec(unit);
- else if ((entry->vendor_id == VEN_MAUDIO1) &&
- (entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH) &&
- !check_audiophile_booted(unit))
- spec = NULL;
- else
- spec = (const struct snd_bebob_spec *)entry->driver_data;
-
- if (spec == NULL) {
- if ((entry->vendor_id == VEN_MAUDIO1) ||
- (entry->vendor_id == VEN_MAUDIO2))
- err = snd_bebob_maudio_load_firmware(unit);
- else
- err = -ENOSYS;
- goto end;
+ err = snd_card_new(&bebob->unit->device, index[card_index],
+ id[card_index], THIS_MODULE, 0, &bebob->card);
+ if (err < 0) {
+ mutex_unlock(&devices_mutex);
+ return;
}
- err = snd_card_new(&unit->device, index[card_index], id[card_index],
- THIS_MODULE, sizeof(struct snd_bebob), &card);
+ err = name_device(bebob);
if (err < 0)
- goto end;
- bebob = card->private_data;
- bebob->card_index = card_index;
- set_bit(card_index, devices_used);
- card->private_free = bebob_card_free;
-
- bebob->card = card;
- bebob->unit = fw_unit_get(unit);
- bebob->spec = spec;
- mutex_init(&bebob->mutex);
- spin_lock_init(&bebob->lock);
- init_waitqueue_head(&bebob->hwdep_wait);
+ goto error;
- err = name_device(bebob, entry->vendor_id);
+ if (bebob->spec == &maudio_special_spec) {
+ if (bebob->entry->model_id == MODEL_MAUDIO_FW1814)
+ err = snd_bebob_maudio_special_discover(bebob, true);
+ else
+ err = snd_bebob_maudio_special_discover(bebob, false);
+ } else {
+ err = snd_bebob_stream_discover(bebob);
+ }
if (err < 0)
goto error;
- if ((entry->vendor_id == VEN_MAUDIO1) &&
- (entry->model_id == MODEL_MAUDIO_FW1814))
- err = snd_bebob_maudio_special_discover(bebob, true);
- else if ((entry->vendor_id == VEN_MAUDIO1) &&
- (entry->model_id == MODEL_MAUDIO_PROJECTMIX))
- err = snd_bebob_maudio_special_discover(bebob, false);
- else
- err = snd_bebob_stream_discover(bebob);
+ err = snd_bebob_stream_init_duplex(bebob);
if (err < 0)
goto error;
snd_bebob_proc_init(bebob);
- if ((bebob->midi_input_ports > 0) ||
- (bebob->midi_output_ports > 0)) {
+ if (bebob->midi_input_ports > 0 || bebob->midi_output_ports > 0) {
err = snd_bebob_create_midi_devices(bebob);
if (err < 0)
goto error;
@@ -264,16 +244,75 @@ bebob_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- err = snd_bebob_stream_init_duplex(bebob);
+ err = snd_card_register(bebob->card);
if (err < 0)
goto error;
- if (!bebob->maudio_special_quirk) {
- err = snd_card_register(card);
- if (err < 0) {
- snd_bebob_stream_destroy_duplex(bebob);
- goto error;
- }
+ set_bit(card_index, devices_used);
+ mutex_unlock(&devices_mutex);
+
+ /*
+ * After registered, bebob instance can be released corresponding to
+ * releasing the sound card instance.
+ */
+ bebob->card->private_free = bebob_card_free;
+ bebob->card->private_data = bebob;
+ bebob->registered = true;
+
+ return;
+error:
+ mutex_unlock(&devices_mutex);
+ snd_bebob_stream_destroy_duplex(bebob);
+ snd_card_free(bebob->card);
+ dev_info(&bebob->unit->device,
+ "Sound card registration failed: %d\n", err);
+}
+
+static int
+bebob_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ struct snd_bebob *bebob;
+ const struct snd_bebob_spec *spec;
+
+ if (entry->vendor_id == VEN_FOCUSRITE &&
+ entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)
+ spec = get_saffire_spec(unit);
+ else if (entry->vendor_id == VEN_MAUDIO1 &&
+ entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH &&
+ !check_audiophile_booted(unit))
+ spec = NULL;
+ else
+ spec = (const struct snd_bebob_spec *)entry->driver_data;
+
+ if (spec == NULL) {
+ if (entry->vendor_id == VEN_MAUDIO1 ||
+ entry->vendor_id == VEN_MAUDIO2)
+ return snd_bebob_maudio_load_firmware(unit);
+ else
+ return -ENODEV;
+ }
+
+ /* Allocate this independent of sound card instance. */
+ bebob = kzalloc(sizeof(struct snd_bebob), GFP_KERNEL);
+ if (bebob == NULL)
+ return -ENOMEM;
+
+ bebob->unit = fw_unit_get(unit);
+ bebob->entry = entry;
+ bebob->spec = spec;
+ dev_set_drvdata(&unit->device, bebob);
+
+ mutex_init(&bebob->mutex);
+ spin_lock_init(&bebob->lock);
+ init_waitqueue_head(&bebob->hwdep_wait);
+
+ /* Allocate and register this sound card later. */
+ INIT_DEFERRABLE_WORK(&bebob->dwork, do_registration);
+
+ if (entry->vendor_id != VEN_MAUDIO1 ||
+ (entry->model_id != MODEL_MAUDIO_FW1814 &&
+ entry->model_id != MODEL_MAUDIO_PROJECTMIX)) {
+ snd_fw_schedule_registration(unit, &bebob->dwork);
} else {
/*
* This is a workaround. This bus reset seems to have an effect
@@ -285,19 +324,11 @@ bebob_probe(struct fw_unit *unit,
* signals from dbus and starts I/Os. To avoid I/Os till the
* future bus reset, registration is done in next update().
*/
- bebob->deferred_registration = true;
fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card,
false, true);
}
- dev_set_drvdata(&unit->device, bebob);
-end:
- mutex_unlock(&devices_mutex);
- return err;
-error:
- mutex_unlock(&devices_mutex);
- snd_card_free(card);
- return err;
+ return 0;
}
/*
@@ -324,15 +355,11 @@ bebob_update(struct fw_unit *unit)
if (bebob == NULL)
return;
- fcp_bus_reset(bebob->unit);
-
- if (bebob->deferred_registration) {
- if (snd_card_register(bebob->card) < 0) {
- snd_bebob_stream_destroy_duplex(bebob);
- snd_card_free(bebob->card);
- }
- bebob->deferred_registration = false;
- }
+ /* Postpone a workqueue for deferred registration. */
+ if (!bebob->registered)
+ snd_fw_schedule_registration(unit, &bebob->dwork);
+ else
+ fcp_bus_reset(bebob->unit);
}
static void bebob_remove(struct fw_unit *unit)
@@ -342,8 +369,20 @@ static void bebob_remove(struct fw_unit *unit)
if (bebob == NULL)
return;
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(bebob->card);
+ /*
+ * Confirm to stop the work for registration before the sound card is
+ * going to be released. The work is not scheduled again because bus
+ * reset handler is not called anymore.
+ */
+ cancel_delayed_work_sync(&bebob->dwork);
+
+ if (bebob->registered) {
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(bebob->card);
+ } else {
+ /* Don't forget this case. */
+ bebob_free(bebob);
+ }
}
static const struct snd_bebob_rate_spec normal_rate_spec = {
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index b50bb33d9d46..e7f1bb925b12 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -83,6 +83,10 @@ struct snd_bebob {
struct mutex mutex;
spinlock_t lock;
+ bool registered;
+ struct delayed_work dwork;
+
+ const struct ieee1394_device_id *entry;
const struct snd_bebob_spec *spec;
unsigned int midi_input_ports;
@@ -90,7 +94,6 @@ struct snd_bebob {
bool connected;
- struct amdtp_stream *master;
struct amdtp_stream tx_stream;
struct amdtp_stream rx_stream;
struct cmp_connection out_conn;
@@ -111,7 +114,6 @@ struct snd_bebob {
/* for M-Audio special devices */
void *maudio_special_quirk;
- bool deferred_registration;
/* For BeBoB version quirk. */
unsigned int version;
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 77cbb02bff34..4d3034a68bdf 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -484,30 +484,6 @@ destroy_both_connections(struct snd_bebob *bebob)
}
static int
-get_sync_mode(struct snd_bebob *bebob, enum cip_flags *sync_mode)
-{
- enum snd_bebob_clock_type src;
- int err;
-
- err = snd_bebob_stream_get_clock_src(bebob, &src);
- if (err < 0)
- return err;
-
- switch (src) {
- case SND_BEBOB_CLOCK_TYPE_INTERNAL:
- case SND_BEBOB_CLOCK_TYPE_EXTERNAL:
- *sync_mode = CIP_SYNC_TO_DEVICE;
- break;
- default:
- case SND_BEBOB_CLOCK_TYPE_SYT:
- *sync_mode = 0;
- break;
- }
-
- return 0;
-}
-
-static int
start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
unsigned int rate)
{
@@ -550,8 +526,6 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
goto end;
}
- bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
-
/*
* BeBoB v3 transfers packets with these qurks:
* - In the beginning of streaming, the value of dbc is incremented
@@ -584,8 +558,6 @@ end:
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
{
const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
- struct amdtp_stream *master, *slave;
- enum cip_flags sync_mode;
unsigned int curr_rate;
int err = 0;
@@ -593,22 +565,11 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
if (bebob->substreams_counter == 0)
goto end;
- err = get_sync_mode(bebob, &sync_mode);
- if (err < 0)
- goto end;
- if (sync_mode == CIP_SYNC_TO_DEVICE) {
- master = &bebob->tx_stream;
- slave = &bebob->rx_stream;
- } else {
- master = &bebob->rx_stream;
- slave = &bebob->tx_stream;
- }
-
/*
* Considering JACK/FFADO streaming:
* TODO: This can be removed hwdep functionality becomes popular.
*/
- err = check_connection_used_by_others(bebob, master);
+ err = check_connection_used_by_others(bebob, &bebob->rx_stream);
if (err < 0)
goto end;
@@ -618,11 +579,12 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
* At bus reset, connections should not be broken here. So streams need
* to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag.
*/
- if (amdtp_streaming_error(master))
- amdtp_stream_stop(master);
- if (amdtp_streaming_error(slave))
- amdtp_stream_stop(slave);
- if (!amdtp_stream_running(master) && !amdtp_stream_running(slave))
+ if (amdtp_streaming_error(&bebob->rx_stream))
+ amdtp_stream_stop(&bebob->rx_stream);
+ if (amdtp_streaming_error(&bebob->tx_stream))
+ amdtp_stream_stop(&bebob->tx_stream);
+ if (!amdtp_stream_running(&bebob->rx_stream) &&
+ !amdtp_stream_running(&bebob->tx_stream))
break_both_connections(bebob);
/* stop streams if rate is different */
@@ -635,16 +597,13 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
if (rate == 0)
rate = curr_rate;
if (rate != curr_rate) {
- amdtp_stream_stop(master);
- amdtp_stream_stop(slave);
+ amdtp_stream_stop(&bebob->rx_stream);
+ amdtp_stream_stop(&bebob->tx_stream);
break_both_connections(bebob);
}
/* master should be always running */
- if (!amdtp_stream_running(master)) {
- amdtp_stream_set_sync(sync_mode, master, slave);
- bebob->master = master;
-
+ if (!amdtp_stream_running(&bebob->rx_stream)) {
/*
* NOTE:
* If establishing connections at first, Yamaha GO46
@@ -666,7 +625,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
if (err < 0)
goto end;
- err = start_stream(bebob, master, rate);
+ err = start_stream(bebob, &bebob->rx_stream, rate);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to run AMDTP master stream:%d\n", err);
@@ -685,15 +644,16 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
dev_err(&bebob->unit->device,
"fail to ensure sampling rate: %d\n",
err);
- amdtp_stream_stop(master);
+ amdtp_stream_stop(&bebob->rx_stream);
break_both_connections(bebob);
goto end;
}
}
/* wait first callback */
- if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT)) {
- amdtp_stream_stop(master);
+ if (!amdtp_stream_wait_callback(&bebob->rx_stream,
+ CALLBACK_TIMEOUT)) {
+ amdtp_stream_stop(&bebob->rx_stream);
break_both_connections(bebob);
err = -ETIMEDOUT;
goto end;
@@ -701,20 +661,21 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
}
/* start slave if needed */
- if (!amdtp_stream_running(slave)) {
- err = start_stream(bebob, slave, rate);
+ if (!amdtp_stream_running(&bebob->tx_stream)) {
+ err = start_stream(bebob, &bebob->tx_stream, rate);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to run AMDTP slave stream:%d\n", err);
- amdtp_stream_stop(master);
+ amdtp_stream_stop(&bebob->rx_stream);
break_both_connections(bebob);
goto end;
}
/* wait first callback */
- if (!amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) {
- amdtp_stream_stop(slave);
- amdtp_stream_stop(master);
+ if (!amdtp_stream_wait_callback(&bebob->tx_stream,
+ CALLBACK_TIMEOUT)) {
+ amdtp_stream_stop(&bebob->tx_stream);
+ amdtp_stream_stop(&bebob->rx_stream);
break_both_connections(bebob);
err = -ETIMEDOUT;
}
@@ -725,22 +686,12 @@ end:
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
{
- struct amdtp_stream *master, *slave;
-
- if (bebob->master == &bebob->rx_stream) {
- slave = &bebob->tx_stream;
- master = &bebob->rx_stream;
- } else {
- slave = &bebob->rx_stream;
- master = &bebob->tx_stream;
- }
-
if (bebob->substreams_counter == 0) {
- amdtp_stream_pcm_abort(master);
- amdtp_stream_stop(master);
+ amdtp_stream_pcm_abort(&bebob->rx_stream);
+ amdtp_stream_stop(&bebob->rx_stream);
- amdtp_stream_pcm_abort(slave);
- amdtp_stream_stop(slave);
+ amdtp_stream_pcm_abort(&bebob->tx_stream);
+ amdtp_stream_stop(&bebob->tx_stream);
break_both_connections(bebob);
}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 8b64aef31a86..25e9f77275c4 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -20,8 +20,6 @@ MODULE_LICENSE("GPL v2");
#define WEISS_CATEGORY_ID 0x00
#define LOUD_CATEGORY_ID 0x10
-#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
-
/*
* Some models support several isochronous channels, while these streams are not
* always available. In this case, add the model name to this list.
@@ -201,6 +199,10 @@ static void do_registration(struct work_struct *work)
dice_card_strings(dice);
+ err = snd_dice_stream_init_duplex(dice);
+ if (err < 0)
+ goto error;
+
snd_dice_create_proc(dice);
err = snd_dice_create_pcm(dice);
@@ -229,28 +231,14 @@ static void do_registration(struct work_struct *work)
return;
error:
+ snd_dice_stream_destroy_duplex(dice);
snd_dice_transaction_destroy(dice);
+ snd_dice_stream_destroy_duplex(dice);
snd_card_free(dice->card);
dev_info(&dice->unit->device,
"Sound card registration failed: %d\n", err);
}
-static void schedule_registration(struct snd_dice *dice)
-{
- struct fw_card *fw_card = fw_parent_device(dice->unit)->card;
- u64 now, delay;
-
- now = get_jiffies_64();
- delay = fw_card->reset_jiffies + msecs_to_jiffies(PROBE_DELAY_MS);
-
- if (time_after64(delay, now))
- delay -= now;
- else
- delay = 0;
-
- mod_delayed_work(system_wq, &dice->dwork, delay);
-}
-
static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
{
struct snd_dice *dice;
@@ -273,15 +261,9 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
init_completion(&dice->clock_accepted);
init_waitqueue_head(&dice->hwdep_wait);
- err = snd_dice_stream_init_duplex(dice);
- if (err < 0) {
- dice_free(dice);
- return err;
- }
-
/* Allocate and register this sound card later. */
INIT_DEFERRABLE_WORK(&dice->dwork, do_registration);
- schedule_registration(dice);
+ snd_fw_schedule_registration(unit, &dice->dwork);
return 0;
}
@@ -312,7 +294,7 @@ static void dice_bus_reset(struct fw_unit *unit)
/* Postpone a workqueue for deferred registration. */
if (!dice->registered)
- schedule_registration(dice);
+ snd_fw_schedule_registration(unit, &dice->dwork);
/* The handler address register becomes initialized. */
snd_dice_transaction_reinit(dice);
@@ -335,6 +317,13 @@ static const struct ieee1394_device_id dice_id_table[] = {
.match_flags = IEEE1394_MATCH_VERSION,
.version = DICE_INTERFACE,
},
+ /* M-Audio Profire 610/2626 has a different value in version field. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID,
+ .vendor_id = 0x000d6c,
+ .specifier_id = 0x000d6c,
+ },
{ }
};
MODULE_DEVICE_TABLE(ieee1394, dice_id_table);
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c
index 0ac92aba5bc1..b3cffd01a19f 100644
--- a/sound/firewire/digi00x/amdtp-dot.c
+++ b/sound/firewire/digi00x/amdtp-dot.c
@@ -421,7 +421,7 @@ int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
/* Use different mode between incoming/outgoing. */
if (dir == AMDTP_IN_STREAM) {
- flags = CIP_NONBLOCKING | CIP_SKIP_INIT_DBC_CHECK;
+ flags = CIP_NONBLOCKING;
process_data_blocks = process_tx_data_blocks;
} else {
flags = CIP_BLOCKING;
diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c
index 554324d8c602..735d35640807 100644
--- a/sound/firewire/digi00x/digi00x-transaction.c
+++ b/sound/firewire/digi00x/digi00x-transaction.c
@@ -126,12 +126,17 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
return err;
error:
fw_core_remove_address_handler(&dg00x->async_handler);
- dg00x->async_handler.address_callback = NULL;
+ dg00x->async_handler.callback_data = NULL;
return err;
}
void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
{
+ if (dg00x->async_handler.callback_data == NULL)
+ return;
+
snd_fw_async_midi_port_destroy(&dg00x->out_control);
fw_core_remove_address_handler(&dg00x->async_handler);
+
+ dg00x->async_handler.callback_data = NULL;
}
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
index 1f33b7a1fca4..cc4776c6ded3 100644
--- a/sound/firewire/digi00x/digi00x.c
+++ b/sound/firewire/digi00x/digi00x.c
@@ -40,10 +40,8 @@ static int name_card(struct snd_dg00x *dg00x)
return 0;
}
-static void dg00x_card_free(struct snd_card *card)
+static void dg00x_free(struct snd_dg00x *dg00x)
{
- struct snd_dg00x *dg00x = card->private_data;
-
snd_dg00x_stream_destroy_duplex(dg00x);
snd_dg00x_transaction_unregister(dg00x);
@@ -52,28 +50,24 @@ static void dg00x_card_free(struct snd_card *card)
mutex_destroy(&dg00x->mutex);
}
-static int snd_dg00x_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
+static void dg00x_card_free(struct snd_card *card)
{
- struct snd_card *card;
- struct snd_dg00x *dg00x;
- int err;
+ dg00x_free(card->private_data);
+}
- /* create card */
- err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
- sizeof(struct snd_dg00x), &card);
- if (err < 0)
- return err;
- card->private_free = dg00x_card_free;
+static void do_registration(struct work_struct *work)
+{
+ struct snd_dg00x *dg00x =
+ container_of(work, struct snd_dg00x, dwork.work);
+ int err;
- /* initialize myself */
- dg00x = card->private_data;
- dg00x->card = card;
- dg00x->unit = fw_unit_get(unit);
+ if (dg00x->registered)
+ return;
- mutex_init(&dg00x->mutex);
- spin_lock_init(&dg00x->lock);
- init_waitqueue_head(&dg00x->hwdep_wait);
+ err = snd_card_new(&dg00x->unit->device, -1, NULL, THIS_MODULE, 0,
+ &dg00x->card);
+ if (err < 0)
+ return;
err = name_card(dg00x);
if (err < 0)
@@ -101,35 +95,86 @@ static int snd_dg00x_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- err = snd_card_register(card);
+ err = snd_card_register(dg00x->card);
if (err < 0)
goto error;
- dev_set_drvdata(&unit->device, dg00x);
+ dg00x->card->private_free = dg00x_card_free;
+ dg00x->card->private_data = dg00x;
+ dg00x->registered = true;
- return err;
+ return;
error:
- snd_card_free(card);
- return err;
+ snd_dg00x_transaction_unregister(dg00x);
+ snd_dg00x_stream_destroy_duplex(dg00x);
+ snd_card_free(dg00x->card);
+ dev_info(&dg00x->unit->device,
+ "Sound card registration failed: %d\n", err);
+}
+
+static int snd_dg00x_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_dg00x *dg00x;
+
+ /* Allocate this independent of sound card instance. */
+ dg00x = kzalloc(sizeof(struct snd_dg00x), GFP_KERNEL);
+ if (dg00x == NULL)
+ return -ENOMEM;
+
+ dg00x->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, dg00x);
+
+ mutex_init(&dg00x->mutex);
+ spin_lock_init(&dg00x->lock);
+ init_waitqueue_head(&dg00x->hwdep_wait);
+
+ /* Allocate and register this sound card later. */
+ INIT_DEFERRABLE_WORK(&dg00x->dwork, do_registration);
+ snd_fw_schedule_registration(unit, &dg00x->dwork);
+
+ return 0;
}
static void snd_dg00x_update(struct fw_unit *unit)
{
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+ /* Postpone a workqueue for deferred registration. */
+ if (!dg00x->registered)
+ snd_fw_schedule_registration(unit, &dg00x->dwork);
+
snd_dg00x_transaction_reregister(dg00x);
- mutex_lock(&dg00x->mutex);
- snd_dg00x_stream_update_duplex(dg00x);
- mutex_unlock(&dg00x->mutex);
+ /*
+ * After registration, userspace can start packet streaming, then this
+ * code block works fine.
+ */
+ if (dg00x->registered) {
+ mutex_lock(&dg00x->mutex);
+ snd_dg00x_stream_update_duplex(dg00x);
+ mutex_unlock(&dg00x->mutex);
+ }
}
static void snd_dg00x_remove(struct fw_unit *unit)
{
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(dg00x->card);
+ /*
+ * Confirm to stop the work for registration before the sound card is
+ * going to be released. The work is not scheduled again because bus
+ * reset handler is not called anymore.
+ */
+ cancel_delayed_work_sync(&dg00x->dwork);
+
+ if (dg00x->registered) {
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(dg00x->card);
+ } else {
+ /* Don't forget this case. */
+ dg00x_free(dg00x);
+ }
}
static const struct ieee1394_device_id snd_dg00x_id_table[] = {
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
index 907e73993677..2cd465c0caae 100644
--- a/sound/firewire/digi00x/digi00x.h
+++ b/sound/firewire/digi00x/digi00x.h
@@ -37,6 +37,9 @@ struct snd_dg00x {
struct mutex mutex;
spinlock_t lock;
+ bool registered;
+ struct delayed_work dwork;
+
struct amdtp_stream tx_stream;
struct fw_iso_resources tx_resources;
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index 8f27b67503c8..71a0613d3da0 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -168,11 +168,34 @@ get_hardware_info(struct snd_efw *efw)
sizeof(struct snd_efw_phys_grp) * hwinfo->phys_in_grp_count);
memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps,
sizeof(struct snd_efw_phys_grp) * hwinfo->phys_out_grp_count);
+
+ /* AudioFire8 (since 2009) and AudioFirePre8 */
+ if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_9)
+ efw->is_af9 = true;
+ /* These models uses the same firmware. */
+ if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_2 ||
+ hwinfo->type == MODEL_ECHO_AUDIOFIRE_4 ||
+ hwinfo->type == MODEL_ECHO_AUDIOFIRE_9 ||
+ hwinfo->type == MODEL_GIBSON_RIP ||
+ hwinfo->type == MODEL_GIBSON_GOLDTOP)
+ efw->is_fireworks3 = true;
end:
kfree(hwinfo);
return err;
}
+static void efw_free(struct snd_efw *efw)
+{
+ snd_efw_stream_destroy_duplex(efw);
+ snd_efw_transaction_remove_instance(efw);
+ fw_unit_put(efw->unit);
+
+ kfree(efw->resp_buf);
+
+ mutex_destroy(&efw->mutex);
+ kfree(efw);
+}
+
/*
* This module releases the FireWire unit data after all ALSA character devices
* are released by applications. This is for releasing stream data or finishing
@@ -184,28 +207,24 @@ efw_card_free(struct snd_card *card)
{
struct snd_efw *efw = card->private_data;
- snd_efw_stream_destroy_duplex(efw);
- snd_efw_transaction_remove_instance(efw);
- fw_unit_put(efw->unit);
-
- kfree(efw->resp_buf);
-
if (efw->card_index >= 0) {
mutex_lock(&devices_mutex);
clear_bit(efw->card_index, devices_used);
mutex_unlock(&devices_mutex);
}
- mutex_destroy(&efw->mutex);
+ efw_free(card->private_data);
}
-static int
-efw_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
+static void
+do_registration(struct work_struct *work)
{
- struct snd_card *card;
- struct snd_efw *efw;
- int card_index, err;
+ struct snd_efw *efw = container_of(work, struct snd_efw, dwork.work);
+ unsigned int card_index;
+ int err;
+
+ if (efw->registered)
+ return;
mutex_lock(&devices_mutex);
@@ -215,24 +234,16 @@ efw_probe(struct fw_unit *unit,
break;
}
if (card_index >= SNDRV_CARDS) {
- err = -ENOENT;
- goto end;
+ mutex_unlock(&devices_mutex);
+ return;
}
- err = snd_card_new(&unit->device, index[card_index], id[card_index],
- THIS_MODULE, sizeof(struct snd_efw), &card);
- if (err < 0)
- goto end;
- efw = card->private_data;
- efw->card_index = card_index;
- set_bit(card_index, devices_used);
- card->private_free = efw_card_free;
-
- efw->card = card;
- efw->unit = fw_unit_get(unit);
- mutex_init(&efw->mutex);
- spin_lock_init(&efw->lock);
- init_waitqueue_head(&efw->hwdep_wait);
+ err = snd_card_new(&efw->unit->device, index[card_index],
+ id[card_index], THIS_MODULE, 0, &efw->card);
+ if (err < 0) {
+ mutex_unlock(&devices_mutex);
+ return;
+ }
/* prepare response buffer */
snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size,
@@ -248,16 +259,10 @@ efw_probe(struct fw_unit *unit,
err = get_hardware_info(efw);
if (err < 0)
goto error;
- /* AudioFire8 (since 2009) and AudioFirePre8 */
- if (entry->model_id == MODEL_ECHO_AUDIOFIRE_9)
- efw->is_af9 = true;
- /* These models uses the same firmware. */
- if (entry->model_id == MODEL_ECHO_AUDIOFIRE_2 ||
- entry->model_id == MODEL_ECHO_AUDIOFIRE_4 ||
- entry->model_id == MODEL_ECHO_AUDIOFIRE_9 ||
- entry->model_id == MODEL_GIBSON_RIP ||
- entry->model_id == MODEL_GIBSON_GOLDTOP)
- efw->is_fireworks3 = true;
+
+ err = snd_efw_stream_init_duplex(efw);
+ if (err < 0)
+ goto error;
snd_efw_proc_init(efw);
@@ -275,44 +280,93 @@ efw_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- err = snd_efw_stream_init_duplex(efw);
+ err = snd_card_register(efw->card);
if (err < 0)
goto error;
- err = snd_card_register(card);
- if (err < 0) {
- snd_efw_stream_destroy_duplex(efw);
- goto error;
- }
-
- dev_set_drvdata(&unit->device, efw);
-end:
+ set_bit(card_index, devices_used);
mutex_unlock(&devices_mutex);
- return err;
+
+ /*
+ * After registered, efw instance can be released corresponding to
+ * releasing the sound card instance.
+ */
+ efw->card->private_free = efw_card_free;
+ efw->card->private_data = efw;
+ efw->registered = true;
+
+ return;
error:
- snd_efw_transaction_remove_instance(efw);
mutex_unlock(&devices_mutex);
- snd_card_free(card);
- return err;
+ snd_efw_transaction_remove_instance(efw);
+ snd_efw_stream_destroy_duplex(efw);
+ snd_card_free(efw->card);
+ dev_info(&efw->unit->device,
+ "Sound card registration failed: %d\n", err);
+}
+
+static int
+efw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ struct snd_efw *efw;
+
+ efw = kzalloc(sizeof(struct snd_efw), GFP_KERNEL);
+ if (efw == NULL)
+ return -ENOMEM;
+
+ efw->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, efw);
+
+ mutex_init(&efw->mutex);
+ spin_lock_init(&efw->lock);
+ init_waitqueue_head(&efw->hwdep_wait);
+
+ /* Allocate and register this sound card later. */
+ INIT_DEFERRABLE_WORK(&efw->dwork, do_registration);
+ snd_fw_schedule_registration(unit, &efw->dwork);
+
+ return 0;
}
static void efw_update(struct fw_unit *unit)
{
struct snd_efw *efw = dev_get_drvdata(&unit->device);
+ /* Postpone a workqueue for deferred registration. */
+ if (!efw->registered)
+ snd_fw_schedule_registration(unit, &efw->dwork);
+
snd_efw_transaction_bus_reset(efw->unit);
- mutex_lock(&efw->mutex);
- snd_efw_stream_update_duplex(efw);
- mutex_unlock(&efw->mutex);
+ /*
+ * After registration, userspace can start packet streaming, then this
+ * code block works fine.
+ */
+ if (efw->registered) {
+ mutex_lock(&efw->mutex);
+ snd_efw_stream_update_duplex(efw);
+ mutex_unlock(&efw->mutex);
+ }
}
static void efw_remove(struct fw_unit *unit)
{
struct snd_efw *efw = dev_get_drvdata(&unit->device);
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(efw->card);
+ /*
+ * Confirm to stop the work for registration before the sound card is
+ * going to be released. The work is not scheduled again because bus
+ * reset handler is not called anymore.
+ */
+ cancel_delayed_work_sync(&efw->dwork);
+
+ if (efw->registered) {
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(efw->card);
+ } else {
+ /* Don't forget this case. */
+ efw_free(efw);
+ }
}
static const struct ieee1394_device_id efw_id_table[] = {
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 96c4e0c6a9bd..03ed35237e2b 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -65,6 +65,9 @@ struct snd_efw {
struct mutex mutex;
spinlock_t lock;
+ bool registered;
+ struct delayed_work dwork;
+
/* for transaction */
u32 seqnum;
bool resp_addr_changable;
@@ -81,7 +84,6 @@ struct snd_efw {
unsigned int pcm_capture_channels[SND_EFW_MULTIPLIER_MODES];
unsigned int pcm_playback_channels[SND_EFW_MULTIPLIER_MODES];
- struct amdtp_stream *master;
struct amdtp_stream tx_stream;
struct amdtp_stream rx_stream;
struct cmp_connection out_conn;
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
index 425db8d88235..ee47924aef0d 100644
--- a/sound/firewire/fireworks/fireworks_stream.c
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -121,23 +121,6 @@ destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
}
static int
-get_sync_mode(struct snd_efw *efw, enum cip_flags *sync_mode)
-{
- enum snd_efw_clock_source clock_source;
- int err;
-
- err = snd_efw_command_get_clock_source(efw, &clock_source);
- if (err < 0)
- return err;
-
- if (clock_source == SND_EFW_CLOCK_SOURCE_SYTMATCH)
- return -ENOSYS;
-
- *sync_mode = CIP_SYNC_TO_DEVICE;
- return 0;
-}
-
-static int
check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)
{
struct cmp_connection *conn;
@@ -208,9 +191,6 @@ end:
int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
{
- struct amdtp_stream *master, *slave;
- unsigned int slave_substreams;
- enum cip_flags sync_mode;
unsigned int curr_rate;
int err = 0;
@@ -218,32 +198,19 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
if (efw->playback_substreams == 0 && efw->capture_substreams == 0)
goto end;
- err = get_sync_mode(efw, &sync_mode);
- if (err < 0)
- goto end;
- if (sync_mode == CIP_SYNC_TO_DEVICE) {
- master = &efw->tx_stream;
- slave = &efw->rx_stream;
- slave_substreams = efw->playback_substreams;
- } else {
- master = &efw->rx_stream;
- slave = &efw->tx_stream;
- slave_substreams = efw->capture_substreams;
- }
-
/*
* Considering JACK/FFADO streaming:
* TODO: This can be removed hwdep functionality becomes popular.
*/
- err = check_connection_used_by_others(efw, master);
+ err = check_connection_used_by_others(efw, &efw->rx_stream);
if (err < 0)
goto end;
/* packet queueing error */
- if (amdtp_streaming_error(slave))
- stop_stream(efw, slave);
- if (amdtp_streaming_error(master))
- stop_stream(efw, master);
+ if (amdtp_streaming_error(&efw->tx_stream))
+ stop_stream(efw, &efw->tx_stream);
+ if (amdtp_streaming_error(&efw->rx_stream))
+ stop_stream(efw, &efw->rx_stream);
/* stop streams if rate is different */
err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
@@ -252,20 +219,17 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
if (rate == 0)
rate = curr_rate;
if (rate != curr_rate) {
- stop_stream(efw, slave);
- stop_stream(efw, master);
+ stop_stream(efw, &efw->tx_stream);
+ stop_stream(efw, &efw->rx_stream);
}
/* master should be always running */
- if (!amdtp_stream_running(master)) {
- amdtp_stream_set_sync(sync_mode, master, slave);
- efw->master = master;
-
+ if (!amdtp_stream_running(&efw->rx_stream)) {
err = snd_efw_command_set_sampling_rate(efw, rate);
if (err < 0)
goto end;
- err = start_stream(efw, master, rate);
+ err = start_stream(efw, &efw->rx_stream, rate);
if (err < 0) {
dev_err(&efw->unit->device,
"fail to start AMDTP master stream:%d\n", err);
@@ -274,12 +238,13 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
}
/* start slave if needed */
- if (slave_substreams > 0 && !amdtp_stream_running(slave)) {
- err = start_stream(efw, slave, rate);
+ if (efw->capture_substreams > 0 &&
+ !amdtp_stream_running(&efw->tx_stream)) {
+ err = start_stream(efw, &efw->tx_stream, rate);
if (err < 0) {
dev_err(&efw->unit->device,
"fail to start AMDTP slave stream:%d\n", err);
- stop_stream(efw, master);
+ stop_stream(efw, &efw->rx_stream);
}
}
end:
@@ -288,26 +253,11 @@ end:
void snd_efw_stream_stop_duplex(struct snd_efw *efw)
{
- struct amdtp_stream *master, *slave;
- unsigned int master_substreams, slave_substreams;
-
- if (efw->master == &efw->rx_stream) {
- slave = &efw->tx_stream;
- master = &efw->rx_stream;
- slave_substreams = efw->capture_substreams;
- master_substreams = efw->playback_substreams;
- } else {
- slave = &efw->rx_stream;
- master = &efw->tx_stream;
- slave_substreams = efw->playback_substreams;
- master_substreams = efw->capture_substreams;
- }
-
- if (slave_substreams == 0) {
- stop_stream(efw, slave);
+ if (efw->capture_substreams == 0) {
+ stop_stream(efw, &efw->tx_stream);
- if (master_substreams == 0)
- stop_stream(efw, master);
+ if (efw->playback_substreams == 0)
+ stop_stream(efw, &efw->rx_stream);
}
}
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
index f80aafa44c89..ca4dfcf43175 100644
--- a/sound/firewire/lib.c
+++ b/sound/firewire/lib.c
@@ -67,6 +67,38 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
}
EXPORT_SYMBOL(snd_fw_transaction);
+#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
+
+/**
+ * snd_fw_schedule_registration - schedule work for sound card registration
+ * @unit: an instance for unit on IEEE 1394 bus
+ * @dwork: delayed work with callback function
+ *
+ * This function is not designed for general purposes. When new unit is
+ * connected to IEEE 1394 bus, the bus is under bus-reset state because of
+ * topological change. In this state, units tend to fail both of asynchronous
+ * and isochronous communication. To avoid this problem, this function is used
+ * to postpone sound card registration after the state. The callers must
+ * set up instance of delayed work in advance.
+ */
+void snd_fw_schedule_registration(struct fw_unit *unit,
+ struct delayed_work *dwork)
+{
+ u64 now, delay;
+
+ now = get_jiffies_64();
+ delay = fw_parent_device(unit)->card->reset_jiffies
+ + msecs_to_jiffies(PROBE_DELAY_MS);
+
+ if (time_after64(delay, now))
+ delay -= now;
+ else
+ delay = 0;
+
+ mod_delayed_work(system_wq, dwork, delay);
+}
+EXPORT_SYMBOL(snd_fw_schedule_registration);
+
static void async_midi_port_callback(struct fw_card *card, int rcode,
void *data, size_t length,
void *callback_data)
diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h
index f3f6f84c48d6..f6769312ebfc 100644
--- a/sound/firewire/lib.h
+++ b/sound/firewire/lib.h
@@ -22,6 +22,9 @@ static inline bool rcode_is_permanent_error(int rcode)
return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
}
+void snd_fw_schedule_registration(struct fw_unit *unit,
+ struct delayed_work *dwork);
+
struct snd_fw_async_midi_port;
typedef int (*snd_fw_async_midi_port_fill)(
struct snd_rawmidi_substream *substream,
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index 7cb5743c073b..d9361f352133 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -242,8 +242,7 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
* blocks than IEC 61883-6 defines.
*/
if (stream == &oxfw->tx_stream) {
- oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK |
- CIP_JUMBO_PAYLOAD;
+ oxfw->tx_stream.flags |= CIP_JUMBO_PAYLOAD;
if (oxfw->wrong_dbs)
oxfw->tx_stream.flags |= CIP_WRONG_DBS;
}
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index abedc2207261..e629b88f7d93 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -118,15 +118,8 @@ end:
return err;
}
-/*
- * This module releases the FireWire unit data after all ALSA character devices
- * are released by applications. This is for releasing stream data or finishing
- * transactions safely. Thus at returning from .remove(), this module still keep
- * references for the unit.
- */
-static void oxfw_card_free(struct snd_card *card)
+static void oxfw_free(struct snd_oxfw *oxfw)
{
- struct snd_oxfw *oxfw = card->private_data;
unsigned int i;
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
@@ -144,6 +137,17 @@ static void oxfw_card_free(struct snd_card *card)
mutex_destroy(&oxfw->mutex);
}
+/*
+ * This module releases the FireWire unit data after all ALSA character devices
+ * are released by applications. This is for releasing stream data or finishing
+ * transactions safely. Thus at returning from .remove(), this module still keep
+ * references for the unit.
+ */
+static void oxfw_card_free(struct snd_card *card)
+{
+ oxfw_free(card->private_data);
+}
+
static int detect_quirks(struct snd_oxfw *oxfw)
{
struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
@@ -205,41 +209,39 @@ static int detect_quirks(struct snd_oxfw *oxfw)
return 0;
}
-static int oxfw_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
+static void do_registration(struct work_struct *work)
{
- struct snd_card *card;
- struct snd_oxfw *oxfw;
+ struct snd_oxfw *oxfw = container_of(work, struct snd_oxfw, dwork.work);
int err;
- if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
- return -ENODEV;
+ if (oxfw->registered)
+ return;
- err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
- sizeof(*oxfw), &card);
+ err = snd_card_new(&oxfw->unit->device, -1, NULL, THIS_MODULE, 0,
+ &oxfw->card);
if (err < 0)
- return err;
+ return;
- card->private_free = oxfw_card_free;
- oxfw = card->private_data;
- oxfw->card = card;
- mutex_init(&oxfw->mutex);
- oxfw->unit = fw_unit_get(unit);
- oxfw->entry = entry;
- spin_lock_init(&oxfw->lock);
- init_waitqueue_head(&oxfw->hwdep_wait);
+ err = name_card(oxfw);
+ if (err < 0)
+ goto error;
- err = snd_oxfw_stream_discover(oxfw);
+ err = detect_quirks(oxfw);
if (err < 0)
goto error;
- err = name_card(oxfw);
+ err = snd_oxfw_stream_discover(oxfw);
if (err < 0)
goto error;
- err = detect_quirks(oxfw);
+ err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
if (err < 0)
goto error;
+ if (oxfw->has_output) {
+ err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
+ if (err < 0)
+ goto error;
+ }
err = snd_oxfw_create_pcm(oxfw);
if (err < 0)
@@ -255,54 +257,97 @@ static int oxfw_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
+ err = snd_card_register(oxfw->card);
if (err < 0)
goto error;
- if (oxfw->has_output) {
- err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
- if (err < 0)
- goto error;
- }
- err = snd_card_register(card);
- if (err < 0) {
- snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
- if (oxfw->has_output)
- snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
- goto error;
- }
+ /*
+ * After registered, oxfw instance can be released corresponding to
+ * releasing the sound card instance.
+ */
+ oxfw->card->private_free = oxfw_card_free;
+ oxfw->card->private_data = oxfw;
+ oxfw->registered = true;
+
+ return;
+error:
+ snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
+ if (oxfw->has_output)
+ snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
+ snd_card_free(oxfw->card);
+ dev_info(&oxfw->unit->device,
+ "Sound card registration failed: %d\n", err);
+}
+
+static int oxfw_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_oxfw *oxfw;
+
+ if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
+ return -ENODEV;
+
+ /* Allocate this independent of sound card instance. */
+ oxfw = kzalloc(sizeof(struct snd_oxfw), GFP_KERNEL);
+ if (oxfw == NULL)
+ return -ENOMEM;
+
+ oxfw->entry = entry;
+ oxfw->unit = fw_unit_get(unit);
dev_set_drvdata(&unit->device, oxfw);
+ mutex_init(&oxfw->mutex);
+ spin_lock_init(&oxfw->lock);
+ init_waitqueue_head(&oxfw->hwdep_wait);
+
+ /* Allocate and register this sound card later. */
+ INIT_DEFERRABLE_WORK(&oxfw->dwork, do_registration);
+ snd_fw_schedule_registration(unit, &oxfw->dwork);
+
return 0;
-error:
- snd_card_free(card);
- return err;
}
static void oxfw_bus_reset(struct fw_unit *unit)
{
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
+ if (!oxfw->registered)
+ snd_fw_schedule_registration(unit, &oxfw->dwork);
+
fcp_bus_reset(oxfw->unit);
- mutex_lock(&oxfw->mutex);
+ if (oxfw->registered) {
+ mutex_lock(&oxfw->mutex);
- snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
- if (oxfw->has_output)
- snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
+ snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
+ if (oxfw->has_output)
+ snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
- mutex_unlock(&oxfw->mutex);
+ mutex_unlock(&oxfw->mutex);
- if (oxfw->entry->vendor_id == OUI_STANTON)
- snd_oxfw_scs1x_update(oxfw);
+ if (oxfw->entry->vendor_id == OUI_STANTON)
+ snd_oxfw_scs1x_update(oxfw);
+ }
}
static void oxfw_remove(struct fw_unit *unit)
{
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(oxfw->card);
+ /*
+ * Confirm to stop the work for registration before the sound card is
+ * going to be released. The work is not scheduled again because bus
+ * reset handler is not called anymore.
+ */
+ cancel_delayed_work_sync(&oxfw->dwork);
+
+ if (oxfw->registered) {
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(oxfw->card);
+ } else {
+ /* Don't forget this case. */
+ oxfw_free(oxfw);
+ }
}
static const struct compat_info griffin_firewave = {
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index 9beecc214767..2047dcb27625 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -36,10 +36,12 @@
struct snd_oxfw {
struct snd_card *card;
struct fw_unit *unit;
- const struct device_info *device_info;
struct mutex mutex;
spinlock_t lock;
+ bool registered;
+ struct delayed_work dwork;
+
bool wrong_dbs;
bool has_output;
u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c
index 0e6dd5c61f53..4ad3bd7fd445 100644
--- a/sound/firewire/tascam/tascam-stream.c
+++ b/sound/firewire/tascam/tascam-stream.c
@@ -381,19 +381,17 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
if (err < 0)
return err;
if (curr_rate != rate ||
- amdtp_streaming_error(&tscm->tx_stream) ||
- amdtp_streaming_error(&tscm->rx_stream)) {
+ amdtp_streaming_error(&tscm->rx_stream) ||
+ amdtp_streaming_error(&tscm->tx_stream)) {
finish_session(tscm);
- amdtp_stream_stop(&tscm->tx_stream);
amdtp_stream_stop(&tscm->rx_stream);
+ amdtp_stream_stop(&tscm->tx_stream);
release_resources(tscm);
}
- if (!amdtp_stream_running(&tscm->tx_stream)) {
- amdtp_stream_set_sync(CIP_SYNC_TO_DEVICE,
- &tscm->tx_stream, &tscm->rx_stream);
+ if (!amdtp_stream_running(&tscm->rx_stream)) {
err = keep_resources(tscm, rate);
if (err < 0)
goto error;
@@ -406,27 +404,27 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
if (err < 0)
goto error;
- err = amdtp_stream_start(&tscm->tx_stream,
- tscm->tx_resources.channel,
+ err = amdtp_stream_start(&tscm->rx_stream,
+ tscm->rx_resources.channel,
fw_parent_device(tscm->unit)->max_speed);
if (err < 0)
goto error;
- if (!amdtp_stream_wait_callback(&tscm->tx_stream,
+ if (!amdtp_stream_wait_callback(&tscm->rx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
}
- if (!amdtp_stream_running(&tscm->rx_stream)) {
- err = amdtp_stream_start(&tscm->rx_stream,
- tscm->rx_resources.channel,
+ if (!amdtp_stream_running(&tscm->tx_stream)) {
+ err = amdtp_stream_start(&tscm->tx_stream,
+ tscm->tx_resources.channel,
fw_parent_device(tscm->unit)->max_speed);
if (err < 0)
goto error;
- if (!amdtp_stream_wait_callback(&tscm->rx_stream,
+ if (!amdtp_stream_wait_callback(&tscm->tx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
@@ -435,8 +433,8 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
return 0;
error:
- amdtp_stream_stop(&tscm->tx_stream);
amdtp_stream_stop(&tscm->rx_stream);
+ amdtp_stream_stop(&tscm->tx_stream);
finish_session(tscm);
release_resources(tscm);
diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c
index e281c338e562..9dc93a7eb9da 100644
--- a/sound/firewire/tascam/tascam.c
+++ b/sound/firewire/tascam/tascam.c
@@ -85,10 +85,8 @@ static int identify_model(struct snd_tscm *tscm)
return 0;
}
-static void tscm_card_free(struct snd_card *card)
+static void tscm_free(struct snd_tscm *tscm)
{
- struct snd_tscm *tscm = card->private_data;
-
snd_tscm_transaction_unregister(tscm);
snd_tscm_stream_destroy_duplex(tscm);
@@ -97,44 +95,36 @@ static void tscm_card_free(struct snd_card *card)
mutex_destroy(&tscm->mutex);
}
-static int snd_tscm_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
+static void tscm_card_free(struct snd_card *card)
{
- struct snd_card *card;
- struct snd_tscm *tscm;
+ tscm_free(card->private_data);
+}
+
+static void do_registration(struct work_struct *work)
+{
+ struct snd_tscm *tscm = container_of(work, struct snd_tscm, dwork.work);
int err;
- /* create card */
- err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
- sizeof(struct snd_tscm), &card);
+ err = snd_card_new(&tscm->unit->device, -1, NULL, THIS_MODULE, 0,
+ &tscm->card);
if (err < 0)
- return err;
- card->private_free = tscm_card_free;
-
- /* initialize myself */
- tscm = card->private_data;
- tscm->card = card;
- tscm->unit = fw_unit_get(unit);
-
- mutex_init(&tscm->mutex);
- spin_lock_init(&tscm->lock);
- init_waitqueue_head(&tscm->hwdep_wait);
+ return;
err = identify_model(tscm);
if (err < 0)
goto error;
- snd_tscm_proc_init(tscm);
-
- err = snd_tscm_stream_init_duplex(tscm);
+ err = snd_tscm_transaction_register(tscm);
if (err < 0)
goto error;
- err = snd_tscm_create_pcm_devices(tscm);
+ err = snd_tscm_stream_init_duplex(tscm);
if (err < 0)
goto error;
- err = snd_tscm_transaction_register(tscm);
+ snd_tscm_proc_init(tscm);
+
+ err = snd_tscm_create_pcm_devices(tscm);
if (err < 0)
goto error;
@@ -146,35 +136,91 @@ static int snd_tscm_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- err = snd_card_register(card);
+ err = snd_card_register(tscm->card);
if (err < 0)
goto error;
- dev_set_drvdata(&unit->device, tscm);
+ /*
+ * After registered, tscm instance can be released corresponding to
+ * releasing the sound card instance.
+ */
+ tscm->card->private_free = tscm_card_free;
+ tscm->card->private_data = tscm;
+ tscm->registered = true;
- return err;
+ return;
error:
- snd_card_free(card);
- return err;
+ snd_tscm_transaction_unregister(tscm);
+ snd_tscm_stream_destroy_duplex(tscm);
+ snd_card_free(tscm->card);
+ dev_info(&tscm->unit->device,
+ "Sound card registration failed: %d\n", err);
+}
+
+static int snd_tscm_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_tscm *tscm;
+
+ /* Allocate this independent of sound card instance. */
+ tscm = kzalloc(sizeof(struct snd_tscm), GFP_KERNEL);
+ if (tscm == NULL)
+ return -ENOMEM;
+
+ /* initialize myself */
+ tscm->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, tscm);
+
+ mutex_init(&tscm->mutex);
+ spin_lock_init(&tscm->lock);
+ init_waitqueue_head(&tscm->hwdep_wait);
+
+ /* Allocate and register this sound card later. */
+ INIT_DEFERRABLE_WORK(&tscm->dwork, do_registration);
+ snd_fw_schedule_registration(unit, &tscm->dwork);
+
+ return 0;
}
static void snd_tscm_update(struct fw_unit *unit)
{
struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+ /* Postpone a workqueue for deferred registration. */
+ if (!tscm->registered)
+ snd_fw_schedule_registration(unit, &tscm->dwork);
+
snd_tscm_transaction_reregister(tscm);
- mutex_lock(&tscm->mutex);
- snd_tscm_stream_update_duplex(tscm);
- mutex_unlock(&tscm->mutex);
+ /*
+ * After registration, userspace can start packet streaming, then this
+ * code block works fine.
+ */
+ if (tscm->registered) {
+ mutex_lock(&tscm->mutex);
+ snd_tscm_stream_update_duplex(tscm);
+ mutex_unlock(&tscm->mutex);
+ }
}
static void snd_tscm_remove(struct fw_unit *unit)
{
struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(tscm->card);
+ /*
+ * Confirm to stop the work for registration before the sound card is
+ * going to be released. The work is not scheduled again because bus
+ * reset handler is not called anymore.
+ */
+ cancel_delayed_work_sync(&tscm->dwork);
+
+ if (tscm->registered) {
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(tscm->card);
+ } else {
+ /* Don't forget this case. */
+ tscm_free(tscm);
+ }
}
static const struct ieee1394_device_id snd_tscm_id_table[] = {
diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h
index 30ab77e924f7..1f61011579a7 100644
--- a/sound/firewire/tascam/tascam.h
+++ b/sound/firewire/tascam/tascam.h
@@ -51,6 +51,8 @@ struct snd_tscm {
struct mutex mutex;
spinlock_t lock;
+ bool registered;
+ struct delayed_work dwork;
const struct snd_tscm_spec *spec;
struct fw_iso_resources tx_resources;
diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c
index 3b7ae24900fd..31b510c5ca0b 100644
--- a/sound/hda/ext/hdac_ext_bus.c
+++ b/sound/hda/ext/hdac_ext_bus.c
@@ -147,6 +147,7 @@ int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr)
if (!edev)
return -ENOMEM;
hdev = &edev->hdac;
+ edev->ebus = ebus;
snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr);
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
index 8c486235c905..9fee464e5d49 100644
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/hdac_controller.c
@@ -80,6 +80,22 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io);
+/* wait for cmd dmas till they are stopped */
+static void hdac_wait_for_cmd_dmas(struct hdac_bus *bus)
+{
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while ((snd_hdac_chip_readb(bus, RIRBCTL) & AZX_RBCTL_DMA_EN)
+ && time_before(jiffies, timeout))
+ udelay(10);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while ((snd_hdac_chip_readb(bus, CORBCTL) & AZX_CORBCTL_RUN)
+ && time_before(jiffies, timeout))
+ udelay(10);
+}
+
/**
* snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers
* @bus: HD-audio core bus
@@ -90,6 +106,7 @@ void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus)
/* disable ringbuffer DMAs */
snd_hdac_chip_writeb(bus, RIRBCTL, 0);
snd_hdac_chip_writeb(bus, CORBCTL, 0);
+ hdac_wait_for_cmd_dmas(bus);
/* disable unsolicited responses */
snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0);
spin_unlock_irq(&bus->reg_lock);
diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c
index 607bbeaebddf..c9af022676c2 100644
--- a/sound/hda/hdac_i915.c
+++ b/sound/hda/hdac_i915.c
@@ -158,22 +158,40 @@ void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
}
EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk);
-/* There is a fixed mapping between audio pin node and display port
- * on current Intel platforms:
+/* There is a fixed mapping between audio pin node and display port.
+ * on SNB, IVY, HSW, BSW, SKL, BXT, KBL:
* Pin Widget 5 - PORT B (port = 1 in i915 driver)
* Pin Widget 6 - PORT C (port = 2 in i915 driver)
* Pin Widget 7 - PORT D (port = 3 in i915 driver)
+ *
+ * on VLV, ILK:
+ * Pin Widget 4 - PORT B (port = 1 in i915 driver)
+ * Pin Widget 5 - PORT C (port = 2 in i915 driver)
+ * Pin Widget 6 - PORT D (port = 3 in i915 driver)
*/
-static int pin2port(hda_nid_t pin_nid)
+static int pin2port(struct hdac_device *codec, hda_nid_t pin_nid)
{
- if (WARN_ON(pin_nid < 5 || pin_nid > 7))
+ int base_nid;
+
+ switch (codec->vendor_id) {
+ case 0x80860054: /* ILK */
+ case 0x80862804: /* ILK */
+ case 0x80862882: /* VLV */
+ base_nid = 3;
+ break;
+ default:
+ base_nid = 4;
+ break;
+ }
+
+ if (WARN_ON(pin_nid <= base_nid || pin_nid > base_nid + 3))
return -1;
- return pin_nid - 4;
+ return pin_nid - base_nid;
}
/**
* snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
- * @bus: HDA core bus
+ * @codec: HDA codec
* @nid: the pin widget NID
* @rate: the sample rate to set
*
@@ -183,14 +201,15 @@ static int pin2port(hda_nid_t pin_nid)
* This function sets N/CTS value based on the given sample rate.
* Returns zero for success, or a negative error code.
*/
-int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate)
+int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, int rate)
{
+ struct hdac_bus *bus = codec->bus;
struct i915_audio_component *acomp = bus->audio_component;
int port;
if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
return -ENODEV;
- port = pin2port(nid);
+ port = pin2port(codec, nid);
if (port < 0)
return -EINVAL;
return acomp->ops->sync_audio_rate(acomp->dev, port, rate);
@@ -199,7 +218,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
/**
* snd_hdac_acomp_get_eld - Get the audio state and ELD via component
- * @bus: HDA core bus
+ * @codec: HDA codec
* @nid: the pin widget NID
* @audio_enabled: the pointer to store the current audio state
* @buffer: the buffer pointer to store ELD bytes
@@ -217,16 +236,17 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
* thus it may be over @max_bytes. If it's over @max_bytes, it implies
* that only a part of ELD bytes have been fetched.
*/
-int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid,
+int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
bool *audio_enabled, char *buffer, int max_bytes)
{
+ struct hdac_bus *bus = codec->bus;
struct i915_audio_component *acomp = bus->audio_component;
int port;
if (!acomp || !acomp->ops || !acomp->ops->get_eld)
return -ENODEV;
- port = pin2port(nid);
+ port = pin2port(codec, nid);
if (port < 0)
return -EINVAL;
return acomp->ops->get_eld(acomp->dev, port, audio_enabled,
@@ -338,6 +358,9 @@ int snd_hdac_i915_init(struct hdac_bus *bus)
struct i915_audio_component *acomp;
int ret;
+ if (WARN_ON(hdac_acomp))
+ return -EBUSY;
+
if (!i915_gfx_present())
return -ENODEV;
@@ -371,6 +394,7 @@ out_master_del:
out_err:
kfree(acomp);
bus->audio_component = NULL;
+ hdac_acomp = NULL;
dev_info(dev, "failed to add i915 component master (%d)\n", ret);
return ret;
@@ -404,6 +428,7 @@ int snd_hdac_i915_exit(struct hdac_bus *bus)
kfree(acomp);
bus->audio_component = NULL;
+ hdac_acomp = NULL;
return 0;
}
diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c
index 87041ddd29cb..47a358fab132 100644
--- a/sound/hda/hdac_regmap.c
+++ b/sound/hda/hdac_regmap.c
@@ -444,7 +444,7 @@ int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
err = reg_raw_write(codec, reg, val);
if (err == -EAGAIN) {
err = snd_hdac_power_up_pm(codec);
- if (!err)
+ if (err >= 0)
err = reg_raw_write(codec, reg, val);
snd_hdac_power_down_pm(codec);
}
@@ -470,7 +470,7 @@ static int __snd_hdac_regmap_read_raw(struct hdac_device *codec,
err = reg_raw_read(codec, reg, val, uncached);
if (err == -EAGAIN) {
err = snd_hdac_power_up_pm(codec);
- if (!err)
+ if (err >= 0)
err = reg_raw_read(codec, reg, val, uncached);
snd_hdac_power_down_pm(codec);
}
diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c
index d7ec86263828..c6c75e7e0981 100644
--- a/sound/hda/hdmi_chmap.c
+++ b/sound/hda/hdmi_chmap.c
@@ -625,13 +625,30 @@ static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
WARN_ON(count != channels);
}
+static int spk_mask_from_spk_alloc(int spk_alloc)
+{
+ int i;
+ int spk_mask = eld_speaker_allocation_bits[0];
+
+ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+ if (spk_alloc & (1 << i))
+ spk_mask |= eld_speaker_allocation_bits[i];
+ }
+
+ return spk_mask;
+}
+
static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *tlv)
{
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
struct hdac_chmap *chmap = info->private_data;
+ int pcm_idx = kcontrol->private_value;
unsigned int __user *dst;
int chs, count = 0;
+ unsigned long max_chs;
+ int type;
+ int spk_alloc, spk_mask;
if (size < 8)
return -ENOMEM;
@@ -639,40 +656,59 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
return -EFAULT;
size -= 8;
dst = tlv + 2;
- for (chs = 2; chs <= chmap->channels_max; chs++) {
+
+ spk_alloc = chmap->ops.get_spk_alloc(chmap->hdac, pcm_idx);
+ spk_mask = spk_mask_from_spk_alloc(spk_alloc);
+
+ max_chs = hweight_long(spk_mask);
+
+ for (chs = 2; chs <= max_chs; chs++) {
int i;
struct hdac_cea_channel_speaker_allocation *cap;
cap = channel_allocations;
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
int chs_bytes = chs * 4;
- int type = chmap->ops.chmap_cea_alloc_validate_get_type(
- chmap, cap, chs);
unsigned int tlv_chmap[8];
- if (type < 0)
+ if (cap->channels != chs)
+ continue;
+
+ if (!(cap->spk_mask == (spk_mask & cap->spk_mask)))
continue;
+
+ type = chmap->ops.chmap_cea_alloc_validate_get_type(
+ chmap, cap, chs);
+ if (type < 0)
+ return -ENODEV;
if (size < 8)
return -ENOMEM;
+
if (put_user(type, dst) ||
put_user(chs_bytes, dst + 1))
return -EFAULT;
+
dst += 2;
size -= 8;
count += 8;
+
if (size < chs_bytes)
return -ENOMEM;
+
size -= chs_bytes;
count += chs_bytes;
chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
tlv_chmap, chs);
+
if (copy_to_user(dst, tlv_chmap, chs_bytes))
return -EFAULT;
dst += chs;
}
}
+
if (put_user(count, tlv + 1))
return -EFAULT;
+
return 0;
}
diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
index 69f76ff5693d..718d5e3b7806 100644
--- a/sound/isa/wavefront/wavefront_synth.c
+++ b/sound/isa/wavefront/wavefront_synth.c
@@ -785,6 +785,9 @@ wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header)
DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n",
header->number);
+ if (header->number >= ARRAY_SIZE(dev->patch_status))
+ return -EINVAL;
+
dev->patch_status[header->number] |= WF_SLOT_FILLED;
bptr = buf;
@@ -809,6 +812,9 @@ wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header)
DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n",
header->number);
+ if (header->number >= ARRAY_SIZE(dev->prog_status))
+ return -EINVAL;
+
dev->prog_status[header->number] = WF_SLOT_USED;
/* XXX need to zero existing SLOT_USED bit for program_status[i]
@@ -898,6 +904,9 @@ wavefront_send_sample (snd_wavefront_t *dev,
header->number = x;
}
+ if (header->number >= WF_MAX_SAMPLE)
+ return -EINVAL;
+
if (header->size) {
/* XXX it's a debatable point whether or not RDONLY semantics
diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c
index b36ea47527e8..0b8d0de87273 100644
--- a/sound/oss/waveartist.c
+++ b/sound/oss/waveartist.c
@@ -1414,11 +1414,9 @@ attach_waveartist(struct address_info *hw, const struct waveartist_mixer_info *m
else {
#ifdef CONFIG_ARCH_NETWINDER
if (machine_is_netwinder()) {
- init_timer(&vnc_timer);
- vnc_timer.function = vnc_slider_tick;
- vnc_timer.expires = jiffies;
- vnc_timer.data = nr_waveartist_devs;
- add_timer(&vnc_timer);
+ setup_timer(&vnc_timer, vnc_slider_tick,
+ nr_waveartist_devs);
+ mod_timer(&vnc_timer, jiffies);
vnc_configure_mixer(devc, 0);
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index 4667c3232b7f..4a054d720112 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -2151,8 +2151,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
stream->resources, en,
VORTEX_RESOURCE_SRC)) < 0) {
memset(stream->resources, 0,
- sizeof(unsigned char) *
- VORTEX_RESOURCE_LAST);
+ sizeof(stream->resources));
return -EBUSY;
}
if (stream->type != VORTEX_PCM_A3D) {
@@ -2162,7 +2161,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
VORTEX_RESOURCE_MIXIN)) < 0) {
memset(stream->resources,
0,
- sizeof(unsigned char) * VORTEX_RESOURCE_LAST);
+ sizeof(stream->resources));
return -EBUSY;
}
}
@@ -2175,8 +2174,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
stream->resources, en,
VORTEX_RESOURCE_A3D)) < 0) {
memset(stream->resources, 0,
- sizeof(unsigned char) *
- VORTEX_RESOURCE_LAST);
+ sizeof(stream->resources));
dev_err(vortex->card->dev,
"out of A3D sources. Sorry\n");
return -EBUSY;
@@ -2290,8 +2288,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
VORTEX_RESOURCE_MIXOUT))
< 0) {
memset(stream->resources, 0,
- sizeof(unsigned char) *
- VORTEX_RESOURCE_LAST);
+ sizeof(stream->resources));
return -EBUSY;
}
if ((src[i] =
@@ -2299,8 +2296,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
stream->resources, en,
VORTEX_RESOURCE_SRC)) < 0) {
memset(stream->resources, 0,
- sizeof(unsigned char) *
- VORTEX_RESOURCE_LAST);
+ sizeof(stream->resources));
return -EBUSY;
}
}
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
index a6d6d8d0867a..df5741a78fd2 100644
--- a/sound/pci/au88x0/au88x0_pcm.c
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -432,7 +432,10 @@ static snd_pcm_uframes_t snd_vortex_pcm_pointer(struct snd_pcm_substream *substr
#endif
//printk(KERN_INFO "vortex: pointer = 0x%x\n", current_ptr);
spin_unlock(&chip->lock);
- return (bytes_to_frames(substream->runtime, current_ptr));
+ current_ptr = bytes_to_frames(substream->runtime, current_ptr);
+ if (current_ptr >= substream->runtime->buffer_size)
+ current_ptr = 0;
+ return current_ptr;
}
/* operators */
diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c
index a5d460453d7b..8f945341720b 100644
--- a/sound/pci/ctxfi/cttimer.c
+++ b/sound/pci/ctxfi/cttimer.c
@@ -49,7 +49,7 @@ struct ct_timer {
spinlock_t lock; /* global timer lock (for xfitimer) */
spinlock_t list_lock; /* lock for instance list */
struct ct_atc *atc;
- struct ct_timer_ops *ops;
+ const struct ct_timer_ops *ops;
struct list_head instance_head;
struct list_head running_head;
unsigned int wc; /* current wallclock */
@@ -128,7 +128,7 @@ static void ct_systimer_prepare(struct ct_timer_instance *ti)
#define ct_systimer_free ct_systimer_prepare
-static struct ct_timer_ops ct_systimer_ops = {
+static const struct ct_timer_ops ct_systimer_ops = {
.init = ct_systimer_init,
.free_instance = ct_systimer_free,
.prepare = ct_systimer_prepare,
@@ -322,7 +322,7 @@ static void ct_xfitimer_free_global(struct ct_timer *atimer)
ct_xfitimer_irq_stop(atimer);
}
-static struct ct_timer_ops ct_xfitimer_ops = {
+static const struct ct_timer_ops ct_xfitimer_ops = {
.prepare = ct_xfitimer_prepare,
.start = ct_xfitimer_start,
.stop = ct_xfitimer_stop,
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index 0dc44ebb0032..626cd2167d29 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -1548,7 +1548,7 @@ static int snd_es1373_line_get(struct snd_kcontrol *kcontrol,
int val = 0;
spin_lock_irq(&ensoniq->reg_lock);
- if ((ensoniq->ctrl & ES_1371_GPIO_OUTM) >= 4)
+ if (ensoniq->ctrl & ES_1371_GPIO_OUT(4))
val = 1;
ucontrol->value.integer.value[0] = val;
spin_unlock_irq(&ensoniq->reg_lock);
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index bb02c2d48fd5..7f3b5ed81995 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -50,9 +50,13 @@ config SND_HDA_RECONFIG
bool "Allow dynamic codec reconfiguration"
help
Say Y here to enable the HD-audio codec re-configuration feature.
- This adds the sysfs interfaces to allow user to clear the whole
- codec configuration, change the codec setup, add extra verbs,
- and re-configure the codec dynamically.
+ It allows user to clear the whole codec configuration, change the
+ codec setup, add extra verbs, and re-configure the codec dynamically.
+
+ Note that this item alone doesn't provide the sysfs interface, but
+ enables the feature just for the patch loader below.
+ If you need the traditional sysfs entries for the manual interaction,
+ turn on CONFIG_SND_HDA_HWDEP as well.
config SND_HDA_INPUT_BEEP
bool "Support digital beep via input layer"
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index dfaf1a93fb8a..320445f3bf73 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -5434,6 +5434,7 @@ static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
spec->cur_adc_stream_tag = stream_tag;
spec->cur_adc_format = format;
snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+ call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE);
return 0;
}
@@ -5444,6 +5445,7 @@ static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_gen_spec *spec = codec->spec;
snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
spec->cur_adc = 0;
+ call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP);
return 0;
}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 9a0d1445ca5c..94089fc71884 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -365,8 +365,11 @@ enum {
#define IS_SKL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa170)
#define IS_SKL_LP(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9d70)
+#define IS_KBL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa171)
+#define IS_KBL_LP(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9d71)
#define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98)
-#define IS_SKL_PLUS(pci) (IS_SKL(pci) || IS_SKL_LP(pci) || IS_BXT(pci))
+#define IS_SKL_PLUS(pci) (IS_SKL(pci) || IS_SKL_LP(pci) || IS_BXT(pci)) || \
+ IS_KBL(pci) || IS_KBL_LP(pci)
static char *driver_short_names[] = {
[AZX_DRIVER_ICH] = "HDA Intel",
@@ -2181,6 +2184,12 @@ static const struct pci_device_id azx_ids[] = {
/* Sunrise Point-LP */
{ PCI_DEVICE(0x8086, 0x9d70),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
+ /* Kabylake */
+ { PCI_DEVICE(0x8086, 0xa171),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
+ /* Kabylake-LP */
+ { PCI_DEVICE(0x8086, 0x9d71),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
/* Broxton-P(Apollolake) */
{ PCI_DEVICE(0x8086, 0x5a98),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BROXTON },
diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c
index 64e0d1d81ca5..9739fce9e032 100644
--- a/sound/pci/hda/hda_sysfs.c
+++ b/sound/pci/hda/hda_sysfs.c
@@ -141,14 +141,6 @@ static int reconfig_codec(struct hda_codec *codec)
err = snd_hda_codec_configure(codec);
if (err < 0)
goto error;
- /* rebuild PCMs */
- err = snd_hda_codec_build_pcms(codec);
- if (err < 0)
- goto error;
- /* rebuild mixers */
- err = snd_hda_codec_build_controls(codec);
- if (err < 0)
- goto error;
err = snd_card_register(codec->card);
error:
snd_hda_power_down(codec);
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
index 17fd81736d3d..0621920f7617 100644
--- a/sound/pci/hda/hda_tegra.c
+++ b/sound/pci/hda/hda_tegra.c
@@ -115,20 +115,20 @@ static int substream_free_pages(struct azx *chip,
/*
* Register access ops. Tegra HDA register access is DWORD only.
*/
-static void hda_tegra_writel(u32 value, u32 *addr)
+static void hda_tegra_writel(u32 value, u32 __iomem *addr)
{
writel(value, addr);
}
-static u32 hda_tegra_readl(u32 *addr)
+static u32 hda_tegra_readl(u32 __iomem *addr)
{
return readl(addr);
}
-static void hda_tegra_writew(u16 value, u16 *addr)
+static void hda_tegra_writew(u16 value, u16 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
- void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
@@ -137,20 +137,20 @@ static void hda_tegra_writew(u16 value, u16 *addr)
writel(v, dword_addr);
}
-static u16 hda_tegra_readw(u16 *addr)
+static u16 hda_tegra_readw(u16 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
- void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
return (v >> shift) & 0xffff;
}
-static void hda_tegra_writeb(u8 value, u8 *addr)
+static void hda_tegra_writeb(u8 value, u8 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
- void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
@@ -159,10 +159,10 @@ static void hda_tegra_writeb(u8 value, u8 *addr)
writel(v, dword_addr);
}
-static u8 hda_tegra_readb(u8 *addr)
+static u8 hda_tegra_readb(u8 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
- void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 1483f85999ec..d0d5ad8beac5 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -114,6 +114,9 @@ struct hdmi_ops {
int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
hda_nid_t pin_nid, u32 stream_tag, int format);
+ void (*pin_cvt_fixup)(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ hda_nid_t cvt_nid);
};
struct hdmi_pcm {
@@ -684,7 +687,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
if (!channels)
return;
- if (is_haswell_plus(codec))
+ /* some HW (e.g. HSW+) needs reprogramming the amp at each time */
+ if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE);
@@ -864,9 +868,6 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
struct hdmi_spec *spec = codec->spec;
int err;
- if (is_haswell_plus(codec))
- haswell_verify_D0(codec, cvt_nid, pin_nid);
-
err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format));
if (err) {
@@ -884,7 +885,7 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
* of the pin.
*/
static int hdmi_choose_cvt(struct hda_codec *codec,
- int pin_idx, int *cvt_id, int *mux_id)
+ int pin_idx, int *cvt_id)
{
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin;
@@ -925,8 +926,6 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
if (cvt_id)
*cvt_id = cvt_idx;
- if (mux_id)
- *mux_id = mux_idx;
return 0;
}
@@ -1019,9 +1018,6 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
int mux_idx;
struct hdmi_spec *spec = codec->spec;
- if (!is_haswell_plus(codec) && !is_valleyview_plus(codec))
- return;
-
/* On Intel platform, the mapping of converter nid to
* mux index of the pins are always the same.
* The pin nid may be 0, this means all pins will not
@@ -1032,6 +1028,17 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
intel_not_share_assigned_cvt(codec, pin_nid, mux_idx);
}
+/* skeleton caller of pin_cvt_fixup ops */
+static void pin_cvt_fixup(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ hda_nid_t cvt_nid)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (spec->ops.pin_cvt_fixup)
+ spec->ops.pin_cvt_fixup(codec, per_pin, cvt_nid);
+}
+
/* called in hdmi_pcm_open when no pin is assigned to the PCM
* in dyn_pcm_assign mode.
*/
@@ -1049,7 +1056,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
if (pcm_idx < 0)
return -EINVAL;
- err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL);
+ err = hdmi_choose_cvt(codec, -1, &cvt_idx);
if (err)
return err;
@@ -1057,7 +1064,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
per_cvt->assigned = 1;
hinfo->nid = per_cvt->cvt_nid;
- intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid);
+ pin_cvt_fixup(codec, NULL, per_cvt->cvt_nid);
set_bit(pcm_idx, &spec->pcm_in_use);
/* todo: setup spdif ctls assign */
@@ -1089,7 +1096,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
{
struct hdmi_spec *spec = codec->spec;
struct snd_pcm_runtime *runtime = substream->runtime;
- int pin_idx, cvt_idx, pcm_idx, mux_idx = 0;
+ int pin_idx, cvt_idx, pcm_idx;
struct hdmi_spec_per_pin *per_pin;
struct hdmi_eld *eld;
struct hdmi_spec_per_cvt *per_cvt = NULL;
@@ -1118,7 +1125,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
}
}
- err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx);
+ err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx);
if (err < 0) {
mutex_unlock(&spec->pcm_lock);
return err;
@@ -1135,11 +1142,10 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
AC_VERB_SET_CONNECT_SEL,
- mux_idx);
+ per_pin->mux_idx);
/* configure unused pins to choose other converters */
- if (is_haswell_plus(codec) || is_valleyview_plus(codec))
- intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx);
+ pin_cvt_fixup(codec, per_pin, 0);
snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid);
@@ -1372,12 +1378,7 @@ static void update_eld(struct hda_codec *codec,
* and this can make HW reset converter selection on a pin.
*/
if (eld->eld_valid && !old_eld_valid && per_pin->setup) {
- if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
- intel_verify_pin_cvt_connect(codec, per_pin);
- intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
- per_pin->mux_idx);
- }
-
+ pin_cvt_fixup(codec, per_pin, 0);
hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
}
@@ -1484,7 +1485,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
mutex_lock(&per_pin->lock);
eld->monitor_present = false;
- size = snd_hdac_acomp_get_eld(&codec->bus->core, per_pin->pin_nid,
+ size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid,
&eld->monitor_present, eld->eld_buffer,
ELD_MAX_SIZE);
if (size > 0) {
@@ -1711,7 +1712,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
* skip pin setup and return 0 to make audio playback
* be ongoing
*/
- intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid);
+ pin_cvt_fixup(codec, NULL, cvt_nid);
snd_hda_codec_setup_stream(codec, cvt_nid,
stream_tag, 0, format);
mutex_unlock(&spec->pcm_lock);
@@ -1724,23 +1725,21 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
}
per_pin = get_pin(spec, pin_idx);
pin_nid = per_pin->pin_nid;
- if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
- /* Verify pin:cvt selections to avoid silent audio after S3.
- * After S3, the audio driver restores pin:cvt selections
- * but this can happen before gfx is ready and such selection
- * is overlooked by HW. Thus multiple pins can share a same
- * default convertor and mute control will affect each other,
- * which can cause a resumed audio playback become silent
- * after S3.
- */
- intel_verify_pin_cvt_connect(codec, per_pin);
- intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx);
- }
+
+ /* Verify pin:cvt selections to avoid silent audio after S3.
+ * After S3, the audio driver restores pin:cvt selections
+ * but this can happen before gfx is ready and such selection
+ * is overlooked by HW. Thus multiple pins can share a same
+ * default convertor and mute control will affect each other,
+ * which can cause a resumed audio playback become silent
+ * after S3.
+ */
+ pin_cvt_fixup(codec, per_pin, 0);
/* Call sync_audio_rate to set the N/CTS/M manually if necessary */
/* Todo: add DP1.2 MST audio support later */
if (codec_has_acomp(codec))
- snd_hdac_sync_audio_rate(&codec->bus->core, pin_nid, runtime->rate);
+ snd_hdac_sync_audio_rate(&codec->core, pin_nid, runtime->rate);
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
mutex_lock(&per_pin->lock);
@@ -1837,6 +1836,18 @@ static const struct hda_pcm_ops generic_ops = {
.cleanup = generic_hdmi_playback_pcm_cleanup,
};
+static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
+{
+ struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+ if (!per_pin)
+ return 0;
+
+ return per_pin->sink_eld.info.spk_alloc;
+}
+
static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap)
{
@@ -2075,6 +2086,20 @@ static void hdmi_array_free(struct hdmi_spec *spec)
snd_array_free(&spec->cvts);
}
+static void generic_spec_free(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (spec) {
+ if (spec->i915_bound)
+ snd_hdac_i915_exit(&codec->bus->core);
+ hdmi_array_free(spec);
+ kfree(spec);
+ codec->spec = NULL;
+ }
+ codec->dp_mst = false;
+}
+
static void generic_hdmi_free(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
@@ -2099,10 +2124,7 @@ static void generic_hdmi_free(struct hda_codec *codec)
spec->pcm_rec[pcm_idx].jack = NULL;
}
- if (spec->i915_bound)
- snd_hdac_i915_exit(&codec->bus->core);
- hdmi_array_free(spec);
- kfree(spec);
+ generic_spec_free(codec);
}
#ifdef CONFIG_PM
@@ -2140,6 +2162,55 @@ static const struct hdmi_ops generic_standard_hdmi_ops = {
.setup_stream = hdmi_setup_stream,
};
+/* allocate codec->spec and assign/initialize generic parser ops */
+static int alloc_generic_hdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+
+ spec->ops = generic_standard_hdmi_ops;
+ mutex_init(&spec->pcm_lock);
+ snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
+
+ spec->chmap.ops.get_chmap = hdmi_get_chmap;
+ spec->chmap.ops.set_chmap = hdmi_set_chmap;
+ spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
+ spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc,
+
+ codec->spec = spec;
+ hdmi_array_init(spec, 4);
+
+ codec->patch_ops = generic_hdmi_patch_ops;
+
+ return 0;
+}
+
+/* generic HDMI parser */
+static int patch_generic_hdmi(struct hda_codec *codec)
+{
+ int err;
+
+ err = alloc_generic_hdmi(codec);
+ if (err < 0)
+ return err;
+
+ err = hdmi_parse_codec(codec);
+ if (err < 0) {
+ generic_spec_free(codec);
+ return err;
+ }
+
+ generic_hdmi_init_per_pins(codec);
+ return 0;
+}
+
+/*
+ * Intel codec parsers and helpers
+ */
+
static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
hda_nid_t nid)
{
@@ -2217,12 +2288,23 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg,
static void intel_pin_eld_notify(void *audio_ptr, int port)
{
struct hda_codec *codec = audio_ptr;
- int pin_nid = port + 0x04;
+ int pin_nid;
/* we assume only from port-B to port-D */
if (port < 1 || port > 3)
return;
+ switch (codec->core.vendor_id) {
+ case 0x80860054: /* ILK */
+ case 0x80862804: /* ILK */
+ case 0x80862882: /* VLV */
+ pin_nid = port + 0x03;
+ break;
+ default:
+ pin_nid = port + 0x04;
+ break;
+ }
+
/* skip notification during system suspend (but not in runtime PM);
* the state will be updated at resume
*/
@@ -2236,93 +2318,159 @@ static void intel_pin_eld_notify(void *audio_ptr, int port)
check_presence_and_report(codec, pin_nid);
}
-static int patch_generic_hdmi(struct hda_codec *codec)
+/* register i915 component pin_eld_notify callback */
+static void register_i915_notifier(struct hda_codec *codec)
{
- struct hdmi_spec *spec;
+ struct hdmi_spec *spec = codec->spec;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ spec->use_acomp_notifier = true;
+ spec->i915_audio_ops.audio_ptr = codec;
+ /* intel_audio_codec_enable() or intel_audio_codec_disable()
+ * will call pin_eld_notify with using audio_ptr pointer
+ * We need make sure audio_ptr is really setup
+ */
+ wmb();
+ spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
+ snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
+}
- spec->ops = generic_standard_hdmi_ops;
- mutex_init(&spec->pcm_lock);
- snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
+/* setup_stream ops override for HSW+ */
+static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
+ hda_nid_t pin_nid, u32 stream_tag, int format)
+{
+ haswell_verify_D0(codec, cvt_nid, pin_nid);
+ return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+}
- spec->chmap.ops.get_chmap = hdmi_get_chmap;
- spec->chmap.ops.set_chmap = hdmi_set_chmap;
- spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
+/* pin_cvt_fixup ops override for HSW+ and VLV+ */
+static void i915_pin_cvt_fixup(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ hda_nid_t cvt_nid)
+{
+ if (per_pin) {
+ intel_verify_pin_cvt_connect(codec, per_pin);
+ intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
+ per_pin->mux_idx);
+ } else {
+ intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid);
+ }
+}
- codec->spec = spec;
- hdmi_array_init(spec, 4);
+/* Intel Haswell and onwards; audio component with eld notifier */
+static int patch_i915_hsw_hdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err;
-#ifdef CONFIG_SND_HDA_I915
- /* Try to bind with i915 for Intel HSW+ codecs (if not done yet) */
- if ((codec->core.vendor_id >> 16) == 0x8086 &&
- is_haswell_plus(codec)) {
-#if 0
- /* on-demand binding leads to an unbalanced refcount when
- * both i915 and hda drivers are probed concurrently;
- * disabled temporarily for now
- */
- if (!codec->bus->core.audio_component)
- if (!snd_hdac_i915_init(&codec->bus->core))
- spec->i915_bound = true;
-#endif
- /* use i915 audio component notifier for hotplug */
- if (codec->bus->core.audio_component)
- spec->use_acomp_notifier = true;
+ /* HSW+ requires i915 binding */
+ if (!codec->bus->core.audio_component) {
+ codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
+ return -ENODEV;
}
-#endif
- if (is_haswell_plus(codec)) {
- intel_haswell_enable_all_pins(codec, true);
- intel_haswell_fixup_enable_dp12(codec);
- }
+ err = alloc_generic_hdmi(codec);
+ if (err < 0)
+ return err;
+ spec = codec->spec;
- /* For Valleyview/Cherryview, only the display codec is in the display
- * power well and can use link_power ops to request/release the power.
- * For Haswell/Broadwell, the controller is also in the power well and
+ intel_haswell_enable_all_pins(codec, true);
+ intel_haswell_fixup_enable_dp12(codec);
+
+ /* For Haswell/Broadwell, the controller is also in the power well and
* can cover the codec power request, and so need not set this flag.
- * For previous platforms, there is no such power well feature.
*/
- if (is_valleyview_plus(codec) || is_skylake(codec) ||
- is_broxton(codec))
+ if (!is_haswell(codec) && !is_broadwell(codec))
codec->core.link_power_control = 1;
- if (hdmi_parse_codec(codec) < 0) {
- if (spec->i915_bound)
- snd_hdac_i915_exit(&codec->bus->core);
- codec->spec = NULL;
- kfree(spec);
- return -EINVAL;
+ codec->patch_ops.set_power_state = haswell_set_power_state;
+ codec->dp_mst = true;
+ codec->depop_delay = 0;
+ codec->auto_runtime_pm = 1;
+
+ spec->ops.setup_stream = i915_hsw_setup_stream;
+ spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
+
+ err = hdmi_parse_codec(codec);
+ if (err < 0) {
+ generic_spec_free(codec);
+ return err;
}
- codec->patch_ops = generic_hdmi_patch_ops;
- if (is_haswell_plus(codec)) {
- codec->patch_ops.set_power_state = haswell_set_power_state;
- codec->dp_mst = true;
+
+ generic_hdmi_init_per_pins(codec);
+ register_i915_notifier(codec);
+ return 0;
+}
+
+/* Intel Baytrail and Braswell; with eld notifier */
+static int patch_i915_byt_hdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ /* requires i915 binding */
+ if (!codec->bus->core.audio_component) {
+ codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
+ return -ENODEV;
}
- /* Enable runtime pm for HDMI audio codec of HSW/BDW/SKL/BYT/BSW */
- if (is_haswell_plus(codec) || is_valleyview_plus(codec))
- codec->auto_runtime_pm = 1;
+ err = alloc_generic_hdmi(codec);
+ if (err < 0)
+ return err;
+ spec = codec->spec;
- generic_hdmi_init_per_pins(codec);
+ /* For Valleyview/Cherryview, only the display codec is in the display
+ * power well and can use link_power ops to request/release the power.
+ */
+ codec->core.link_power_control = 1;
+ codec->depop_delay = 0;
+ codec->auto_runtime_pm = 1;
- if (codec_has_acomp(codec)) {
- codec->depop_delay = 0;
- spec->i915_audio_ops.audio_ptr = codec;
- /* intel_audio_codec_enable() or intel_audio_codec_disable()
- * will call pin_eld_notify with using audio_ptr pointer
- * We need make sure audio_ptr is really setup
- */
- wmb();
- spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
- snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
+ spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
+
+ err = hdmi_parse_codec(codec);
+ if (err < 0) {
+ generic_spec_free(codec);
+ return err;
}
- WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec));
+ generic_hdmi_init_per_pins(codec);
+ register_i915_notifier(codec);
+ return 0;
+}
+
+/* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */
+static int patch_i915_cpt_hdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ /* no i915 component should have been bound before this */
+ if (WARN_ON(codec->bus->core.audio_component))
+ return -EBUSY;
+
+ err = alloc_generic_hdmi(codec);
+ if (err < 0)
+ return err;
+ spec = codec->spec;
+
+ /* Try to bind with i915 now */
+ err = snd_hdac_i915_init(&codec->bus->core);
+ if (err < 0)
+ goto error;
+ spec->i915_bound = true;
+
+ err = hdmi_parse_codec(codec);
+ if (err < 0)
+ goto error;
+
+ generic_hdmi_init_per_pins(codec);
+ register_i915_notifier(codec);
return 0;
+
+ error:
+ generic_spec_free(codec);
+ return err;
}
/*
@@ -3401,6 +3549,9 @@ static int patch_atihdmi(struct hda_codec *codec)
spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup;
spec->ops.setup_stream = atihdmi_setup_stream;
+ spec->chmap.ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel;
+ spec->chmap.ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel;
+
if (!has_amd_full_remap_support(codec)) {
/* override to ATI/AMD-specific versions with pairwise mapping */
spec->chmap.ops.chmap_cea_alloc_validate_get_type =
@@ -3408,10 +3559,6 @@ static int patch_atihdmi(struct hda_codec *codec)
spec->chmap.ops.cea_alloc_to_tlv_chmap =
atihdmi_paired_cea_alloc_to_tlv_chmap;
spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate;
- spec->chmap.ops.pin_get_slot_channel =
- atihdmi_pin_get_slot_channel;
- spec->chmap.ops.pin_set_slot_channel =
- atihdmi_pin_set_slot_channel;
}
/* ATI/AMD converters do not advertise all of their capabilities */
@@ -3493,21 +3640,21 @@ HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi),
HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi),
HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_i915_cpt_hdmi),
HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_i915_cpt_hdmi),
+HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_i915_cpt_hdmi),
+HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_i915_cpt_hdmi),
+HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi),
+HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi),
HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi),
/* special ID for generic HDMI */
HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index ac4490a96863..900bfbc3368c 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -335,6 +335,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
case 0x10ec0283:
case 0x10ec0286:
case 0x10ec0288:
+ case 0x10ec0295:
case 0x10ec0298:
alc_update_coef_idx(codec, 0x10, 1<<9, 0);
break;
@@ -342,6 +343,14 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
case 0x10ec0293:
alc_update_coef_idx(codec, 0xa, 1<<13, 0);
break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ case 0x10ec0700:
+ case 0x10ec0701:
+ case 0x10ec0703:
+ alc_update_coef_idx(codec, 0x10, 1<<15, 0);
+ break;
case 0x10ec0662:
if ((coef & 0x00f0) == 0x0030)
alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
@@ -902,6 +911,7 @@ static struct alc_codec_rename_pci_table rename_pci_tbl[] = {
{ 0x10ec0298, 0x1028, 0, "ALC3266" },
{ 0x10ec0256, 0x1028, 0, "ALC3246" },
{ 0x10ec0225, 0x1028, 0, "ALC3253" },
+ { 0x10ec0295, 0x1028, 0, "ALC3254" },
{ 0x10ec0670, 0x1025, 0, "ALC669X" },
{ 0x10ec0676, 0x1025, 0, "ALC679X" },
{ 0x10ec0282, 0x1043, 0, "ALC3229" },
@@ -2647,6 +2657,8 @@ enum {
ALC269_TYPE_ALC255,
ALC269_TYPE_ALC256,
ALC269_TYPE_ALC225,
+ ALC269_TYPE_ALC294,
+ ALC269_TYPE_ALC700,
};
/*
@@ -2677,6 +2689,8 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
case ALC269_TYPE_ALC255:
case ALC269_TYPE_ALC256:
case ALC269_TYPE_ALC225:
+ case ALC269_TYPE_ALC294:
+ case ALC269_TYPE_ALC700:
ssids = alc269_ssids;
break;
default:
@@ -3609,13 +3623,20 @@ static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
static void alc_headset_mode_unplugged(struct hda_codec *codec)
{
static struct coef_fw coef0255[] = {
- WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */
WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */
UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/
WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */
WRITE_COEFEX(0x57, 0x03, 0x8aa6), /* Direct Drive HP Amp control */
{}
};
+ static struct coef_fw coef0255_1[] = {
+ WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */
+ {}
+ };
+ static struct coef_fw coef0256[] = {
+ WRITE_COEF(0x1b, 0x0c4b), /* LDO and MISC control */
+ {}
+ };
static struct coef_fw coef0233[] = {
WRITE_COEF(0x1b, 0x0c0b),
WRITE_COEF(0x45, 0xc429),
@@ -3668,7 +3689,11 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
switch (codec->core.vendor_id) {
case 0x10ec0255:
+ alc_process_coef_fw(codec, coef0255_1);
+ alc_process_coef_fw(codec, coef0255);
+ break;
case 0x10ec0256:
+ alc_process_coef_fw(codec, coef0256);
alc_process_coef_fw(codec, coef0255);
break;
case 0x10ec0233:
@@ -3690,6 +3715,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
alc_process_coef_fw(codec, coef0668);
break;
case 0x10ec0225:
+ case 0x10ec0295:
alc_process_coef_fw(codec, coef0225);
break;
}
@@ -3790,6 +3816,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
break;
case 0x10ec0225:
+ case 0x10ec0295:
alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10);
snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
alc_process_coef_fw(codec, coef0225);
@@ -3847,6 +3874,7 @@ static void alc_headset_mode_default(struct hda_codec *codec)
switch (codec->core.vendor_id) {
case 0x10ec0225:
+ case 0x10ec0295:
alc_process_coef_fw(codec, coef0225);
break;
case 0x10ec0255:
@@ -3884,6 +3912,12 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
WRITE_COEFEX(0x57, 0x03, 0x8ea6),
{}
};
+ static struct coef_fw coef0256[] = {
+ WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */
+ WRITE_COEF(0x1b, 0x0c6b),
+ WRITE_COEFEX(0x57, 0x03, 0x8ea6),
+ {}
+ };
static struct coef_fw coef0233[] = {
WRITE_COEF(0x45, 0xd429),
WRITE_COEF(0x1b, 0x0c2b),
@@ -3924,9 +3958,11 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
switch (codec->core.vendor_id) {
case 0x10ec0255:
- case 0x10ec0256:
alc_process_coef_fw(codec, coef0255);
break;
+ case 0x10ec0256:
+ alc_process_coef_fw(codec, coef0256);
+ break;
case 0x10ec0233:
case 0x10ec0283:
alc_process_coef_fw(codec, coef0233);
@@ -3950,6 +3986,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
alc_process_coef_fw(codec, coef0688);
break;
case 0x10ec0225:
+ case 0x10ec0295:
alc_process_coef_fw(codec, coef0225);
break;
}
@@ -3965,6 +4002,12 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
WRITE_COEFEX(0x57, 0x03, 0x8ea6),
{}
};
+ static struct coef_fw coef0256[] = {
+ WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */
+ WRITE_COEF(0x1b, 0x0c6b),
+ WRITE_COEFEX(0x57, 0x03, 0x8ea6),
+ {}
+ };
static struct coef_fw coef0233[] = {
WRITE_COEF(0x45, 0xe429),
WRITE_COEF(0x1b, 0x0c2b),
@@ -4005,9 +4048,11 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
switch (codec->core.vendor_id) {
case 0x10ec0255:
- case 0x10ec0256:
alc_process_coef_fw(codec, coef0255);
break;
+ case 0x10ec0256:
+ alc_process_coef_fw(codec, coef0256);
+ break;
case 0x10ec0233:
case 0x10ec0283:
alc_process_coef_fw(codec, coef0233);
@@ -4031,6 +4076,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
alc_process_coef_fw(codec, coef0688);
break;
case 0x10ec0225:
+ case 0x10ec0295:
alc_process_coef_fw(codec, coef0225);
break;
}
@@ -4114,6 +4160,7 @@ static void alc_determine_headset_type(struct hda_codec *codec)
is_ctia = (val & 0x1c02) == 0x1c02;
break;
case 0x10ec0225:
+ case 0x10ec0295:
alc_process_coef_fw(codec, coef0225);
msleep(800);
val = alc_read_coef_idx(codec, 0x46);
@@ -4251,7 +4298,7 @@ static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
static void alc255_set_default_jack_type(struct hda_codec *codec)
{
/* Set to iphone type */
- static struct coef_fw fw[] = {
+ static struct coef_fw alc255fw[] = {
WRITE_COEF(0x1b, 0x880b),
WRITE_COEF(0x45, 0xd089),
WRITE_COEF(0x1b, 0x080b),
@@ -4259,7 +4306,22 @@ static void alc255_set_default_jack_type(struct hda_codec *codec)
WRITE_COEF(0x1b, 0x0c0b),
{}
};
- alc_process_coef_fw(codec, fw);
+ static struct coef_fw alc256fw[] = {
+ WRITE_COEF(0x1b, 0x884b),
+ WRITE_COEF(0x45, 0xd089),
+ WRITE_COEF(0x1b, 0x084b),
+ WRITE_COEF(0x46, 0x0004),
+ WRITE_COEF(0x1b, 0x0c4b),
+ {}
+ };
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, alc255fw);
+ break;
+ case 0x10ec0256:
+ alc_process_coef_fw(codec, alc256fw);
+ break;
+ }
msleep(30);
}
@@ -5459,8 +5521,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
- SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
+ SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13 9350", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE),
+ SND_PCI_QUIRK(0x1028, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_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),
@@ -5571,6 +5634,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK),
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, 0x2231, "Thinkpad T560", ALC292_FIXUP_TPT460),
SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC292_FIXUP_TPT460),
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),
@@ -5586,6 +5650,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x504a, "ThinkPad X260", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x504b, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
+ SND_PCI_QUIRK(0x17aa, 0x5050, "Thinkpad T560p", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x5053, "Thinkpad T460", ALC292_FIXUP_TPT460),
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),
@@ -5704,6 +5770,9 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x14, 0x90170110},
{0x21, 0x02211020}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170130},
+ {0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60140},
{0x14, 0x90170110},
{0x21, 0x02211020}),
@@ -5756,11 +5825,19 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x12, 0x90a60180},
{0x14, 0x90170130},
{0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5565", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60180},
+ {0x14, 0x90170120},
+ {0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60160},
{0x14, 0x90170120},
{0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60170},
+ {0x14, 0x90170120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC256_STANDARD_PINS),
SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4,
{0x12, 0x90a60130},
@@ -6026,8 +6103,22 @@ static int patch_alc269(struct hda_codec *codec)
alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/
break;
case 0x10ec0225:
+ case 0x10ec0295:
spec->codec_variant = ALC269_TYPE_ALC225;
break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ spec->codec_variant = ALC269_TYPE_ALC294;
+ break;
+ case 0x10ec0700:
+ case 0x10ec0701:
+ case 0x10ec0703:
+ spec->codec_variant = ALC269_TYPE_ALC700;
+ spec->gen.mixer_nid = 0; /* ALC700 does not have any loopback mixer path */
+ alc_update_coef_idx(codec, 0x4a, 0, 1 << 15); /* Combo jack auto trigger control */
+ break;
+
}
if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
@@ -6426,6 +6517,7 @@ enum {
ALC668_FIXUP_DELL_DISABLE_AAMIX,
ALC668_FIXUP_DELL_XPS13,
ALC662_FIXUP_ASUS_Nx50,
+ ALC668_FIXUP_ASUS_Nx51,
};
static const struct hda_fixup alc662_fixups[] = {
@@ -6672,6 +6764,15 @@ static const struct hda_fixup alc662_fixups[] = {
.chained = true,
.chain_id = ALC662_FIXUP_BASS_1A
},
+ [ALC668_FIXUP_ASUS_Nx51] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x1a, 0x90170151}, /* bass speaker */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_BASS_CHMAP,
+ },
};
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@@ -6694,11 +6795,14 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
+ SND_PCI_QUIRK(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE),
SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50),
SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A),
SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50),
SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16),
+ SND_PCI_QUIRK(0x1043, 0x177d, "ASUS N551", ALC668_FIXUP_ASUS_Nx51),
+ SND_PCI_QUIRK(0x1043, 0x17bd, "ASUS N751", ALC668_FIXUP_ASUS_Nx51),
SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16),
SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
@@ -6929,6 +7033,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269),
@@ -6939,6 +7044,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0274, "ALC274", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269),
@@ -6951,6 +7057,8 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0294, "ALC294", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0295, "ALC295", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),
@@ -6966,6 +7074,9 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662),
HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662),
HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680),
+ HDA_CODEC_ENTRY(0x10ec0700, "ALC700", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0701, "ALC701", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0703, "ALC703", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc882),
HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880),
HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882),
diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c
index 59ab6cee1ad8..f0955fd7a2e7 100644
--- a/sound/pci/hda/thinkpad_helper.c
+++ b/sound/pci/hda/thinkpad_helper.c
@@ -13,7 +13,7 @@ static void (*old_vmaster_hook)(void *, int);
static bool is_thinkpad(struct hda_codec *codec)
{
return (codec->core.subsystem_id >> 16 == 0x17aa) &&
- (acpi_dev_present("LEN0068") || acpi_dev_present("IBM0068"));
+ (acpi_dev_found("LEN0068") || acpi_dev_found("IBM0068"));
}
static void update_tpacpi_mute_led(void *private_data, int enabled)
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 8151318a69a2..9720a30dbfff 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -42,12 +42,6 @@
#include <asm/pgtable.h>
#include <asm/cacheflush.h>
-#ifdef CONFIG_KVM_GUEST
-#include <linux/kvm_para.h>
-#else
-#define kvm_para_available() (0)
-#endif
-
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455");
MODULE_LICENSE("GPL");
@@ -2972,25 +2966,17 @@ static int snd_intel8x0_inside_vm(struct pci_dev *pci)
goto fini;
}
- /* detect KVM and Parallels virtual environments */
- result = kvm_para_available();
-#ifdef X86_FEATURE_HYPERVISOR
- result = result || boot_cpu_has(X86_FEATURE_HYPERVISOR);
-#endif
- if (!result)
- goto fini;
-
/* check for known (emulated) devices */
+ result = 0;
if (pci->subsystem_vendor == PCI_SUBVENDOR_ID_REDHAT_QUMRANET &&
pci->subsystem_device == PCI_SUBDEVICE_ID_QEMU) {
/* KVM emulated sound, PCI SSID: 1af4:1100 */
msg = "enable KVM";
+ result = 1;
} else if (pci->subsystem_vendor == 0x1ab8) {
/* Parallels VM emulated sound, PCI SSID: 1ab8:xxxx */
msg = "enable Parallels VM";
- } else {
- msg = "disable (unknown or VT-d) VM";
- result = 0;
+ result = 1;
}
fini:
diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c
index f3d62020ef66..a80684bdc30d 100644
--- a/sound/pci/lx6464es/lx_core.c
+++ b/sound/pci/lx6464es/lx_core.c
@@ -644,7 +644,7 @@ static int lx_pipe_wait_for_state(struct lx6464es *chip, u32 pipe,
if (err < 0)
return err;
- if (current_state == state)
+ if (!err && current_state == state)
return 0;
mdelay(1);
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index b3afae990e39..f3fb98f0a995 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -43,6 +43,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AK5386
select SND_SOC_ALC5623 if I2C
select SND_SOC_ALC5632 if I2C
+ select SND_SOC_BT_SCO
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
select SND_SOC_CS35L32 if I2C
select SND_SOC_CS42L51_I2C if I2C
@@ -64,7 +65,6 @@ config SND_SOC_ALL_CODECS
select SND_SOC_DA732X if I2C
select SND_SOC_DA9055 if I2C
select SND_SOC_DMIC
- select SND_SOC_BT_SCO
select SND_SOC_ES8328_SPI if SPI_MASTER
select SND_SOC_ES8328_I2C if I2C
select SND_SOC_GTM601
@@ -79,6 +79,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX98090 if I2C
select SND_SOC_MAX98095 if I2C
select SND_SOC_MAX98357A if GPIOLIB
+ select SND_SOC_MAX98371 if I2C
select SND_SOC_MAX9867 if I2C
select SND_SOC_MAX98925 if I2C
select SND_SOC_MAX98926 if I2C
@@ -126,12 +127,14 @@ config SND_SOC_ALL_CODECS
select SND_SOC_TAS2552 if I2C
select SND_SOC_TAS5086 if I2C
select SND_SOC_TAS571X if I2C
+ select SND_SOC_TAS5720 if I2C
select SND_SOC_TFA9879 if I2C
select SND_SOC_TLV320AIC23_I2C if I2C
select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
select SND_SOC_TLV320AIC26 if SPI_MASTER
select SND_SOC_TLV320AIC31XX if I2C
- select SND_SOC_TLV320AIC32X4 if I2C
+ select SND_SOC_TLV320AIC32X4_I2C if I2C
+ select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER
select SND_SOC_TLV320AIC3X if I2C
select SND_SOC_TPA6130A2 if I2C
select SND_SOC_TLV320DAC33 if I2C
@@ -367,6 +370,9 @@ config SND_SOC_ALC5623
config SND_SOC_ALC5632
tristate
+config SND_SOC_BT_SCO
+ tristate
+
config SND_SOC_CQ0093VC
tristate
@@ -473,16 +479,14 @@ config SND_SOC_DA732X
config SND_SOC_DA9055
tristate
-config SND_SOC_BT_SCO
- tristate
-
config SND_SOC_DMIC
tristate
config SND_SOC_HDMI_CODEC
- tristate
- select SND_PCM_ELD
- select SND_PCM_IEC958
+ tristate
+ select SND_PCM_ELD
+ select SND_PCM_IEC958
+ select HDMI
config SND_SOC_ES8328
tristate "Everest Semi ES8328 CODEC"
@@ -529,6 +533,9 @@ config SND_SOC_MAX98095
config SND_SOC_MAX98357A
tristate
+config SND_SOC_MAX98371
+ tristate
+
config SND_SOC_MAX9867
tristate
@@ -748,8 +755,15 @@ config SND_SOC_TAS5086
depends on I2C
config SND_SOC_TAS571X
- tristate "Texas Instruments TAS5711/TAS5717/TAS5719 power amplifiers"
+ tristate "Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 power amplifiers"
+ depends on I2C
+
+config SND_SOC_TAS5720
+ tristate "Texas Instruments TAS5720 Mono Audio amplifier"
depends on I2C
+ help
+ Enable support for Texas Instruments TAS5720L/M high-efficiency mono
+ Class-D audio power amplifiers.
config SND_SOC_TFA9879
tristate "NXP Semiconductors TFA9879 amplifier"
@@ -780,6 +794,16 @@ config SND_SOC_TLV320AIC31XX
config SND_SOC_TLV320AIC32X4
tristate
+config SND_SOC_TLV320AIC32X4_I2C
+ tristate
+ depends on I2C
+ select SND_SOC_TLV320AIC32X4
+
+config SND_SOC_TLV320AIC32X4_SPI
+ tristate
+ depends on SPI_MASTER
+ select SND_SOC_TLV320AIC32X4
+
config SND_SOC_TLV320AIC3X
tristate "Texas Instruments TLV320AIC3x CODECs"
depends on I2C
@@ -920,7 +944,8 @@ config SND_SOC_WM8955
tristate
config SND_SOC_WM8960
- tristate
+ tristate "Wolfson Microelectronics WM8960 CODEC"
+ depends on I2C
config SND_SOC_WM8961
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index b7b99416537f..0f548fd34ca3 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -32,6 +32,7 @@ snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
snd-soc-ak5386-objs := ak5386.o
snd-soc-arizona-objs := arizona.o
+snd-soc-bt-sco-objs := bt-sco.o
snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cs35l32-objs := cs35l32.o
snd-soc-cs42l51-objs := cs42l51.o
@@ -55,7 +56,6 @@ snd-soc-da7218-objs := da7218.o
snd-soc-da7219-objs := da7219.o da7219-aad.o
snd-soc-da732x-objs := da732x.o
snd-soc-da9055-objs := da9055.o
-snd-soc-bt-sco-objs := bt-sco.o
snd-soc-dmic-objs := dmic.o
snd-soc-es8328-objs := es8328.o
snd-soc-es8328-i2c-objs := es8328-i2c.o
@@ -74,6 +74,7 @@ snd-soc-max98088-objs := max98088.o
snd-soc-max98090-objs := max98090.o
snd-soc-max98095-objs := max98095.o
snd-soc-max98357a-objs := max98357a.o
+snd-soc-max98371-objs := max98371.o
snd-soc-max9867-objs := max9867.o
snd-soc-max98925-objs := max98925.o
snd-soc-max98926-objs := max98926.o
@@ -131,6 +132,7 @@ snd-soc-stac9766-objs := stac9766.o
snd-soc-sti-sas-objs := sti-sas.o
snd-soc-tas5086-objs := tas5086.o
snd-soc-tas571x-objs := tas571x.o
+snd-soc-tas5720-objs := tas5720.o
snd-soc-tfa9879-objs := tfa9879.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
@@ -138,6 +140,8 @@ snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o
snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
+snd-soc-tlv320aic32x4-i2c-objs := tlv320aic32x4-i2c.o
+snd-soc-tlv320aic32x4-spi-objs := tlv320aic32x4-spi.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
snd-soc-tlv320dac33-objs := tlv320dac33.o
snd-soc-ts3a227e-objs := ts3a227e.o
@@ -243,6 +247,7 @@ obj-$(CONFIG_SND_SOC_AK5386) += snd-soc-ak5386.o
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o
obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o
+obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
@@ -266,7 +271,6 @@ obj-$(CONFIG_SND_SOC_DA7218) += snd-soc-da7218.o
obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
-obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
@@ -339,6 +343,7 @@ obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o
obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
+obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
@@ -346,6 +351,8 @@ obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC31XX) += snd-soc-tlv320aic31xx.o
obj-$(CONFIG_SND_SOC_TLV320AIC32X4) += snd-soc-tlv320aic32x4.o
+obj-$(CONFIG_SND_SOC_TLV320AIC32X4_I2C) += snd-soc-tlv320aic32x4-i2c.o
+obj-$(CONFIG_SND_SOC_TLV320AIC32X4_SPI) += snd-soc-tlv320aic32x4-spi.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o
obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
index 647f69de6baa..5013d2ba0c10 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -146,6 +146,7 @@ static const struct regmap_config ak4613_regmap_cfg = {
.max_register = 0x16,
.reg_defaults = ak4613_reg,
.num_reg_defaults = ARRAY_SIZE(ak4613_reg),
+ .cache_type = REGCACHE_RBTREE,
};
static const struct of_device_id ak4613_of_match[] = {
@@ -530,7 +531,6 @@ static int ak4613_i2c_remove(struct i2c_client *client)
static struct i2c_driver ak4613_i2c_driver = {
.driver = {
.name = "ak4613-codec",
- .owner = THIS_MODULE,
.of_match_table = ak4613_of_match,
},
.probe = ak4613_i2c_probe,
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 1ee8506c06c7..4d8b9e49e8d6 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -560,6 +560,7 @@ static const struct regmap_config ak4642_regmap = {
.max_register = FIL1_3,
.reg_defaults = ak4642_reg,
.num_reg_defaults = NUM_AK4642_REG_DEFAULTS,
+ .cache_type = REGCACHE_RBTREE,
};
static const struct regmap_config ak4643_regmap = {
@@ -568,6 +569,7 @@ static const struct regmap_config ak4643_regmap = {
.max_register = SPK_MS,
.reg_defaults = ak4643_reg,
.num_reg_defaults = ARRAY_SIZE(ak4643_reg),
+ .cache_type = REGCACHE_RBTREE,
};
static const struct regmap_config ak4648_regmap = {
@@ -576,6 +578,7 @@ static const struct regmap_config ak4648_regmap = {
.max_register = EQ_FBEQE,
.reg_defaults = ak4648_reg,
.num_reg_defaults = ARRAY_SIZE(ak4648_reg),
+ .cache_type = REGCACHE_RBTREE,
};
static const struct ak4642_drvdata ak4642_drvdata = {
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index d6f4abbbf8a7..fb3885fe0afb 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -226,6 +226,7 @@ static int v253_open(struct tty_struct *tty)
if (!tty->disc_data)
return -ENODEV;
+ tty->receive_room = 16;
if (tty->ops->write(tty, v253_init, len) != len) {
ret = -EIO;
goto err;
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 181cd3bf0b92..2abb742fc47b 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -1474,6 +1474,11 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec)
* exit, we call pm_runtime_suspend() so that will do for us
*/
hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev));
+ if (!hlink) {
+ dev_err(&edev->hdac.dev, "hdac link not found\n");
+ return -EIO;
+ }
+
snd_hdac_ext_bus_link_get(edev->ebus, hlink);
ret = create_fill_widget_route_map(dapm);
@@ -1634,6 +1639,11 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
/* hold the ref while we probe */
hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev));
+ if (!hlink) {
+ dev_err(&edev->hdac.dev, "hdac link not found\n");
+ return -EIO;
+ }
+
snd_hdac_ext_bus_link_get(edev->ebus, hlink);
hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
@@ -1744,6 +1754,11 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
}
hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
+ if (!hlink) {
+ dev_err(dev, "hdac link not found\n");
+ return -EIO;
+ }
+
snd_hdac_ext_bus_link_put(ebus, hlink);
return 0;
@@ -1765,6 +1780,11 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
return 0;
hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
+ if (!hlink) {
+ dev_err(dev, "hdac link not found\n");
+ return -EIO;
+ }
+
snd_hdac_ext_bus_link_get(ebus, hlink);
err = snd_hdac_display_power(bus, true);
diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c
new file mode 100644
index 000000000000..cf0a39bb631a
--- /dev/null
+++ b/sound/soc/codecs/max98371.c
@@ -0,0 +1,441 @@
+/*
+ * max98371.c -- ALSA SoC Stereo MAX98371 driver
+ *
+ * Copyright 2015-16 Maxim Integrated Products
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98371.h"
+
+static const char *const monomix_text[] = {
+ "Left", "Right", "LeftRightDiv2",
+};
+
+static const char *const hpf_cutoff_txt[] = {
+ "Disable", "DC Block", "50Hz",
+ "100Hz", "200Hz", "400Hz", "800Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(max98371_monomix, MAX98371_MONOMIX_CFG, 0,
+ monomix_text);
+
+static SOC_ENUM_SINGLE_DECL(max98371_hpf_cutoff, MAX98371_HPF, 0,
+ hpf_cutoff_txt);
+
+static const DECLARE_TLV_DB_RANGE(max98371_dht_min_gain,
+ 0, 1, TLV_DB_SCALE_ITEM(537, 66, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(677, 82, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(852, 104, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(1072, 131, 0),
+ 8, 9, TLV_DB_SCALE_ITEM(1350, 165, 0),
+ 10, 11, TLV_DB_SCALE_ITEM(1699, 101, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98371_dht_max_gain,
+ 0, 1, TLV_DB_SCALE_ITEM(537, 66, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(677, 82, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(852, 104, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(1072, 131, 0),
+ 8, 9, TLV_DB_SCALE_ITEM(1350, 165, 0),
+ 10, 11, TLV_DB_SCALE_ITEM(1699, 208, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98371_dht_rot_gain,
+ 0, 1, TLV_DB_SCALE_ITEM(-50, -50, 0),
+ 2, 6, TLV_DB_SCALE_ITEM(-100, -100, 0),
+ 7, 8, TLV_DB_SCALE_ITEM(-800, -200, 0),
+ 9, 11, TLV_DB_SCALE_ITEM(-1200, -300, 0),
+ 12, 13, TLV_DB_SCALE_ITEM(-2000, -200, 0),
+ 14, 15, TLV_DB_SCALE_ITEM(-2500, -500, 0),
+);
+
+static const struct reg_default max98371_reg[] = {
+ { 0x01, 0x00 },
+ { 0x02, 0x00 },
+ { 0x03, 0x00 },
+ { 0x04, 0x00 },
+ { 0x05, 0x00 },
+ { 0x06, 0x00 },
+ { 0x07, 0x00 },
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0A, 0x00 },
+ { 0x10, 0x06 },
+ { 0x11, 0x08 },
+ { 0x14, 0x80 },
+ { 0x15, 0x00 },
+ { 0x16, 0x00 },
+ { 0x18, 0x00 },
+ { 0x19, 0x00 },
+ { 0x1C, 0x00 },
+ { 0x1D, 0x00 },
+ { 0x1E, 0x00 },
+ { 0x1F, 0x00 },
+ { 0x20, 0x00 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+ { 0x23, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x27, 0x00 },
+ { 0x28, 0x00 },
+ { 0x29, 0x00 },
+ { 0x2A, 0x00 },
+ { 0x2B, 0x00 },
+ { 0x2C, 0x00 },
+ { 0x2D, 0x00 },
+ { 0x2E, 0x0B },
+ { 0x31, 0x00 },
+ { 0x32, 0x18 },
+ { 0x33, 0x00 },
+ { 0x34, 0x00 },
+ { 0x36, 0x00 },
+ { 0x37, 0x00 },
+ { 0x38, 0x00 },
+ { 0x39, 0x00 },
+ { 0x3A, 0x00 },
+ { 0x3B, 0x00 },
+ { 0x3C, 0x00 },
+ { 0x3D, 0x00 },
+ { 0x3E, 0x00 },
+ { 0x3F, 0x00 },
+ { 0x40, 0x00 },
+ { 0x41, 0x00 },
+ { 0x42, 0x00 },
+ { 0x43, 0x00 },
+ { 0x4A, 0x00 },
+ { 0x4B, 0x00 },
+ { 0x4C, 0x00 },
+ { 0x4D, 0x00 },
+ { 0x4E, 0x00 },
+ { 0x50, 0x00 },
+ { 0x51, 0x00 },
+ { 0x55, 0x00 },
+ { 0x58, 0x00 },
+ { 0x59, 0x00 },
+ { 0x5C, 0x00 },
+ { 0xFF, 0x43 },
+};
+
+static bool max98371_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98371_IRQ_CLEAR1:
+ case MAX98371_IRQ_CLEAR2:
+ case MAX98371_IRQ_CLEAR3:
+ case MAX98371_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98371_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98371_SOFT_RESET:
+ return false;
+ default:
+ return true;
+ }
+};
+
+static const DECLARE_TLV_DB_RANGE(max98371_gain_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(0, 50, 0),
+ 8, 10, TLV_DB_SCALE_ITEM(400, 100, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(max98371_noload_gain_tlv,
+ 0, 11, TLV_DB_SCALE_ITEM(950, 100, 0),
+);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -6300, 50, 1);
+
+static const struct snd_kcontrol_new max98371_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", MAX98371_GAIN,
+ MAX98371_GAIN_SHIFT, (1<<MAX98371_GAIN_WIDTH)-1, 0,
+ max98371_gain_tlv),
+ SOC_SINGLE_TLV("Digital Volume", MAX98371_DIGITAL_GAIN, 0,
+ (1<<MAX98371_DIGITAL_GAIN_WIDTH)-1, 1, digital_tlv),
+ SOC_SINGLE_TLV("Speaker DHT Max Volume", MAX98371_GAIN,
+ 0, (1<<MAX98371_DHT_MAX_WIDTH)-1, 0,
+ max98371_dht_max_gain),
+ SOC_SINGLE_TLV("Speaker DHT Min Volume", MAX98371_DHT_GAIN,
+ 0, (1<<MAX98371_DHT_GAIN_WIDTH)-1, 0,
+ max98371_dht_min_gain),
+ SOC_SINGLE_TLV("Speaker DHT Rotation Volume", MAX98371_DHT_GAIN,
+ 0, (1<<MAX98371_DHT_ROT_WIDTH)-1, 0,
+ max98371_dht_rot_gain),
+ SOC_SINGLE("DHT Attack Step", MAX98371_DHT, MAX98371_DHT_STEP, 3, 0),
+ SOC_SINGLE("DHT Attack Rate", MAX98371_DHT, 0, 7, 0),
+ SOC_ENUM("Monomix Select", max98371_monomix),
+ SOC_ENUM("HPF Cutoff", max98371_hpf_cutoff),
+};
+
+static int max98371_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98371_priv *max98371 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ dev_err(codec->dev, "DAI clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val |= 0;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val |= MAX98371_DAI_RIGHT;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val |= MAX98371_DAI_LEFT;
+ break;
+ default:
+ dev_err(codec->dev, "DAI wrong mode unsupported");
+ return -EINVAL;
+ }
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MODE_MASK, val);
+ return 0;
+}
+
+static int max98371_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98371_priv *max98371 = snd_soc_codec_get_drvdata(codec);
+ int blr_clk_ratio, ch_size, channels = params_channels(params);
+ int rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_16);
+ ch_size = 8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_16);
+ ch_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_32);
+ ch_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ regmap_update_bits(max98371->regmap, MAX98371_FMT,
+ MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_32);
+ ch_size = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* BCLK/LRCLK ratio calculation */
+ blr_clk_ratio = channels * ch_size;
+ switch (blr_clk_ratio) {
+ case 32:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_DAI_CLK,
+ MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_32);
+ break;
+ case 48:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_DAI_CLK,
+ MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_48);
+ break;
+ case 64:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_DAI_CLK,
+ MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_64);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (rate) {
+ case 32000:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_32);
+ break;
+ case 44100:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_44);
+ break;
+ case 48000:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_48);
+ break;
+ case 88200:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_88);
+ break;
+ case 96000:
+ regmap_update_bits(max98371->regmap,
+ MAX98371_SPK_SR,
+ MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_96);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* enabling both the RX channels*/
+ regmap_update_bits(max98371->regmap, MAX98371_MONOMIX_SRC,
+ MAX98371_MONOMIX_SRC_MASK, MONOMIX_RX_0_1);
+ regmap_update_bits(max98371->regmap, MAX98371_DAI_CHANNEL,
+ MAX98371_CHANNEL_MASK, MAX98371_CHANNEL_MASK);
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget max98371_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", NULL, MAX98371_SPK_ENABLE, 0, 0),
+ SND_SOC_DAPM_SUPPLY("Global Enable", MAX98371_GLOBAL_ENABLE,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("SPK_OUT"),
+};
+
+static const struct snd_soc_dapm_route max98371_audio_map[] = {
+ {"DAC", NULL, "HiFi Playback"},
+ {"SPK_OUT", NULL, "DAC"},
+ {"SPK_OUT", NULL, "Global Enable"},
+};
+
+#define MAX98371_RATES SNDRV_PCM_RATE_8000_48000
+#define MAX98371_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
+ SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
+
+static const struct snd_soc_dai_ops max98371_dai_ops = {
+ .set_fmt = max98371_dai_set_fmt,
+ .hw_params = max98371_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver max98371_dai[] = {
+ {
+ .name = "max98371-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = MAX98371_FORMATS,
+ },
+ .ops = &max98371_dai_ops,
+ }
+};
+
+static const struct snd_soc_codec_driver max98371_codec = {
+ .controls = max98371_snd_controls,
+ .num_controls = ARRAY_SIZE(max98371_snd_controls),
+ .dapm_routes = max98371_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98371_audio_map),
+ .dapm_widgets = max98371_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98371_dapm_widgets),
+};
+
+static const struct regmap_config max98371_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX98371_VERSION,
+ .reg_defaults = max98371_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98371_reg),
+ .volatile_reg = max98371_volatile_register,
+ .readable_reg = max98371_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max98371_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max98371_priv *max98371;
+ int ret, reg;
+
+ max98371 = devm_kzalloc(&i2c->dev,
+ sizeof(*max98371), GFP_KERNEL);
+ if (!max98371)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max98371);
+ max98371->regmap = devm_regmap_init_i2c(i2c, &max98371_regmap);
+ if (IS_ERR(max98371->regmap)) {
+ ret = PTR_ERR(max98371->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(max98371->regmap, MAX98371_VERSION, &reg);
+ if (ret < 0) {
+ dev_info(&i2c->dev, "device error %d\n", ret);
+ return ret;
+ }
+ dev_info(&i2c->dev, "device version %x\n", reg);
+
+ ret = snd_soc_register_codec(&i2c->dev, &max98371_codec,
+ max98371_dai, ARRAY_SIZE(max98371_dai));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int max98371_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id max98371_i2c_id[] = {
+ { "max98371", 0 },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98371_i2c_id);
+
+static const struct of_device_id max98371_of_match[] = {
+ { .compatible = "maxim,max98371", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98371_of_match);
+
+static struct i2c_driver max98371_i2c_driver = {
+ .driver = {
+ .name = "max98371",
+ .owner = THIS_MODULE,
+ .pm = NULL,
+ .of_match_table = of_match_ptr(max98371_of_match),
+ },
+ .probe = max98371_i2c_probe,
+ .remove = max98371_i2c_remove,
+ .id_table = max98371_i2c_id,
+};
+
+module_i2c_driver(max98371_i2c_driver);
+
+MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC MAX98371 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98371.h b/sound/soc/codecs/max98371.h
new file mode 100644
index 000000000000..9f6330964d98
--- /dev/null
+++ b/sound/soc/codecs/max98371.h
@@ -0,0 +1,67 @@
+/*
+ * max98371.h -- MAX98371 ALSA SoC Audio driver
+ *
+ * Copyright 2011-2012 Maxim Integrated Products
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MAX98371_H
+#define _MAX98371_H
+
+#define MAX98371_IRQ_CLEAR1 0x01
+#define MAX98371_IRQ_CLEAR2 0x02
+#define MAX98371_IRQ_CLEAR3 0x03
+#define MAX98371_DAI_CLK 0x10
+#define MAX98371_DAI_BSEL_MASK 0xF
+#define MAX98371_DAI_BSEL_32 2
+#define MAX98371_DAI_BSEL_48 3
+#define MAX98371_DAI_BSEL_64 4
+#define MAX98371_SPK_SR 0x11
+#define MAX98371_SPK_SR_MASK 0xF
+#define MAX98371_SPK_SR_32 6
+#define MAX98371_SPK_SR_44 7
+#define MAX98371_SPK_SR_48 8
+#define MAX98371_SPK_SR_88 10
+#define MAX98371_SPK_SR_96 11
+#define MAX98371_DAI_CHANNEL 0x15
+#define MAX98371_CHANNEL_MASK 0x3
+#define MAX98371_MONOMIX_SRC 0x18
+#define MAX98371_MONOMIX_CFG 0x19
+#define MAX98371_HPF 0x1C
+#define MAX98371_MONOMIX_SRC_MASK 0xFF
+#define MONOMIX_RX_0_1 ((0x1)<<(4))
+#define M98371_DAI_CHANNEL_I2S 0x3
+#define MAX98371_DIGITAL_GAIN 0x2D
+#define MAX98371_DIGITAL_GAIN_WIDTH 0x7
+#define MAX98371_GAIN 0x2E
+#define MAX98371_GAIN_SHIFT 0x4
+#define MAX98371_GAIN_WIDTH 0x4
+#define MAX98371_DHT_MAX_WIDTH 4
+#define MAX98371_FMT 0x14
+#define MAX98371_CHANSZ_WIDTH 6
+#define MAX98371_FMT_MASK ((0x3)<<(MAX98371_CHANSZ_WIDTH))
+#define MAX98371_FMT_MODE_MASK ((0x7)<<(3))
+#define MAX98371_DAI_LEFT ((0x1)<<(3))
+#define MAX98371_DAI_RIGHT ((0x2)<<(3))
+#define MAX98371_DAI_CHANSZ_16 ((1)<<(MAX98371_CHANSZ_WIDTH))
+#define MAX98371_DAI_CHANSZ_24 ((2)<<(MAX98371_CHANSZ_WIDTH))
+#define MAX98371_DAI_CHANSZ_32 ((3)<<(MAX98371_CHANSZ_WIDTH))
+#define MAX98371_DHT 0x32
+#define MAX98371_DHT_STEP 0x3
+#define MAX98371_DHT_GAIN 0x31
+#define MAX98371_DHT_GAIN_WIDTH 0x4
+#define MAX98371_DHT_ROT_WIDTH 0x4
+#define MAX98371_SPK_ENABLE 0x4A
+#define MAX98371_GLOBAL_ENABLE 0x50
+#define MAX98371_SOFT_RESET 0x51
+#define MAX98371_VERSION 0xFF
+
+
+struct max98371_priv {
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+};
+#endif
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index a1aaffc20862..f80cfe4d2ef2 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -276,6 +276,8 @@ static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic)
} else {
*mic = false;
regmap_write(rt298->regmap, RT298_SET_MIC1, 0x20);
+ regmap_update_bits(rt298->regmap,
+ RT298_CBJ_CTRL1, 0x0400, 0x0000);
}
} else {
regmap_read(rt298->regmap, RT298_GET_HP_SENSE, &buf);
@@ -482,6 +484,26 @@ static int rt298_adc_event(struct snd_soc_dapm_widget *w,
snd_soc_update_bits(codec,
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
0x7080, 0x7000);
+ /* If MCLK doesn't exist, reset AD filter */
+ if (!(snd_soc_read(codec, RT298_VAD_CTRL) & 0x200)) {
+ pr_info("NO MCLK\n");
+ switch (nid) {
+ case RT298_ADC_IN1:
+ snd_soc_update_bits(codec,
+ RT298_D_FILTER_CTRL, 0x2, 0x2);
+ mdelay(10);
+ snd_soc_update_bits(codec,
+ RT298_D_FILTER_CTRL, 0x2, 0x0);
+ break;
+ case RT298_ADC_IN2:
+ snd_soc_update_bits(codec,
+ RT298_D_FILTER_CTRL, 0x4, 0x4);
+ mdelay(10);
+ snd_soc_update_bits(codec,
+ RT298_D_FILTER_CTRL, 0x4, 0x0);
+ break;
+ }
+ }
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec,
@@ -520,30 +542,12 @@ static int rt298_mic1_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static int rt298_vref_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec,
- RT298_CBJ_CTRL1, 0x0400, 0x0000);
- mdelay(50);
- break;
- default:
- return 0;
- }
-
- return 0;
-}
-
static const struct snd_soc_dapm_widget rt298_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("HV", 1, RT298_POWER_CTRL1,
12, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("VREF", RT298_POWER_CTRL1,
- 0, 1, rt298_vref_event, SND_SOC_DAPM_PRE_PMU),
+ 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("BG_MBIAS", 1, RT298_POWER_CTRL2,
1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT298_POWER_CTRL2,
@@ -934,18 +938,9 @@ static int rt298_set_bias_level(struct snd_soc_codec *codec,
}
break;
- case SND_SOC_BIAS_ON:
- mdelay(30);
- snd_soc_update_bits(codec,
- RT298_CBJ_CTRL1, 0x0400, 0x0400);
-
- break;
-
case SND_SOC_BIAS_STANDBY:
snd_soc_write(codec,
RT298_SET_AUDIO_POWER, AC_PWRST_D3);
- snd_soc_update_bits(codec,
- RT298_CBJ_CTRL1, 0x0400, 0x0000);
break;
default:
diff --git a/sound/soc/codecs/rt298.h b/sound/soc/codecs/rt298.h
index d66f8847b676..3638f3d61209 100644
--- a/sound/soc/codecs/rt298.h
+++ b/sound/soc/codecs/rt298.h
@@ -137,6 +137,7 @@
#define RT298_A_BIAS_CTRL2 0x02
#define RT298_POWER_CTRL1 0x03
#define RT298_A_BIAS_CTRL3 0x04
+#define RT298_D_FILTER_CTRL 0x05
#define RT298_POWER_CTRL2 0x08
#define RT298_I2S_CTRL1 0x09
#define RT298_I2S_CTRL2 0x0a
@@ -148,6 +149,7 @@
#define RT298_IRQ_CTRL 0x33
#define RT298_WIND_FILTER_CTRL 0x46
#define RT298_PLL_CTRL1 0x49
+#define RT298_VAD_CTRL 0x4e
#define RT298_CBJ_CTRL1 0x4f
#define RT298_CBJ_CTRL2 0x50
#define RT298_PLL_CTRL 0x63
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 3c6594da6c9c..d70847c9eeb0 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -253,7 +253,7 @@ static const struct reg_default rt5650_reg[] = {
{ 0x2b, 0x5454 },
{ 0x2c, 0xaaa0 },
{ 0x2d, 0x0000 },
- { 0x2f, 0x1002 },
+ { 0x2f, 0x5002 },
{ 0x31, 0x5000 },
{ 0x32, 0x0000 },
{ 0x33, 0x0000 },
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index 49a9e7049e2b..0af5ddbef1da 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -619,7 +619,7 @@ static const struct snd_kcontrol_new rt5670_snd_controls[] = {
RT5670_L_MUTE_SFT, RT5670_R_MUTE_SFT, 1, 1),
SOC_DOUBLE_TLV("HP Playback Volume", RT5670_HP_VOL,
RT5670_L_VOL_SFT, RT5670_R_VOL_SFT,
- 39, 0, out_vol_tlv),
+ 39, 1, out_vol_tlv),
/* OUTPUT Control */
SOC_DOUBLE("OUT Channel Switch", RT5670_LOUT1,
RT5670_VOL_L_SFT, RT5670_VOL_R_SFT, 1, 1),
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 33e290b703df..da9483c1c6fb 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -1241,60 +1241,46 @@ static int rt5677_dmic_use_asrc(struct snd_soc_dapm_widget *source,
regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
asrc_setting = (asrc_setting & RT5677_AD_STO1_CLK_SEL_MASK) >>
RT5677_AD_STO1_CLK_SEL_SFT;
- if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
- asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
- return 1;
break;
case 10:
regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
asrc_setting = (asrc_setting & RT5677_AD_STO2_CLK_SEL_MASK) >>
RT5677_AD_STO2_CLK_SEL_SFT;
- if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
- asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
- return 1;
break;
case 9:
regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
asrc_setting = (asrc_setting & RT5677_AD_STO3_CLK_SEL_MASK) >>
RT5677_AD_STO3_CLK_SEL_SFT;
- if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
- asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
- return 1;
break;
case 8:
regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
asrc_setting = (asrc_setting & RT5677_AD_STO4_CLK_SEL_MASK) >>
RT5677_AD_STO4_CLK_SEL_SFT;
- if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
- asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
- return 1;
break;
case 7:
regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting);
asrc_setting = (asrc_setting & RT5677_AD_MONOL_CLK_SEL_MASK) >>
RT5677_AD_MONOL_CLK_SEL_SFT;
- if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
- asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
- return 1;
break;
case 6:
regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting);
asrc_setting = (asrc_setting & RT5677_AD_MONOR_CLK_SEL_MASK) >>
RT5677_AD_MONOR_CLK_SEL_SFT;
- if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
- asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
- return 1;
break;
default:
- break;
+ return 0;
}
+ if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
+ asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
+ return 1;
+
return 0;
}
@@ -4520,14 +4506,9 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
}
#ifdef CONFIG_GPIOLIB
-static inline struct rt5677_priv *gpio_to_rt5677(struct gpio_chip *chip)
-{
- return container_of(chip, struct rt5677_priv, gpio_chip);
-}
-
static void rt5677_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
- struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
switch (offset) {
case RT5677_GPIO1 ... RT5677_GPIO5:
@@ -4548,7 +4529,7 @@ static void rt5677_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
static int rt5677_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
- struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
switch (offset) {
case RT5677_GPIO1 ... RT5677_GPIO5:
@@ -4572,7 +4553,7 @@ static int rt5677_gpio_direction_out(struct gpio_chip *chip,
static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
int value, ret;
ret = regmap_read(rt5677->regmap, RT5677_GPIO_ST, &value);
@@ -4584,7 +4565,7 @@ static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset)
static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
- struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
switch (offset) {
case RT5677_GPIO1 ... RT5677_GPIO5:
@@ -4638,7 +4619,7 @@ static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
{
- struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
struct regmap_irq_chip_data *data = rt5677->irq_data;
int irq;
@@ -4697,7 +4678,7 @@ static void rt5677_init_gpio(struct i2c_client *i2c)
rt5677->gpio_chip.parent = &i2c->dev;
rt5677->gpio_chip.base = -1;
- ret = gpiochip_add(&rt5677->gpio_chip);
+ ret = gpiochip_add_data(&rt5677->gpio_chip, rt5677);
if (ret != 0)
dev_err(&i2c->dev, "Failed to add GPIOs: %d\n", ret);
}
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
index 39307ad41a34..b8d19b77bde9 100644
--- a/sound/soc/codecs/tas571x.c
+++ b/sound/soc/codecs/tas571x.c
@@ -4,6 +4,9 @@
* Copyright (C) 2015 Google, Inc.
* Copyright (c) 2013 Daniel Mack <zonque@gmail.com>
*
+ * TAS5721 support:
+ * Copyright (C) 2016 Petr Kulhavy, Barix AG <petr@barix.com>
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -57,6 +60,10 @@ static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg)
case TAS571X_CH1_VOL_REG:
case TAS571X_CH2_VOL_REG:
return priv->chip->vol_reg_size;
+ case TAS571X_INPUT_MUX_REG:
+ case TAS571X_CH4_SRC_SELECT_REG:
+ case TAS571X_PWM_MUX_REG:
+ return 4;
default:
return 1;
}
@@ -167,6 +174,23 @@ static int tas571x_hw_params(struct snd_pcm_substream *substream,
TAS571X_SDI_FMT_MASK, val);
}
+static int tas571x_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 sysctl2;
+ int ret;
+
+ sysctl2 = mute ? TAS571X_SYS_CTRL_2_SDN_MASK : 0;
+
+ ret = snd_soc_update_bits(codec,
+ TAS571X_SYS_CTRL_2_REG,
+ TAS571X_SYS_CTRL_2_SDN_MASK,
+ sysctl2);
+ usleep_range(1000, 2000);
+
+ return ret;
+}
+
static int tas571x_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
@@ -214,6 +238,7 @@ static int tas571x_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops tas571x_dai_ops = {
.set_fmt = tas571x_set_dai_fmt,
.hw_params = tas571x_hw_params,
+ .digital_mute = tas571x_mute,
};
static const char *const tas5711_supply_names[] = {
@@ -241,6 +266,26 @@ static const struct snd_kcontrol_new tas5711_controls[] = {
1, 1),
};
+static const struct regmap_range tas571x_readonly_regs_range[] = {
+ regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_DEV_ID_REG),
+};
+
+static const struct regmap_range tas571x_volatile_regs_range[] = {
+ regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_ERR_STATUS_REG),
+ regmap_reg_range(TAS571X_OSC_TRIM_REG, TAS571X_OSC_TRIM_REG),
+};
+
+static const struct regmap_access_table tas571x_write_regs = {
+ .no_ranges = tas571x_readonly_regs_range,
+ .n_no_ranges = ARRAY_SIZE(tas571x_readonly_regs_range),
+};
+
+static const struct regmap_access_table tas571x_volatile_regs = {
+ .yes_ranges = tas571x_volatile_regs_range,
+ .n_yes_ranges = ARRAY_SIZE(tas571x_volatile_regs_range),
+
+};
+
static const struct reg_default tas5711_reg_defaults[] = {
{ 0x04, 0x05 },
{ 0x05, 0x40 },
@@ -260,6 +305,8 @@ static const struct regmap_config tas5711_regmap_config = {
.reg_defaults = tas5711_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults),
.cache_type = REGCACHE_RBTREE,
+ .wr_table = &tas571x_write_regs,
+ .volatile_table = &tas571x_volatile_regs,
};
static const struct tas571x_chip tas5711_chip = {
@@ -314,6 +361,8 @@ static const struct regmap_config tas5717_regmap_config = {
.reg_defaults = tas5717_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults),
.cache_type = REGCACHE_RBTREE,
+ .wr_table = &tas571x_write_regs,
+ .volatile_table = &tas571x_volatile_regs,
};
/* This entry is reused for tas5719 as the software interface is identical. */
@@ -326,6 +375,77 @@ static const struct tas571x_chip tas5717_chip = {
.vol_reg_size = 2,
};
+static const char *const tas5721_supply_names[] = {
+ "AVDD",
+ "DVDD",
+ "DRVDD",
+ "PVDD",
+};
+
+static const struct snd_kcontrol_new tas5721_controls[] = {
+ SOC_SINGLE_TLV("Master Volume",
+ TAS571X_MVOL_REG,
+ 0, 0xff, 1, tas5711_volume_tlv),
+ SOC_DOUBLE_R_TLV("Speaker Volume",
+ TAS571X_CH1_VOL_REG,
+ TAS571X_CH2_VOL_REG,
+ 0, 0xff, 1, tas5711_volume_tlv),
+ SOC_DOUBLE("Speaker Switch",
+ TAS571X_SOFT_MUTE_REG,
+ TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
+ 1, 1),
+};
+
+static const struct reg_default tas5721_reg_defaults[] = {
+ {TAS571X_CLK_CTRL_REG, 0x6c},
+ {TAS571X_DEV_ID_REG, 0x00},
+ {TAS571X_ERR_STATUS_REG, 0x00},
+ {TAS571X_SYS_CTRL_1_REG, 0xa0},
+ {TAS571X_SDI_REG, 0x05},
+ {TAS571X_SYS_CTRL_2_REG, 0x40},
+ {TAS571X_SOFT_MUTE_REG, 0x00},
+ {TAS571X_MVOL_REG, 0xff},
+ {TAS571X_CH1_VOL_REG, 0x30},
+ {TAS571X_CH2_VOL_REG, 0x30},
+ {TAS571X_CH3_VOL_REG, 0x30},
+ {TAS571X_VOL_CFG_REG, 0x91},
+ {TAS571X_MODULATION_LIMIT_REG, 0x02},
+ {TAS571X_IC_DELAY_CH1_REG, 0xac},
+ {TAS571X_IC_DELAY_CH2_REG, 0x54},
+ {TAS571X_IC_DELAY_CH3_REG, 0xac},
+ {TAS571X_IC_DELAY_CH4_REG, 0x54},
+ {TAS571X_PWM_CH_SDN_GROUP_REG, 0x30},
+ {TAS571X_START_STOP_PERIOD_REG, 0x0f},
+ {TAS571X_OSC_TRIM_REG, 0x82},
+ {TAS571X_BKND_ERR_REG, 0x02},
+ {TAS571X_INPUT_MUX_REG, 0x17772},
+ {TAS571X_CH4_SRC_SELECT_REG, 0x4303},
+ {TAS571X_PWM_MUX_REG, 0x1021345},
+};
+
+static const struct regmap_config tas5721_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = 0xff,
+ .reg_read = tas571x_reg_read,
+ .reg_write = tas571x_reg_write,
+ .reg_defaults = tas5721_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas5721_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .wr_table = &tas571x_write_regs,
+ .volatile_table = &tas571x_volatile_regs,
+};
+
+
+static const struct tas571x_chip tas5721_chip = {
+ .supply_names = tas5721_supply_names,
+ .num_supply_names = ARRAY_SIZE(tas5721_supply_names),
+ .controls = tas5711_controls,
+ .num_controls = ARRAY_SIZE(tas5711_controls),
+ .regmap_config = &tas5721_regmap_config,
+ .vol_reg_size = 1,
+};
+
static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
@@ -386,11 +506,10 @@ static int tas571x_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, priv);
of_id = of_match_device(tas571x_of_match, dev);
- if (!of_id) {
- dev_err(dev, "Unknown device type\n");
- return -EINVAL;
- }
- priv->chip = of_id->data;
+ if (of_id)
+ priv->chip = of_id->data;
+ else
+ priv->chip = (void *) id->driver_data;
priv->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) {
@@ -445,10 +564,6 @@ static int tas571x_i2c_probe(struct i2c_client *client,
if (ret)
return ret;
- ret = regmap_update_bits(priv->regmap, TAS571X_SYS_CTRL_2_REG,
- TAS571X_SYS_CTRL_2_SDN_MASK, 0);
- if (ret)
- return ret;
memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver));
priv->codec_driver.controls = priv->chip->controls;
@@ -486,14 +601,16 @@ static const struct of_device_id tas571x_of_match[] = {
{ .compatible = "ti,tas5711", .data = &tas5711_chip, },
{ .compatible = "ti,tas5717", .data = &tas5717_chip, },
{ .compatible = "ti,tas5719", .data = &tas5717_chip, },
+ { .compatible = "ti,tas5721", .data = &tas5721_chip, },
{ }
};
MODULE_DEVICE_TABLE(of, tas571x_of_match);
static const struct i2c_device_id tas571x_i2c_id[] = {
- { "tas5711", 0 },
- { "tas5717", 0 },
- { "tas5719", 0 },
+ { "tas5711", (kernel_ulong_t) &tas5711_chip },
+ { "tas5717", (kernel_ulong_t) &tas5717_chip },
+ { "tas5719", (kernel_ulong_t) &tas5717_chip },
+ { "tas5721", (kernel_ulong_t) &tas5721_chip },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id);
diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h
index 0aee471232cd..cf800c364f0f 100644
--- a/sound/soc/codecs/tas571x.h
+++ b/sound/soc/codecs/tas571x.h
@@ -13,6 +13,10 @@
#define _TAS571X_H
/* device registers */
+#define TAS571X_CLK_CTRL_REG 0x00
+#define TAS571X_DEV_ID_REG 0x01
+#define TAS571X_ERR_STATUS_REG 0x02
+#define TAS571X_SYS_CTRL_1_REG 0x03
#define TAS571X_SDI_REG 0x04
#define TAS571X_SDI_FMT_MASK 0x0f
@@ -27,7 +31,25 @@
#define TAS571X_MVOL_REG 0x07
#define TAS571X_CH1_VOL_REG 0x08
#define TAS571X_CH2_VOL_REG 0x09
+#define TAS571X_CH3_VOL_REG 0x0a
+#define TAS571X_VOL_CFG_REG 0x0e
+#define TAS571X_MODULATION_LIMIT_REG 0x10
+#define TAS571X_IC_DELAY_CH1_REG 0x11
+#define TAS571X_IC_DELAY_CH2_REG 0x12
+#define TAS571X_IC_DELAY_CH3_REG 0x13
+#define TAS571X_IC_DELAY_CH4_REG 0x14
+#define TAS571X_PWM_CH_SDN_GROUP_REG 0x19 /* N/A on TAS5717, TAS5719 */
+#define TAS571X_PWM_CH1_SDN_MASK (1<<0)
+#define TAS571X_PWM_CH2_SDN_SHIFT (1<<1)
+#define TAS571X_PWM_CH3_SDN_SHIFT (1<<2)
+#define TAS571X_PWM_CH4_SDN_SHIFT (1<<3)
+
+#define TAS571X_START_STOP_PERIOD_REG 0x1a
#define TAS571X_OSC_TRIM_REG 0x1b
+#define TAS571X_BKND_ERR_REG 0x1c
+#define TAS571X_INPUT_MUX_REG 0x20
+#define TAS571X_CH4_SRC_SELECT_REG 0x21
+#define TAS571X_PWM_MUX_REG 0x25
#endif /* _TAS571X_H */
diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c
new file mode 100644
index 000000000000..f54fb46b77c2
--- /dev/null
+++ b/sound/soc/codecs/tas5720.c
@@ -0,0 +1,620 @@
+/*
+ * tas5720.c - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier
+ *
+ * Copyright (C)2015-2016 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Andreas Dannenberg <dannenberg@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "tas5720.h"
+
+/* Define how often to check (and clear) the fault status register (in ms) */
+#define TAS5720_FAULT_CHECK_INTERVAL 200
+
+static const char * const tas5720_supply_names[] = {
+ "dvdd", /* Digital power supply. Connect to 3.3-V supply. */
+ "pvdd", /* Class-D amp and analog power supply (connected). */
+};
+
+#define TAS5720_NUM_SUPPLIES ARRAY_SIZE(tas5720_supply_names)
+
+struct tas5720_data {
+ struct snd_soc_codec *codec;
+ struct regmap *regmap;
+ struct i2c_client *tas5720_client;
+ struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES];
+ struct delayed_work fault_check_work;
+ unsigned int last_fault;
+};
+
+static int tas5720_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int rate = params_rate(params);
+ bool ssz_ds;
+ int ret;
+
+ switch (rate) {
+ case 44100:
+ case 48000:
+ ssz_ds = false;
+ break;
+ case 88200:
+ case 96000:
+ ssz_ds = true;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported sample rate: %u\n", rate);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG,
+ TAS5720_SSZ_DS, ssz_ds);
+ if (ret < 0) {
+ dev_err(codec->dev, "error setting sample rate: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tas5720_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 serial_format;
+ int ret;
+
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+ dev_vdbg(codec->dev, "DAI Format master is not found\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
+ SND_SOC_DAIFMT_INV_MASK)) {
+ case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
+ /* 1st data bit occur one BCLK cycle after the frame sync */
+ serial_format = TAS5720_SAIF_I2S;
+ break;
+ case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF):
+ /*
+ * Note that although the TAS5720 does not have a dedicated DSP
+ * mode it doesn't care about the LRCLK duty cycle during TDM
+ * operation. Therefore we can use the device's I2S mode with
+ * its delaying of the 1st data bit to receive DSP_A formatted
+ * data. See device datasheet for additional details.
+ */
+ serial_format = TAS5720_SAIF_I2S;
+ break;
+ case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF):
+ /*
+ * Similar to DSP_A, we can use the fact that the TAS5720 does
+ * not care about the LRCLK duty cycle during TDM to receive
+ * DSP_B formatted data in LEFTJ mode (no delaying of the 1st
+ * data bit).
+ */
+ serial_format = TAS5720_SAIF_LEFTJ;
+ break;
+ case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):
+ /* No delay after the frame sync */
+ serial_format = TAS5720_SAIF_LEFTJ;
+ break;
+ default:
+ dev_vdbg(codec->dev, "DAI Format is not found\n");
+ return -EINVAL;
+ }
+
+ ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG,
+ TAS5720_SAIF_FORMAT_MASK,
+ serial_format);
+ if (ret < 0) {
+ dev_err(codec->dev, "error setting SAIF format: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int first_slot;
+ int ret;
+
+ if (!tx_mask) {
+ dev_err(codec->dev, "tx masks must not be 0\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Determine the first slot that is being requested. We will only
+ * use the first slot that is found since the TAS5720 is a mono
+ * amplifier.
+ */
+ first_slot = __ffs(tx_mask);
+
+ if (first_slot > 7) {
+ dev_err(codec->dev, "slot selection out of bounds (%u)\n",
+ first_slot);
+ return -EINVAL;
+ }
+
+ /* Enable manual TDM slot selection (instead of I2C ID based) */
+ ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL1_REG,
+ TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC);
+ if (ret < 0)
+ goto error_snd_soc_update_bits;
+
+ /* Configure the TDM slot to process audio from */
+ ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG,
+ TAS5720_TDM_SLOT_SEL_MASK, first_slot);
+ if (ret < 0)
+ goto error_snd_soc_update_bits;
+
+ return 0;
+
+error_snd_soc_update_bits:
+ dev_err(codec->dev, "error configuring TDM mode: %d\n", ret);
+ return ret;
+}
+
+static int tas5720_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int ret;
+
+ ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG,
+ TAS5720_MUTE, mute ? TAS5720_MUTE : 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "error (un-)muting device: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void tas5720_fault_check_work(struct work_struct *work)
+{
+ struct tas5720_data *tas5720 = container_of(work, struct tas5720_data,
+ fault_check_work.work);
+ struct device *dev = tas5720->codec->dev;
+ unsigned int curr_fault;
+ int ret;
+
+ ret = regmap_read(tas5720->regmap, TAS5720_FAULT_REG, &curr_fault);
+ if (ret < 0) {
+ dev_err(dev, "failed to read FAULT register: %d\n", ret);
+ goto out;
+ }
+
+ /* Check/handle all errors except SAIF clock errors */
+ curr_fault &= TAS5720_OCE | TAS5720_DCE | TAS5720_OTE;
+
+ /*
+ * Only flag errors once for a given occurrence. This is needed as
+ * the TAS5720 will take time clearing the fault condition internally
+ * during which we don't want to bombard the system with the same
+ * error message over and over.
+ */
+ if ((curr_fault & TAS5720_OCE) && !(tas5720->last_fault & TAS5720_OCE))
+ dev_crit(dev, "experienced an over current hardware fault\n");
+
+ if ((curr_fault & TAS5720_DCE) && !(tas5720->last_fault & TAS5720_DCE))
+ dev_crit(dev, "experienced a DC detection fault\n");
+
+ if ((curr_fault & TAS5720_OTE) && !(tas5720->last_fault & TAS5720_OTE))
+ dev_crit(dev, "experienced an over temperature fault\n");
+
+ /* Store current fault value so we can detect any changes next time */
+ tas5720->last_fault = curr_fault;
+
+ if (!curr_fault)
+ goto out;
+
+ /*
+ * Periodically toggle SDZ (shutdown bit) H->L->H to clear any latching
+ * faults as long as a fault condition persists. Always going through
+ * the full sequence no matter the first return value to minimizes
+ * chances for the device to end up in shutdown mode.
+ */
+ ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG,
+ TAS5720_SDZ, 0);
+ if (ret < 0)
+ dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret);
+
+ ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG,
+ TAS5720_SDZ, TAS5720_SDZ);
+ if (ret < 0)
+ dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret);
+
+out:
+ /* Schedule the next fault check at the specified interval */
+ schedule_delayed_work(&tas5720->fault_check_work,
+ msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL));
+}
+
+static int tas5720_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
+ unsigned int device_id;
+ int ret;
+
+ tas5720->codec = codec;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies),
+ tas5720->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(tas5720->regmap, TAS5720_DEVICE_ID_REG, &device_id);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to read device ID register: %d\n",
+ ret);
+ goto probe_fail;
+ }
+
+ if (device_id != TAS5720_DEVICE_ID) {
+ dev_err(codec->dev, "wrong device ID. expected: %u read: %u\n",
+ TAS5720_DEVICE_ID, device_id);
+ ret = -ENODEV;
+ goto probe_fail;
+ }
+
+ /* Set device to mute */
+ ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG,
+ TAS5720_MUTE, TAS5720_MUTE);
+ if (ret < 0)
+ goto error_snd_soc_update_bits;
+
+ /*
+ * Enter shutdown mode - our default when not playing audio - to
+ * minimize current consumption. On the TAS5720 there is no real down
+ * side doing so as all device registers are preserved and the wakeup
+ * of the codec is rather quick which we do using a dapm widget.
+ */
+ ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG,
+ TAS5720_SDZ, 0);
+ if (ret < 0)
+ goto error_snd_soc_update_bits;
+
+ INIT_DELAYED_WORK(&tas5720->fault_check_work, tas5720_fault_check_work);
+
+ return 0;
+
+error_snd_soc_update_bits:
+ dev_err(codec->dev, "error configuring device registers: %d\n", ret);
+
+probe_fail:
+ regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
+ tas5720->supplies);
+ return ret;
+}
+
+static int tas5720_codec_remove(struct snd_soc_codec *codec)
+{
+ struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ cancel_delayed_work_sync(&tas5720->fault_check_work);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
+ tas5720->supplies);
+ if (ret < 0)
+ dev_err(codec->dev, "failed to disable supplies: %d\n", ret);
+
+ return ret;
+};
+
+static int tas5720_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ if (event & SND_SOC_DAPM_POST_PMU) {
+ /* Take TAS5720 out of shutdown mode */
+ ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG,
+ TAS5720_SDZ, TAS5720_SDZ);
+ if (ret < 0) {
+ dev_err(codec->dev, "error waking codec: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Observe codec shutdown-to-active time. The datasheet only
+ * lists a nominal value however just use-it as-is without
+ * additional padding to minimize the delay introduced in
+ * starting to play audio (actually there is other setup done
+ * by the ASoC framework that will provide additional delays,
+ * so we should always be safe).
+ */
+ msleep(25);
+
+ /* Turn on TAS5720 periodic fault checking/handling */
+ tas5720->last_fault = 0;
+ schedule_delayed_work(&tas5720->fault_check_work,
+ msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL));
+ } else if (event & SND_SOC_DAPM_PRE_PMD) {
+ /* Disable TAS5720 periodic fault checking/handling */
+ cancel_delayed_work_sync(&tas5720->fault_check_work);
+
+ /* Place TAS5720 in shutdown mode to minimize current draw */
+ ret = snd_soc_update_bits(codec, TAS5720_POWER_CTRL_REG,
+ TAS5720_SDZ, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "error shutting down codec: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tas5720_suspend(struct snd_soc_codec *codec)
+{
+ struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ regcache_cache_only(tas5720->regmap, true);
+ regcache_mark_dirty(tas5720->regmap);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),
+ tas5720->supplies);
+ if (ret < 0)
+ dev_err(codec->dev, "failed to disable supplies: %d\n", ret);
+
+ return ret;
+}
+
+static int tas5720_resume(struct snd_soc_codec *codec)
+{
+ struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies),
+ tas5720->supplies);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(tas5720->regmap, false);
+
+ ret = regcache_sync(tas5720->regmap);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to sync regcache: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+#else
+#define tas5720_suspend NULL
+#define tas5720_resume NULL
+#endif
+
+static bool tas5720_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS5720_DEVICE_ID_REG:
+ case TAS5720_FAULT_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tas5720_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = TAS5720_MAX_REG,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = tas5720_is_volatile_reg,
+};
+
+/*
+ * DAC analog gain. There are four discrete values to select from, ranging
+ * from 19.2 dB to 26.3dB.
+ */
+static const DECLARE_TLV_DB_RANGE(dac_analog_tlv,
+ 0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0),
+ 0x1, 0x1, TLV_DB_SCALE_ITEM(2070, 0, 0),
+ 0x2, 0x2, TLV_DB_SCALE_ITEM(2350, 0, 0),
+ 0x3, 0x3, TLV_DB_SCALE_ITEM(2630, 0, 0),
+);
+
+/*
+ * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that
+ * setting the gain below -100 dB (register value <0x7) is effectively a MUTE
+ * as per device datasheet.
+ */
+static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0);
+
+static const struct snd_kcontrol_new tas5720_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Driver Playback Volume",
+ TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, dac_tlv),
+ SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,
+ TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv),
+};
+
+static const struct snd_soc_dapm_widget tas5720_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas5720_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT")
+};
+
+static const struct snd_soc_dapm_route tas5720_audio_map[] = {
+ { "DAC", NULL, "DAC IN" },
+ { "OUT", NULL, "DAC" },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_tas5720 = {
+ .probe = tas5720_codec_probe,
+ .remove = tas5720_codec_remove,
+ .suspend = tas5720_suspend,
+ .resume = tas5720_resume,
+
+ .controls = tas5720_snd_controls,
+ .num_controls = ARRAY_SIZE(tas5720_snd_controls),
+ .dapm_widgets = tas5720_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets),
+ .dapm_routes = tas5720_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map),
+};
+
+/* PCM rates supported by the TAS5720 driver */
+#define TAS5720_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+/* Formats supported by TAS5720 driver */
+#define TAS5720_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops tas5720_speaker_dai_ops = {
+ .hw_params = tas5720_hw_params,
+ .set_fmt = tas5720_set_dai_fmt,
+ .set_tdm_slot = tas5720_set_dai_tdm_slot,
+ .digital_mute = tas5720_mute,
+};
+
+/*
+ * TAS5720 DAI structure
+ *
+ * Note that were are advertising .playback.channels_max = 2 despite this being
+ * a mono amplifier. The reason for that is that some serial ports such as TI's
+ * McASP module have a minimum number of channels (2) that they can output.
+ * Advertising more channels than we have will allow us to interface with such
+ * a serial port without really any negative side effects as the TAS5720 will
+ * simply ignore any extra channel(s) asides from the one channel that is
+ * configured to be played back.
+ */
+static struct snd_soc_dai_driver tas5720_dai[] = {
+ {
+ .name = "tas5720-amplifier",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAS5720_RATES,
+ .formats = TAS5720_FORMATS,
+ },
+ .ops = &tas5720_speaker_dai_ops,
+ },
+};
+
+static int tas5720_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct tas5720_data *data;
+ int ret;
+ int i;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->tas5720_client = client;
+ data->regmap = devm_regmap_init_i2c(client, &tas5720_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ ret = PTR_ERR(data->regmap);
+ dev_err(dev, "failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
+ data->supplies[i].supply = tas5720_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
+ data->supplies);
+ if (ret != 0) {
+ dev_err(dev, "failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ dev_set_drvdata(dev, data);
+
+ ret = snd_soc_register_codec(&client->dev,
+ &soc_codec_dev_tas5720,
+ tas5720_dai, ARRAY_SIZE(tas5720_dai));
+ if (ret < 0) {
+ dev_err(dev, "failed to register codec: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tas5720_remove(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+
+ snd_soc_unregister_codec(dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id tas5720_id[] = {
+ { "tas5720", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas5720_id);
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id tas5720_of_match[] = {
+ { .compatible = "ti,tas5720", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tas5720_of_match);
+#endif
+
+static struct i2c_driver tas5720_i2c_driver = {
+ .driver = {
+ .name = "tas5720",
+ .of_match_table = of_match_ptr(tas5720_of_match),
+ },
+ .probe = tas5720_probe,
+ .remove = tas5720_remove,
+ .id_table = tas5720_id,
+};
+
+module_i2c_driver(tas5720_i2c_driver);
+
+MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
+MODULE_DESCRIPTION("TAS5720 Audio amplifier driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas5720.h b/sound/soc/codecs/tas5720.h
new file mode 100644
index 000000000000..3d077c779b12
--- /dev/null
+++ b/sound/soc/codecs/tas5720.h
@@ -0,0 +1,90 @@
+/*
+ * tas5720.h - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier
+ *
+ * Copyright (C)2015-2016 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Andreas Dannenberg <dannenberg@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef __TAS5720_H__
+#define __TAS5720_H__
+
+/* Register Address Map */
+#define TAS5720_DEVICE_ID_REG 0x00
+#define TAS5720_POWER_CTRL_REG 0x01
+#define TAS5720_DIGITAL_CTRL1_REG 0x02
+#define TAS5720_DIGITAL_CTRL2_REG 0x03
+#define TAS5720_VOLUME_CTRL_REG 0x04
+#define TAS5720_ANALOG_CTRL_REG 0x06
+#define TAS5720_FAULT_REG 0x08
+#define TAS5720_DIGITAL_CLIP2_REG 0x10
+#define TAS5720_DIGITAL_CLIP1_REG 0x11
+#define TAS5720_MAX_REG TAS5720_DIGITAL_CLIP1_REG
+
+/* TAS5720_DEVICE_ID_REG */
+#define TAS5720_DEVICE_ID 0x01
+
+/* TAS5720_POWER_CTRL_REG */
+#define TAS5720_DIG_CLIP_MASK GENMASK(7, 2)
+#define TAS5720_SLEEP BIT(1)
+#define TAS5720_SDZ BIT(0)
+
+/* TAS5720_DIGITAL_CTRL1_REG */
+#define TAS5720_HPF_BYPASS BIT(7)
+#define TAS5720_TDM_CFG_SRC BIT(6)
+#define TAS5720_SSZ_DS BIT(3)
+#define TAS5720_SAIF_RIGHTJ_24BIT (0x0)
+#define TAS5720_SAIF_RIGHTJ_20BIT (0x1)
+#define TAS5720_SAIF_RIGHTJ_18BIT (0x2)
+#define TAS5720_SAIF_RIGHTJ_16BIT (0x3)
+#define TAS5720_SAIF_I2S (0x4)
+#define TAS5720_SAIF_LEFTJ (0x5)
+#define TAS5720_SAIF_FORMAT_MASK GENMASK(2, 0)
+
+/* TAS5720_DIGITAL_CTRL2_REG */
+#define TAS5720_MUTE BIT(4)
+#define TAS5720_TDM_SLOT_SEL_MASK GENMASK(2, 0)
+
+/* TAS5720_ANALOG_CTRL_REG */
+#define TAS5720_PWM_RATE_6_3_FSYNC (0x0 << 4)
+#define TAS5720_PWM_RATE_8_4_FSYNC (0x1 << 4)
+#define TAS5720_PWM_RATE_10_5_FSYNC (0x2 << 4)
+#define TAS5720_PWM_RATE_12_6_FSYNC (0x3 << 4)
+#define TAS5720_PWM_RATE_14_7_FSYNC (0x4 << 4)
+#define TAS5720_PWM_RATE_16_8_FSYNC (0x5 << 4)
+#define TAS5720_PWM_RATE_20_10_FSYNC (0x6 << 4)
+#define TAS5720_PWM_RATE_24_12_FSYNC (0x7 << 4)
+#define TAS5720_PWM_RATE_MASK GENMASK(6, 4)
+#define TAS5720_ANALOG_GAIN_19_2DBV (0x0 << 2)
+#define TAS5720_ANALOG_GAIN_20_7DBV (0x1 << 2)
+#define TAS5720_ANALOG_GAIN_23_5DBV (0x2 << 2)
+#define TAS5720_ANALOG_GAIN_26_3DBV (0x3 << 2)
+#define TAS5720_ANALOG_GAIN_MASK GENMASK(3, 2)
+#define TAS5720_ANALOG_GAIN_SHIFT (0x2)
+
+/* TAS5720_FAULT_REG */
+#define TAS5720_OC_THRESH_100PCT (0x0 << 4)
+#define TAS5720_OC_THRESH_75PCT (0x1 << 4)
+#define TAS5720_OC_THRESH_50PCT (0x2 << 4)
+#define TAS5720_OC_THRESH_25PCT (0x3 << 4)
+#define TAS5720_OC_THRESH_MASK GENMASK(5, 4)
+#define TAS5720_CLKE BIT(3)
+#define TAS5720_OCE BIT(2)
+#define TAS5720_DCE BIT(1)
+#define TAS5720_OTE BIT(0)
+#define TAS5720_FAULT_MASK GENMASK(3, 0)
+
+/* TAS5720_DIGITAL_CLIP1_REG */
+#define TAS5720_CLIP1_MASK GENMASK(7, 2)
+#define TAS5720_CLIP1_SHIFT (0x2)
+
+#endif /* __TAS5720_H__ */
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index ee4def4f819f..3c5e1df01c19 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -28,6 +28,7 @@
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
+#include <linux/acpi.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
@@ -1280,10 +1281,19 @@ static const struct i2c_device_id aic31xx_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id aic31xx_acpi_match[] = {
+ { "10TI3100", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, aic31xx_acpi_match);
+#endif
+
static struct i2c_driver aic31xx_i2c_driver = {
.driver = {
.name = "tlv320aic31xx-codec",
.of_match_table = of_match_ptr(tlv320aic31xx_of_match),
+ .acpi_match_table = ACPI_PTR(aic31xx_acpi_match),
},
.probe = aic31xx_i2c_probe,
.remove = aic31xx_i2c_remove,
diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c
new file mode 100644
index 000000000000..59606cf3008f
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic32x4-i2c.c
@@ -0,0 +1,74 @@
+/*
+ * linux/sound/soc/codecs/tlv320aic32x4-i2c.c
+ *
+ * Copyright 2011 NW Digital Radio
+ *
+ * Author: Jeremy McDermond <nh6z@nh6z.net>
+ *
+ * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "tlv320aic32x4.h"
+
+static int aic32x4_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+ struct regmap_config config;
+
+ config = aic32x4_regmap_config;
+ config.reg_bits = 8;
+ config.val_bits = 8;
+
+ regmap = devm_regmap_init_i2c(i2c, &config);
+ return aic32x4_probe(&i2c->dev, regmap);
+}
+
+static int aic32x4_i2c_remove(struct i2c_client *i2c)
+{
+ return aic32x4_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id aic32x4_i2c_id[] = {
+ { "tlv320aic32x4", 0 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id);
+
+static const struct of_device_id aic32x4_of_id[] = {
+ { .compatible = "ti,tlv320aic32x4", },
+ { /* senitel */ }
+};
+MODULE_DEVICE_TABLE(of, aic32x4_of_id);
+
+static struct i2c_driver aic32x4_i2c_driver = {
+ .driver = {
+ .name = "tlv320aic32x4",
+ .of_match_table = aic32x4_of_id,
+ },
+ .probe = aic32x4_i2c_probe,
+ .remove = aic32x4_i2c_remove,
+ .id_table = aic32x4_i2c_id,
+};
+
+module_i2c_driver(aic32x4_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC32x4 codec driver I2C");
+MODULE_AUTHOR("Jeremy McDermond <nh6z@nh6z.net>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic32x4-spi.c b/sound/soc/codecs/tlv320aic32x4-spi.c
new file mode 100644
index 000000000000..724fcdd491b2
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic32x4-spi.c
@@ -0,0 +1,76 @@
+/*
+ * linux/sound/soc/codecs/tlv320aic32x4-spi.c
+ *
+ * Copyright 2011 NW Digital Radio
+ *
+ * Author: Jeremy McDermond <nh6z@nh6z.net>
+ *
+ * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "tlv320aic32x4.h"
+
+static int aic32x4_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+ struct regmap_config config;
+
+ config = aic32x4_regmap_config;
+ config.reg_bits = 7;
+ config.pad_bits = 1;
+ config.val_bits = 8;
+ config.read_flag_mask = 0x01;
+
+ regmap = devm_regmap_init_spi(spi, &config);
+ return aic32x4_probe(&spi->dev, regmap);
+}
+
+static int aic32x4_spi_remove(struct spi_device *spi)
+{
+ return aic32x4_remove(&spi->dev);
+}
+
+static const struct spi_device_id aic32x4_spi_id[] = {
+ { "tlv320aic32x4", 0 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, aic32x4_spi_id);
+
+static const struct of_device_id aic32x4_of_id[] = {
+ { .compatible = "ti,tlv320aic32x4", },
+ { /* senitel */ }
+};
+MODULE_DEVICE_TABLE(of, aic32x4_of_id);
+
+static struct spi_driver aic32x4_spi_driver = {
+ .driver = {
+ .name = "tlv320aic32x4",
+ .owner = THIS_MODULE,
+ .of_match_table = aic32x4_of_id,
+ },
+ .probe = aic32x4_spi_probe,
+ .remove = aic32x4_spi_remove,
+ .id_table = aic32x4_spi_id,
+};
+
+module_spi_driver(aic32x4_spi_driver);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC32x4 codec driver SPI");
+MODULE_AUTHOR("Jeremy McDermond <nh6z@nh6z.net>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index f2d3191961e1..85d4978d0384 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -30,7 +30,6 @@
#include <linux/pm.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
-#include <linux/i2c.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/clk.h>
@@ -160,7 +159,10 @@ static const struct aic32x4_rate_divs aic32x4_divs[] = {
/* 48k rate */
{AIC32X4_FREQ_12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4},
{AIC32X4_FREQ_24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4},
- {AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4}
+ {AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4},
+
+ /* 96k rate */
+ {AIC32X4_FREQ_25000000, 96000, 2, 7, 8643, 64, 4, 4, 64, 4, 4, 1},
};
static const struct snd_kcontrol_new hpl_output_mixer_controls[] = {
@@ -181,16 +183,71 @@ static const struct snd_kcontrol_new lor_output_mixer_controls[] = {
SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_LORROUTE, 3, 1, 0),
};
-static const struct snd_kcontrol_new left_input_mixer_controls[] = {
- SOC_DAPM_SINGLE("IN1_L P Switch", AIC32X4_LMICPGAPIN, 6, 1, 0),
- SOC_DAPM_SINGLE("IN2_L P Switch", AIC32X4_LMICPGAPIN, 4, 1, 0),
- SOC_DAPM_SINGLE("IN3_L P Switch", AIC32X4_LMICPGAPIN, 2, 1, 0),
+static const char * const resistor_text[] = {
+ "Off", "10 kOhm", "20 kOhm", "40 kOhm",
};
-static const struct snd_kcontrol_new right_input_mixer_controls[] = {
- SOC_DAPM_SINGLE("IN1_R P Switch", AIC32X4_RMICPGAPIN, 6, 1, 0),
- SOC_DAPM_SINGLE("IN2_R P Switch", AIC32X4_RMICPGAPIN, 4, 1, 0),
- SOC_DAPM_SINGLE("IN3_R P Switch", AIC32X4_RMICPGAPIN, 2, 1, 0),
+/* Left mixer pins */
+static SOC_ENUM_SINGLE_DECL(in1l_lpga_p_enum, AIC32X4_LMICPGAPIN, 6, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in2l_lpga_p_enum, AIC32X4_LMICPGAPIN, 4, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in3l_lpga_p_enum, AIC32X4_LMICPGAPIN, 2, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in1r_lpga_p_enum, AIC32X4_LMICPGAPIN, 0, resistor_text);
+
+static SOC_ENUM_SINGLE_DECL(cml_lpga_n_enum, AIC32X4_LMICPGANIN, 6, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in2r_lpga_n_enum, AIC32X4_LMICPGANIN, 4, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in3r_lpga_n_enum, AIC32X4_LMICPGANIN, 2, resistor_text);
+
+static const struct snd_kcontrol_new in1l_to_lmixer_controls[] = {
+ SOC_DAPM_ENUM("IN1_L L+ Switch", in1l_lpga_p_enum),
+};
+static const struct snd_kcontrol_new in2l_to_lmixer_controls[] = {
+ SOC_DAPM_ENUM("IN2_L L+ Switch", in2l_lpga_p_enum),
+};
+static const struct snd_kcontrol_new in3l_to_lmixer_controls[] = {
+ SOC_DAPM_ENUM("IN3_L L+ Switch", in3l_lpga_p_enum),
+};
+static const struct snd_kcontrol_new in1r_to_lmixer_controls[] = {
+ SOC_DAPM_ENUM("IN1_R L+ Switch", in1r_lpga_p_enum),
+};
+static const struct snd_kcontrol_new cml_to_lmixer_controls[] = {
+ SOC_DAPM_ENUM("CM_L L- Switch", cml_lpga_n_enum),
+};
+static const struct snd_kcontrol_new in2r_to_lmixer_controls[] = {
+ SOC_DAPM_ENUM("IN2_R L- Switch", in2r_lpga_n_enum),
+};
+static const struct snd_kcontrol_new in3r_to_lmixer_controls[] = {
+ SOC_DAPM_ENUM("IN3_R L- Switch", in3r_lpga_n_enum),
+};
+
+/* Right mixer pins */
+static SOC_ENUM_SINGLE_DECL(in1r_rpga_p_enum, AIC32X4_RMICPGAPIN, 6, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in2r_rpga_p_enum, AIC32X4_RMICPGAPIN, 4, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in3r_rpga_p_enum, AIC32X4_RMICPGAPIN, 2, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in2l_rpga_p_enum, AIC32X4_RMICPGAPIN, 0, resistor_text);
+static SOC_ENUM_SINGLE_DECL(cmr_rpga_n_enum, AIC32X4_RMICPGANIN, 6, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in1l_rpga_n_enum, AIC32X4_RMICPGANIN, 4, resistor_text);
+static SOC_ENUM_SINGLE_DECL(in3l_rpga_n_enum, AIC32X4_RMICPGANIN, 2, resistor_text);
+
+static const struct snd_kcontrol_new in1r_to_rmixer_controls[] = {
+ SOC_DAPM_ENUM("IN1_R R+ Switch", in1r_rpga_p_enum),
+};
+static const struct snd_kcontrol_new in2r_to_rmixer_controls[] = {
+ SOC_DAPM_ENUM("IN2_R R+ Switch", in2r_rpga_p_enum),
+};
+static const struct snd_kcontrol_new in3r_to_rmixer_controls[] = {
+ SOC_DAPM_ENUM("IN3_R R+ Switch", in3r_rpga_p_enum),
+};
+static const struct snd_kcontrol_new in2l_to_rmixer_controls[] = {
+ SOC_DAPM_ENUM("IN2_L R+ Switch", in2l_rpga_p_enum),
+};
+static const struct snd_kcontrol_new cmr_to_rmixer_controls[] = {
+ SOC_DAPM_ENUM("CM_R R- Switch", cmr_rpga_n_enum),
+};
+static const struct snd_kcontrol_new in1l_to_rmixer_controls[] = {
+ SOC_DAPM_ENUM("IN1_L R- Switch", in1l_rpga_n_enum),
+};
+static const struct snd_kcontrol_new in3l_to_rmixer_controls[] = {
+ SOC_DAPM_ENUM("IN3_L R- Switch", in3l_rpga_n_enum),
};
static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = {
@@ -214,14 +271,39 @@ static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = {
&lor_output_mixer_controls[0],
ARRAY_SIZE(lor_output_mixer_controls)),
SND_SOC_DAPM_PGA("LOR Power", AIC32X4_OUTPWRCTL, 2, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0,
- &left_input_mixer_controls[0],
- ARRAY_SIZE(left_input_mixer_controls)),
- SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0,
- &right_input_mixer_controls[0],
- ARRAY_SIZE(right_input_mixer_controls)),
- SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0),
+
SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0),
+ SND_SOC_DAPM_MUX("IN1_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in1r_to_rmixer_controls),
+ SND_SOC_DAPM_MUX("IN2_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in2r_to_rmixer_controls),
+ SND_SOC_DAPM_MUX("IN3_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in3r_to_rmixer_controls),
+ SND_SOC_DAPM_MUX("IN2_L to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in2l_to_rmixer_controls),
+ SND_SOC_DAPM_MUX("CM_R to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
+ cmr_to_rmixer_controls),
+ SND_SOC_DAPM_MUX("IN1_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
+ in1l_to_rmixer_controls),
+ SND_SOC_DAPM_MUX("IN3_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
+ in3l_to_rmixer_controls),
+
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0),
+ SND_SOC_DAPM_MUX("IN1_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in1l_to_lmixer_controls),
+ SND_SOC_DAPM_MUX("IN2_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in2l_to_lmixer_controls),
+ SND_SOC_DAPM_MUX("IN3_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in3l_to_lmixer_controls),
+ SND_SOC_DAPM_MUX("IN1_R to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0,
+ in1r_to_lmixer_controls),
+ SND_SOC_DAPM_MUX("CM_L to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
+ cml_to_lmixer_controls),
+ SND_SOC_DAPM_MUX("IN2_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
+ in2r_to_lmixer_controls),
+ SND_SOC_DAPM_MUX("IN3_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0,
+ in3r_to_lmixer_controls),
+
SND_SOC_DAPM_MICBIAS("Mic Bias", AIC32X4_MICBIAS, 6, 0),
SND_SOC_DAPM_OUTPUT("HPL"),
@@ -261,19 +343,77 @@ static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = {
{"LOR Power", NULL, "LOR Output Mixer"},
{"LOR", NULL, "LOR Power"},
- /* Left input */
- {"Left Input Mixer", "IN1_L P Switch", "IN1_L"},
- {"Left Input Mixer", "IN2_L P Switch", "IN2_L"},
- {"Left Input Mixer", "IN3_L P Switch", "IN3_L"},
-
- {"Left ADC", NULL, "Left Input Mixer"},
-
/* Right Input */
- {"Right Input Mixer", "IN1_R P Switch", "IN1_R"},
- {"Right Input Mixer", "IN2_R P Switch", "IN2_R"},
- {"Right Input Mixer", "IN3_R P Switch", "IN3_R"},
-
- {"Right ADC", NULL, "Right Input Mixer"},
+ {"Right ADC", NULL, "IN1_R to Right Mixer Positive Resistor"},
+ {"IN1_R to Right Mixer Positive Resistor", "10 kOhm", "IN1_R"},
+ {"IN1_R to Right Mixer Positive Resistor", "20 kOhm", "IN1_R"},
+ {"IN1_R to Right Mixer Positive Resistor", "40 kOhm", "IN1_R"},
+
+ {"Right ADC", NULL, "IN2_R to Right Mixer Positive Resistor"},
+ {"IN2_R to Right Mixer Positive Resistor", "10 kOhm", "IN2_R"},
+ {"IN2_R to Right Mixer Positive Resistor", "20 kOhm", "IN2_R"},
+ {"IN2_R to Right Mixer Positive Resistor", "40 kOhm", "IN2_R"},
+
+ {"Right ADC", NULL, "IN3_R to Right Mixer Positive Resistor"},
+ {"IN3_R to Right Mixer Positive Resistor", "10 kOhm", "IN3_R"},
+ {"IN3_R to Right Mixer Positive Resistor", "20 kOhm", "IN3_R"},
+ {"IN3_R to Right Mixer Positive Resistor", "40 kOhm", "IN3_R"},
+
+ {"Right ADC", NULL, "IN2_L to Right Mixer Positive Resistor"},
+ {"IN2_L to Right Mixer Positive Resistor", "10 kOhm", "IN2_L"},
+ {"IN2_L to Right Mixer Positive Resistor", "20 kOhm", "IN2_L"},
+ {"IN2_L to Right Mixer Positive Resistor", "40 kOhm", "IN2_L"},
+
+ {"Right ADC", NULL, "CM_R to Right Mixer Negative Resistor"},
+ {"CM_R to Right Mixer Negative Resistor", "10 kOhm", "CM_R"},
+ {"CM_R to Right Mixer Negative Resistor", "20 kOhm", "CM_R"},
+ {"CM_R to Right Mixer Negative Resistor", "40 kOhm", "CM_R"},
+
+ {"Right ADC", NULL, "IN1_L to Right Mixer Negative Resistor"},
+ {"IN1_L to Right Mixer Negative Resistor", "10 kOhm", "IN1_L"},
+ {"IN1_L to Right Mixer Negative Resistor", "20 kOhm", "IN1_L"},
+ {"IN1_L to Right Mixer Negative Resistor", "40 kOhm", "IN1_L"},
+
+ {"Right ADC", NULL, "IN3_L to Right Mixer Negative Resistor"},
+ {"IN3_L to Right Mixer Negative Resistor", "10 kOhm", "IN3_L"},
+ {"IN3_L to Right Mixer Negative Resistor", "20 kOhm", "IN3_L"},
+ {"IN3_L to Right Mixer Negative Resistor", "40 kOhm", "IN3_L"},
+
+ /* Left Input */
+ {"Left ADC", NULL, "IN1_L to Left Mixer Positive Resistor"},
+ {"IN1_L to Left Mixer Positive Resistor", "10 kOhm", "IN1_L"},
+ {"IN1_L to Left Mixer Positive Resistor", "20 kOhm", "IN1_L"},
+ {"IN1_L to Left Mixer Positive Resistor", "40 kOhm", "IN1_L"},
+
+ {"Left ADC", NULL, "IN2_L to Left Mixer Positive Resistor"},
+ {"IN2_L to Left Mixer Positive Resistor", "10 kOhm", "IN2_L"},
+ {"IN2_L to Left Mixer Positive Resistor", "20 kOhm", "IN2_L"},
+ {"IN2_L to Left Mixer Positive Resistor", "40 kOhm", "IN2_L"},
+
+ {"Left ADC", NULL, "IN3_L to Left Mixer Positive Resistor"},
+ {"IN3_L to Left Mixer Positive Resistor", "10 kOhm", "IN3_L"},
+ {"IN3_L to Left Mixer Positive Resistor", "20 kOhm", "IN3_L"},
+ {"IN3_L to Left Mixer Positive Resistor", "40 kOhm", "IN3_L"},
+
+ {"Left ADC", NULL, "IN1_R to Left Mixer Positive Resistor"},
+ {"IN1_R to Left Mixer Positive Resistor", "10 kOhm", "IN1_R"},
+ {"IN1_R to Left Mixer Positive Resistor", "20 kOhm", "IN1_R"},
+ {"IN1_R to Left Mixer Positive Resistor", "40 kOhm", "IN1_R"},
+
+ {"Left ADC", NULL, "CM_L to Left Mixer Negative Resistor"},
+ {"CM_L to Left Mixer Negative Resistor", "10 kOhm", "CM_L"},
+ {"CM_L to Left Mixer Negative Resistor", "20 kOhm", "CM_L"},
+ {"CM_L to Left Mixer Negative Resistor", "40 kOhm", "CM_L"},
+
+ {"Left ADC", NULL, "IN2_R to Left Mixer Negative Resistor"},
+ {"IN2_R to Left Mixer Negative Resistor", "10 kOhm", "IN2_R"},
+ {"IN2_R to Left Mixer Negative Resistor", "20 kOhm", "IN2_R"},
+ {"IN2_R to Left Mixer Negative Resistor", "40 kOhm", "IN2_R"},
+
+ {"Left ADC", NULL, "IN3_R to Left Mixer Negative Resistor"},
+ {"IN3_R to Left Mixer Negative Resistor", "10 kOhm", "IN3_R"},
+ {"IN3_R to Left Mixer Negative Resistor", "20 kOhm", "IN3_R"},
+ {"IN3_R to Left Mixer Negative Resistor", "40 kOhm", "IN3_R"},
};
static const struct regmap_range_cfg aic32x4_regmap_pages[] = {
@@ -287,14 +427,12 @@ static const struct regmap_range_cfg aic32x4_regmap_pages[] = {
},
};
-static const struct regmap_config aic32x4_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
-
+const struct regmap_config aic32x4_regmap_config = {
.max_register = AIC32X4_RMICPGAVOL,
.ranges = aic32x4_regmap_pages,
.num_ranges = ARRAY_SIZE(aic32x4_regmap_pages),
};
+EXPORT_SYMBOL(aic32x4_regmap_config);
static inline int aic32x4_get_divs(int mclk, int rate)
{
@@ -567,7 +705,7 @@ static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-#define AIC32X4_RATES SNDRV_PCM_RATE_8000_48000
+#define AIC32X4_RATES SNDRV_PCM_RATE_8000_96000
#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
| SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
@@ -596,7 +734,7 @@ static struct snd_soc_dai_driver aic32x4_dai = {
.symmetric_rates = 1,
};
-static int aic32x4_probe(struct snd_soc_codec *codec)
+static int aic32x4_codec_probe(struct snd_soc_codec *codec)
{
struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
u32 tmp_reg;
@@ -655,7 +793,7 @@ static int aic32x4_probe(struct snd_soc_codec *codec)
}
static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
- .probe = aic32x4_probe,
+ .probe = aic32x4_codec_probe,
.set_bias_level = aic32x4_set_bias_level,
.suspend_bias_off = true,
@@ -777,24 +915,22 @@ error_ldo:
return ret;
}
-static int aic32x4_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+int aic32x4_probe(struct device *dev, struct regmap *regmap)
{
- struct aic32x4_pdata *pdata = i2c->dev.platform_data;
struct aic32x4_priv *aic32x4;
- struct device_node *np = i2c->dev.of_node;
+ struct aic32x4_pdata *pdata = dev->platform_data;
+ struct device_node *np = dev->of_node;
int ret;
- aic32x4 = devm_kzalloc(&i2c->dev, sizeof(struct aic32x4_priv),
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv),
GFP_KERNEL);
if (aic32x4 == NULL)
return -ENOMEM;
- aic32x4->regmap = devm_regmap_init_i2c(i2c, &aic32x4_regmap);
- if (IS_ERR(aic32x4->regmap))
- return PTR_ERR(aic32x4->regmap);
-
- i2c_set_clientdata(i2c, aic32x4);
+ dev_set_drvdata(dev, aic32x4);
if (pdata) {
aic32x4->power_cfg = pdata->power_cfg;
@@ -804,7 +940,7 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c,
} else if (np) {
ret = aic32x4_parse_dt(aic32x4, np);
if (ret) {
- dev_err(&i2c->dev, "Failed to parse DT node\n");
+ dev_err(dev, "Failed to parse DT node\n");
return ret;
}
} else {
@@ -814,71 +950,48 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c,
aic32x4->rstn_gpio = -1;
}
- aic32x4->mclk = devm_clk_get(&i2c->dev, "mclk");
+ aic32x4->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(aic32x4->mclk)) {
- dev_err(&i2c->dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n");
+ dev_err(dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n");
return PTR_ERR(aic32x4->mclk);
}
if (gpio_is_valid(aic32x4->rstn_gpio)) {
- ret = devm_gpio_request_one(&i2c->dev, aic32x4->rstn_gpio,
+ ret = devm_gpio_request_one(dev, aic32x4->rstn_gpio,
GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn");
if (ret != 0)
return ret;
}
- ret = aic32x4_setup_regulators(&i2c->dev, aic32x4);
+ ret = aic32x4_setup_regulators(dev, aic32x4);
if (ret) {
- dev_err(&i2c->dev, "Failed to setup regulators\n");
+ dev_err(dev, "Failed to setup regulators\n");
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
+ ret = snd_soc_register_codec(dev,
&soc_codec_dev_aic32x4, &aic32x4_dai, 1);
if (ret) {
- dev_err(&i2c->dev, "Failed to register codec\n");
+ dev_err(dev, "Failed to register codec\n");
aic32x4_disable_regulators(aic32x4);
return ret;
}
- i2c_set_clientdata(i2c, aic32x4);
-
return 0;
}
+EXPORT_SYMBOL(aic32x4_probe);
-static int aic32x4_i2c_remove(struct i2c_client *client)
+int aic32x4_remove(struct device *dev)
{
- struct aic32x4_priv *aic32x4 = i2c_get_clientdata(client);
+ struct aic32x4_priv *aic32x4 = dev_get_drvdata(dev);
aic32x4_disable_regulators(aic32x4);
- snd_soc_unregister_codec(&client->dev);
+ snd_soc_unregister_codec(dev);
+
return 0;
}
-
-static const struct i2c_device_id aic32x4_i2c_id[] = {
- { "tlv320aic32x4", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id);
-
-static const struct of_device_id aic32x4_of_id[] = {
- { .compatible = "ti,tlv320aic32x4", },
- { /* senitel */ }
-};
-MODULE_DEVICE_TABLE(of, aic32x4_of_id);
-
-static struct i2c_driver aic32x4_i2c_driver = {
- .driver = {
- .name = "tlv320aic32x4",
- .of_match_table = aic32x4_of_id,
- },
- .probe = aic32x4_i2c_probe,
- .remove = aic32x4_i2c_remove,
- .id_table = aic32x4_i2c_id,
-};
-
-module_i2c_driver(aic32x4_i2c_driver);
+EXPORT_SYMBOL(aic32x4_remove);
MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver");
MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h
index 995f033a855d..a197dd51addc 100644
--- a/sound/soc/codecs/tlv320aic32x4.h
+++ b/sound/soc/codecs/tlv320aic32x4.h
@@ -10,6 +10,13 @@
#ifndef _TLV320AIC32X4_H
#define _TLV320AIC32X4_H
+struct device;
+struct regmap_config;
+
+extern const struct regmap_config aic32x4_regmap_config;
+int aic32x4_probe(struct device *dev, struct regmap *regmap);
+int aic32x4_remove(struct device *dev);
+
/* tlv320aic32x4 register space (in decimal to match datasheet) */
#define AIC32X4_PAGE1 128
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index bc3de2e844e6..1f7081043566 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -824,7 +824,7 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
{
struct twl6040 *twl6040 = codec->control_data;
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
- int ret;
+ int ret = 0;
switch (level) {
case SND_SOC_BIAS_ON:
@@ -832,12 +832,16 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (priv->codec_powered)
+ if (priv->codec_powered) {
+ /* Select low power PLL in standby */
+ ret = twl6040_set_pll(twl6040, TWL6040_SYSCLK_SEL_LPPLL,
+ 32768, 19200000);
break;
+ }
ret = twl6040_power(twl6040, 1);
if (ret)
- return ret;
+ break;
priv->codec_powered = 1;
@@ -853,7 +857,7 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
break;
}
- return 0;
+ return ret;
}
static int twl6040_startup(struct snd_pcm_substream *substream,
@@ -983,9 +987,9 @@ static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id i
if (mute) {
/* Power down drivers and DACs */
hflctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
- TWL6040_HFDRVENA);
+ TWL6040_HFDRVENA | TWL6040_HFSWENA);
hfrctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
- TWL6040_HFDRVENA);
+ TWL6040_HFDRVENA | TWL6040_HFSWENA);
}
twl6040_reg_write(twl6040, TWL6040_REG_HFLCTL, hflctl);
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 171a23ddd15d..512a9d25fe6f 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -17,6 +17,7 @@
#include <linux/export.h>
#include <linux/pm.h>
#include <linux/gcd.h>
+#include <linux/gpio/driver.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
@@ -2236,14 +2237,9 @@ static irqreturn_t wm5100_edge_irq(int irq, void *data)
}
#ifdef CONFIG_GPIOLIB
-static inline struct wm5100_priv *gpio_to_wm5100(struct gpio_chip *chip)
-{
- return container_of(chip, struct wm5100_priv, gpio_chip);
-}
-
static void wm5100_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
- struct wm5100_priv *wm5100 = gpio_to_wm5100(chip);
+ struct wm5100_priv *wm5100 = gpiochip_get_data(chip);
regmap_update_bits(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset,
WM5100_GP1_LVL, !!value << WM5100_GP1_LVL_SHIFT);
@@ -2252,7 +2248,7 @@ static void wm5100_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
static int wm5100_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
- struct wm5100_priv *wm5100 = gpio_to_wm5100(chip);
+ struct wm5100_priv *wm5100 = gpiochip_get_data(chip);
int val, ret;
val = (1 << WM5100_GP1_FN_SHIFT) | (!!value << WM5100_GP1_LVL_SHIFT);
@@ -2268,7 +2264,7 @@ static int wm5100_gpio_direction_out(struct gpio_chip *chip,
static int wm5100_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- struct wm5100_priv *wm5100 = gpio_to_wm5100(chip);
+ struct wm5100_priv *wm5100 = gpiochip_get_data(chip);
unsigned int reg;
int ret;
@@ -2281,7 +2277,7 @@ static int wm5100_gpio_get(struct gpio_chip *chip, unsigned offset)
static int wm5100_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
- struct wm5100_priv *wm5100 = gpio_to_wm5100(chip);
+ struct wm5100_priv *wm5100 = gpiochip_get_data(chip);
return regmap_update_bits(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset,
WM5100_GP1_FN_MASK | WM5100_GP1_DIR,
@@ -2313,7 +2309,7 @@ static void wm5100_init_gpio(struct i2c_client *i2c)
else
wm5100->gpio_chip.base = -1;
- ret = gpiochip_add(&wm5100->gpio_chip);
+ ret = gpiochip_add_data(&wm5100->gpio_chip, wm5100);
if (ret != 0)
dev_err(&i2c->dev, "Failed to add GPIOs: %d\n", ret);
}
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index da60e3fe5ee7..e7fe6b7b95b7 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -1872,7 +1872,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
.capture = {
.stream_name = "Audio Trace CPU",
.channels_min = 1,
- .channels_max = 6,
+ .channels_max = 4,
.rates = WM5102_RATES,
.formats = WM5102_FORMATS,
},
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index b5820e4d5471..d54f1b46c9ec 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -1723,6 +1723,7 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "OUT2L", NULL, "SYSCLK" },
{ "OUT2R", NULL, "SYSCLK" },
{ "OUT3L", NULL, "SYSCLK" },
+ { "OUT3R", NULL, "SYSCLK" },
{ "OUT4L", NULL, "SYSCLK" },
{ "OUT4R", NULL, "SYSCLK" },
{ "OUT5L", NULL, "SYSCLK" },
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index a82b8bc2cfc0..a26ca490cf31 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -20,7 +20,7 @@
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
@@ -1766,11 +1766,6 @@ static int wm8903_resume(struct snd_soc_codec *codec)
}
#ifdef CONFIG_GPIOLIB
-static inline struct wm8903_priv *gpio_to_wm8903(struct gpio_chip *chip)
-{
- return container_of(chip, struct wm8903_priv, gpio_chip);
-}
-
static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset)
{
if (offset >= WM8903_NUM_GPIO)
@@ -1781,7 +1776,7 @@ static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset)
static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
- struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+ struct wm8903_priv *wm8903 = gpiochip_get_data(chip);
unsigned int mask, val;
int ret;
@@ -1799,7 +1794,7 @@ static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+ struct wm8903_priv *wm8903 = gpiochip_get_data(chip);
unsigned int reg;
regmap_read(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset, &reg);
@@ -1810,7 +1805,7 @@ static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset)
static int wm8903_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
- struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+ struct wm8903_priv *wm8903 = gpiochip_get_data(chip);
unsigned int mask, val;
int ret;
@@ -1828,7 +1823,7 @@ static int wm8903_gpio_direction_out(struct gpio_chip *chip,
static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
- struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+ struct wm8903_priv *wm8903 = gpiochip_get_data(chip);
regmap_update_bits(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset,
WM8903_GP1_LVL_MASK,
@@ -1860,7 +1855,7 @@ static void wm8903_init_gpio(struct wm8903_priv *wm8903)
else
wm8903->gpio_chip.base = -1;
- ret = gpiochip_add(&wm8903->gpio_chip);
+ ret = gpiochip_add_data(&wm8903->gpio_chip, wm8903);
if (ret != 0)
dev_err(wm8903->dev, "Failed to add GPIOs: %d\n", ret);
}
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index f6f9395ea38e..1c600819f768 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -743,6 +743,7 @@ static const struct regmap_config wm8940_regmap = {
.max_register = WM8940_MONOMIX,
.reg_defaults = wm8940_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8940_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
.readable_reg = wm8940_readable_register,
.volatile_reg = wm8940_volatile_register,
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 720a14e0687d..f3109da24769 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -18,7 +18,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/gcd.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/pm_runtime.h>
@@ -3307,14 +3307,9 @@ static void wm8962_set_gpio_mode(struct wm8962_priv *wm8962, int gpio)
}
#ifdef CONFIG_GPIOLIB
-static inline struct wm8962_priv *gpio_to_wm8962(struct gpio_chip *chip)
-{
- return container_of(chip, struct wm8962_priv, gpio_chip);
-}
-
static int wm8962_gpio_request(struct gpio_chip *chip, unsigned offset)
{
- struct wm8962_priv *wm8962 = gpio_to_wm8962(chip);
+ struct wm8962_priv *wm8962 = gpiochip_get_data(chip);
/* The WM8962 GPIOs aren't linearly numbered. For simplicity
* we export linear numbers and error out if the unsupported
@@ -3337,7 +3332,7 @@ static int wm8962_gpio_request(struct gpio_chip *chip, unsigned offset)
static void wm8962_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
- struct wm8962_priv *wm8962 = gpio_to_wm8962(chip);
+ struct wm8962_priv *wm8962 = gpiochip_get_data(chip);
struct snd_soc_codec *codec = wm8962->codec;
snd_soc_update_bits(codec, WM8962_GPIO_BASE + offset,
@@ -3347,7 +3342,7 @@ static void wm8962_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
static int wm8962_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
- struct wm8962_priv *wm8962 = gpio_to_wm8962(chip);
+ struct wm8962_priv *wm8962 = gpiochip_get_data(chip);
struct snd_soc_codec *codec = wm8962->codec;
int ret, val;
@@ -3386,7 +3381,7 @@ static void wm8962_init_gpio(struct snd_soc_codec *codec)
else
wm8962->gpio_chip.base = -1;
- ret = gpiochip_add(&wm8962->gpio_chip);
+ ret = gpiochip_add_data(&wm8962->gpio_chip, wm8962);
if (ret != 0)
dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
}
@@ -3798,9 +3793,8 @@ static int wm8962_runtime_resume(struct device *dev)
ret = regulator_bulk_enable(ARRAY_SIZE(wm8962->supplies),
wm8962->supplies);
if (ret != 0) {
- dev_err(dev,
- "Failed to enable supplies: %d\n", ret);
- return ret;
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ goto disable_clock;
}
regcache_cache_only(wm8962->regmap, false);
@@ -3838,6 +3832,10 @@ static int wm8962_runtime_resume(struct device *dev)
msleep(5);
return 0;
+
+disable_clock:
+ clk_disable_unprepare(wm8962->pdata.mclk);
+ return ret;
}
static int wm8962_runtime_suspend(struct device *dev)
diff --git a/sound/soc/codecs/wm8962.h b/sound/soc/codecs/wm8962.h
index 910aafd09d21..e63a318a3015 100644
--- a/sound/soc/codecs/wm8962.h
+++ b/sound/soc/codecs/wm8962.h
@@ -16,9 +16,9 @@
#include <asm/types.h>
#include <sound/soc.h>
-#define WM8962_SYSCLK_MCLK 1
-#define WM8962_SYSCLK_FLL 2
-#define WM8962_SYSCLK_PLL3 3
+#define WM8962_SYSCLK_MCLK 0
+#define WM8962_SYSCLK_FLL 1
+#define WM8962_SYSCLK_PLL3 2
#define WM8962_FLL 1
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index f99b34f7647b..a73044251218 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -17,6 +17,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/gcd.h>
+#include <linux/gpio/driver.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
@@ -2139,14 +2140,9 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
}
#ifdef CONFIG_GPIOLIB
-static inline struct wm8996_priv *gpio_to_wm8996(struct gpio_chip *chip)
-{
- return container_of(chip, struct wm8996_priv, gpio_chip);
-}
-
static void wm8996_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
- struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
+ struct wm8996_priv *wm8996 = gpiochip_get_data(chip);
regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset,
WM8996_GP1_LVL, !!value << WM8996_GP1_LVL_SHIFT);
@@ -2155,7 +2151,7 @@ static void wm8996_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
static int wm8996_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
- struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
+ struct wm8996_priv *wm8996 = gpiochip_get_data(chip);
int val;
val = (1 << WM8996_GP1_FN_SHIFT) | (!!value << WM8996_GP1_LVL_SHIFT);
@@ -2167,7 +2163,7 @@ static int wm8996_gpio_direction_out(struct gpio_chip *chip,
static int wm8996_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
+ struct wm8996_priv *wm8996 = gpiochip_get_data(chip);
unsigned int reg;
int ret;
@@ -2180,7 +2176,7 @@ static int wm8996_gpio_get(struct gpio_chip *chip, unsigned offset)
static int wm8996_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
- struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
+ struct wm8996_priv *wm8996 = gpiochip_get_data(chip);
return regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset,
WM8996_GP1_FN_MASK | WM8996_GP1_DIR,
@@ -2211,7 +2207,7 @@ static void wm8996_init_gpio(struct wm8996_priv *wm8996)
else
wm8996->gpio_chip.base = -1;
- ret = gpiochip_add(&wm8996->gpio_chip);
+ ret = gpiochip_add_data(&wm8996->gpio_chip, wm8996);
if (ret != 0)
dev_err(wm8996->dev, "Failed to add GPIOs: %d\n", ret);
}
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 0f66fda2c772..237dc67002ef 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -1513,8 +1513,9 @@ static struct davinci_mcasp_pdata am33xx_mcasp_pdata = {
};
static struct davinci_mcasp_pdata dra7_mcasp_pdata = {
- .tx_dma_offset = 0x200,
- .rx_dma_offset = 0x284,
+ /* The CFG port offset will be calculated if it is needed */
+ .tx_dma_offset = 0,
+ .rx_dma_offset = 0,
.version = MCASP_VERSION_4,
};
@@ -1734,6 +1735,52 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp)
return PCM_EDMA;
}
+static u32 davinci_mcasp_txdma_offset(struct davinci_mcasp_pdata *pdata)
+{
+ int i;
+ u32 offset = 0;
+
+ if (pdata->version != MCASP_VERSION_4)
+ return pdata->tx_dma_offset;
+
+ for (i = 0; i < pdata->num_serializer; i++) {
+ if (pdata->serial_dir[i] == TX_MODE) {
+ if (!offset) {
+ offset = DAVINCI_MCASP_TXBUF_REG(i);
+ } else {
+ pr_err("%s: Only one serializer allowed!\n",
+ __func__);
+ break;
+ }
+ }
+ }
+
+ return offset;
+}
+
+static u32 davinci_mcasp_rxdma_offset(struct davinci_mcasp_pdata *pdata)
+{
+ int i;
+ u32 offset = 0;
+
+ if (pdata->version != MCASP_VERSION_4)
+ return pdata->rx_dma_offset;
+
+ for (i = 0; i < pdata->num_serializer; i++) {
+ if (pdata->serial_dir[i] == RX_MODE) {
+ if (!offset) {
+ offset = DAVINCI_MCASP_RXBUF_REG(i);
+ } else {
+ pr_err("%s: Only one serializer allowed!\n",
+ __func__);
+ break;
+ }
+ }
+ }
+
+ return offset;
+}
+
static int davinci_mcasp_probe(struct platform_device *pdev)
{
struct snd_dmaengine_dai_dma_data *dma_data;
@@ -1862,7 +1909,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
if (dat)
dma_data->addr = dat->start;
else
- dma_data->addr = mem->start + pdata->tx_dma_offset;
+ dma_data->addr = mem->start + davinci_mcasp_txdma_offset(pdata);
dma = &mcasp->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
@@ -1883,7 +1930,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
if (dat)
dma_data->addr = dat->start;
else
- dma_data->addr = mem->start + pdata->rx_dma_offset;
+ dma_data->addr =
+ mem->start + davinci_mcasp_rxdma_offset(pdata);
dma = &mcasp->dma_request[SNDRV_PCM_STREAM_CAPTURE];
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h
index 1e8787fb3fb7..afddc8010c54 100644
--- a/sound/soc/davinci/davinci-mcasp.h
+++ b/sound/soc/davinci/davinci-mcasp.h
@@ -85,9 +85,9 @@
(n << 2))
/* Transmit Buffer for Serializer n */
-#define DAVINCI_MCASP_TXBUF_REG 0x200
+#define DAVINCI_MCASP_TXBUF_REG(n) (0x200 + (n << 2))
/* Receive Buffer for Serializer n */
-#define DAVINCI_MCASP_RXBUF_REG 0x280
+#define DAVINCI_MCASP_RXBUF_REG(n) (0x280 + (n << 2))
/* McASP FIFO Registers */
#define DAVINCI_MCASP_V2_AFIFO_BASE (0x1010)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 632ecc0e3956..bedec4a32581 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -952,16 +952,16 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
ssi_private->i2s_mode = CCSR_SSI_SCR_NET;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
+ regmap_update_bits(regs, CCSR_SSI_STCCR,
+ CCSR_SSI_SxCCR_DC_MASK,
+ CCSR_SSI_SxCCR_DC(2));
+ regmap_update_bits(regs, CCSR_SSI_SRCCR,
+ CCSR_SSI_SxCCR_DC_MASK,
+ CCSR_SSI_SxCCR_DC(2));
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFS:
case SND_SOC_DAIFMT_CBS_CFS:
ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_MASTER;
- regmap_update_bits(regs, CCSR_SSI_STCCR,
- CCSR_SSI_SxCCR_DC_MASK,
- CCSR_SSI_SxCCR_DC(2));
- regmap_update_bits(regs, CCSR_SSI_SRCCR,
- CCSR_SSI_SxCCR_DC_MASK,
- CCSR_SSI_SxCCR_DC(2));
break;
case SND_SOC_DAIFMT_CBM_CFM:
ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_SLAVE;
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 2389ab47e25f..466492b7d4f5 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -643,6 +643,7 @@ MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
static struct platform_driver asoc_simple_card = {
.driver = {
.name = "asoc-simple-card",
+ .pm = &snd_soc_pm_ops,
.of_match_table = asoc_simple_of_match,
},
.probe = asoc_simple_card_probe,
diff --git a/sound/soc/intel/atom/sst-mfld-platform-compress.c b/sound/soc/intel/atom/sst-mfld-platform-compress.c
index 395168986462..1bead81bb510 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-compress.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-compress.c
@@ -182,24 +182,29 @@ static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
case SNDRV_PCM_TRIGGER_START:
if (stream->compr_ops->stream_start)
return stream->compr_ops->stream_start(sst->dev, stream->id);
+ break;
case SNDRV_PCM_TRIGGER_STOP:
if (stream->compr_ops->stream_drop)
return stream->compr_ops->stream_drop(sst->dev, stream->id);
+ break;
case SND_COMPR_TRIGGER_DRAIN:
if (stream->compr_ops->stream_drain)
return stream->compr_ops->stream_drain(sst->dev, stream->id);
+ break;
case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
if (stream->compr_ops->stream_partial_drain)
return stream->compr_ops->stream_partial_drain(sst->dev, stream->id);
+ break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (stream->compr_ops->stream_pause)
return stream->compr_ops->stream_pause(sst->dev, stream->id);
+ break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (stream->compr_ops->stream_pause_release)
return stream->compr_ops->stream_pause_release(sst->dev, stream->id);
- default:
- return -EINVAL;
+ break;
}
+ return -EINVAL;
}
static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
index 6260df6bd49c..cdcced9f32b6 100644
--- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c
+++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
@@ -296,7 +296,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
if (!drv)
return -ENOMEM;
- drv->ts3a227e_present = acpi_dev_present("104C227E");
+ drv->ts3a227e_present = acpi_dev_found("104C227E");
if (!drv->ts3a227e_present) {
/* no need probe TI jack detection chip */
snd_soc_card_cht.aux_dev = NULL;
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 0618a7f1025b..d7ef292c402d 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -357,7 +357,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) {
- if (acpi_dev_present(snd_soc_cards[i].codec_id)) {
+ if (acpi_dev_found(snd_soc_cards[i].codec_id)) {
dev_dbg(&pdev->dev,
"found codec %s\n", snd_soc_cards[i].codec_id);
card = snd_soc_cards[i].soc_card;
diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c
index ef4881e7753a..25993527370b 100644
--- a/sound/soc/intel/common/sst-firmware.c
+++ b/sound/soc/intel/common/sst-firmware.c
@@ -203,7 +203,7 @@ static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
chip->dev = dev;
- err = dw_dma_probe(chip, NULL);
+ err = dw_dma_probe(chip);
if (err)
return ERR_PTR(err);
diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c
index 965ce40ce752..8b95e09e23e8 100644
--- a/sound/soc/intel/skylake/bxt-sst.c
+++ b/sound/soc/intel/skylake/bxt-sst.c
@@ -291,6 +291,7 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
sst_dsp_mailbox_init(sst, (BXT_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ),
SKL_ADSP_W0_UP_SZ, BXT_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ);
+ INIT_LIST_HEAD(&sst->module_list);
ret = skl_ipc_init(dev, skl);
if (ret)
return ret;
diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig
index 132bb83f8e99..bc3c7b5ac752 100644
--- a/sound/soc/kirkwood/Kconfig
+++ b/sound/soc/kirkwood/Kconfig
@@ -1,6 +1,7 @@
config SND_KIRKWOOD_SOC
tristate "SoC Audio for the Marvell Kirkwood and Dove chips"
depends on ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST
+ depends on HAS_DMA
help
Say Y or M if you want to add support for codecs attached to
the Kirkwood I2S interface. You will also need to select the
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index f7e789e97fbc..3abf51c07851 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -43,6 +43,7 @@ config SND_SOC_MT8173_RT5650_RT5676
depends on SND_SOC_MEDIATEK && I2C
select SND_SOC_RT5645
select SND_SOC_RT5677
+ select SND_SOC_HDMI_CODEC
help
This adds ASoC driver for Mediatek MT8173 boards
with the RT5650 and RT5676 codecs.
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c
index 5c4c58c69c51..bb593926c62d 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c
+++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c
@@ -134,7 +134,9 @@ static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = {
enum {
DAI_LINK_PLAYBACK,
DAI_LINK_CAPTURE,
+ DAI_LINK_HDMI,
DAI_LINK_CODEC_I2S,
+ DAI_LINK_HDMI_I2S,
DAI_LINK_INTERCODEC
};
@@ -161,6 +163,16 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
.dynamic = 1,
.dpcm_capture = 1,
},
+ [DAI_LINK_HDMI] = {
+ .name = "HDMI",
+ .stream_name = "HDMI PCM",
+ .cpu_dai_name = "HDMI",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ },
/* Back End DAI links */
[DAI_LINK_CODEC_I2S] = {
@@ -177,6 +189,13 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
.dpcm_playback = 1,
.dpcm_capture = 1,
},
+ [DAI_LINK_HDMI_I2S] = {
+ .name = "HDMI BE",
+ .cpu_dai_name = "HDMIO",
+ .no_pcm = 1,
+ .codec_dai_name = "i2s-hifi",
+ .dpcm_playback = 1,
+ },
/* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */
[DAI_LINK_INTERCODEC] = {
.name = "rt5650_rt5676 intercodec",
@@ -251,6 +270,14 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codec_of_node =
mt8173_rt5650_rt5676_codecs[1].of_node;
+ mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codec_of_node =
+ of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 2);
+ if (!mt8173_rt5650_rt5676_dais[DAI_LINK_HDMI_I2S].codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
+
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173-rt5650.c
index bb09bb1b7f1c..a27a6673dbe3 100644
--- a/sound/soc/mediatek/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173-rt5650.c
@@ -85,12 +85,29 @@ static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_card *card = runtime->card;
struct snd_soc_codec *codec = runtime->codec_dais[0]->codec;
+ const char *codec_capture_dai = runtime->codec_dais[1]->name;
int ret;
rt5645_sel_asrc_clk_src(codec,
- RT5645_DA_STEREO_FILTER |
- RT5645_AD_STEREO_FILTER,
+ RT5645_DA_STEREO_FILTER,
RT5645_CLK_SEL_I2S1_ASRC);
+
+ if (!strcmp(codec_capture_dai, "rt5645-aif1")) {
+ rt5645_sel_asrc_clk_src(codec,
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S1_ASRC);
+ } else if (!strcmp(codec_capture_dai, "rt5645-aif2")) {
+ rt5645_sel_asrc_clk_src(codec,
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S2_ASRC);
+ } else {
+ dev_warn(card->dev,
+ "Only one dai codec found in DTS, enabled rt5645 AD filter\n");
+ rt5645_sel_asrc_clk_src(codec,
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S1_ASRC);
+ }
+
/* enable jack detection */
ret = snd_soc_card_jack_new(card, "Headset Jack",
SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
@@ -110,6 +127,11 @@ static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime)
static struct snd_soc_dai_link_component mt8173_rt5650_codecs[] = {
{
+ /* Playback */
+ .dai_name = "rt5645-aif1",
+ },
+ {
+ /* Capture */
.dai_name = "rt5645-aif1",
},
};
@@ -149,7 +171,7 @@ static struct snd_soc_dai_link mt8173_rt5650_dais[] = {
.cpu_dai_name = "I2S",
.no_pcm = 1,
.codecs = mt8173_rt5650_codecs,
- .num_codecs = 1,
+ .num_codecs = 2,
.init = mt8173_rt5650_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
@@ -177,6 +199,8 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt8173_rt5650_card;
struct device_node *platform_node;
+ struct device_node *np;
+ const char *codec_capture_dai;
int i, ret;
platform_node = of_parse_phandle(pdev->dev.of_node,
@@ -199,6 +223,26 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
"Property 'audio-codec' missing or invalid\n");
return -EINVAL;
}
+ mt8173_rt5650_codecs[1].of_node = mt8173_rt5650_codecs[0].of_node;
+
+ if (of_find_node_by_name(platform_node, "codec-capture")) {
+ np = of_get_child_by_name(pdev->dev.of_node, "codec-capture");
+ if (!np) {
+ dev_err(&pdev->dev,
+ "%s: Can't find codec-capture DT node\n",
+ __func__);
+ return -EINVAL;
+ }
+ ret = snd_soc_of_get_dai_name(np, &codec_capture_dai);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "%s codec_capture_dai name fail %d\n",
+ __func__, ret);
+ return ret;
+ }
+ mt8173_rt5650_codecs[1].dai_name = codec_capture_dai;
+ }
+
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c
index f1c58a2c12fb..2b5df2ef51a3 100644
--- a/sound/soc/mediatek/mtk-afe-pcm.c
+++ b/sound/soc/mediatek/mtk-afe-pcm.c
@@ -123,6 +123,7 @@
#define AFE_TDM_CON1_WLEN_32BIT (0x2 << 8)
#define AFE_TDM_CON1_MSB_ALIGNED (0x1 << 4)
#define AFE_TDM_CON1_1_BCK_DELAY (0x1 << 3)
+#define AFE_TDM_CON1_LRCK_INV (0x1 << 2)
#define AFE_TDM_CON1_BCK_INV (0x1 << 1)
#define AFE_TDM_CON1_EN (0x1 << 0)
@@ -449,6 +450,7 @@ static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
runtime->rate * runtime->channels * 32);
val = AFE_TDM_CON1_BCK_INV |
+ AFE_TDM_CON1_LRCK_INV |
AFE_TDM_CON1_1_BCK_DELAY |
AFE_TDM_CON1_MSB_ALIGNED | /* I2S mode */
AFE_TDM_CON1_WLEN_32BIT |
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c
index c7563e230c7d..4a16e778966b 100644
--- a/sound/soc/omap/mcbsp.c
+++ b/sound/soc/omap/mcbsp.c
@@ -260,6 +260,10 @@ static void omap_st_on(struct omap_mcbsp *mcbsp)
if (mcbsp->pdata->enable_st_clock)
mcbsp->pdata->enable_st_clock(mcbsp->id, 1);
+ /* Disable Sidetone clock auto-gating for normal operation */
+ w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
+ MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE));
+
/* Enable McBSP Sidetone */
w = MCBSP_READ(mcbsp, SSELCR);
MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN);
@@ -279,6 +283,10 @@ static void omap_st_off(struct omap_mcbsp *mcbsp)
w = MCBSP_READ(mcbsp, SSELCR);
MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN));
+ /* Enable Sidetone clock auto-gating to reduce power consumption */
+ w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
+ MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE);
+
if (mcbsp->pdata->enable_st_clock)
mcbsp->pdata->enable_st_clock(mcbsp->id, 0);
}
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 99381a27295b..a84f677234f0 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -82,6 +82,8 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
struct dma_chan *chan;
int err = 0;
+ memset(&config, 0x00, sizeof(config));
+
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
/* return if this is a bufferless transfer e.g.
diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c
index ec522e94b0e2..b6cb9950f05d 100644
--- a/sound/soc/pxa/brownstone.c
+++ b/sound/soc/pxa/brownstone.c
@@ -133,3 +133,4 @@ module_platform_driver(mmp_driver);
MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
MODULE_DESCRIPTION("ALSA SoC Brownstone");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:brownstone-audio");
diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c
index 5c8f9db50a47..d1661fa6ee08 100644
--- a/sound/soc/pxa/mioa701_wm9713.c
+++ b/sound/soc/pxa/mioa701_wm9713.c
@@ -207,3 +207,4 @@ module_platform_driver(mioa701_wm9713_driver);
MODULE_AUTHOR("Robert Jarzmik (rjarzmik@free.fr)");
MODULE_DESCRIPTION("ALSA SoC WM9713 MIO A701");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mioa701-wm9713");
diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c
index 51e790d006f5..96df9b2d8fc4 100644
--- a/sound/soc/pxa/mmp-pcm.c
+++ b/sound/soc/pxa/mmp-pcm.c
@@ -248,3 +248,4 @@ module_platform_driver(mmp_pcm_driver);
MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
MODULE_DESCRIPTION("MMP Soc Audio DMA module");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mmp-pcm-audio");
diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c
index eca60c29791a..ca8b23f8c525 100644
--- a/sound/soc/pxa/mmp-sspa.c
+++ b/sound/soc/pxa/mmp-sspa.c
@@ -482,3 +482,4 @@ module_platform_driver(asoc_mmp_sspa_driver);
MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
MODULE_DESCRIPTION("MMP SSPA SoC Interface");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mmp-sspa-dai");
diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c
index 4e74d9573f03..bcc81e920a67 100644
--- a/sound/soc/pxa/palm27x.c
+++ b/sound/soc/pxa/palm27x.c
@@ -161,3 +161,4 @@ module_platform_driver(palm27x_wm9712_driver);
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
MODULE_DESCRIPTION("ALSA SoC Palm T|X, T5 and LifeDrive");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:palm27x-asoc");
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index da03fad1b9cd..3cad990dad2c 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -833,3 +833,4 @@ module_platform_driver(asoc_ssp_driver);
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_DESCRIPTION("PXA SSP/PCM SoC Interface");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa-ssp-dai");
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index f3de615aacd7..9615e6de1306 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -287,3 +287,4 @@ module_platform_driver(pxa2xx_ac97_driver);
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa2xx-ac97");
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index 9f390398d518..410d48b93031 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -117,3 +117,4 @@ module_platform_driver(pxa_pcm_driver);
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa-pcm-audio");
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index 6e8665430bd5..db000c6987a1 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -474,7 +474,7 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant;
- int ret;
+ int ret = -EINVAL;
struct lpass_pcm_data *data;
size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
@@ -491,7 +491,7 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
data->rdma_ch = v->alloc_dma_channel(drvdata,
SNDRV_PCM_STREAM_PLAYBACK);
- if (IS_ERR_VALUE(data->rdma_ch))
+ if (data->rdma_ch < 0)
return data->rdma_ch;
drvdata->substream[data->rdma_ch] = psubstream;
@@ -518,8 +518,10 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
data->wrdma_ch = v->alloc_dma_channel(drvdata,
SNDRV_PCM_STREAM_CAPTURE);
- if (IS_ERR_VALUE(data->wrdma_ch))
+ if (data->wrdma_ch < 0) {
+ ret = data->wrdma_ch;
goto capture_alloc_err;
+ }
drvdata->substream[data->wrdma_ch] = csubstream;
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 0891014c262f..c4c51a4d3c8f 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -492,9 +492,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
*/
if (!count) {
clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
- parent_clk_name,
- (parent_clk_name) ?
- 0 : CLK_IS_ROOT, req_rate);
+ parent_clk_name, 0, req_rate);
if (!IS_ERR(clk)) {
adg->clkout[CLKOUT] = clk;
of_clk_add_provider(np, of_clk_src_simple_get, clk);
@@ -506,9 +504,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
else {
for (i = 0; i < CLKOUTMAX; i++) {
clk = clk_register_fixed_rate(dev, clkout_name[i],
- parent_clk_name,
- (parent_clk_name) ?
- 0 : CLK_IS_ROOT,
+ parent_clk_name, 0,
req_rate);
if (!IS_ERR(clk)) {
adg->onecell.clks = adg->clkout;
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
index 7658e8fd7bdc..6bc93cbb3049 100644
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/sh/rcar/dma.c
@@ -316,11 +316,15 @@ static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io,
size = ARRAY_SIZE(gen2_id_table_cmd);
}
- if (!entry)
- return 0xFF;
+ if ((!entry) || (size <= id)) {
+ struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io));
- if (size <= id)
- return 0xFF;
+ dev_err(dev, "unknown connection (%s[%d])\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+ /* use non-prohibited SRS number as error */
+ return 0x00; /* SSI00 */
+ }
return entry[id];
}
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index fc89a67258ca..a8f61d79333b 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -276,8 +276,9 @@ struct rsnd_mod {
/*
* status
*
- * 0xH0000CB0
+ * 0xH0000CBA
*
+ * A 0: probe 1: remove
* B 0: init 1: quit
* C 0: start 1: stop
*
@@ -287,19 +288,19 @@ struct rsnd_mod {
* H 0: fallback
* H 0: hw_params
*/
+#define __rsnd_mod_shift_probe 0
+#define __rsnd_mod_shift_remove 0
#define __rsnd_mod_shift_init 4
#define __rsnd_mod_shift_quit 4
#define __rsnd_mod_shift_start 8
#define __rsnd_mod_shift_stop 8
-#define __rsnd_mod_shift_probe 28 /* always called */
-#define __rsnd_mod_shift_remove 28 /* always called */
#define __rsnd_mod_shift_irq 28 /* always called */
#define __rsnd_mod_shift_pcm_new 28 /* always called */
#define __rsnd_mod_shift_fallback 28 /* always called */
#define __rsnd_mod_shift_hw_params 28 /* always called */
-#define __rsnd_mod_add_probe 0
-#define __rsnd_mod_add_remove 0
+#define __rsnd_mod_add_probe 1
+#define __rsnd_mod_add_remove -1
#define __rsnd_mod_add_init 1
#define __rsnd_mod_add_quit -1
#define __rsnd_mod_add_start 1
@@ -310,7 +311,7 @@ struct rsnd_mod {
#define __rsnd_mod_add_hw_params 0
#define __rsnd_mod_call_probe 0
-#define __rsnd_mod_call_remove 0
+#define __rsnd_mod_call_remove 1
#define __rsnd_mod_call_init 0
#define __rsnd_mod_call_quit 1
#define __rsnd_mod_call_start 0
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 15d6ffe8be74..e39f916d0f2f 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -572,6 +572,9 @@ int rsnd_src_probe(struct rsnd_priv *priv)
i = 0;
for_each_child_of_node(node, np) {
+ if (!of_device_is_available(np))
+ goto skip;
+
src = rsnd_src_get(priv, i);
snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d",
@@ -595,6 +598,7 @@ int rsnd_src_probe(struct rsnd_priv *priv)
if (ret)
goto rsnd_src_probe_done;
+skip:
i++;
}
diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c
index 7e0acd83b0e6..bc4a55bb3fd9 100644
--- a/sound/soc/soc-ac97.c
+++ b/sound/soc/soc-ac97.c
@@ -59,8 +59,7 @@ static void soc_ac97_device_release(struct device *dev)
#ifdef CONFIG_GPIOLIB
static inline struct snd_soc_codec *gpio_to_codec(struct gpio_chip *chip)
{
- struct snd_ac97_gpio_priv *gpio_priv =
- container_of(chip, struct snd_ac97_gpio_priv, gpio_chip);
+ struct snd_ac97_gpio_priv *gpio_priv = gpiochip_get_data(chip);
return gpio_priv->codec;
}
@@ -98,8 +97,7 @@ static int snd_soc_ac97_gpio_get(struct gpio_chip *chip, unsigned offset)
static void snd_soc_ac97_gpio_set(struct gpio_chip *chip, unsigned offset,
int value)
{
- struct snd_ac97_gpio_priv *gpio_priv =
- container_of(chip, struct snd_ac97_gpio_priv, gpio_chip);
+ struct snd_ac97_gpio_priv *gpio_priv = gpiochip_get_data(chip);
struct snd_soc_codec *codec = gpio_to_codec(chip);
gpio_priv->gpios_set &= ~(1 << offset);
@@ -145,7 +143,7 @@ static int snd_soc_ac97_init_gpio(struct snd_ac97 *ac97,
gpio_priv->gpio_chip.parent = codec->dev;
gpio_priv->gpio_chip.base = -1;
- ret = gpiochip_add(&gpio_priv->gpio_chip);
+ ret = gpiochip_add_data(&gpio_priv->gpio_chip, gpio_priv);
if (ret != 0)
dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
return ret;
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 1cf94d7fb9f4..ee7f15aa46fc 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -1023,6 +1023,11 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos;
+ if (control_hdr->size != sizeof(*control_hdr)) {
+ dev_err(tplg->dev, "ASoC: invalid control size\n");
+ return -EINVAL;
+ }
+
switch (control_hdr->ops.info) {
case SND_SOC_TPLG_CTL_VOLSW:
case SND_SOC_TPLG_CTL_STROBE:
@@ -1476,6 +1481,8 @@ widget:
widget->dobj.type = SND_SOC_DOBJ_WIDGET;
widget->dobj.ops = tplg->ops;
widget->dobj.index = tplg->index;
+ kfree(template.sname);
+ kfree(template.name);
list_add(&widget->dobj.list, &tplg->comp->dobj_list);
return 0;
@@ -1499,10 +1506,17 @@ static int soc_tplg_dapm_widget_elems_load(struct soc_tplg *tplg,
for (i = 0; i < count; i++) {
widget = (struct snd_soc_tplg_dapm_widget *) tplg->pos;
+ if (widget->size != sizeof(*widget)) {
+ dev_err(tplg->dev, "ASoC: invalid widget size\n");
+ return -EINVAL;
+ }
+
ret = soc_tplg_dapm_widget_create(tplg, widget);
- if (ret < 0)
+ if (ret < 0) {
dev_err(tplg->dev, "ASoC: failed to load widget %s\n",
widget->name);
+ return ret;
+ }
}
return 0;
@@ -1586,6 +1600,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
return snd_soc_register_dai(tplg->comp, dai_drv);
}
+/* create the FE DAI link */
static int soc_tplg_link_create(struct soc_tplg *tplg,
struct snd_soc_tplg_pcm *pcm)
{
@@ -1598,6 +1613,16 @@ static int soc_tplg_link_create(struct soc_tplg *tplg,
link->name = pcm->pcm_name;
link->stream_name = pcm->pcm_name;
+ link->id = pcm->pcm_id;
+
+ link->cpu_dai_name = pcm->dai_name;
+ link->codec_name = "snd-soc-dummy";
+ link->codec_dai_name = "snd-soc-dummy-dai";
+
+ /* enable DPCM */
+ link->dynamic = 1;
+ link->dpcm_playback = pcm->playback;
+ link->dpcm_capture = pcm->capture;
/* pass control to component driver for optional further init */
ret = soc_tplg_dai_link_load(tplg, link);
@@ -1639,8 +1664,6 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
if (tplg->pass != SOC_TPLG_PASS_PCM_DAI)
return 0;
- pcm = (struct snd_soc_tplg_pcm *)tplg->pos;
-
if (soc_tplg_check_elem_count(tplg,
sizeof(struct snd_soc_tplg_pcm), count,
hdr->payload_size, "PCM DAI")) {
@@ -1650,7 +1673,13 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
}
/* create the FE DAIs and DAI links */
+ pcm = (struct snd_soc_tplg_pcm *)tplg->pos;
for (i = 0; i < count; i++) {
+ if (pcm->size != sizeof(*pcm)) {
+ dev_err(tplg->dev, "ASoC: invalid pcm size\n");
+ return -EINVAL;
+ }
+
soc_tplg_pcm_create(tplg, pcm);
pcm++;
}
@@ -1670,6 +1699,11 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg,
return 0;
manifest = (struct snd_soc_tplg_manifest *)tplg->pos;
+ if (manifest->size != sizeof(*manifest)) {
+ dev_err(tplg->dev, "ASoC: invalid manifest size\n");
+ return -EINVAL;
+ }
+
tplg->pos += sizeof(struct snd_soc_tplg_manifest);
if (tplg->comp && tplg->ops && tplg->ops->manifest)
@@ -1686,6 +1720,14 @@ static int soc_valid_header(struct soc_tplg *tplg,
if (soc_tplg_get_hdr_offset(tplg) >= tplg->fw->size)
return 0;
+ if (hdr->size != sizeof(*hdr)) {
+ dev_err(tplg->dev,
+ "ASoC: invalid header size for type %d at offset 0x%lx size 0x%zx.\n",
+ hdr->type, soc_tplg_get_hdr_offset(tplg),
+ tplg->fw->size);
+ return -EINVAL;
+ }
+
/* big endian firmware objects not supported atm */
if (hdr->magic == cpu_to_be32(SND_SOC_TPLG_MAGIC)) {
dev_err(tplg->dev,
diff --git a/sound/soc/sti/sti_uniperif.c b/sound/soc/sti/sti_uniperif.c
index 39bcefe5eea0..488ef4ed8fba 100644
--- a/sound/soc/sti/sti_uniperif.c
+++ b/sound/soc/sti/sti_uniperif.c
@@ -11,6 +11,142 @@
#include "uniperif.h"
/*
+ * User frame size shall be 2, 4, 6 or 8 32-bits words length
+ * (i.e. 8, 16, 24 or 32 bytes)
+ * This constraint comes from allowed values for
+ * UNIPERIF_I2S_FMT_NUM_CH register
+ */
+#define UNIPERIF_MAX_FRAME_SZ 0x20
+#define UNIPERIF_ALLOWED_FRAME_SZ (0x08 | 0x10 | 0x18 | UNIPERIF_MAX_FRAME_SZ)
+
+int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots,
+ int slot_width)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *uni = priv->dai_data.uni;
+ int i, frame_size, avail_slots;
+
+ if (!UNIPERIF_TYPE_IS_TDM(uni)) {
+ dev_err(uni->dev, "cpu dai not in tdm mode\n");
+ return -EINVAL;
+ }
+
+ /* store info in unip context */
+ uni->tdm_slot.slots = slots;
+ uni->tdm_slot.slot_width = slot_width;
+ /* unip is unidirectionnal */
+ uni->tdm_slot.mask = (tx_mask != 0) ? tx_mask : rx_mask;
+
+ /* number of available timeslots */
+ for (i = 0, avail_slots = 0; i < uni->tdm_slot.slots; i++) {
+ if ((uni->tdm_slot.mask >> i) & 0x01)
+ avail_slots++;
+ }
+ uni->tdm_slot.avail_slots = avail_slots;
+
+ /* frame size in bytes */
+ frame_size = uni->tdm_slot.avail_slots * uni->tdm_slot.slot_width / 8;
+
+ /* check frame size is allowed */
+ if ((frame_size > UNIPERIF_MAX_FRAME_SZ) ||
+ (frame_size & ~(int)UNIPERIF_ALLOWED_FRAME_SZ)) {
+ dev_err(uni->dev, "frame size not allowed: %d bytes\n",
+ frame_size);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int sti_uniperiph_fix_tdm_chan(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct uniperif *uni = rule->private;
+ struct snd_interval t;
+
+ t.min = uni->tdm_slot.avail_slots;
+ t.max = uni->tdm_slot.avail_slots;
+ t.openmin = 0;
+ t.openmax = 0;
+ t.integer = 0;
+
+ return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+int sti_uniperiph_fix_tdm_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct uniperif *uni = rule->private;
+ struct snd_mask *maskp = hw_param_mask(params, rule->var);
+ u64 format;
+
+ switch (uni->tdm_slot.slot_width) {
+ case 16:
+ format = SNDRV_PCM_FMTBIT_S16_LE;
+ break;
+ case 32:
+ format = SNDRV_PCM_FMTBIT_S32_LE;
+ break;
+ default:
+ dev_err(uni->dev, "format not supported: %d bits\n",
+ uni->tdm_slot.slot_width);
+ return -EINVAL;
+ }
+
+ maskp->bits[0] &= (u_int32_t)format;
+ maskp->bits[1] &= (u_int32_t)(format >> 32);
+ /* clear remaining indexes */
+ memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX - 64) / 8);
+
+ if (!maskp->bits[0] && !maskp->bits[1])
+ return -EINVAL;
+
+ return 0;
+}
+
+int sti_uniperiph_get_tdm_word_pos(struct uniperif *uni,
+ unsigned int *word_pos)
+{
+ int slot_width = uni->tdm_slot.slot_width / 8;
+ int slots_num = uni->tdm_slot.slots;
+ unsigned int slots_mask = uni->tdm_slot.mask;
+ int i, j, k;
+ unsigned int word16_pos[4];
+
+ /* word16_pos:
+ * word16_pos[0] = WORDX_LSB
+ * word16_pos[1] = WORDX_MSB,
+ * word16_pos[2] = WORDX+1_LSB
+ * word16_pos[3] = WORDX+1_MSB
+ */
+
+ /* set unip word position */
+ for (i = 0, j = 0, k = 0; (i < slots_num) && (k < WORD_MAX); i++) {
+ if ((slots_mask >> i) & 0x01) {
+ word16_pos[j] = i * slot_width;
+
+ if (slot_width == 4) {
+ word16_pos[j + 1] = word16_pos[j] + 2;
+ j++;
+ }
+ j++;
+
+ if (j > 3) {
+ word_pos[k] = word16_pos[1] |
+ (word16_pos[0] << 8) |
+ (word16_pos[3] << 16) |
+ (word16_pos[2] << 24);
+ j = 0;
+ k++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
* sti_uniperiph_dai_create_ctrl
* This function is used to create Ctrl associated to DAI but also pcm device.
* Request is done by front end to associate ctrl with pcm device id
@@ -45,10 +181,16 @@ int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *uni = priv->dai_data.uni;
struct snd_dmaengine_dai_dma_data *dma_data;
int transfer_size;
- transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES;
+ if (uni->info->type == SND_ST_UNIPERIF_TYPE_TDM)
+ /* transfer size = user frame size (in 32-bits FIFO cell) */
+ transfer_size = snd_soc_params_to_frame_size(params) / 32;
+ else
+ transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES;
dma_data = snd_soc_dai_get_dma_data(dai, substream);
dma_data->maxburst = transfer_size;
diff --git a/sound/soc/sti/uniperif.h b/sound/soc/sti/uniperif.h
index f0fd5a9944e9..eb9933c62ad6 100644
--- a/sound/soc/sti/uniperif.h
+++ b/sound/soc/sti/uniperif.h
@@ -25,7 +25,7 @@
writel_relaxed((((value) & mask) << shift), ip->base + offset)
/*
- * AUD_UNIPERIF_SOFT_RST reg
+ * UNIPERIF_SOFT_RST reg
*/
#define UNIPERIF_SOFT_RST_OFFSET(ip) 0x0000
@@ -50,7 +50,7 @@
UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip))
/*
- * AUD_UNIPERIF_FIFO_DATA reg
+ * UNIPERIF_FIFO_DATA reg
*/
#define UNIPERIF_FIFO_DATA_OFFSET(ip) 0x0004
@@ -58,7 +58,7 @@
writel_relaxed(value, ip->base + UNIPERIF_FIFO_DATA_OFFSET(ip))
/*
- * AUD_UNIPERIF_CHANNEL_STA_REGN reg
+ * UNIPERIF_CHANNEL_STA_REGN reg
*/
#define UNIPERIF_CHANNEL_STA_REGN(ip, n) (0x0060 + (4 * n))
@@ -105,7 +105,7 @@
writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip))
/*
- * AUD_UNIPERIF_ITS reg
+ * UNIPERIF_ITS reg
*/
#define UNIPERIF_ITS_OFFSET(ip) 0x000C
@@ -143,7 +143,7 @@
0 : (BIT(UNIPERIF_ITS_UNDERFLOW_REC_FAILED_SHIFT(ip))))
/*
- * AUD_UNIPERIF_ITS_BCLR reg
+ * UNIPERIF_ITS_BCLR reg
*/
/* FIFO_ERROR */
@@ -160,7 +160,7 @@
writel_relaxed(value, ip->base + UNIPERIF_ITS_BCLR_OFFSET(ip))
/*
- * AUD_UNIPERIF_ITM reg
+ * UNIPERIF_ITM reg
*/
#define UNIPERIF_ITM_OFFSET(ip) 0x0018
@@ -188,7 +188,7 @@
0 : (BIT(UNIPERIF_ITM_UNDERFLOW_REC_FAILED_SHIFT(ip))))
/*
- * AUD_UNIPERIF_ITM_BCLR reg
+ * UNIPERIF_ITM_BCLR reg
*/
#define UNIPERIF_ITM_BCLR_OFFSET(ip) 0x001c
@@ -213,7 +213,7 @@
UNIPERIF_ITM_BCLR_DMA_ERROR_MASK(ip))
/*
- * AUD_UNIPERIF_ITM_BSET reg
+ * UNIPERIF_ITM_BSET reg
*/
#define UNIPERIF_ITM_BSET_OFFSET(ip) 0x0020
@@ -767,7 +767,7 @@
SET_UNIPERIF_REG(ip, \
UNIPERIF_CTRL_OFFSET(ip), \
UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \
- CORAUD_UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip), 1)
+ UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip), 1)
/* UNDERFLOW_REC_WINDOW */
#define UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip) 20
@@ -1046,7 +1046,7 @@
UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip), value)
/*
- * AUD_UNIPERIF_CHANNEL_STA_REGN reg
+ * UNIPERIF_CHANNEL_STA_REGN reg
*/
#define UNIPERIF_CHANNEL_STA_REGN(ip, n) (0x0060 + (4 * n))
@@ -1057,7 +1057,7 @@
UNIPERIF_CHANNEL_STA_REGN(ip, n))
/*
- * AUD_UNIPERIF_USER_VALIDITY reg
+ * UNIPERIF_USER_VALIDITY reg
*/
#define UNIPERIF_USER_VALIDITY_OFFSET(ip) 0x0090
@@ -1101,12 +1101,136 @@
UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip), value)
/*
+ * UNIPERIF_TDM_ENABLE
+ */
+#define UNIPERIF_TDM_ENABLE_OFFSET(ip) 0x0118
+#define GET_UNIPERIF_TDM_ENABLE(ip) \
+ readl_relaxed(ip->base + UNIPERIF_TDM_ENABLE_OFFSET(ip))
+#define SET_UNIPERIF_TDM_ENABLE(ip, value) \
+ writel_relaxed(value, ip->base + UNIPERIF_TDM_ENABLE_OFFSET(ip))
+
+/* TDM_ENABLE */
+#define UNIPERIF_TDM_ENABLE_EN_TDM_SHIFT(ip) 0x0
+#define UNIPERIF_TDM_ENABLE_EN_TDM_MASK(ip) 0x1
+#define GET_UNIPERIF_TDM_ENABLE_EN_TDM(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_TDM_ENABLE_OFFSET(ip), \
+ UNIPERIF_TDM_ENABLE_EN_TDM_SHIFT(ip), \
+ UNIPERIF_TDM_ENABLE_EN_TDM_MASK(ip))
+#define SET_UNIPERIF_TDM_ENABLE_TDM_ENABLE(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_TDM_ENABLE_OFFSET(ip), \
+ UNIPERIF_TDM_ENABLE_EN_TDM_SHIFT(ip), \
+ UNIPERIF_TDM_ENABLE_EN_TDM_MASK(ip), 1)
+#define SET_UNIPERIF_TDM_ENABLE_TDM_DISABLE(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_TDM_ENABLE_OFFSET(ip), \
+ UNIPERIF_TDM_ENABLE_EN_TDM_SHIFT(ip), \
+ UNIPERIF_TDM_ENABLE_EN_TDM_MASK(ip), 0)
+
+/*
+ * UNIPERIF_TDM_FS_REF_FREQ
+ */
+#define UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip) 0x011c
+#define GET_UNIPERIF_TDM_FS_REF_FREQ(ip) \
+ readl_relaxed(ip->base + UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip))
+#define SET_UNIPERIF_TDM_FS_REF_FREQ(ip, value) \
+ writel_relaxed(value, ip->base + \
+ UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip))
+
+/* REF_FREQ */
+#define UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip) 0x0
+#define VALUE_UNIPERIF_TDM_FS_REF_FREQ_8KHZ(ip) 0
+#define VALUE_UNIPERIF_TDM_FS_REF_FREQ_16KHZ(ip) 1
+#define VALUE_UNIPERIF_TDM_FS_REF_FREQ_32KHZ(ip) 2
+#define VALUE_UNIPERIF_TDM_FS_REF_FREQ_48KHZ(ip) 3
+#define UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip) 0x3
+#define GET_UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
+ UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
+ UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip))
+#define SET_UNIPERIF_TDM_FS_REF_FREQ_8KHZ(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
+ UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
+ UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip), \
+ VALUE_UNIPERIF_TDM_FS_REF_FREQ_8KHZ(ip))
+#define SET_UNIPERIF_TDM_FS_REF_FREQ_16KHZ(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
+ UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
+ UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip), \
+ VALUE_UNIPERIF_TDM_FS_REF_FREQ_16KHZ(ip))
+#define SET_UNIPERIF_TDM_FS_REF_FREQ_32KHZ(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
+ UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
+ UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip), \
+ VALUE_UNIPERIF_TDM_FS_REF_FREQ_32KHZ(ip))
+#define SET_UNIPERIF_TDM_FS_REF_FREQ_48KHZ(ip) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_TDM_FS_REF_FREQ_OFFSET(ip), \
+ UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_SHIFT(ip), \
+ UNIPERIF_TDM_FS_REF_FREQ_REF_FREQ_MASK(ip), \
+ VALUE_UNIPERIF_TDM_FS_REF_FREQ_48KHZ(ip))
+
+/*
+ * UNIPERIF_TDM_FS_REF_DIV
+ */
+#define UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip) 0x0120
+#define GET_UNIPERIF_TDM_FS_REF_DIV(ip) \
+ readl_relaxed(ip->base + UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip))
+#define SET_UNIPERIF_TDM_FS_REF_DIV(ip, value) \
+ writel_relaxed(value, ip->base + \
+ UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip))
+
+/* NUM_TIMESLOT */
+#define UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_SHIFT(ip) 0x0
+#define UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_MASK(ip) 0xff
+#define GET_UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT(ip) \
+ GET_UNIPERIF_REG(ip, \
+ UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip), \
+ UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_SHIFT(ip), \
+ UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_MASK(ip))
+#define SET_UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT(ip, value) \
+ SET_UNIPERIF_REG(ip, \
+ UNIPERIF_TDM_FS_REF_DIV_OFFSET(ip), \
+ UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_SHIFT(ip), \
+ UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT_MASK(ip), value)
+
+/*
+ * UNIPERIF_TDM_WORD_POS_X_Y
+ * 32 bits of UNIPERIF_TDM_WORD_POS_X_Y register shall be set in 1 shot
+ */
+#define UNIPERIF_TDM_WORD_POS_1_2_OFFSET(ip) 0x013c
+#define UNIPERIF_TDM_WORD_POS_3_4_OFFSET(ip) 0x0140
+#define UNIPERIF_TDM_WORD_POS_5_6_OFFSET(ip) 0x0144
+#define UNIPERIF_TDM_WORD_POS_7_8_OFFSET(ip) 0x0148
+#define GET_UNIPERIF_TDM_WORD_POS(ip, words) \
+ readl_relaxed(ip->base + UNIPERIF_TDM_WORD_POS_##words##_OFFSET(ip))
+#define SET_UNIPERIF_TDM_WORD_POS(ip, words, value) \
+ writel_relaxed(value, ip->base + \
+ UNIPERIF_TDM_WORD_POS_##words##_OFFSET(ip))
+/*
* uniperipheral IP capabilities
*/
#define UNIPERIF_FIFO_SIZE 70 /* FIFO is 70 cells deep */
#define UNIPERIF_FIFO_FRAMES 4 /* FDMA trigger limit in frames */
+#define UNIPERIF_TYPE_IS_HDMI(p) \
+ ((p)->info->type == SND_ST_UNIPERIF_TYPE_HDMI)
+#define UNIPERIF_TYPE_IS_PCM(p) \
+ ((p)->info->type == SND_ST_UNIPERIF_TYPE_PCM)
+#define UNIPERIF_TYPE_IS_SPDIF(p) \
+ ((p)->info->type == SND_ST_UNIPERIF_TYPE_SPDIF)
+#define UNIPERIF_TYPE_IS_IEC958(p) \
+ (UNIPERIF_TYPE_IS_HDMI(p) || \
+ UNIPERIF_TYPE_IS_SPDIF(p))
+#define UNIPERIF_TYPE_IS_TDM(p) \
+ ((p)->info->type == SND_ST_UNIPERIF_TYPE_TDM)
+
/*
* Uniperipheral IP revisions
*/
@@ -1125,10 +1249,11 @@ enum uniperif_version {
};
enum uniperif_type {
- SND_ST_UNIPERIF_PLAYER_TYPE_NONE,
- SND_ST_UNIPERIF_PLAYER_TYPE_HDMI,
- SND_ST_UNIPERIF_PLAYER_TYPE_PCM,
- SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF
+ SND_ST_UNIPERIF_TYPE_NONE,
+ SND_ST_UNIPERIF_TYPE_HDMI,
+ SND_ST_UNIPERIF_TYPE_PCM,
+ SND_ST_UNIPERIF_TYPE_SPDIF,
+ SND_ST_UNIPERIF_TYPE_TDM
};
enum uniperif_state {
@@ -1145,9 +1270,17 @@ enum uniperif_iec958_encoding_mode {
UNIPERIF_IEC958_ENCODING_MODE_ENCODED
};
+enum uniperif_word_pos {
+ WORD_1_2,
+ WORD_3_4,
+ WORD_5_6,
+ WORD_7_8,
+ WORD_MAX
+};
+
struct uniperif_info {
int id; /* instance value of the uniperipheral IP */
- enum uniperif_type player_type;
+ enum uniperif_type type;
int underflow_enabled; /* Underflow recovery mode */
};
@@ -1156,12 +1289,20 @@ struct uniperif_iec958_settings {
struct snd_aes_iec958 iec958;
};
+struct dai_tdm_slot {
+ unsigned int mask;
+ int slots;
+ int slot_width;
+ unsigned int avail_slots;
+};
+
struct uniperif {
/* System information */
struct uniperif_info *info;
struct device *dev;
int ver; /* IP version, used by register access macros */
struct regmap_field *clk_sel;
+ struct regmap_field *valid_sel;
/* capabilities */
const struct snd_pcm_hardware *hw;
@@ -1192,6 +1333,7 @@ struct uniperif {
/* dai properties */
unsigned int daifmt;
+ struct dai_tdm_slot tdm_slot;
/* DAI callbacks */
const struct snd_soc_dai_ops *dai_ops;
@@ -1209,6 +1351,28 @@ struct sti_uniperiph_data {
struct sti_uniperiph_dai dai_data;
};
+static const struct snd_pcm_hardware uni_tdm_hw = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 48000,
+
+ .channels_min = 1,
+ .channels_max = 32,
+
+ .periods_min = 2,
+ .periods_max = 10,
+
+ .period_bytes_min = 128,
+ .period_bytes_max = 64 * PAGE_SIZE,
+ .buffer_bytes_max = 256 * PAGE_SIZE
+};
+
/* uniperiph player*/
int uni_player_init(struct platform_device *pdev,
struct uniperif *uni_player);
@@ -1226,4 +1390,28 @@ int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai);
+static inline int sti_uniperiph_get_user_frame_size(
+ struct snd_pcm_runtime *runtime)
+{
+ return (runtime->channels * snd_pcm_format_width(runtime->format) / 8);
+}
+
+static inline int sti_uniperiph_get_unip_tdm_frame_size(struct uniperif *uni)
+{
+ return (uni->tdm_slot.slots * uni->tdm_slot.slot_width / 8);
+}
+
+int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots,
+ int slot_width);
+
+int sti_uniperiph_get_tdm_word_pos(struct uniperif *uni,
+ unsigned int *word_pos);
+
+int sti_uniperiph_fix_tdm_chan(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule);
+
+int sti_uniperiph_fix_tdm_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule);
+
#endif
diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c
index 7aca6b92f718..ee1c7c245bc7 100644
--- a/sound/soc/sti/uniperif_player.c
+++ b/sound/soc/sti/uniperif_player.c
@@ -21,23 +21,14 @@
/* sys config registers definitions */
#define SYS_CFG_AUDIO_GLUE 0xA4
-#define SYS_CFG_AUDI0_GLUE_PCM_CLKX 8
/*
* Driver specific types.
*/
-#define UNIPERIF_PLAYER_TYPE_IS_HDMI(p) \
- ((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_HDMI)
-#define UNIPERIF_PLAYER_TYPE_IS_PCM(p) \
- ((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_PCM)
-#define UNIPERIF_PLAYER_TYPE_IS_SPDIF(p) \
- ((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF)
-#define UNIPERIF_PLAYER_TYPE_IS_IEC958(p) \
- (UNIPERIF_PLAYER_TYPE_IS_HDMI(p) || \
- UNIPERIF_PLAYER_TYPE_IS_SPDIF(p))
#define UNIPERIF_PLAYER_CLK_ADJ_MIN -999999
#define UNIPERIF_PLAYER_CLK_ADJ_MAX 1000000
+#define UNIPERIF_PLAYER_I2S_OUT 1 /* player id connected to I2S/TDM TX bus */
/*
* Note: snd_pcm_hardware is linked to DMA controller but is declared here to
@@ -444,18 +435,11 @@ static int uni_player_prepare_pcm(struct uniperif *player,
/* Force slot width to 32 in I2S mode (HW constraint) */
if ((player->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
- SND_SOC_DAIFMT_I2S) {
+ SND_SOC_DAIFMT_I2S)
slot_width = 32;
- } else {
- switch (runtime->format) {
- case SNDRV_PCM_FORMAT_S16_LE:
- slot_width = 16;
- break;
- default:
- slot_width = 32;
- break;
- }
- }
+ else
+ slot_width = snd_pcm_format_width(runtime->format);
+
output_frame_size = slot_width * runtime->channels;
clk_div = player->mclk / runtime->rate;
@@ -530,7 +514,6 @@ static int uni_player_prepare_pcm(struct uniperif *player,
SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE(player);
SET_UNIPERIF_I2S_FMT_ORDER_MSB(player);
- SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(player);
/* No iec958 formatting as outputting to DAC */
SET_UNIPERIF_CTRL_SPDIF_FMT_OFF(player);
@@ -538,6 +521,55 @@ static int uni_player_prepare_pcm(struct uniperif *player,
return 0;
}
+static int uni_player_prepare_tdm(struct uniperif *player,
+ struct snd_pcm_runtime *runtime)
+{
+ int tdm_frame_size; /* unip tdm frame size in bytes */
+ int user_frame_size; /* user tdm frame size in bytes */
+ /* default unip TDM_WORD_POS_X_Y */
+ unsigned int word_pos[4] = {
+ 0x04060002, 0x0C0E080A, 0x14161012, 0x1C1E181A};
+ int freq, ret;
+
+ tdm_frame_size =
+ sti_uniperiph_get_unip_tdm_frame_size(player);
+ user_frame_size =
+ sti_uniperiph_get_user_frame_size(runtime);
+
+ /* fix 16/0 format */
+ SET_UNIPERIF_CONFIG_MEM_FMT_16_0(player);
+ SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(player);
+
+ /* number of words inserted on the TDM line */
+ SET_UNIPERIF_I2S_FMT_NUM_CH(player, user_frame_size / 4 / 2);
+
+ SET_UNIPERIF_I2S_FMT_ORDER_MSB(player);
+ SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(player);
+
+ /* Enable the tdm functionality */
+ SET_UNIPERIF_TDM_ENABLE_TDM_ENABLE(player);
+
+ /* number of 8 bits timeslots avail in unip tdm frame */
+ SET_UNIPERIF_TDM_FS_REF_DIV_NUM_TIMESLOT(player, tdm_frame_size);
+
+ /* set the timeslot allocation for words in FIFO */
+ sti_uniperiph_get_tdm_word_pos(player, word_pos);
+ SET_UNIPERIF_TDM_WORD_POS(player, 1_2, word_pos[WORD_1_2]);
+ SET_UNIPERIF_TDM_WORD_POS(player, 3_4, word_pos[WORD_3_4]);
+ SET_UNIPERIF_TDM_WORD_POS(player, 5_6, word_pos[WORD_5_6]);
+ SET_UNIPERIF_TDM_WORD_POS(player, 7_8, word_pos[WORD_7_8]);
+
+ /* set unip clk rate (not done vai set_sysclk ops) */
+ freq = runtime->rate * tdm_frame_size * 8;
+ mutex_lock(&player->ctrl_lock);
+ ret = uni_player_clk_set_rate(player, freq);
+ if (!ret)
+ player->mclk = freq;
+ mutex_unlock(&player->ctrl_lock);
+
+ return 0;
+}
+
/*
* ALSA uniperipheral iec958 controls
*/
@@ -668,11 +700,29 @@ static int uni_player_startup(struct snd_pcm_substream *substream,
{
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
struct uniperif *player = priv->dai_data.uni;
+ int ret;
+
player->substream = substream;
player->clk_adj = 0;
- return 0;
+ if (!UNIPERIF_TYPE_IS_TDM(player))
+ return 0;
+
+ /* refine hw constraint in tdm mode */
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ sti_uniperiph_fix_tdm_chan,
+ player, SNDRV_PCM_HW_PARAM_CHANNELS,
+ -1);
+ if (ret < 0)
+ return ret;
+
+ return snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ sti_uniperiph_fix_tdm_format,
+ player, SNDRV_PCM_HW_PARAM_FORMAT,
+ -1);
}
static int uni_player_set_sysclk(struct snd_soc_dai *dai, int clk_id,
@@ -682,7 +732,7 @@ static int uni_player_set_sysclk(struct snd_soc_dai *dai, int clk_id,
struct uniperif *player = priv->dai_data.uni;
int ret;
- if (dir == SND_SOC_CLOCK_IN)
+ if (UNIPERIF_TYPE_IS_TDM(player) || (dir == SND_SOC_CLOCK_IN))
return 0;
if (clk_id != 0)
@@ -714,7 +764,13 @@ static int uni_player_prepare(struct snd_pcm_substream *substream,
}
/* Calculate transfer size (in fifo cells and bytes) for frame count */
- transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES;
+ if (player->info->type == SND_ST_UNIPERIF_TYPE_TDM) {
+ /* transfer size = user frame size (in 32 bits FIFO cell) */
+ transfer_size =
+ sti_uniperiph_get_user_frame_size(runtime) / 4;
+ } else {
+ transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES;
+ }
/* Calculate number of empty cells available before asserting DREQ */
if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) {
@@ -738,16 +794,19 @@ static int uni_player_prepare(struct snd_pcm_substream *substream,
SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(player, trigger_limit);
/* Uniperipheral setup depends on player type */
- switch (player->info->player_type) {
- case SND_ST_UNIPERIF_PLAYER_TYPE_HDMI:
+ switch (player->info->type) {
+ case SND_ST_UNIPERIF_TYPE_HDMI:
ret = uni_player_prepare_iec958(player, runtime);
break;
- case SND_ST_UNIPERIF_PLAYER_TYPE_PCM:
+ case SND_ST_UNIPERIF_TYPE_PCM:
ret = uni_player_prepare_pcm(player, runtime);
break;
- case SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF:
+ case SND_ST_UNIPERIF_TYPE_SPDIF:
ret = uni_player_prepare_iec958(player, runtime);
break;
+ case SND_ST_UNIPERIF_TYPE_TDM:
+ ret = uni_player_prepare_tdm(player, runtime);
+ break;
default:
dev_err(player->dev, "invalid player type");
return -EINVAL;
@@ -852,8 +911,8 @@ static int uni_player_start(struct uniperif *player)
* will not take affect and hang the player.
*/
if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
- if (UNIPERIF_PLAYER_TYPE_IS_IEC958(player))
- SET_UNIPERIF_CTRL_SPDIF_FMT_ON(player);
+ if (UNIPERIF_TYPE_IS_IEC958(player))
+ SET_UNIPERIF_CTRL_SPDIF_FMT_ON(player);
/* Force channel status update (no update if clk disable) */
if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
@@ -954,27 +1013,30 @@ static void uni_player_shutdown(struct snd_pcm_substream *substream,
player->substream = NULL;
}
-static int uni_player_parse_dt_clk_glue(struct platform_device *pdev,
- struct uniperif *player)
+static int uni_player_parse_dt_audio_glue(struct platform_device *pdev,
+ struct uniperif *player)
{
- int bit_offset;
struct device_node *node = pdev->dev.of_node;
struct regmap *regmap;
-
- bit_offset = SYS_CFG_AUDI0_GLUE_PCM_CLKX + player->info->id;
+ struct reg_field regfield[2] = {
+ /* PCM_CLK_SEL */
+ REG_FIELD(SYS_CFG_AUDIO_GLUE,
+ 8 + player->info->id,
+ 8 + player->info->id),
+ /* PCMP_VALID_SEL */
+ REG_FIELD(SYS_CFG_AUDIO_GLUE, 0, 1)
+ };
regmap = syscon_regmap_lookup_by_phandle(node, "st,syscfg");
- if (regmap) {
- struct reg_field regfield =
- REG_FIELD(SYS_CFG_AUDIO_GLUE, bit_offset, bit_offset);
-
- player->clk_sel = regmap_field_alloc(regmap, regfield);
- } else {
+ if (!regmap) {
dev_err(&pdev->dev, "sti-audio-clk-glue syscf not found\n");
return -EINVAL;
}
+ player->clk_sel = regmap_field_alloc(regmap, regfield[0]);
+ player->valid_sel = regmap_field_alloc(regmap, regfield[1]);
+
return 0;
}
@@ -1012,19 +1074,21 @@ static int uni_player_parse_dt(struct platform_device *pdev,
}
if (strcasecmp(mode, "hdmi") == 0)
- info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_HDMI;
+ info->type = SND_ST_UNIPERIF_TYPE_HDMI;
else if (strcasecmp(mode, "pcm") == 0)
- info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_PCM;
+ info->type = SND_ST_UNIPERIF_TYPE_PCM;
else if (strcasecmp(mode, "spdif") == 0)
- info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF;
+ info->type = SND_ST_UNIPERIF_TYPE_SPDIF;
+ else if (strcasecmp(mode, "tdm") == 0)
+ info->type = SND_ST_UNIPERIF_TYPE_TDM;
else
- info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_NONE;
+ info->type = SND_ST_UNIPERIF_TYPE_NONE;
/* Save the info structure */
player->info = info;
- /* Get the PCM_CLK_SEL bit from audio-glue-ctrl SoC register */
- if (uni_player_parse_dt_clk_glue(pdev, player))
+ /* Get PCM_CLK_SEL & PCMP_VALID_SEL from audio-glue-ctrl SoC reg */
+ if (uni_player_parse_dt_audio_glue(pdev, player))
return -EINVAL;
return 0;
@@ -1037,7 +1101,8 @@ static const struct snd_soc_dai_ops uni_player_dai_ops = {
.trigger = uni_player_trigger,
.hw_params = sti_uniperiph_dai_hw_params,
.set_fmt = sti_uniperiph_dai_set_fmt,
- .set_sysclk = uni_player_set_sysclk
+ .set_sysclk = uni_player_set_sysclk,
+ .set_tdm_slot = sti_uniperiph_set_tdm_slot
};
int uni_player_init(struct platform_device *pdev,
@@ -1047,7 +1112,6 @@ int uni_player_init(struct platform_device *pdev,
player->dev = &pdev->dev;
player->state = UNIPERIF_STATE_STOPPED;
- player->hw = &uni_player_pcm_hw;
player->dai_ops = &uni_player_dai_ops;
ret = uni_player_parse_dt(pdev, player);
@@ -1057,6 +1121,11 @@ int uni_player_init(struct platform_device *pdev,
return ret;
}
+ if (UNIPERIF_TYPE_IS_TDM(player))
+ player->hw = &uni_tdm_hw;
+ else
+ player->hw = &uni_player_pcm_hw;
+
/* Get uniperif resource */
player->clk = of_clk_get(pdev->dev.of_node, 0);
if (IS_ERR(player->clk))
@@ -1073,6 +1142,17 @@ int uni_player_init(struct platform_device *pdev,
}
}
+ /* connect to I2S/TDM TX bus */
+ if (player->valid_sel &&
+ (player->info->id == UNIPERIF_PLAYER_I2S_OUT)) {
+ ret = regmap_field_write(player->valid_sel, player->info->id);
+ if (ret) {
+ dev_err(player->dev,
+ "%s: unable to connect to tdm bus", __func__);
+ return ret;
+ }
+ }
+
ret = devm_request_irq(&pdev->dev, player->irq,
uni_player_irq_handler, IRQF_SHARED,
dev_name(&pdev->dev), player);
@@ -1087,7 +1167,7 @@ int uni_player_init(struct platform_device *pdev,
SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(player);
SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(player);
- if (UNIPERIF_PLAYER_TYPE_IS_IEC958(player)) {
+ if (UNIPERIF_TYPE_IS_IEC958(player)) {
/* Set default iec958 status bits */
/* Consumer, PCM, copyright, 2ch, mode 0 */
diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c
index 8a0eb2050169..eb74a328c928 100644
--- a/sound/soc/sti/uniperif_reader.c
+++ b/sound/soc/sti/uniperif_reader.c
@@ -73,55 +73,10 @@ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id)
return ret;
}
-static int uni_reader_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int uni_reader_prepare_pcm(struct snd_pcm_runtime *runtime,
+ struct uniperif *reader)
{
- struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
- struct uniperif *reader = priv->dai_data.uni;
- struct snd_pcm_runtime *runtime = substream->runtime;
- int transfer_size, trigger_limit;
int slot_width;
- int count = 10;
-
- /* The reader should be stopped */
- if (reader->state != UNIPERIF_STATE_STOPPED) {
- dev_err(reader->dev, "%s: invalid reader state %d", __func__,
- reader->state);
- return -EINVAL;
- }
-
- /* Calculate transfer size (in fifo cells and bytes) for frame count */
- transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES;
-
- /* Calculate number of empty cells available before asserting DREQ */
- if (reader->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
- trigger_limit = UNIPERIF_FIFO_SIZE - transfer_size;
- else
- /*
- * Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
- * FDMA_TRIGGER_LIMIT also controls when the state switches
- * from OFF or STANDBY to AUDIO DATA.
- */
- trigger_limit = transfer_size;
-
- /* Trigger limit must be an even number */
- if ((!trigger_limit % 2) ||
- (trigger_limit != 1 && transfer_size % 2) ||
- (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(reader))) {
- dev_err(reader->dev, "invalid trigger limit %d", trigger_limit);
- return -EINVAL;
- }
-
- SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(reader, trigger_limit);
-
- switch (reader->daifmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_IB_IF:
- case SND_SOC_DAIFMT_NB_IF:
- SET_UNIPERIF_I2S_FMT_LR_POL_HIG(reader);
- break;
- default:
- SET_UNIPERIF_I2S_FMT_LR_POL_LOW(reader);
- }
/* Force slot width to 32 in I2S mode */
if ((reader->daifmt & SND_SOC_DAIFMT_FORMAT_MASK)
@@ -173,6 +128,109 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ /* Number of channels must be even */
+ if ((runtime->channels % 2) || (runtime->channels < 2) ||
+ (runtime->channels > 10)) {
+ dev_err(reader->dev, "%s: invalid nb of channels", __func__);
+ return -EINVAL;
+ }
+
+ SET_UNIPERIF_I2S_FMT_NUM_CH(reader, runtime->channels / 2);
+ SET_UNIPERIF_I2S_FMT_ORDER_MSB(reader);
+
+ return 0;
+}
+
+static int uni_reader_prepare_tdm(struct snd_pcm_runtime *runtime,
+ struct uniperif *reader)
+{
+ int frame_size; /* user tdm frame size in bytes */
+ /* default unip TDM_WORD_POS_X_Y */
+ unsigned int word_pos[4] = {
+ 0x04060002, 0x0C0E080A, 0x14161012, 0x1C1E181A};
+
+ frame_size = sti_uniperiph_get_user_frame_size(runtime);
+
+ /* fix 16/0 format */
+ SET_UNIPERIF_CONFIG_MEM_FMT_16_0(reader);
+ SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(reader);
+
+ /* number of words inserted on the TDM line */
+ SET_UNIPERIF_I2S_FMT_NUM_CH(reader, frame_size / 4 / 2);
+
+ SET_UNIPERIF_I2S_FMT_ORDER_MSB(reader);
+ SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(reader);
+ SET_UNIPERIF_TDM_ENABLE_TDM_ENABLE(reader);
+
+ /*
+ * set the timeslots allocation for words in FIFO
+ *
+ * HW bug: (LSB word < MSB word) => this config is not possible
+ * So if we want (LSB word < MSB) word, then it shall be
+ * handled by user
+ */
+ sti_uniperiph_get_tdm_word_pos(reader, word_pos);
+ SET_UNIPERIF_TDM_WORD_POS(reader, 1_2, word_pos[WORD_1_2]);
+ SET_UNIPERIF_TDM_WORD_POS(reader, 3_4, word_pos[WORD_3_4]);
+ SET_UNIPERIF_TDM_WORD_POS(reader, 5_6, word_pos[WORD_5_6]);
+ SET_UNIPERIF_TDM_WORD_POS(reader, 7_8, word_pos[WORD_7_8]);
+
+ return 0;
+}
+
+static int uni_reader_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *reader = priv->dai_data.uni;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int transfer_size, trigger_limit, ret;
+ int count = 10;
+
+ /* The reader should be stopped */
+ if (reader->state != UNIPERIF_STATE_STOPPED) {
+ dev_err(reader->dev, "%s: invalid reader state %d", __func__,
+ reader->state);
+ return -EINVAL;
+ }
+
+ /* Calculate transfer size (in fifo cells and bytes) for frame count */
+ if (reader->info->type == SND_ST_UNIPERIF_TYPE_TDM) {
+ /* transfer size = unip frame size (in 32 bits FIFO cell) */
+ transfer_size =
+ sti_uniperiph_get_user_frame_size(runtime) / 4;
+ } else {
+ transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES;
+ }
+
+ /* Calculate number of empty cells available before asserting DREQ */
+ if (reader->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
+ trigger_limit = UNIPERIF_FIFO_SIZE - transfer_size;
+ else
+ /*
+ * Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
+ * FDMA_TRIGGER_LIMIT also controls when the state switches
+ * from OFF or STANDBY to AUDIO DATA.
+ */
+ trigger_limit = transfer_size;
+
+ /* Trigger limit must be an even number */
+ if ((!trigger_limit % 2) ||
+ (trigger_limit != 1 && transfer_size % 2) ||
+ (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(reader))) {
+ dev_err(reader->dev, "invalid trigger limit %d", trigger_limit);
+ return -EINVAL;
+ }
+
+ SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(reader, trigger_limit);
+
+ if (UNIPERIF_TYPE_IS_TDM(reader))
+ ret = uni_reader_prepare_tdm(runtime, reader);
+ else
+ ret = uni_reader_prepare_pcm(runtime, reader);
+ if (ret)
+ return ret;
+
switch (reader->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(reader);
@@ -191,21 +249,26 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream,
return -EINVAL;
}
- SET_UNIPERIF_I2S_FMT_ORDER_MSB(reader);
-
- /* Data clocking (changing) on the rising edge */
- SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(reader);
-
- /* Number of channels must be even */
-
- if ((runtime->channels % 2) || (runtime->channels < 2) ||
- (runtime->channels > 10)) {
- dev_err(reader->dev, "%s: invalid nb of channels", __func__);
- return -EINVAL;
+ /* Data clocking (changing) on the rising/falling edge */
+ switch (reader->daifmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ SET_UNIPERIF_I2S_FMT_LR_POL_LOW(reader);
+ SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(reader);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ SET_UNIPERIF_I2S_FMT_LR_POL_HIG(reader);
+ SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(reader);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ SET_UNIPERIF_I2S_FMT_LR_POL_LOW(reader);
+ SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(reader);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ SET_UNIPERIF_I2S_FMT_LR_POL_HIG(reader);
+ SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(reader);
+ break;
}
- SET_UNIPERIF_I2S_FMT_NUM_CH(reader, runtime->channels / 2);
-
/* Clear any pending interrupts */
SET_UNIPERIF_ITS_BCLR(reader, GET_UNIPERIF_ITS(reader));
@@ -293,6 +356,32 @@ static int uni_reader_trigger(struct snd_pcm_substream *substream,
}
}
+static int uni_reader_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
+ struct uniperif *reader = priv->dai_data.uni;
+ int ret;
+
+ if (!UNIPERIF_TYPE_IS_TDM(reader))
+ return 0;
+
+ /* refine hw constraint in tdm mode */
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ sti_uniperiph_fix_tdm_chan,
+ reader, SNDRV_PCM_HW_PARAM_CHANNELS,
+ -1);
+ if (ret < 0)
+ return ret;
+
+ return snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ sti_uniperiph_fix_tdm_format,
+ reader, SNDRV_PCM_HW_PARAM_FORMAT,
+ -1);
+}
+
static void uni_reader_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -310,6 +399,7 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
{
struct uniperif_info *info;
struct device_node *node = pdev->dev.of_node;
+ const char *mode;
/* Allocate memory for the info structure */
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
@@ -322,6 +412,17 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
return -EINVAL;
}
+ /* Read the device mode property */
+ if (of_property_read_string(node, "st,mode", &mode)) {
+ dev_err(&pdev->dev, "uniperipheral mode not defined");
+ return -EINVAL;
+ }
+
+ if (strcasecmp(mode, "tdm") == 0)
+ info->type = SND_ST_UNIPERIF_TYPE_TDM;
+ else
+ info->type = SND_ST_UNIPERIF_TYPE_PCM;
+
/* Save the info structure */
reader->info = info;
@@ -329,11 +430,13 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
}
static const struct snd_soc_dai_ops uni_reader_dai_ops = {
+ .startup = uni_reader_startup,
.shutdown = uni_reader_shutdown,
.prepare = uni_reader_prepare,
.trigger = uni_reader_trigger,
.hw_params = sti_uniperiph_dai_hw_params,
.set_fmt = sti_uniperiph_dai_set_fmt,
+ .set_tdm_slot = sti_uniperiph_set_tdm_slot
};
int uni_reader_init(struct platform_device *pdev,
@@ -343,7 +446,6 @@ int uni_reader_init(struct platform_device *pdev,
reader->dev = &pdev->dev;
reader->state = UNIPERIF_STATE_STOPPED;
- reader->hw = &uni_reader_pcm_hw;
reader->dai_ops = &uni_reader_dai_ops;
ret = uni_reader_parse_dt(pdev, reader);
@@ -352,6 +454,11 @@ int uni_reader_init(struct platform_device *pdev,
return ret;
}
+ if (UNIPERIF_TYPE_IS_TDM(reader))
+ reader->hw = &uni_tdm_hw;
+ else
+ reader->hw = &uni_reader_pcm_hw;
+
ret = devm_request_irq(&pdev->dev, reader->irq,
uni_reader_irq_handler, IRQF_SHARED,
dev_name(&pdev->dev), reader);
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 3fc63583a537..69860da473ea 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -350,6 +350,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
case USB_SPEED_HIGH:
case USB_SPEED_WIRELESS:
case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
break;
default:
dev_err(&dev->dev, "unknown device speed %d\n", snd_usb_get_speed(dev));
@@ -450,6 +451,9 @@ static int snd_usb_audio_create(struct usb_interface *intf,
case USB_SPEED_SUPER:
strlcat(card->longname, ", super speed", sizeof(card->longname));
break;
+ case USB_SPEED_SUPER_PLUS:
+ strlcat(card->longname, ", super speed plus", sizeof(card->longname));
+ break;
default:
break;
}
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 7ccbcaf6a147..26dd5f20f149 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -309,6 +309,9 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
* support reading */
if (snd_usb_get_sample_rate_quirk(chip))
return 0;
+ /* the firmware is likely buggy, don't repeat to fail too many times */
+ if (chip->sample_rate_read_error > 2)
+ return 0;
if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
@@ -316,6 +319,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
data, sizeof(data))) < 0) {
dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n",
iface, fmt->altsetting, ep);
+ chip->sample_rate_read_error++;
return 0; /* some devices don't support reading */
}
diff --git a/sound/usb/helper.c b/sound/usb/helper.c
index 51ed1ac825fd..7712e2b84183 100644
--- a/sound/usb/helper.c
+++ b/sound/usb/helper.c
@@ -120,6 +120,7 @@ unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
case USB_SPEED_HIGH:
case USB_SPEED_WIRELESS:
case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
if (get_endpoint(alts, 0)->bInterval >= 1 &&
get_endpoint(alts, 0)->bInterval <= 4)
return get_endpoint(alts, 0)->bInterval - 1;
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 47de8af42f16..7ba92921bf28 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -911,6 +911,7 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
switch (snd_usb_get_speed(ep->umidi->dev)) {
case USB_SPEED_HIGH:
case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
count = 1;
break;
default:
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 4f85757009b3..2f8c388ef84f 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -45,6 +45,7 @@
#include <linux/bitops.h>
#include <linux/init.h>
#include <linux/list.h>
+#include <linux/log2.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/usb.h>
@@ -1378,6 +1379,71 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
snd_usb_mixer_add_control(&cval->head, kctl);
}
+static int parse_clock_source_unit(struct mixer_build *state, int unitid,
+ void *_ftr)
+{
+ struct uac_clock_source_descriptor *hdr = _ftr;
+ struct usb_mixer_elem_info *cval;
+ struct snd_kcontrol *kctl;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int ret;
+
+ if (state->mixer->protocol != UAC_VERSION_2)
+ return -EINVAL;
+
+ if (hdr->bLength != sizeof(*hdr)) {
+ usb_audio_dbg(state->chip,
+ "Bogus clock source descriptor length of %d, ignoring.\n",
+ hdr->bLength);
+ return 0;
+ }
+
+ /*
+ * The only property of this unit we are interested in is the
+ * clock source validity. If that isn't readable, just bail out.
+ */
+ if (!uac2_control_is_readable(hdr->bmControls,
+ ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
+ return 0;
+
+ cval = kzalloc(sizeof(*cval), GFP_KERNEL);
+ if (!cval)
+ return -ENOMEM;
+
+ snd_usb_mixer_elem_init_std(&cval->head, state->mixer, hdr->bClockID);
+
+ cval->min = 0;
+ cval->max = 1;
+ cval->channels = 1;
+ cval->val_type = USB_MIXER_BOOLEAN;
+ cval->control = UAC2_CS_CONTROL_CLOCK_VALID;
+
+ if (uac2_control_is_writeable(hdr->bmControls,
+ ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
+ kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
+ else {
+ cval->master_readonly = 1;
+ kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval);
+ }
+
+ if (!kctl) {
+ kfree(cval);
+ return -ENOMEM;
+ }
+
+ kctl->private_free = snd_usb_mixer_elem_free;
+ ret = snd_usb_copy_string_desc(state, hdr->iClockSource,
+ name, sizeof(name));
+ if (ret > 0)
+ snprintf(kctl->id.name, sizeof(kctl->id.name),
+ "%s Validity", name);
+ else
+ snprintf(kctl->id.name, sizeof(kctl->id.name),
+ "Clock Source %d Validity", hdr->bClockID);
+
+ return snd_usb_mixer_add_control(&cval->head, kctl);
+}
+
/*
* parse a feature unit
*
@@ -2126,10 +2192,11 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
switch (p1[2]) {
case UAC_INPUT_TERMINAL:
- case UAC2_CLOCK_SOURCE:
return 0; /* NOP */
case UAC_MIXER_UNIT:
return parse_audio_mixer_unit(state, unitid, p1);
+ case UAC2_CLOCK_SOURCE:
+ return parse_clock_source_unit(state, unitid, p1);
case UAC_SELECTOR_UNIT:
case UAC2_CLOCK_SELECTOR:
return parse_audio_selector_unit(state, unitid, p1);
@@ -2307,6 +2374,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
__u8 unitid = (index >> 8) & 0xff;
__u8 control = (value >> 8) & 0xff;
__u8 channel = value & 0xff;
+ unsigned int count = 0;
if (channel >= MAX_CHANNELS) {
usb_audio_dbg(mixer->chip,
@@ -2315,6 +2383,12 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
return;
}
+ for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem)
+ count++;
+
+ if (count == 0)
+ return;
+
for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) {
struct usb_mixer_elem_info *info;
@@ -2322,7 +2396,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
continue;
info = (struct usb_mixer_elem_info *)list;
- if (info->control != control)
+ if (count > 1 && info->control != control)
continue;
switch (attribute) {
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 0adfd9537cf7..6adde457b602 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1137,8 +1137,11 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
case USB_ID(0x047F, 0x0415): /* Plantronics BT-300 */
case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */
case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */
+ case USB_ID(0x0556, 0x0014): /* Phoenix Audio TMX320VC */
case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */
+ case USB_ID(0x1de7, 0x0013): /* Phoenix Audio MT202exe */
case USB_ID(0x1de7, 0x0014): /* Phoenix Audio TMX320 */
+ case USB_ID(0x1de7, 0x0114): /* Phoenix Audio MT202pcs */
case USB_ID(0x21B4, 0x0081): /* AudioQuest DragonFly */
return true;
}
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index b665d85555cb..4d5c89a7ba2b 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -47,6 +47,7 @@ struct snd_usb_audio {
int num_interfaces;
int num_suspended_intf;
+ int sample_rate_read_error;
struct list_head pcm_list; /* list of pcm streams */
struct list_head ep_list; /* list of audio-related endpoints */