aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/core/compress_offload.c6
-rw-r--r--sound/core/control.c5
-rw-r--r--sound/core/hwdep.c4
-rw-r--r--sound/core/info.c4
-rw-r--r--sound/core/oss/linear.c2
-rw-r--r--sound/core/oss/mulaw.c2
-rw-r--r--sound/core/oss/pcm_plugin.c40
-rw-r--r--sound/core/oss/route.c2
-rw-r--r--sound/core/pcm_lib.c9
-rw-r--r--sound/core/pcm_native.c27
-rw-r--r--sound/core/rawmidi.c31
-rw-r--r--sound/core/seq/oss/seq_oss.c8
-rw-r--r--sound/core/seq/oss/seq_oss_midi.c1
-rw-r--r--sound/core/seq/seq_clientmgr.c4
-rw-r--r--sound/core/seq/seq_queue.c29
-rw-r--r--sound/core/seq/seq_timer.c27
-rw-r--r--sound/core/seq/seq_timer.h3
-rw-r--r--sound/core/seq/seq_virmidi.c1
-rw-r--r--sound/core/timer.c40
-rw-r--r--sound/drivers/dummy.c2
-rw-r--r--sound/drivers/opl3/opl3_synth.c2
-rw-r--r--sound/firewire/bebob/bebob_focusrite.c3
-rw-r--r--sound/firewire/bebob/bebob_stream.c14
-rw-r--r--sound/firewire/dice/dice-extension.c5
-rw-r--r--sound/firewire/motu/motu-proc.c2
-rw-r--r--sound/firewire/oxfw/oxfw-pcm.c2
-rw-r--r--sound/firewire/tascam/amdtp-tascam.c5
-rw-r--r--sound/hda/ext/hdac_ext_controller.c9
-rw-r--r--sound/hda/hdac_controller.c2
-rw-r--r--sound/hda/hdac_device.c1
-rw-r--r--sound/hda/hdac_regmap.c142
-rw-r--r--sound/hda/hdac_stream.c17
-rw-r--r--sound/hda/hdmi_chmap.c2
-rw-r--r--sound/isa/cs423x/cs4236.c3
-rw-r--r--sound/isa/es1688/es1688.c4
-rw-r--r--sound/isa/opti9xx/miro.c9
-rw-r--r--sound/isa/opti9xx/opti92x-ad1848.c9
-rw-r--r--sound/isa/wavefront/wavefront_synth.c8
-rw-r--r--sound/pci/echoaudio/echoaudio.c2
-rw-r--r--sound/pci/hda/hda_auto_parser.c6
-rw-r--r--sound/pci/hda/hda_beep.c6
-rw-r--r--sound/pci/hda/hda_bind.c4
-rw-r--r--sound/pci/hda/hda_codec.c40
-rw-r--r--sound/pci/hda/hda_controller.c2
-rw-r--r--sound/pci/hda/hda_eld.c2
-rw-r--r--sound/pci/hda/hda_generic.c6
-rw-r--r--sound/pci/hda/hda_intel.c119
-rw-r--r--sound/pci/hda/hda_intel.h1
-rw-r--r--sound/pci/hda/hda_local.h2
-rw-r--r--sound/pci/hda/hda_sysfs.c4
-rw-r--r--sound/pci/hda/patch_analog.c6
-rw-r--r--sound/pci/hda/patch_ca0132.c48
-rw-r--r--sound/pci/hda/patch_conexant.c8
-rw-r--r--sound/pci/hda/patch_hdmi.c81
-rw-r--r--sound/pci/hda/patch_realtek.c1079
-rw-r--r--sound/pci/hda/patch_sigmatel.c4
-rw-r--r--sound/pci/hda/patch_via.c6
-rw-r--r--sound/pci/ice1712/ice1712.c3
-rw-r--r--sound/pci/ice1712/ice1724.c9
-rw-r--r--sound/pci/ice1712/prodigy_hifi.c4
-rw-r--r--sound/sh/aica.c4
-rw-r--r--sound/sh/sh_dac_audio.c3
-rw-r--r--sound/soc/atmel/Kconfig36
-rw-r--r--sound/soc/atmel/Makefile10
-rw-r--r--sound/soc/codecs/hdac_hda.c6
-rw-r--r--sound/soc/codecs/hdac_hdmi.c6
-rw-r--r--sound/soc/codecs/max98090.c8
-rw-r--r--sound/soc/codecs/max98090.h1
-rw-r--r--sound/soc/codecs/max98373.c2
-rw-r--r--sound/soc/codecs/max9867.c4
-rw-r--r--sound/soc/codecs/msm8916-wcd-analog.c24
-rw-r--r--sound/soc/codecs/msm8916-wcd-digital.c28
-rw-r--r--sound/soc/codecs/pcm3168a.c3
-rw-r--r--sound/soc/codecs/pcm512x.c8
-rw-r--r--sound/soc/codecs/rt5640.c7
-rw-r--r--sound/soc/codecs/rt5645.c20
-rw-r--r--sound/soc/codecs/rt5651.c3
-rw-r--r--sound/soc/codecs/rt5670.c71
-rw-r--r--sound/soc/codecs/rt5670.h2
-rw-r--r--sound/soc/codecs/rt5677.c1
-rw-r--r--sound/soc/codecs/rt5682.c14
-rw-r--r--sound/soc/codecs/sgtl5000.c37
-rw-r--r--sound/soc/codecs/sgtl5000.h1
-rw-r--r--sound/soc/codecs/tas571x.c20
-rw-r--r--sound/soc/codecs/wm2200.c5
-rw-r--r--sound/soc/codecs/wm5100.c2
-rw-r--r--sound/soc/codecs/wm8904.c1
-rw-r--r--sound/soc/codecs/wm8960.c3
-rw-r--r--sound/soc/codecs/wm8962.c4
-rw-r--r--sound/soc/codecs/wm8994.c43
-rw-r--r--sound/soc/codecs/wm_adsp.c3
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c1
-rw-r--r--sound/soc/fsl/fsl_audmix.c15
-rw-r--r--sound/soc/fsl/fsl_audmix.h1
-rw-r--r--sound/soc/fsl/fsl_sai.c22
-rw-r--r--sound/soc/fsl/fsl_ssi.c13
-rw-r--r--sound/soc/fsl/imx-audmix.c4
-rw-r--r--sound/soc/img/img-i2s-in.c1
-rw-r--r--sound/soc/intel/Kconfig3
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.c4
-rw-r--r--sound/soc/intel/atom/sst/sst_pci.c2
-rw-r--r--sound/soc/intel/boards/bxt_rt298.c2
-rw-r--r--sound/soc/intel/boards/bytcht_es8316.c7
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c53
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c26
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c5
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_generic.c2
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c27
-rw-r--r--sound/soc/intel/skylake/skl-debug.c32
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c2
-rw-r--r--sound/soc/kirkwood/kirkwood-i2s.c11
-rw-r--r--sound/soc/meson/axg-fifo.c10
-rw-r--r--sound/soc/meson/axg-tdm-formatter.c11
-rw-r--r--sound/soc/meson/axg-tdm-formatter.h1
-rw-r--r--sound/soc/meson/axg-tdm-interface.c26
-rw-r--r--sound/soc/meson/axg-tdmin.c16
-rw-r--r--sound/soc/meson/axg-tdmout.c105
-rw-r--r--sound/soc/qcom/Kconfig2
-rw-r--r--sound/soc/qcom/common.c14
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-dai.c16
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.c8
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.h1
-rw-r--r--sound/soc/qcom/qdsp6/q6asm-dai.c4
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.c7
-rw-r--r--sound/soc/rockchip/Kconfig1
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c2
-rw-r--r--sound/soc/rockchip/rockchip_pdm.c4
-rw-r--r--sound/soc/sh/rcar/core.c20
-rw-r--r--sound/soc/sh/rcar/dma.c4
-rw-r--r--sound/soc/sh/rcar/gen.c8
-rw-r--r--sound/soc/sh/rcar/rsnd.h9
-rw-r--r--sound/soc/sh/rcar/ssi.c156
-rw-r--r--sound/soc/sh/rcar/ssiu.c2
-rw-r--r--sound/soc/soc-core.c20
-rw-r--r--sound/soc/soc-dapm.c30
-rw-r--r--sound/soc/soc-jack.c3
-rw-r--r--sound/soc/soc-ops.c4
-rw-r--r--sound/soc/soc-pcm.c131
-rw-r--r--sound/soc/soc-topology.c176
-rw-r--r--sound/soc/sof/core.c88
-rw-r--r--sound/soc/sof/intel/Kconfig20
-rw-r--r--sound/soc/sof/intel/bdw.c7
-rw-r--r--sound/soc/sof/intel/byt.c13
-rw-r--r--sound/soc/sof/intel/hda-codec.c19
-rw-r--r--sound/soc/sof/intel/hda-ctrl.c12
-rw-r--r--sound/soc/sof/intel/hda-dai.c4
-rw-r--r--sound/soc/sof/intel/hda-loader.c2
-rw-r--r--sound/soc/sof/intel/hda-stream.c45
-rw-r--r--sound/soc/sof/intel/hda.c32
-rw-r--r--sound/soc/sof/intel/hda.h7
-rw-r--r--sound/soc/sof/ipc.c22
-rw-r--r--sound/soc/sof/loader.c21
-rw-r--r--sound/soc/sof/nocodec.c7
-rw-r--r--sound/soc/sof/pm.c35
-rw-r--r--sound/soc/sof/sof-priv.h11
-rw-r--r--sound/soc/sof/topology.c19
-rw-r--r--sound/soc/sof/trace.c7
-rw-r--r--sound/soc/sti/uniperif_player.c7
-rw-r--r--sound/soc/stm/stm32_sai_sub.c220
-rw-r--r--sound/soc/stm/stm32_spdifrx.c40
-rw-r--r--sound/soc/sunxi/sun8i-codec.c3
-rw-r--r--sound/soc/tegra/tegra_wm8903.c6
-rw-r--r--sound/soc/ti/davinci-mcasp.c6
-rw-r--r--sound/soc/ti/omap-mcbsp.c10
-rw-r--r--sound/soc/ti/sdma-pcm.c2
-rw-r--r--sound/soc/ux500/mop500.c11
-rw-r--r--sound/usb/Makefile3
-rw-r--r--sound/usb/card.c35
-rw-r--r--sound/usb/card.h7
-rw-r--r--sound/usb/clock.c150
-rw-r--r--sound/usb/clock.h4
-rw-r--r--sound/usb/endpoint.c247
-rw-r--r--sound/usb/endpoint.h1
-rw-r--r--sound/usb/format.c52
-rw-r--r--sound/usb/helper.h4
-rw-r--r--sound/usb/line6/capture.c2
-rw-r--r--sound/usb/line6/driver.c4
-rw-r--r--sound/usb/line6/midibuf.c2
-rw-r--r--sound/usb/line6/playback.c2
-rw-r--r--sound/usb/line6/podhd.c22
-rw-r--r--sound/usb/midi.c17
-rw-r--r--sound/usb/mixer.c737
-rw-r--r--sound/usb/mixer.h19
-rw-r--r--sound/usb/mixer_maps.c85
-rw-r--r--sound/usb/mixer_quirks.c16
-rw-r--r--sound/usb/pcm.c55
-rw-r--r--sound/usb/power.c2
-rw-r--r--sound/usb/quirks-table.h305
-rw-r--r--sound/usb/quirks.c157
-rw-r--r--sound/usb/quirks.h7
-rw-r--r--sound/usb/stream.c26
-rw-r--r--sound/usb/usbaudio.h5
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c2
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c2
-rw-r--r--sound/usb/validate.c331
195 files changed, 4785 insertions, 1524 deletions
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index 41905afada63..1afa06b80f06 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -528,7 +528,7 @@ static int snd_compress_check_input(struct snd_compr_params *params)
{
/* first let's check the buffer parameter's */
if (params->buffer.fragment_size == 0 ||
- params->buffer.fragments > INT_MAX / params->buffer.fragment_size ||
+ params->buffer.fragments > U32_MAX / params->buffer.fragment_size ||
params->buffer.fragments == 0)
return -EINVAL;
@@ -722,6 +722,9 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
if (!retval) {
+ /* clear flags and stop any drain wait */
+ stream->partial_drain = false;
+ stream->metadata_set = false;
snd_compr_drain_notify(stream);
stream->runtime->total_bytes_available = 0;
stream->runtime->total_bytes_transferred = 0;
@@ -879,6 +882,7 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
if (stream->next_track == false)
return -EPERM;
+ stream->partial_drain = true;
retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
if (retval) {
pr_debug("Partial drain returned failure\n");
diff --git a/sound/core/control.c b/sound/core/control.c
index 5be5b9b931bf..ea99bd961a7a 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -1434,8 +1434,9 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag,
if (kctl->tlv.c == NULL)
return -ENXIO;
- /* When locked, this is unavailable. */
- if (vd->owner != NULL && vd->owner != file)
+ /* Write and command operations are not allowed for locked element. */
+ if (op_flag != SNDRV_CTL_TLV_OP_READ &&
+ vd->owner != NULL && vd->owner != file)
return -EPERM;
return kctl->tlv.c(kctl, op_flag, size, buf);
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index 00cb5aed10a9..28bec15b0959 100644
--- a/sound/core/hwdep.c
+++ b/sound/core/hwdep.c
@@ -216,12 +216,12 @@ static int snd_hwdep_dsp_load(struct snd_hwdep *hw,
if (info.index >= 32)
return -EINVAL;
/* check whether the dsp was already loaded */
- if (hw->dsp_loaded & (1 << info.index))
+ if (hw->dsp_loaded & (1u << info.index))
return -EBUSY;
err = hw->ops.dsp_load(hw, &info);
if (err < 0)
return err;
- hw->dsp_loaded |= (1 << info.index);
+ hw->dsp_loaded |= (1u << info.index);
return 0;
}
diff --git a/sound/core/info.c b/sound/core/info.c
index e051a029ccfb..f18f4ef6661e 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -608,7 +608,9 @@ int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
{
int c = -1;
- if (snd_BUG_ON(!buffer || !buffer->buffer))
+ if (snd_BUG_ON(!buffer))
+ return 1;
+ if (!buffer->buffer)
return 1;
if (len <= 0 || buffer->stop || buffer->error)
return 1;
diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c
index 2045697f449d..797d838a2f9e 100644
--- a/sound/core/oss/linear.c
+++ b/sound/core/oss/linear.c
@@ -107,6 +107,8 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
}
}
#endif
+ if (frames > dst_channels[0].frames)
+ frames = dst_channels[0].frames;
convert(plugin, src_channels, dst_channels, frames);
return frames;
}
diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c
index 7915564bd394..3788906421a7 100644
--- a/sound/core/oss/mulaw.c
+++ b/sound/core/oss/mulaw.c
@@ -269,6 +269,8 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin,
}
}
#endif
+ if (frames > dst_channels[0].frames)
+ frames = dst_channels[0].frames;
data = (struct mulaw_priv *)plugin->extra_data;
data->func(plugin, src_channels, dst_channels, frames);
return frames;
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
index 31cb2acf8afc..da400da1fafe 100644
--- a/sound/core/oss/pcm_plugin.c
+++ b/sound/core/oss/pcm_plugin.c
@@ -111,7 +111,7 @@ int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
while (plugin->next) {
if (plugin->dst_frames)
frames = plugin->dst_frames(plugin, frames);
- if (snd_BUG_ON((snd_pcm_sframes_t)frames <= 0))
+ if ((snd_pcm_sframes_t)frames <= 0)
return -ENXIO;
plugin = plugin->next;
err = snd_pcm_plugin_alloc(plugin, frames);
@@ -123,7 +123,7 @@ int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
while (plugin->prev) {
if (plugin->src_frames)
frames = plugin->src_frames(plugin, frames);
- if (snd_BUG_ON((snd_pcm_sframes_t)frames <= 0))
+ if ((snd_pcm_sframes_t)frames <= 0)
return -ENXIO;
plugin = plugin->prev;
err = snd_pcm_plugin_alloc(plugin, frames);
@@ -196,7 +196,9 @@ int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin)
return 0;
}
-snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames)
+static snd_pcm_sframes_t plug_client_size(struct snd_pcm_substream *plug,
+ snd_pcm_uframes_t drv_frames,
+ bool check_size)
{
struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next;
int stream;
@@ -212,12 +214,18 @@ snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_p
plugin_prev = plugin->prev;
if (plugin->src_frames)
drv_frames = plugin->src_frames(plugin, drv_frames);
+ if (check_size && plugin->buf_frames &&
+ drv_frames > plugin->buf_frames)
+ drv_frames = plugin->buf_frames;
plugin = plugin_prev;
}
} else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
plugin = snd_pcm_plug_first(plug);
while (plugin && drv_frames > 0) {
plugin_next = plugin->next;
+ if (check_size && plugin->buf_frames &&
+ drv_frames > plugin->buf_frames)
+ drv_frames = plugin->buf_frames;
if (plugin->dst_frames)
drv_frames = plugin->dst_frames(plugin, drv_frames);
plugin = plugin_next;
@@ -227,7 +235,9 @@ snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_p
return drv_frames;
}
-snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t clt_frames)
+static snd_pcm_sframes_t plug_slave_size(struct snd_pcm_substream *plug,
+ snd_pcm_uframes_t clt_frames,
+ bool check_size)
{
struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next;
snd_pcm_sframes_t frames;
@@ -243,6 +253,9 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc
plugin = snd_pcm_plug_first(plug);
while (plugin && frames > 0) {
plugin_next = plugin->next;
+ if (check_size && plugin->buf_frames &&
+ frames > plugin->buf_frames)
+ frames = plugin->buf_frames;
if (plugin->dst_frames) {
frames = plugin->dst_frames(plugin, frames);
if (frames < 0)
@@ -259,6 +272,9 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc
if (frames < 0)
return frames;
}
+ if (check_size && plugin->buf_frames &&
+ frames > plugin->buf_frames)
+ frames = plugin->buf_frames;
plugin = plugin_prev;
}
} else
@@ -266,6 +282,18 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc
return frames;
}
+snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug,
+ snd_pcm_uframes_t drv_frames)
+{
+ return plug_client_size(plug, drv_frames, false);
+}
+
+snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug,
+ snd_pcm_uframes_t clt_frames)
+{
+ return plug_slave_size(plug, clt_frames, false);
+}
+
static int snd_pcm_plug_formats(const struct snd_mask *mask,
snd_pcm_format_t format)
{
@@ -622,7 +650,7 @@ snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, st
src_channels = dst_channels;
plugin = next;
}
- return snd_pcm_plug_client_size(plug, frames);
+ return plug_client_size(plug, frames, true);
}
snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, struct snd_pcm_plugin_channel *dst_channels_final, snd_pcm_uframes_t size)
@@ -632,7 +660,7 @@ snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, str
snd_pcm_sframes_t frames = size;
int err;
- frames = snd_pcm_plug_slave_size(plug, frames);
+ frames = plug_slave_size(plug, frames, true);
if (frames < 0)
return frames;
diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c
index c8171f5783c8..72dea04197ef 100644
--- a/sound/core/oss/route.c
+++ b/sound/core/oss/route.c
@@ -57,6 +57,8 @@ static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin,
return -ENXIO;
if (frames == 0)
return 0;
+ if (frames > dst_channels[0].frames)
+ frames = dst_channels[0].frames;
nsrcs = plugin->src_format.channels;
ndsts = plugin->dst_format.channels;
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index d80041ea4e01..1662573a4030 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -423,6 +423,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
no_delta_check:
if (runtime->status->hw_ptr == new_hw_ptr) {
+ runtime->hw_ptr_jiffies = curr_jiffies;
update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
return 0;
}
@@ -1782,11 +1783,14 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime;
unsigned long flags;
- if (PCM_RUNTIME_CHECK(substream))
+ if (snd_BUG_ON(!substream))
return;
- runtime = substream->runtime;
snd_pcm_stream_lock_irqsave(substream, flags);
+ if (PCM_RUNTIME_CHECK(substream))
+ goto _unlock;
+ runtime = substream->runtime;
+
if (!snd_pcm_running(substream) ||
snd_pcm_update_hw_ptr0(substream, 1) < 0)
goto _end;
@@ -1797,6 +1801,7 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
#endif
_end:
kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
+ _unlock:
snd_pcm_stream_unlock_irqrestore(substream, flags);
}
EXPORT_SYMBOL(snd_pcm_period_elapsed);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 703857aab00f..f37cb1ebd728 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -136,6 +136,16 @@ void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
+static void snd_pcm_stream_lock_nested(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_group *group = &substream->self_group;
+
+ if (substream->pcm->nonatomic)
+ mutex_lock_nested(&group->mutex, SINGLE_DEPTH_NESTING);
+ else
+ spin_lock_nested(&group->lock, SINGLE_DEPTH_NESTING);
+}
+
/**
* snd_pcm_stream_unlock_irq - Unlock the PCM stream
* @substream: PCM substream
@@ -706,6 +716,10 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size)
runtime->boundary *= 2;
+ /* clear the buffer for avoiding possible kernel info leaks */
+ if (runtime->dma_area && !substream->ops->copy_user)
+ memset(runtime->dma_area, 0, runtime->dma_bytes);
+
snd_pcm_timer_resolution_change(substream);
snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP);
@@ -1990,6 +2004,12 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
}
pcm_file = f.file->private_data;
substream1 = pcm_file->substream;
+
+ if (substream == substream1) {
+ res = -EINVAL;
+ goto _badf;
+ }
+
group = kzalloc(sizeof(*group), GFP_KERNEL);
if (!group) {
res = -ENOMEM;
@@ -2018,7 +2038,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
snd_pcm_stream_unlock_irq(substream);
snd_pcm_group_lock_irq(target_group, nonatomic);
- snd_pcm_stream_lock(substream1);
+ snd_pcm_stream_lock_nested(substream1);
snd_pcm_group_assign(substream1, target_group);
refcount_inc(&target_group->refs);
snd_pcm_stream_unlock(substream1);
@@ -2034,7 +2054,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
static void relink_to_local(struct snd_pcm_substream *substream)
{
- snd_pcm_stream_lock(substream);
+ snd_pcm_stream_lock_nested(substream);
snd_pcm_group_assign(substream, &substream->self_group);
snd_pcm_stream_unlock(substream);
}
@@ -3404,7 +3424,8 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
#endif /* CONFIG_GENERIC_ALLOCATOR */
#ifndef CONFIG_X86 /* for avoiding warnings arch/x86/mm/pat.c */
if (IS_ENABLED(CONFIG_HAS_DMA) && !substream->ops->page &&
- substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
+ (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV ||
+ substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_UC))
return dma_mmap_coherent(substream->dma_buffer.dev.dev,
area,
substream->runtime->dma_area,
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 8a12a7538d63..94db4683cfaf 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -97,6 +97,17 @@ static void snd_rawmidi_input_event_work(struct work_struct *work)
runtime->event(runtime->substream);
}
+/* buffer refcount management: call with runtime->lock held */
+static inline void snd_rawmidi_buffer_ref(struct snd_rawmidi_runtime *runtime)
+{
+ runtime->buffer_ref++;
+}
+
+static inline void snd_rawmidi_buffer_unref(struct snd_rawmidi_runtime *runtime)
+{
+ runtime->buffer_ref--;
+}
+
static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
{
struct snd_rawmidi_runtime *runtime;
@@ -646,6 +657,11 @@ static int resize_runtime_buffer(struct snd_rawmidi_runtime *runtime,
if (!newbuf)
return -ENOMEM;
spin_lock_irq(&runtime->lock);
+ if (runtime->buffer_ref) {
+ spin_unlock_irq(&runtime->lock);
+ kvfree(newbuf);
+ return -EBUSY;
+ }
oldbuf = runtime->buffer;
runtime->buffer = newbuf;
runtime->buffer_size = params->buffer_size;
@@ -945,8 +961,10 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
long result = 0, count1;
struct snd_rawmidi_runtime *runtime = substream->runtime;
unsigned long appl_ptr;
+ int err = 0;
spin_lock_irqsave(&runtime->lock, flags);
+ snd_rawmidi_buffer_ref(runtime);
while (count > 0 && runtime->avail) {
count1 = runtime->buffer_size - runtime->appl_ptr;
if (count1 > count)
@@ -965,16 +983,19 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
if (userbuf) {
spin_unlock_irqrestore(&runtime->lock, flags);
if (copy_to_user(userbuf + result,
- runtime->buffer + appl_ptr, count1)) {
- return result > 0 ? result : -EFAULT;
- }
+ runtime->buffer + appl_ptr, count1))
+ err = -EFAULT;
spin_lock_irqsave(&runtime->lock, flags);
+ if (err)
+ goto out;
}
result += count1;
count -= count1;
}
+ out:
+ snd_rawmidi_buffer_unref(runtime);
spin_unlock_irqrestore(&runtime->lock, flags);
- return result;
+ return result > 0 ? result : err;
}
long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream,
@@ -1268,6 +1289,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
return -EAGAIN;
}
}
+ snd_rawmidi_buffer_ref(runtime);
while (count > 0 && runtime->avail > 0) {
count1 = runtime->buffer_size - runtime->appl_ptr;
if (count1 > count)
@@ -1299,6 +1321,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
}
__end:
count1 = runtime->avail < runtime->buffer_size;
+ snd_rawmidi_buffer_unref(runtime);
spin_unlock_irqrestore(&runtime->lock, flags);
if (count1)
snd_rawmidi_output_trigger(substream, 1);
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
index 17f913657304..c8b9c0b315d8 100644
--- a/sound/core/seq/oss/seq_oss.c
+++ b/sound/core/seq/oss/seq_oss.c
@@ -168,10 +168,16 @@ static long
odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct seq_oss_devinfo *dp;
+ long rc;
+
dp = file->private_data;
if (snd_BUG_ON(!dp))
return -ENXIO;
- return snd_seq_oss_ioctl(dp, cmd, arg);
+
+ mutex_lock(&register_mutex);
+ rc = snd_seq_oss_ioctl(dp, cmd, arg);
+ mutex_unlock(&register_mutex);
+ return rc;
}
#ifdef CONFIG_COMPAT
diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
index a88c235b2ea3..2ddfe2226651 100644
--- a/sound/core/seq/oss/seq_oss_midi.c
+++ b/sound/core/seq/oss/seq_oss_midi.c
@@ -602,6 +602,7 @@ send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq
len = snd_seq_oss_timer_start(dp->timer);
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
snd_seq_oss_readq_sysex(dp->readq, mdev->seq_device, ev);
+ snd_midi_event_reset_decode(mdev->coder);
} else {
len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev);
if (len > 0)
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 6d9592f0ae1d..cc93157fa950 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -580,7 +580,7 @@ static int update_timestamp_of_queue(struct snd_seq_event *event,
event->queue = queue;
event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK;
if (real_time) {
- event->time.time = snd_seq_timer_get_cur_time(q->timer);
+ event->time.time = snd_seq_timer_get_cur_time(q->timer, true);
event->flags |= SNDRV_SEQ_TIME_STAMP_REAL;
} else {
event->time.tick = snd_seq_timer_get_cur_tick(q->timer);
@@ -1659,7 +1659,7 @@ static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client,
tmr = queue->timer;
status->events = queue->tickq->cells + queue->timeq->cells;
- status->time = snd_seq_timer_get_cur_time(tmr);
+ status->time = snd_seq_timer_get_cur_time(tmr, true);
status->tick = snd_seq_timer_get_cur_tick(tmr);
status->running = tmr->running;
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index caf68bf42f13..71a6ea62c3be 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -238,6 +238,8 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
{
unsigned long flags;
struct snd_seq_event_cell *cell;
+ snd_seq_tick_time_t cur_tick;
+ snd_seq_real_time_t cur_time;
if (q == NULL)
return;
@@ -254,17 +256,18 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
__again:
/* Process tick queue... */
+ cur_tick = snd_seq_timer_get_cur_tick(q->timer);
for (;;) {
- cell = snd_seq_prioq_cell_out(q->tickq,
- &q->timer->tick.cur_tick);
+ cell = snd_seq_prioq_cell_out(q->tickq, &cur_tick);
if (!cell)
break;
snd_seq_dispatch_event(cell, atomic, hop);
}
/* Process time queue... */
+ cur_time = snd_seq_timer_get_cur_time(q->timer, false);
for (;;) {
- cell = snd_seq_prioq_cell_out(q->timeq, &q->timer->cur_time);
+ cell = snd_seq_prioq_cell_out(q->timeq, &cur_time);
if (!cell)
break;
snd_seq_dispatch_event(cell, atomic, hop);
@@ -392,6 +395,7 @@ int snd_seq_queue_check_access(int queueid, int client)
int snd_seq_queue_set_owner(int queueid, int client, int locked)
{
struct snd_seq_queue *q = queueptr(queueid);
+ unsigned long flags;
if (q == NULL)
return -EINVAL;
@@ -401,8 +405,10 @@ int snd_seq_queue_set_owner(int queueid, int client, int locked)
return -EPERM;
}
+ spin_lock_irqsave(&q->owner_lock, flags);
q->locked = locked ? 1 : 0;
q->owner = client;
+ spin_unlock_irqrestore(&q->owner_lock, flags);
queue_access_unlock(q);
queuefree(q);
@@ -539,15 +545,17 @@ void snd_seq_queue_client_termination(int client)
unsigned long flags;
int i;
struct snd_seq_queue *q;
+ bool matched;
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
if ((q = queueptr(i)) == NULL)
continue;
spin_lock_irqsave(&q->owner_lock, flags);
- if (q->owner == client)
+ matched = (q->owner == client);
+ if (matched)
q->klocked = 1;
spin_unlock_irqrestore(&q->owner_lock, flags);
- if (q->owner == client) {
+ if (matched) {
if (q->timer->running)
snd_seq_timer_stop(q->timer);
snd_seq_timer_reset(q->timer);
@@ -739,6 +747,8 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
int i, bpm;
struct snd_seq_queue *q;
struct snd_seq_timer *tmr;
+ bool locked;
+ int owner;
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
if ((q = queueptr(i)) == NULL)
@@ -750,9 +760,14 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
else
bpm = 0;
+ spin_lock_irq(&q->owner_lock);
+ locked = q->locked;
+ owner = q->owner;
+ spin_unlock_irq(&q->owner_lock);
+
snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name);
- snd_iprintf(buffer, "owned by client : %d\n", q->owner);
- snd_iprintf(buffer, "lock status : %s\n", q->locked ? "Locked" : "Free");
+ snd_iprintf(buffer, "owned by client : %d\n", owner);
+ snd_iprintf(buffer, "lock status : %s\n", locked ? "Locked" : "Free");
snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq));
snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq));
snd_iprintf(buffer, "timer state : %s\n", tmr->running ? "Running" : "Stopped");
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index 161f3170bd7e..0b43fc5fe349 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -422,14 +422,15 @@ int snd_seq_timer_continue(struct snd_seq_timer *tmr)
}
/* return current 'real' time. use timeofday() to get better granularity. */
-snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
+snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr,
+ bool adjust_ktime)
{
snd_seq_real_time_t cur_time;
unsigned long flags;
spin_lock_irqsave(&tmr->lock, flags);
cur_time = tmr->cur_time;
- if (tmr->running) {
+ if (adjust_ktime && tmr->running) {
struct timespec64 tm;
ktime_get_ts64(&tm);
@@ -446,7 +447,13 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
high PPQ values) */
snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr)
{
- return tmr->tick.cur_tick;
+ snd_seq_tick_time_t cur_tick;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tmr->lock, flags);
+ cur_tick = tmr->tick.cur_tick;
+ spin_unlock_irqrestore(&tmr->lock, flags);
+ return cur_tick;
}
@@ -465,15 +472,19 @@ void snd_seq_info_timer_read(struct snd_info_entry *entry,
q = queueptr(idx);
if (q == NULL)
continue;
- if ((tmr = q->timer) == NULL ||
- (ti = tmr->timeri) == NULL) {
- queuefree(q);
- continue;
- }
+ mutex_lock(&q->timer_mutex);
+ tmr = q->timer;
+ if (!tmr)
+ goto unlock;
+ ti = tmr->timeri;
+ if (!ti)
+ goto unlock;
snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name);
resolution = snd_timer_resolution(ti) * tmr->ticks;
snd_iprintf(buffer, " Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000);
snd_iprintf(buffer, " Skew : %u / %u\n", tmr->skew, tmr->skew_base);
+unlock:
+ mutex_unlock(&q->timer_mutex);
queuefree(q);
}
}
diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h
index 66c3e344eae3..4bec57df8158 100644
--- a/sound/core/seq/seq_timer.h
+++ b/sound/core/seq/seq_timer.h
@@ -120,7 +120,8 @@ int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq);
int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr, snd_seq_tick_time_t position);
int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr, snd_seq_real_time_t position);
int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigned int base);
-snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr);
+snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr,
+ bool adjust_ktime);
snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr);
extern int seq_default_timer_class;
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index 626d87c1539b..77d7037d1476 100644
--- a/sound/core/seq/seq_virmidi.c
+++ b/sound/core/seq/seq_virmidi.c
@@ -81,6 +81,7 @@ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
continue;
snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream);
+ snd_midi_event_reset_decode(vmidi->parser);
} else {
len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
if (len > 0)
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 5c9fbf3f4340..013f0e69ff0f 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -74,6 +74,9 @@ static LIST_HEAD(snd_timer_slave_list);
/* lock for slave active lists */
static DEFINE_SPINLOCK(slave_active_lock);
+#define MAX_SLAVE_INSTANCES 1000
+static int num_slaves;
+
static DEFINE_MUTEX(register_mutex);
static int snd_timer_free(struct snd_timer *timer);
@@ -226,7 +229,8 @@ static int snd_timer_check_master(struct snd_timer_instance *master)
return 0;
}
-static int snd_timer_close_locked(struct snd_timer_instance *timeri);
+static int snd_timer_close_locked(struct snd_timer_instance *timeri,
+ struct device **card_devp_to_put);
/*
* open a timer instance
@@ -238,6 +242,7 @@ int snd_timer_open(struct snd_timer_instance **ti,
{
struct snd_timer *timer;
struct snd_timer_instance *timeri = NULL;
+ struct device *card_dev_to_put = NULL;
int err;
mutex_lock(&register_mutex);
@@ -250,6 +255,10 @@ int snd_timer_open(struct snd_timer_instance **ti,
err = -EINVAL;
goto unlock;
}
+ if (num_slaves >= MAX_SLAVE_INSTANCES) {
+ err = -EBUSY;
+ goto unlock;
+ }
timeri = snd_timer_instance_new(owner, NULL);
if (!timeri) {
err = -ENOMEM;
@@ -259,9 +268,10 @@ int snd_timer_open(struct snd_timer_instance **ti,
timeri->slave_id = tid->device;
timeri->flags |= SNDRV_TIMER_IFLG_SLAVE;
list_add_tail(&timeri->open_list, &snd_timer_slave_list);
+ num_slaves++;
err = snd_timer_check_slave(timeri);
if (err < 0) {
- snd_timer_close_locked(timeri);
+ snd_timer_close_locked(timeri, &card_dev_to_put);
timeri = NULL;
}
goto unlock;
@@ -282,11 +292,11 @@ int snd_timer_open(struct snd_timer_instance **ti,
goto unlock;
}
if (!list_empty(&timer->open_list_head)) {
- timeri = list_entry(timer->open_list_head.next,
+ struct snd_timer_instance *t =
+ list_entry(timer->open_list_head.next,
struct snd_timer_instance, open_list);
- if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
+ if (t->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
err = -EBUSY;
- timeri = NULL;
goto unlock;
}
}
@@ -313,7 +323,7 @@ int snd_timer_open(struct snd_timer_instance **ti,
timeri = NULL;
if (timer->card)
- put_device(&timer->card->card_dev);
+ card_dev_to_put = &timer->card->card_dev;
module_put(timer->module);
goto unlock;
}
@@ -323,12 +333,15 @@ int snd_timer_open(struct snd_timer_instance **ti,
timer->num_instances++;
err = snd_timer_check_master(timeri);
if (err < 0) {
- snd_timer_close_locked(timeri);
+ snd_timer_close_locked(timeri, &card_dev_to_put);
timeri = NULL;
}
unlock:
mutex_unlock(&register_mutex);
+ /* put_device() is called after unlock for avoiding deadlock */
+ if (card_dev_to_put)
+ put_device(card_dev_to_put);
*ti = timeri;
return err;
}
@@ -338,7 +351,8 @@ EXPORT_SYMBOL(snd_timer_open);
* close a timer instance
* call this with register_mutex down.
*/
-static int snd_timer_close_locked(struct snd_timer_instance *timeri)
+static int snd_timer_close_locked(struct snd_timer_instance *timeri,
+ struct device **card_devp_to_put)
{
struct snd_timer *timer = timeri->timer;
struct snd_timer_instance *slave, *tmp;
@@ -350,6 +364,8 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri)
}
list_del(&timeri->open_list);
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ num_slaves--;
/* force to stop the timer */
snd_timer_stop(timeri);
@@ -395,7 +411,7 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri)
timer->hw.close(timer);
/* release a card refcount for safe disconnection */
if (timer->card)
- put_device(&timer->card->card_dev);
+ *card_devp_to_put = &timer->card->card_dev;
module_put(timer->module);
}
@@ -407,14 +423,18 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri)
*/
int snd_timer_close(struct snd_timer_instance *timeri)
{
+ struct device *card_dev_to_put = NULL;
int err;
if (snd_BUG_ON(!timeri))
return -ENXIO;
mutex_lock(&register_mutex);
- err = snd_timer_close_locked(timeri);
+ err = snd_timer_close_locked(timeri, &card_dev_to_put);
mutex_unlock(&register_mutex);
+ /* put_device() is called after unlock for avoiding deadlock */
+ if (card_dev_to_put)
+ put_device(card_dev_to_put);
return err;
}
EXPORT_SYMBOL(snd_timer_close);
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index aee7c04d49e5..b61ba0321a72 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -915,7 +915,7 @@ static void print_formats(struct snd_dummy *dummy,
{
int i;
- for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
+ for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
if (dummy->pcm_hw.formats & (1ULL << i))
snd_iprintf(buffer, " %s", snd_pcm_format_name(i));
}
diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c
index e69a4ef0d6bd..08c10ac9d6c8 100644
--- a/sound/drivers/opl3/opl3_synth.c
+++ b/sound/drivers/opl3/opl3_synth.c
@@ -91,6 +91,8 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
{
struct snd_dm_fm_info info;
+ memset(&info, 0, sizeof(info));
+
info.fm_mode = opl3->fm_mode;
info.rhythm = opl3->rhythm;
if (copy_to_user(argp, &info, sizeof(struct snd_dm_fm_info)))
diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c
index 32b864bee25f..06d6a37cd853 100644
--- a/sound/firewire/bebob/bebob_focusrite.c
+++ b/sound/firewire/bebob/bebob_focusrite.c
@@ -27,6 +27,8 @@
#define SAFFIRE_CLOCK_SOURCE_SPDIF 1
/* clock sources as returned from register of Saffire Pro 10 and 26 */
+#define SAFFIREPRO_CLOCK_SOURCE_SELECT_MASK 0x000000ff
+#define SAFFIREPRO_CLOCK_SOURCE_DETECT_MASK 0x0000ff00
#define SAFFIREPRO_CLOCK_SOURCE_INTERNAL 0
#define SAFFIREPRO_CLOCK_SOURCE_SKIP 1 /* never used on hardware */
#define SAFFIREPRO_CLOCK_SOURCE_SPDIF 2
@@ -189,6 +191,7 @@ saffirepro_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
map = saffirepro_clk_maps[1];
/* In a case that this driver cannot handle the value of register. */
+ value &= SAFFIREPRO_CLOCK_SOURCE_SELECT_MASK;
if (value >= SAFFIREPRO_CLOCK_SOURCE_COUNT || map[value] < 0) {
err = -EIO;
goto end;
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 0c93a825cb98..e313dd8612da 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -252,8 +252,7 @@ end:
return err;
}
-static unsigned int
-map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
+static int map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
{
unsigned int sec, sections, ch, channels;
unsigned int pcm, midi, location;
@@ -462,17 +461,18 @@ end:
return err;
}
-static void
-break_both_connections(struct snd_bebob *bebob)
+static void break_both_connections(struct snd_bebob *bebob)
{
cmp_connection_break(&bebob->in_conn);
cmp_connection_break(&bebob->out_conn);
bebob->connected = false;
- /* These models seems to be in transition state for a longer time. */
- if (bebob->maudio_special_quirk != NULL)
- msleep(200);
+ // These models seem to be in transition state for a longer time. When
+ // accessing in the state, any transactions is corrupted. In the worst
+ // case, the device is going to reboot.
+ if (bebob->version < 2)
+ msleep(600);
}
static void
diff --git a/sound/firewire/dice/dice-extension.c b/sound/firewire/dice/dice-extension.c
index a63fcbc875ad..02f4a8318e38 100644
--- a/sound/firewire/dice/dice-extension.c
+++ b/sound/firewire/dice/dice-extension.c
@@ -159,8 +159,11 @@ int snd_dice_detect_extension_formats(struct snd_dice *dice)
int j;
for (j = i + 1; j < 9; ++j) {
- if (pointers[i * 2] == pointers[j * 2])
+ if (pointers[i * 2] == pointers[j * 2]) {
+ // Fallback to limited functionality.
+ err = -ENXIO;
goto end;
+ }
}
}
diff --git a/sound/firewire/motu/motu-proc.c b/sound/firewire/motu/motu-proc.c
index ea46fb4c1b5a..126a7bd187bb 100644
--- a/sound/firewire/motu/motu-proc.c
+++ b/sound/firewire/motu/motu-proc.c
@@ -16,7 +16,7 @@ static const char *const clock_names[] = {
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT] = "S/PDIF on optical interface",
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A] = "S/PDIF on optical interface A",
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B] = "S/PDIF on optical interface B",
- [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX] = "S/PCIF on coaxial interface",
+ [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX] = "S/PDIF on coaxial interface",
[SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR] = "AESEBU on XLR interface",
[SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC] = "Word clock on BNC interface",
};
diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
index 94f367cdfdf3..df6d5e58d3c2 100644
--- a/sound/firewire/oxfw/oxfw-pcm.c
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -243,7 +243,7 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&oxfw->mutex);
}
- return 0;
+ return err;
}
static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c
index d9d20ef22f5b..9d7263aed2b4 100644
--- a/sound/firewire/tascam/amdtp-tascam.c
+++ b/sound/firewire/tascam/amdtp-tascam.c
@@ -147,14 +147,15 @@ static void read_status_messages(struct amdtp_stream *s,
if ((before ^ after) & mask) {
struct snd_firewire_tascam_change *entry =
&tscm->queue[tscm->push_pos];
+ unsigned long flag;
- spin_lock_irq(&tscm->lock);
+ spin_lock_irqsave(&tscm->lock, flag);
entry->index = index;
entry->before = before;
entry->after = after;
if (++tscm->push_pos >= SND_TSCM_QUEUE_COUNT)
tscm->push_pos = 0;
- spin_unlock_irq(&tscm->lock);
+ spin_unlock_irqrestore(&tscm->lock, flag);
wake_up(&tscm->hwdep_wait);
}
diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c
index 211ca85acd8c..a70f6f86903d 100644
--- a/sound/hda/ext/hdac_ext_controller.c
+++ b/sound/hda/ext/hdac_ext_controller.c
@@ -254,6 +254,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all);
int snd_hdac_ext_bus_link_get(struct hdac_bus *bus,
struct hdac_ext_link *link)
{
+ unsigned long codec_mask;
int ret = 0;
mutex_lock(&bus->lock);
@@ -275,9 +276,11 @@ int snd_hdac_ext_bus_link_get(struct hdac_bus *bus,
* HDA spec section 4.3 - Codec Discovery
*/
udelay(521);
- bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
- dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask);
- snd_hdac_chip_writew(bus, STATESTS, bus->codec_mask);
+ codec_mask = snd_hdac_chip_readw(bus, STATESTS);
+ dev_dbg(bus->dev, "codec_mask = 0x%lx\n", codec_mask);
+ snd_hdac_chip_writew(bus, STATESTS, codec_mask);
+ if (!bus->codec_mask)
+ bus->codec_mask = codec_mask;
}
mutex_unlock(&bus->lock);
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
index 2258804c5857..812dc144fb5b 100644
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/hdac_controller.c
@@ -445,8 +445,6 @@ static void azx_int_disable(struct hdac_bus *bus)
list_for_each_entry(azx_dev, &bus->stream_list, list)
snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);
- synchronize_irq(bus->irq);
-
/* disable SIE for all streams */
snd_hdac_chip_writeb(bus, INTCTL, 0);
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
index 3842f9d34b7c..c2386b292153 100644
--- a/sound/hda/hdac_device.c
+++ b/sound/hda/hdac_device.c
@@ -57,6 +57,7 @@ int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus,
codec->addr = addr;
codec->type = HDA_DEV_CORE;
mutex_init(&codec->widget_lock);
+ mutex_init(&codec->regmap_lock);
pm_runtime_set_active(&codec->dev);
pm_runtime_get_noresume(&codec->dev);
atomic_set(&codec->in_pm, 0);
diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c
index f399a1552e73..f064178f7cdc 100644
--- a/sound/hda/hdac_regmap.c
+++ b/sound/hda/hdac_regmap.c
@@ -362,6 +362,7 @@ static const struct regmap_config hda_regmap_cfg = {
.reg_write = hda_reg_write,
.use_single_read = true,
.use_single_write = true,
+ .disable_locking = true,
};
/**
@@ -424,12 +425,29 @@ EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb);
static int reg_raw_write(struct hdac_device *codec, unsigned int reg,
unsigned int val)
{
+ int err;
+
+ mutex_lock(&codec->regmap_lock);
if (!codec->regmap)
- return hda_reg_write(codec, reg, val);
+ err = hda_reg_write(codec, reg, val);
else
- return regmap_write(codec->regmap, reg, val);
+ err = regmap_write(codec->regmap, reg, val);
+ mutex_unlock(&codec->regmap_lock);
+ return err;
}
+/* a helper macro to call @func_call; retry with power-up if failed */
+#define CALL_RAW_FUNC(codec, func_call) \
+ ({ \
+ int _err = func_call; \
+ if (_err == -EAGAIN) { \
+ _err = snd_hdac_power_up_pm(codec); \
+ if (_err >= 0) \
+ _err = func_call; \
+ snd_hdac_power_down_pm(codec); \
+ } \
+ _err;})
+
/**
* snd_hdac_regmap_write_raw - write a pseudo register with power mgmt
* @codec: the codec object
@@ -441,42 +459,29 @@ static int reg_raw_write(struct hdac_device *codec, unsigned int reg,
int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
unsigned int val)
{
- int err;
-
- err = reg_raw_write(codec, reg, val);
- if (err == -EAGAIN) {
- err = snd_hdac_power_up_pm(codec);
- if (err >= 0)
- err = reg_raw_write(codec, reg, val);
- snd_hdac_power_down_pm(codec);
- }
- return err;
+ return CALL_RAW_FUNC(codec, reg_raw_write(codec, reg, val));
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);
static int reg_raw_read(struct hdac_device *codec, unsigned int reg,
unsigned int *val, bool uncached)
{
+ int err;
+
+ mutex_lock(&codec->regmap_lock);
if (uncached || !codec->regmap)
- return hda_reg_read(codec, reg, val);
+ err = hda_reg_read(codec, reg, val);
else
- return regmap_read(codec->regmap, reg, val);
+ err = regmap_read(codec->regmap, reg, val);
+ mutex_unlock(&codec->regmap_lock);
+ return err;
}
static int __snd_hdac_regmap_read_raw(struct hdac_device *codec,
unsigned int reg, unsigned int *val,
bool uncached)
{
- int err;
-
- err = reg_raw_read(codec, reg, val, uncached);
- if (err == -EAGAIN) {
- err = snd_hdac_power_up_pm(codec);
- if (err >= 0)
- err = reg_raw_read(codec, reg, val, uncached);
- snd_hdac_power_down_pm(codec);
- }
- return err;
+ return CALL_RAW_FUNC(codec, reg_raw_read(codec, reg, val, uncached));
}
/**
@@ -503,6 +508,35 @@ int snd_hdac_regmap_read_raw_uncached(struct hdac_device *codec,
return __snd_hdac_regmap_read_raw(codec, reg, val, true);
}
+static int reg_raw_update(struct hdac_device *codec, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ unsigned int orig;
+ bool change;
+ int err;
+
+ mutex_lock(&codec->regmap_lock);
+ if (codec->regmap) {
+ err = regmap_update_bits_check(codec->regmap, reg, mask, val,
+ &change);
+ if (!err)
+ err = change ? 1 : 0;
+ } else {
+ err = hda_reg_read(codec, reg, &orig);
+ if (!err) {
+ val &= mask;
+ val |= orig & ~mask;
+ if (val != orig) {
+ err = hda_reg_write(codec, reg, val);
+ if (!err)
+ err = 1;
+ }
+ }
+ }
+ mutex_unlock(&codec->regmap_lock);
+ return err;
+}
+
/**
* snd_hdac_regmap_update_raw - update a pseudo register with power mgmt
* @codec: the codec object
@@ -515,19 +549,57 @@ int snd_hdac_regmap_read_raw_uncached(struct hdac_device *codec,
int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
unsigned int mask, unsigned int val)
{
+ return CALL_RAW_FUNC(codec, reg_raw_update(codec, reg, mask, val));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);
+
+static int reg_raw_update_once(struct hdac_device *codec, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
unsigned int orig;
int err;
- val &= mask;
- err = snd_hdac_regmap_read_raw(codec, reg, &orig);
- if (err < 0)
- return err;
- val |= orig & ~mask;
- if (val == orig)
- return 0;
- err = snd_hdac_regmap_write_raw(codec, reg, val);
+ if (!codec->regmap)
+ return reg_raw_update(codec, reg, mask, val);
+
+ mutex_lock(&codec->regmap_lock);
+ regcache_cache_only(codec->regmap, true);
+ err = regmap_read(codec->regmap, reg, &orig);
+ regcache_cache_only(codec->regmap, false);
if (err < 0)
- return err;
- return 1;
+ err = regmap_update_bits(codec->regmap, reg, mask, val);
+ mutex_unlock(&codec->regmap_lock);
+ return err;
}
-EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);
+
+/**
+ * snd_hdac_regmap_update_raw_once - initialize the register value only once
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @mask: bit mask to update
+ * @val: value to update
+ *
+ * Performs the update of the register bits only once when the register
+ * hasn't been initialized yet. Used in HD-audio legacy driver.
+ * Returns zero if successful or a negative error code
+ */
+int snd_hdac_regmap_update_raw_once(struct hdac_device *codec, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ return CALL_RAW_FUNC(codec, reg_raw_update_once(codec, reg, mask, val));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw_once);
+
+/**
+ * snd_hdac_regmap_sync - sync out the cached values for PM resume
+ * @codec: the codec object
+ */
+void snd_hdac_regmap_sync(struct hdac_device *codec)
+{
+ if (codec->regmap) {
+ mutex_lock(&codec->regmap_lock);
+ regcache_sync(codec->regmap);
+ mutex_unlock(&codec->regmap_lock);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_sync);
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
index 55d53b89ac21..fab8a04b4d81 100644
--- a/sound/hda/hdac_stream.c
+++ b/sound/hda/hdac_stream.c
@@ -96,12 +96,14 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start)
1 << azx_dev->index,
1 << azx_dev->index);
/* set stripe control */
- if (azx_dev->substream)
- stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream);
- else
- stripe_ctl = 0;
- snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
- stripe_ctl);
+ if (azx_dev->stripe) {
+ if (azx_dev->substream)
+ stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream);
+ else
+ stripe_ctl = 0;
+ snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
+ stripe_ctl);
+ }
/* set DMA start and interrupt mask */
snd_hdac_stream_updateb(azx_dev, SD_CTL,
0, SD_CTL_DMA_START | SD_INT_MASK);
@@ -118,7 +120,8 @@ void snd_hdac_stream_clear(struct hdac_stream *azx_dev)
snd_hdac_stream_updateb(azx_dev, SD_CTL,
SD_CTL_DMA_START | SD_INT_MASK, 0);
snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
- snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
+ if (azx_dev->stripe)
+ snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
azx_dev->running = false;
}
EXPORT_SYMBOL_GPL(snd_hdac_stream_clear);
diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c
index 886cb7811bd6..2efee794cac6 100644
--- a/sound/hda/hdmi_chmap.c
+++ b/sound/hda/hdmi_chmap.c
@@ -250,7 +250,7 @@ void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen)
for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
if (spk_alloc & (1 << i))
- j += snprintf(buf + j, buflen - j, " %s",
+ j += scnprintf(buf + j, buflen - j, " %s",
cea_speaker_allocation_names[i]);
}
buf[j] = '\0'; /* necessary when j == 0 */
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
index 78dd213589b4..fa3c39cff5f8 100644
--- a/sound/isa/cs423x/cs4236.c
+++ b/sound/isa/cs423x/cs4236.c
@@ -278,7 +278,8 @@ static int snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev)
} else {
mpu_port[dev] = pnp_port_start(pdev, 0);
if (mpu_irq[dev] >= 0 &&
- pnp_irq_valid(pdev, 0) && pnp_irq(pdev, 0) >= 0) {
+ pnp_irq_valid(pdev, 0) &&
+ pnp_irq(pdev, 0) != (resource_size_t)-1) {
mpu_irq[dev] = pnp_irq(pdev, 0);
} else {
mpu_irq[dev] = -1; /* disable interrupt */
diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c
index 9be89377171b..b4e9b0de3b42 100644
--- a/sound/isa/es1688/es1688.c
+++ b/sound/isa/es1688/es1688.c
@@ -267,8 +267,10 @@ static int snd_es968_pnp_detect(struct pnp_card_link *pcard,
return error;
}
error = snd_es1688_probe(card, dev);
- if (error < 0)
+ if (error < 0) {
+ snd_card_free(card);
return error;
+ }
pnp_set_card_drvdata(pcard, card);
snd_es968_pnp_is_probed = 1;
return 0;
diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
index 0458934de1c7..9ca5c83de8a7 100644
--- a/sound/isa/opti9xx/miro.c
+++ b/sound/isa/opti9xx/miro.c
@@ -867,10 +867,13 @@ static void snd_miro_write(struct snd_miro *chip, unsigned char reg,
spin_unlock_irqrestore(&chip->lock, flags);
}
+static inline void snd_miro_write_mask(struct snd_miro *chip,
+ unsigned char reg, unsigned char value, unsigned char mask)
+{
+ unsigned char oldval = snd_miro_read(chip, reg);
-#define snd_miro_write_mask(chip, reg, value, mask) \
- snd_miro_write(chip, reg, \
- (snd_miro_read(chip, reg) & ~(mask)) | ((value) & (mask)))
+ snd_miro_write(chip, reg, (oldval & ~mask) | (value & mask));
+}
/*
* Proc Interface
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index fb36bb5d55df..fb87eedc8121 100644
--- a/sound/isa/opti9xx/opti92x-ad1848.c
+++ b/sound/isa/opti9xx/opti92x-ad1848.c
@@ -317,10 +317,13 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
}
-#define snd_opti9xx_write_mask(chip, reg, value, mask) \
- snd_opti9xx_write(chip, reg, \
- (snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))
+static inline void snd_opti9xx_write_mask(struct snd_opti9xx *chip,
+ unsigned char reg, unsigned char value, unsigned char mask)
+{
+ unsigned char oldval = snd_opti9xx_read(chip, reg);
+ snd_opti9xx_write(chip, reg, (oldval & ~mask) | (value & mask));
+}
static int snd_opti9xx_configure(struct snd_opti9xx *chip,
long port,
diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
index aec1c46e6697..1dfb2b8e6fd6 100644
--- a/sound/isa/wavefront/wavefront_synth.c
+++ b/sound/isa/wavefront/wavefront_synth.c
@@ -1172,7 +1172,10 @@ wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header)
"alias for %d\n",
header->number,
header->hdr.a.OriginalSample);
-
+
+ if (header->number >= WF_MAX_SAMPLE)
+ return -EINVAL;
+
munge_int32 (header->number, &alias_hdr[0], 2);
munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2);
munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset),
@@ -1203,6 +1206,9 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header)
int num_samples;
unsigned char *msample_hdr;
+ if (header->number >= WF_MAX_SAMPLE)
+ return -EINVAL;
+
msample_hdr = kmalloc(WF_MSAMPLE_BYTES, GFP_KERNEL);
if (! msample_hdr)
return -ENOMEM;
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index b612a536a5a1..0e15d497946a 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -2199,7 +2199,6 @@ static int snd_echo_resume(struct device *dev)
if (err < 0) {
kfree(commpage_bak);
dev_err(dev, "resume init_hw err=%d\n", err);
- snd_echo_free(chip);
return err;
}
@@ -2226,7 +2225,6 @@ static int snd_echo_resume(struct device *dev)
if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED,
KBUILD_MODNAME, chip)) {
dev_err(chip->card->dev, "cannot grab irq\n");
- snd_echo_free(chip);
return -EBUSY;
}
chip->irq = pci->irq;
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
index 18e6546b4467..6465839aa459 100644
--- a/sound/pci/hda/hda_auto_parser.c
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -72,6 +72,12 @@ static int compare_input_type(const void *ap, const void *bp)
if (a->type != b->type)
return (int)(a->type - b->type);
+ /* If has both hs_mic and hp_mic, pick the hs_mic ahead of hp_mic. */
+ if (a->is_headset_mic && b->is_headphone_mic)
+ return -1; /* don't swap */
+ else if (a->is_headphone_mic && b->is_headset_mic)
+ return 1; /* swap */
+
/* In case one has boost and the other one has not,
pick the one with boost first. */
return (int)(b->has_boost_on_pin - a->has_boost_on_pin);
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index b7d9160ed868..c6e1e03a5e4d 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -290,8 +290,12 @@ int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_beep *beep = codec->beep;
+ int chs = get_amp_channels(kcontrol);
+
if (beep && (!beep->enabled || !ctl_has_mute(kcontrol))) {
- ucontrol->value.integer.value[0] =
+ if (chs & 1)
+ ucontrol->value.integer.value[0] = beep->enabled;
+ if (chs & 2)
ucontrol->value.integer.value[1] = beep->enabled;
return 0;
}
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c
index 8272b50b8349..6a8564566375 100644
--- a/sound/pci/hda/hda_bind.c
+++ b/sound/pci/hda/hda_bind.c
@@ -43,6 +43,10 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
{
struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+ /* ignore unsol events during shutdown */
+ if (codec->bus->shutdown)
+ return;
+
if (codec->patch_ops.unsol_event)
codec->patch_ops.unsol_event(codec, ev);
}
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index c2a6554c9877..0922a8bb32d0 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1267,6 +1267,18 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
}
EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
+static unsigned int encode_amp(struct hda_codec *codec, hda_nid_t nid,
+ int ch, int dir, int idx)
+{
+ unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
+
+ /* enable fake mute if no h/w mute but min=mute */
+ if ((query_amp_caps(codec, nid, dir) &
+ (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) == AC_AMPCAP_MIN_MUTE)
+ cmd |= AC_AMP_FAKE_MUTE;
+ return cmd;
+}
+
/**
* snd_hda_codec_amp_update - update the AMP mono value
* @codec: HD-audio codec
@@ -1282,12 +1294,8 @@ EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid,
int ch, int dir, int idx, int mask, int val)
{
- unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
+ unsigned int cmd = encode_amp(codec, nid, ch, dir, idx);
- /* enable fake mute if no h/w mute but min=mute */
- if ((query_amp_caps(codec, nid, dir) &
- (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) == AC_AMPCAP_MIN_MUTE)
- cmd |= AC_AMP_FAKE_MUTE;
return snd_hdac_regmap_update_raw(&codec->core, cmd, mask, val);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update);
@@ -1335,16 +1343,11 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo);
int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
int dir, int idx, int mask, int val)
{
- int orig;
+ unsigned int cmd = encode_amp(codec, nid, ch, dir, idx);
if (!codec->core.regmap)
return -EINVAL;
- regcache_cache_only(codec->core.regmap, true);
- orig = snd_hda_codec_amp_read(codec, nid, ch, dir, idx);
- regcache_cache_only(codec->core.regmap, false);
- if (orig >= 0)
- return 0;
- return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val);
+ return snd_hdac_regmap_update_raw_once(&codec->core, cmd, mask, val);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init);
@@ -2905,8 +2908,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
else {
if (codec->patch_ops.init)
codec->patch_ops.init(codec);
- if (codec->core.regmap)
- regcache_sync(codec->core.regmap);
+ snd_hda_regmap_sync(codec);
}
if (codec->jackpoll_interval)
@@ -2922,6 +2924,10 @@ static int hda_codec_runtime_suspend(struct device *dev)
struct hda_codec *codec = dev_to_hda_codec(dev);
unsigned int state;
+ /* Nothing to do if card registration fails and the component driver never probes */
+ if (!codec->card)
+ return 0;
+
cancel_delayed_work_sync(&codec->jackpoll_work);
state = hda_call_codec_suspend(codec);
if (codec->link_down_at_suspend ||
@@ -2936,6 +2942,10 @@ static int hda_codec_runtime_resume(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
+ /* Nothing to do if card registration fails and the component driver never probes */
+ if (!codec->card)
+ return 0;
+
codec_display_power(codec, true);
snd_hdac_codec_link_up(&codec->core);
hda_call_codec_resume(codec);
@@ -4019,7 +4029,7 @@ void snd_print_pcm_bits(int pcm, char *buf, int buflen)
for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++)
if (pcm & (AC_SUPPCM_BITS_8 << i))
- j += snprintf(buf + j, buflen - j, " %d", bits[i]);
+ j += scnprintf(buf + j, buflen - j, " %d", bits[i]);
buf[j] = '\0'; /* necessary when j == 0 */
}
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index 1158bcf55148..a25a68ab9abb 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -875,7 +875,7 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
return -EAGAIN; /* give a chance to retry */
}
- dev_WARN(chip->card->dev,
+ dev_err(chip->card->dev,
"azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n",
bus->last_cmd[addr]);
chip->single_cmd = 1;
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index d081fb2880a0..82cf1da2ff12 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -360,7 +360,7 @@ static void hdmi_print_pcm_rates(int pcm, char *buf, int buflen)
for (i = 0, j = 0; i < ARRAY_SIZE(alsa_rates); i++)
if (pcm & (1 << i))
- j += snprintf(buf + j, buflen - j, " %d",
+ j += scnprintf(buf + j, buflen - j, " %d",
alsa_rates[i]);
buf[j] = '\0'; /* necessary when j == 0 */
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 10d502328b76..6815f9dc8545 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -4401,7 +4401,7 @@ EXPORT_SYMBOL_GPL(snd_hda_gen_fix_pin_power);
*/
/* check each pin in the given array; returns true if any of them is plugged */
-static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
+static bool detect_jacks(struct hda_codec *codec, int num_pins, const hda_nid_t *pins)
{
int i;
bool present = false;
@@ -4420,7 +4420,7 @@ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
}
/* standard HP/line-out auto-mute helper */
-static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
+static void do_automute(struct hda_codec *codec, int num_pins, const hda_nid_t *pins,
int *paths, bool mute)
{
struct hda_gen_spec *spec = codec->spec;
@@ -6027,7 +6027,7 @@ int snd_hda_gen_init(struct hda_codec *codec)
/* call init functions of standard auto-mute helpers */
update_automute_all(codec);
- regcache_sync(codec->core.regmap);
+ snd_hda_regmap_sync(codec);
if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 03dd532967bd..011f8e958743 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -277,12 +277,13 @@ enum {
/* quirks for old Intel chipsets */
#define AZX_DCAPS_INTEL_ICH \
- (AZX_DCAPS_OLD_SSYNC | AZX_DCAPS_NO_ALIGN_BUFSIZE)
+ (AZX_DCAPS_OLD_SSYNC | AZX_DCAPS_NO_ALIGN_BUFSIZE |\
+ AZX_DCAPS_SYNC_WRITE)
/* quirks for Intel PCH */
#define AZX_DCAPS_INTEL_PCH_BASE \
(AZX_DCAPS_NO_ALIGN_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY |\
- AZX_DCAPS_SNOOP_TYPE(SCH))
+ AZX_DCAPS_SNOOP_TYPE(SCH) | AZX_DCAPS_SYNC_WRITE)
/* PCH up to IVB; no runtime PM; bind with i915 gfx */
#define AZX_DCAPS_INTEL_PCH_NOPM \
@@ -297,13 +298,13 @@ enum {
#define AZX_DCAPS_INTEL_HASWELL \
(/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_COUNT_LPIB_DELAY |\
AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\
- AZX_DCAPS_SNOOP_TYPE(SCH))
+ AZX_DCAPS_SNOOP_TYPE(SCH) | AZX_DCAPS_SYNC_WRITE)
/* Broadwell HDMI can't use position buffer reliably, force to use LPIB */
#define AZX_DCAPS_INTEL_BROADWELL \
(/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_POSFIX_LPIB |\
AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\
- AZX_DCAPS_SNOOP_TYPE(SCH))
+ AZX_DCAPS_SNOOP_TYPE(SCH) | AZX_DCAPS_SYNC_WRITE)
#define AZX_DCAPS_INTEL_BAYTRAIL \
(AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_I915_COMPONENT)
@@ -1067,6 +1068,8 @@ static int azx_freeze_noirq(struct device *dev)
struct azx *chip = card->private_data;
struct pci_dev *pci = to_pci_dev(dev);
+ if (!azx_is_pm_ready(card))
+ return 0;
if (chip->driver_type == AZX_DRIVER_SKL)
pci_set_power_state(pci, PCI_D3hot);
@@ -1079,6 +1082,8 @@ static int azx_thaw_noirq(struct device *dev)
struct azx *chip = card->private_data;
struct pci_dev *pci = to_pci_dev(dev);
+ if (!azx_is_pm_ready(card))
+ return 0;
if (chip->driver_type == AZX_DRIVER_SKL)
pci_set_power_state(pci, PCI_D0);
@@ -1195,10 +1200,8 @@ static void azx_vs_set_state(struct pci_dev *pci,
if (!disabled) {
dev_info(chip->card->dev,
"Start delayed initialization\n");
- if (azx_probe_continue(chip) < 0) {
+ if (azx_probe_continue(chip) < 0)
dev_err(chip->card->dev, "initialization error\n");
- hda->init_failed = true;
- }
}
} else {
dev_info(chip->card->dev, "%s via vga_switcheroo\n",
@@ -1326,12 +1329,15 @@ static int register_vga_switcheroo(struct azx *chip)
/*
* destructor
*/
-static int azx_free(struct azx *chip)
+static void azx_free(struct azx *chip)
{
struct pci_dev *pci = chip->pci;
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
struct hdac_bus *bus = azx_bus(chip);
+ if (hda->freed)
+ return;
+
if (azx_has_pm_runtime(chip) && chip->running)
pm_runtime_get_noresume(&pci->dev);
chip->running = 0;
@@ -1349,9 +1355,9 @@ static int azx_free(struct azx *chip)
}
if (bus->chip_init) {
- azx_stop_chip(chip);
azx_clear_irq_pending(chip);
azx_stop_all_streams(chip);
+ azx_stop_chip(chip);
}
if (bus->irq >= 0)
@@ -1375,22 +1381,25 @@ static int azx_free(struct azx *chip)
if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT)
snd_hdac_i915_exit(bus);
- kfree(hda);
- return 0;
+ hda->freed = 1;
}
static int azx_dev_disconnect(struct snd_device *device)
{
struct azx *chip = device->device_data;
+ struct hdac_bus *bus = azx_bus(chip);
chip->bus.shutdown = 1;
+ cancel_work_sync(&bus->unsol_work);
+
return 0;
}
static int azx_dev_free(struct snd_device *device)
{
- return azx_free(device->device_data);
+ azx_free(device->device_data);
+ return 0;
}
#ifdef SUPPORT_VGA_SWITCHEROO
@@ -1705,7 +1714,7 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
if (err < 0)
return err;
- hda = kzalloc(sizeof(*hda), GFP_KERNEL);
+ hda = devm_kzalloc(&pci->dev, sizeof(*hda), GFP_KERNEL);
if (!hda) {
pci_disable_device(pci);
return -ENOMEM;
@@ -1750,7 +1759,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
err = azx_bus_init(chip, model[dev], &pci_hda_io_ops);
if (err < 0) {
- kfree(hda);
pci_disable_device(pci);
return err;
}
@@ -1942,7 +1950,7 @@ static int azx_first_init(struct azx *chip)
/* codec detection */
if (!azx_bus(chip)->codec_mask) {
dev_err(card->dev, "no codecs found!\n");
- return -ENODEV;
+ /* keep running the rest for the runtime PM */
}
if (azx_acquire_irq(chip, 0) < 0)
@@ -1964,24 +1972,15 @@ static void azx_firmware_cb(const struct firmware *fw, void *context)
{
struct snd_card *card = context;
struct azx *chip = card->private_data;
- struct pci_dev *pci = chip->pci;
-
- if (!fw) {
- dev_err(card->dev, "Cannot load firmware, aborting\n");
- goto error;
- }
- chip->fw = fw;
+ if (fw)
+ chip->fw = fw;
+ else
+ dev_err(card->dev, "Cannot load firmware, continue without patching\n");
if (!chip->disabled) {
/* continue probing */
- if (azx_probe_continue(chip))
- goto error;
+ azx_probe_continue(chip);
}
- return; /* OK */
-
- error:
- snd_card_free(card);
- pci_set_drvdata(pci, NULL);
}
#endif
@@ -2076,6 +2075,17 @@ static const struct hdac_io_ops pci_hda_io_ops = {
.dma_free_pages = dma_free_pages,
};
+/* Blacklist for skipping the whole probe:
+ * some HD-audio PCI entries are exposed without any codecs, and such devices
+ * should be ignored from the beginning.
+ */
+static const struct pci_device_id driver_blacklist[] = {
+ { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1043, 0x874f) }, /* ASUS ROG Zenith II / Strix */
+ { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb59) }, /* MSI TRX40 Creator */
+ { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb60) }, /* MSI TRX40 */
+ {}
+};
+
static const struct hda_controller_ops pci_hda_ops = {
.disable_msi_reset_irq = disable_msi_reset_irq,
.pcm_mmap_prepare = pcm_mmap_prepare,
@@ -2092,6 +2102,11 @@ static int azx_probe(struct pci_dev *pci,
bool schedule_probe;
int err;
+ if (pci_match_id(driver_blacklist, pci)) {
+ dev_info(&pci->dev, "Skipping the blacklisted device\n");
+ return -ENODEV;
+ }
+
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
@@ -2178,6 +2193,8 @@ static struct snd_pci_quirk power_save_blacklist[] = {
/* https://bugzilla.redhat.com/show_bug.cgi?id=1581607 */
SND_PCI_QUIRK(0x1558, 0x3501, "Clevo W35xSS_370SS", 0),
/* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1558, 0x6504, "Clevo W65_67SB", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
SND_PCI_QUIRK(0x1028, 0x0497, "Dell Precision T3600", 0),
/* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
/* Note the P55A-UD3 and Z87-D3HP share the subsys id for the HDA dev */
@@ -2278,9 +2295,11 @@ static int azx_probe_continue(struct azx *chip)
#endif
/* create codec instances */
- err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]);
- if (err < 0)
- goto out_free;
+ if (bus->codec_mask) {
+ err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]);
+ if (err < 0)
+ goto out_free;
+ }
#ifdef CONFIG_SND_HDA_PATCH_LOADER
if (chip->fw) {
@@ -2294,7 +2313,7 @@ static int azx_probe_continue(struct azx *chip)
#endif
}
#endif
- if ((probe_only[dev] & 1) == 0) {
+ if (bus->codec_mask && !(probe_only[dev] & 1)) {
err = azx_codec_configure(chip);
if (err < 0)
goto out_free;
@@ -2311,17 +2330,22 @@ static int azx_probe_continue(struct azx *chip)
set_default_power_save(chip);
- if (azx_has_pm_runtime(chip))
+ if (azx_has_pm_runtime(chip)) {
+ pm_runtime_use_autosuspend(&pci->dev);
pm_runtime_put_autosuspend(&pci->dev);
+ }
out_free:
- if (err < 0 || !hda->need_i915_power)
+ if (err < 0) {
+ azx_free(chip);
+ return err;
+ }
+
+ if (!hda->need_i915_power)
display_power(chip, false);
- if (err < 0)
- hda->init_failed = 1;
complete_all(&hda->probe_wait);
to_hda_bus(bus)->bus_probing = 0;
- return err;
+ return 0;
}
static void azx_remove(struct pci_dev *pci)
@@ -2428,9 +2452,28 @@ static const struct pci_device_id azx_ids[] = {
/* CometLake-H */
{ PCI_DEVICE(0x8086, 0x06C8),
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* CometLake-S */
+ { PCI_DEVICE(0x8086, 0xa3f0),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
/* Icelake */
{ PCI_DEVICE(0x8086, 0x34c8),
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Icelake-H */
+ { PCI_DEVICE(0x8086, 0x3dc8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Jasperlake */
+ { PCI_DEVICE(0x8086, 0x38c8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x4dc8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Tigerlake-H */
+ { PCI_DEVICE(0x8086, 0x43c8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Tigerlake */
+ { PCI_DEVICE(0x8086, 0xa0c8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x4b58),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
/* Broxton-P(Apollolake) */
{ PCI_DEVICE(0x8086, 0x5a98),
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON },
diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h
index 1468865e0342..9302c9e67950 100644
--- a/sound/pci/hda/hda_intel.h
+++ b/sound/pci/hda/hda_intel.h
@@ -28,6 +28,7 @@ struct hda_intel {
unsigned int need_eld_notify_link:1;
unsigned int vga_switcheroo_registered:1;
unsigned int init_failed:1; /* delayed init failed */
+ unsigned int freed:1; /* resources already released */
bool need_i915_power:1; /* the hda controller needs i915 power */
};
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 349a8312d06a..d249fe4098bc 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -138,6 +138,8 @@ int snd_hda_codec_reset(struct hda_codec *codec);
void snd_hda_codec_register(struct hda_codec *codec);
void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec);
+#define snd_hda_regmap_sync(codec) snd_hdac_regmap_sync(&(codec)->core)
+
enum {
HDA_VMUTE_OFF,
HDA_VMUTE_ON,
diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c
index fcc34417cbce..6dbe99131bc4 100644
--- a/sound/pci/hda/hda_sysfs.c
+++ b/sound/pci/hda/hda_sysfs.c
@@ -222,7 +222,7 @@ static ssize_t init_verbs_show(struct device *dev,
int i, len = 0;
mutex_lock(&codec->user_mutex);
snd_array_for_each(&codec->init_verbs, i, v) {
- len += snprintf(buf + len, PAGE_SIZE - len,
+ len += scnprintf(buf + len, PAGE_SIZE - len,
"0x%02x 0x%03x 0x%04x\n",
v->nid, v->verb, v->param);
}
@@ -272,7 +272,7 @@ static ssize_t hints_show(struct device *dev,
int i, len = 0;
mutex_lock(&codec->user_mutex);
snd_array_for_each(&codec->hints, i, hint) {
- len += snprintf(buf + len, PAGE_SIZE - len,
+ len += scnprintf(buf + len, PAGE_SIZE - len,
"%s = %s\n", hint->key, hint->val);
}
mutex_unlock(&codec->user_mutex);
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index bc9dd8e6fd86..c64895f99299 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -389,7 +389,7 @@ static int patch_ad1986a(struct hda_codec *codec)
{
int err;
struct ad198x_spec *spec;
- static hda_nid_t preferred_pairs[] = {
+ static const hda_nid_t preferred_pairs[] = {
0x1a, 0x03,
0x1b, 0x03,
0x1c, 0x04,
@@ -519,9 +519,9 @@ static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec)
static int patch_ad1983(struct hda_codec *codec)
{
+ static const hda_nid_t conn_0c[] = { 0x08 };
+ static const hda_nid_t conn_0d[] = { 0x09 };
struct ad198x_spec *spec;
- static hda_nid_t conn_0c[] = { 0x08 };
- static hda_nid_t conn_0d[] = { 0x09 };
int err;
err = alloc_ad_spec(codec);
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index c41865e1222c..08bf3c2888a0 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -1180,7 +1180,9 @@ static const struct snd_pci_quirk ca0132_quirks[] = {
SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI),
SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", QUIRK_R3DI),
SND_PCI_QUIRK(0x1458, 0xA036, "Gigabyte GA-Z170X-Gaming 7", QUIRK_R3DI),
+ SND_PCI_QUIRK(0x3842, 0x1038, "EVGA X99 Classified", QUIRK_R3DI),
SND_PCI_QUIRK(0x1102, 0x0013, "Recon3D", QUIRK_R3D),
+ SND_PCI_QUIRK(0x1102, 0x0018, "Recon3D", QUIRK_R3D),
SND_PCI_QUIRK(0x1102, 0x0051, "Sound Blaster AE-5", QUIRK_AE5),
{}
};
@@ -1809,13 +1811,14 @@ struct scp_msg {
static void dspio_clear_response_queue(struct hda_codec *codec)
{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
unsigned int dummy = 0;
- int status = -1;
+ int status;
/* clear all from the response queue */
do {
status = dspio_read(codec, &dummy);
- } while (status == 0);
+ } while (status == 0 && time_before(jiffies, timeout));
}
static int dspio_get_response_data(struct hda_codec *codec)
@@ -4668,7 +4671,7 @@ static int ca0132_alt_select_in(struct hda_codec *codec)
tmp = FLOAT_ONE;
break;
case QUIRK_AE5:
- ca0113_mmio_command_set(codec, 0x48, 0x28, 0x00);
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00);
tmp = FLOAT_THREE;
break;
default:
@@ -4714,7 +4717,7 @@ static int ca0132_alt_select_in(struct hda_codec *codec)
r3di_gpio_mic_set(codec, R3DI_REAR_MIC);
break;
case QUIRK_AE5:
- ca0113_mmio_command_set(codec, 0x48, 0x28, 0x00);
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00);
break;
default:
break;
@@ -4753,7 +4756,7 @@ static int ca0132_alt_select_in(struct hda_codec *codec)
tmp = FLOAT_ONE;
break;
case QUIRK_AE5:
- ca0113_mmio_command_set(codec, 0x48, 0x28, 0x3f);
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x3f);
tmp = FLOAT_THREE;
break;
default:
@@ -5745,6 +5748,11 @@ static int ca0132_switch_get(struct snd_kcontrol *kcontrol,
return 0;
}
+ if (nid == ZXR_HEADPHONE_GAIN) {
+ *valp = spec->zxr_gain_set;
+ return 0;
+ }
+
return 0;
}
@@ -7588,12 +7596,14 @@ static void ca0132_process_dsp_response(struct hda_codec *codec,
struct ca0132_spec *spec = codec->spec;
codec_dbg(codec, "ca0132_process_dsp_response\n");
+ snd_hda_power_up_pm(codec);
if (spec->wait_scp) {
if (dspio_get_response_data(codec) >= 0)
spec->wait_scp = 0;
}
dspio_clear_response_queue(codec);
+ snd_hda_power_down_pm(codec);
}
static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
@@ -7604,11 +7614,10 @@ static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
/* Delay enabling the HP amp, to let the mic-detection
* state machine run.
*/
- cancel_delayed_work_sync(&spec->unsol_hp_work);
- schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500));
tbl = snd_hda_jack_tbl_get(codec, cb->nid);
if (tbl)
tbl->block_report = 1;
+ schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500));
}
static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
@@ -7800,23 +7809,23 @@ static void sbz_region2_exit(struct hda_codec *codec)
static void sbz_set_pin_ctl_default(struct hda_codec *codec)
{
- hda_nid_t pins[5] = {0x0B, 0x0C, 0x0E, 0x12, 0x13};
+ static const hda_nid_t pins[] = {0x0B, 0x0C, 0x0E, 0x12, 0x13};
unsigned int i;
snd_hda_codec_write(codec, 0x11, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40);
- for (i = 0; i < 5; i++)
+ for (i = 0; i < ARRAY_SIZE(pins); i++)
snd_hda_codec_write(codec, pins[i], 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00);
}
static void ca0132_clear_unsolicited(struct hda_codec *codec)
{
- hda_nid_t pins[7] = {0x0B, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13};
+ static const hda_nid_t pins[] = {0x0B, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13};
unsigned int i;
- for (i = 0; i < 7; i++) {
+ for (i = 0; i < ARRAY_SIZE(pins); i++) {
snd_hda_codec_write(codec, pins[i], 0,
AC_VERB_SET_UNSOLICITED_ENABLE, 0x00);
}
@@ -7840,10 +7849,10 @@ static void sbz_gpio_shutdown_commands(struct hda_codec *codec, int dir,
static void zxr_dbpro_power_state_shutdown(struct hda_codec *codec)
{
- hda_nid_t pins[7] = {0x05, 0x0c, 0x09, 0x0e, 0x08, 0x11, 0x01};
+ static const hda_nid_t pins[] = {0x05, 0x0c, 0x09, 0x0e, 0x08, 0x11, 0x01};
unsigned int i;
- for (i = 0; i < 7; i++)
+ for (i = 0; i < ARRAY_SIZE(pins); i++)
snd_hda_codec_write(codec, pins[i], 0,
AC_VERB_SET_POWER_STATE, 0x03);
}
@@ -8454,12 +8463,25 @@ static void ca0132_reboot_notify(struct hda_codec *codec)
codec->patch_ops.free(codec);
}
+#ifdef CONFIG_PM
+static int ca0132_suspend(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+ return 0;
+}
+#endif
+
static const struct hda_codec_ops ca0132_patch_ops = {
.build_controls = ca0132_build_controls,
.build_pcms = ca0132_build_pcms,
.init = ca0132_init,
.free = ca0132_free,
.unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .suspend = ca0132_suspend,
+#endif
.reboot_notify = ca0132_reboot_notify,
};
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 968d3caab6ac..396b5503038a 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -116,7 +116,7 @@ static void cx_auto_parse_eapd(struct hda_codec *codec)
}
static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins,
- hda_nid_t *pins, bool on)
+ const hda_nid_t *pins, bool on)
{
int i;
for (i = 0; i < num_pins; i++) {
@@ -910,6 +910,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x103c, 0x837f, "HP ProBook 470 G5", CXT_FIXUP_MUTE_LED_GPIO),
SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x8402, "HP ProBook 645 G4", CXT_FIXUP_MUTE_LED_GPIO),
SND_PCI_QUIRK(0x103c, 0x8455, "HP Z2 G4", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x8456, "HP Z2 G4 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x8457, "HP Z2 G4 mini", CXT_FIXUP_HP_MIC_NO_PRESENCE),
@@ -921,6 +922,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21d2, "Lenovo T420s", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD),
@@ -958,10 +960,10 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = {
static void add_cx5051_fake_mutes(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- static hda_nid_t out_nids[] = {
+ static const hda_nid_t out_nids[] = {
0x10, 0x11, 0
};
- hda_nid_t *p;
+ const hda_nid_t *p;
for (p = out_nids; *p; p++)
snd_hda_override_amp_caps(codec, *p, HDA_OUTPUT,
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index ca0404edd939..499e671bc2cc 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -31,6 +31,7 @@
#include <sound/hda_codec.h>
#include "hda_local.h"
#include "hda_jack.h"
+#include "hda_controller.h"
static bool static_hdmi_pcm;
module_param(static_hdmi_pcm, bool, 0644);
@@ -45,10 +46,12 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
((codec)->core.vendor_id == 0x80862800))
#define is_cannonlake(codec) ((codec)->core.vendor_id == 0x8086280c)
#define is_icelake(codec) ((codec)->core.vendor_id == 0x8086280f)
+#define is_tigerlake(codec) ((codec)->core.vendor_id == 0x80862812)
#define is_haswell_plus(codec) (is_haswell(codec) || is_broadwell(codec) \
|| is_skylake(codec) || is_broxton(codec) \
|| is_kabylake(codec) || is_geminilake(codec) \
- || is_cannonlake(codec) || is_icelake(codec))
+ || is_cannonlake(codec) || is_icelake(codec) \
+ || is_tigerlake(codec))
#define is_valleyview(codec) ((codec)->core.vendor_id == 0x80862882)
#define is_cherryview(codec) ((codec)->core.vendor_id == 0x80862883)
#define is_valleyview_plus(codec) (is_valleyview(codec) || is_cherryview(codec))
@@ -1226,6 +1229,10 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
per_pin->cvt_nid = per_cvt->cvt_nid;
hinfo->nid = per_cvt->cvt_nid;
+ /* flip stripe flag for the assigned stream if supported */
+ if (get_wcaps(codec, per_cvt->cvt_nid) & AC_WCAP_STRIPE)
+ azx_stream(get_azx_dev(substream))->stripe = 1;
+
snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id);
snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
AC_VERB_SET_CONNECT_SEL,
@@ -1794,33 +1801,43 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
static int hdmi_parse_codec(struct hda_codec *codec)
{
- hda_nid_t nid;
+ hda_nid_t start_nid;
+ unsigned int caps;
int i, nodes;
- nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &nid);
- if (!nid || nodes < 0) {
+ nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &start_nid);
+ if (!start_nid || nodes < 0) {
codec_warn(codec, "HDMI: failed to get afg sub nodes\n");
return -EINVAL;
}
- for (i = 0; i < nodes; i++, nid++) {
- unsigned int caps;
- unsigned int type;
+ /*
+ * hdmi_add_pin() assumes total amount of converters to
+ * be known, so first discover all converters
+ */
+ for (i = 0; i < nodes; i++) {
+ hda_nid_t nid = start_nid + i;
caps = get_wcaps(codec, nid);
- type = get_wcaps_type(caps);
if (!(caps & AC_WCAP_DIGITAL))
continue;
- switch (type) {
- case AC_WID_AUD_OUT:
+ if (get_wcaps_type(caps) == AC_WID_AUD_OUT)
hdmi_add_cvt(codec, nid);
- break;
- case AC_WID_PIN:
+ }
+
+ /* discover audio pins */
+ for (i = 0; i < nodes; i++) {
+ hda_nid_t nid = start_nid + i;
+
+ caps = get_wcaps(codec, nid);
+
+ if (!(caps & AC_WCAP_DIGITAL))
+ continue;
+
+ if (get_wcaps_type(caps) == AC_WID_PIN)
hdmi_add_pin(codec, nid);
- break;
- }
}
return 0;
@@ -1838,8 +1855,10 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
/* Add sanity check to pass klockwork check.
* This should never happen.
*/
- if (WARN_ON(spdif == NULL))
+ if (WARN_ON(spdif == NULL)) {
+ mutex_unlock(&codec->spdif_mutex);
return true;
+ }
non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO);
mutex_unlock(&codec->spdif_mutex);
return non_pcm;
@@ -1964,6 +1983,8 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
per_cvt->assigned = 0;
hinfo->nid = 0;
+ azx_stream(get_azx_dev(substream))->stripe = 0;
+
mutex_lock(&spec->pcm_lock);
snd_hda_spdif_ctls_unassign(codec, pcm_idx);
clear_bit(pcm_idx, &spec->pcm_in_use);
@@ -2207,7 +2228,9 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ struct hdmi_eld *pin_eld = &per_pin->sink_eld;
+ pin_eld->eld_valid = false;
hdmi_present_sense(per_pin, 0);
}
@@ -2322,7 +2345,7 @@ static int generic_hdmi_resume(struct hda_codec *codec)
int pin_idx;
codec->patch_ops.init(codec);
- regcache_sync(codec->core.regmap);
+ snd_hda_regmap_sync(codec);
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
@@ -2632,9 +2655,12 @@ static int alloc_intel_hdmi(struct hda_codec *codec)
/* parse and post-process for Intel codecs */
static int parse_intel_hdmi(struct hda_codec *codec)
{
- int err;
+ int err, retries = 3;
+
+ do {
+ err = hdmi_parse_codec(codec);
+ } while (err < 0 && retries--);
- err = hdmi_parse_codec(codec);
if (err < 0) {
generic_spec_free(codec);
return err;
@@ -2698,6 +2724,18 @@ static int patch_i915_icl_hdmi(struct hda_codec *codec)
return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map));
}
+static int patch_i915_tgl_hdmi(struct hda_codec *codec)
+{
+ /*
+ * pin to port mapping table where the value indicate the pin number and
+ * the index indicate the port number with 1 base.
+ */
+ static const int map[] = {0x4, 0x6, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+
+ return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map));
+}
+
+
/* Intel Baytrail and Braswell; with eld notifier */
static int patch_i915_byt_hdmi(struct hda_codec *codec)
{
@@ -3931,6 +3969,11 @@ HDA_CODEC_ENTRY(0x10de0095, "GPU 95 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0097, "GPU 97 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0098, "GPU 98 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0099, "GPU 99 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de009a, "GPU 9a HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de009d, "GPU 9d HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de009e, "GPU 9e HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de009f, "GPU 9f HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de00a0, "GPU a0 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch),
HDA_CODEC_ENTRY(0x10de8067, "MCP67/68 HDMI", patch_nvhdmi_2ch),
HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi),
@@ -3953,6 +3996,8 @@ HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x8086280c, "Cannonlake HDMI", patch_i915_glk_hdmi),
HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi),
HDA_CODEC_ENTRY(0x8086280f, "Icelake HDMI", patch_i915_icl_hdmi),
+HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI", patch_i915_tgl_hdmi),
+HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi),
HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi),
HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 26249c607f2c..d496ad64a880 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -81,11 +81,20 @@ struct alc_spec {
/* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */
int mute_led_polarity;
+ int micmute_led_polarity;
hda_nid_t mute_led_nid;
hda_nid_t cap_mute_led_nid;
unsigned int gpio_mute_led_mask;
unsigned int gpio_mic_led_mask;
+ unsigned int mute_led_coef_idx;
+ unsigned int mute_led_coefbit_mask;
+ unsigned int mute_led_coefbit_on;
+ unsigned int mute_led_coefbit_off;
+ unsigned int mic_led_coef_idx;
+ unsigned int mic_led_coefbit_mask;
+ unsigned int mic_led_coefbit_on;
+ unsigned int mic_led_coefbit_off;
hda_nid_t headset_mic_pin;
hda_nid_t headphone_mic_pin;
@@ -107,6 +116,7 @@ struct alc_spec {
unsigned int done_hp_init:1;
unsigned int no_shutup_pins:1;
unsigned int ultra_low_power:1;
+ unsigned int has_hs_key:1;
/* for PLL fix */
hda_nid_t pll_nid;
@@ -368,12 +378,14 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
case 0x10ec0233:
case 0x10ec0235:
case 0x10ec0236:
+ case 0x10ec0245:
case 0x10ec0255:
case 0x10ec0256:
case 0x10ec0257:
case 0x10ec0282:
case 0x10ec0283:
case 0x10ec0286:
+ case 0x10ec0287:
case 0x10ec0288:
case 0x10ec0285:
case 0x10ec0298:
@@ -409,6 +421,10 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
case 0x10ec0672:
alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */
break;
+ case 0x10ec0222:
+ case 0x10ec0623:
+ alc_update_coef_idx(codec, 0x19, 1<<13, 0);
+ break;
case 0x10ec0668:
alc_update_coef_idx(codec, 0x7, 3<<13, 0);
break;
@@ -424,6 +440,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
break;
case 0x10ec0899:
case 0x10ec0900:
+ case 0x10ec0b00:
case 0x10ec1168:
case 0x10ec1220:
alc_update_coef_idx(codec, 0x7, 1<<1, 0);
@@ -458,10 +475,10 @@ static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on)
static void alc_auto_setup_eapd(struct hda_codec *codec, bool on)
{
/* We currently only handle front, HP */
- static hda_nid_t pins[] = {
+ static const hda_nid_t pins[] = {
0x0f, 0x10, 0x14, 0x15, 0x17, 0
};
- hda_nid_t *p;
+ const hda_nid_t *p;
for (p = pins; *p; p++)
set_eapd(codec, *p, on);
}
@@ -495,6 +512,7 @@ static void alc_shutup_pins(struct hda_codec *codec)
struct alc_spec *spec = codec->spec;
switch (codec->core.vendor_id) {
+ case 0x10ec0283:
case 0x10ec0286:
case 0x10ec0288:
case 0x10ec0298:
@@ -782,9 +800,11 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports)
{
if (!alc_subsystem_id(codec, ports)) {
struct alc_spec *spec = codec->spec;
- codec_dbg(codec,
- "realtek: Enable default setup for auto mode as fallback\n");
- spec->init_amp = ALC_INIT_DEFAULT;
+ if (spec->init_amp == ALC_INIT_UNDEFINED) {
+ codec_dbg(codec,
+ "realtek: Enable default setup for auto mode as fallback\n");
+ spec->init_amp = ALC_INIT_DEFAULT;
+ }
}
}
@@ -898,7 +918,7 @@ static int alc_resume(struct hda_codec *codec)
if (!spec->no_depop_delay)
msleep(150); /* to avoid pop noise */
codec->patch_ops.init(codec);
- regcache_sync(codec->core.regmap);
+ snd_hda_regmap_sync(codec);
hda_call_check_power_status(codec, 0x01);
return 0;
}
@@ -940,7 +960,7 @@ struct alc_codec_rename_pci_table {
const char *name;
};
-static struct alc_codec_rename_table rename_tbl[] = {
+static const struct alc_codec_rename_table rename_tbl[] = {
{ 0x10ec0221, 0xf00f, 0x1003, "ALC231" },
{ 0x10ec0269, 0xfff0, 0x3010, "ALC277" },
{ 0x10ec0269, 0xf0f0, 0x2010, "ALC259" },
@@ -961,7 +981,7 @@ static struct alc_codec_rename_table rename_tbl[] = {
{ } /* terminator */
};
-static struct alc_codec_rename_pci_table rename_pci_tbl[] = {
+static const struct alc_codec_rename_pci_table rename_pci_tbl[] = {
{ 0x10ec0280, 0x1028, 0, "ALC3220" },
{ 0x10ec0282, 0x1028, 0, "ALC3221" },
{ 0x10ec0283, 0x1028, 0, "ALC3223" },
@@ -1929,19 +1949,19 @@ static void alc889_fixup_dac_route(struct hda_codec *codec,
{
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
/* fake the connections during parsing the tree */
- hda_nid_t conn1[2] = { 0x0c, 0x0d };
- hda_nid_t conn2[2] = { 0x0e, 0x0f };
- snd_hda_override_conn_list(codec, 0x14, 2, conn1);
- snd_hda_override_conn_list(codec, 0x15, 2, conn1);
- snd_hda_override_conn_list(codec, 0x18, 2, conn2);
- snd_hda_override_conn_list(codec, 0x1a, 2, conn2);
+ static const hda_nid_t conn1[] = { 0x0c, 0x0d };
+ static const hda_nid_t conn2[] = { 0x0e, 0x0f };
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn2), conn2);
+ snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn2), conn2);
} else if (action == HDA_FIXUP_ACT_PROBE) {
/* restore the connections */
- hda_nid_t conn[5] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 };
- snd_hda_override_conn_list(codec, 0x14, 5, conn);
- snd_hda_override_conn_list(codec, 0x15, 5, conn);
- snd_hda_override_conn_list(codec, 0x18, 5, conn);
- snd_hda_override_conn_list(codec, 0x1a, 5, conn);
+ static const hda_nid_t conn[] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 };
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn);
+ snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn);
+ snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn), conn);
+ snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn), conn);
}
}
@@ -1949,8 +1969,8 @@ static void alc889_fixup_dac_route(struct hda_codec *codec,
static void alc889_fixup_mbp_vref(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
+ static const hda_nid_t nids[] = { 0x14, 0x15, 0x19 };
struct alc_spec *spec = codec->spec;
- static hda_nid_t nids[3] = { 0x14, 0x15, 0x19 };
int i;
if (action != HDA_FIXUP_ACT_INIT)
@@ -1986,7 +2006,7 @@ static void alc889_fixup_mac_pins(struct hda_codec *codec,
static void alc889_fixup_imac91_vref(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
- static hda_nid_t nids[2] = { 0x18, 0x1a };
+ static const hda_nid_t nids[] = { 0x18, 0x1a };
if (action == HDA_FIXUP_ACT_INIT)
alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
@@ -1996,7 +2016,7 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec,
static void alc889_fixup_mba11_vref(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
- static hda_nid_t nids[1] = { 0x18 };
+ static const hda_nid_t nids[] = { 0x18 };
if (action == HDA_FIXUP_ACT_INIT)
alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
@@ -2006,7 +2026,7 @@ static void alc889_fixup_mba11_vref(struct hda_codec *codec,
static void alc889_fixup_mba21_vref(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
- static hda_nid_t nids[2] = { 0x18, 0x19 };
+ static const hda_nid_t nids[] = { 0x18, 0x19 };
if (action == HDA_FIXUP_ACT_INIT)
alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
@@ -2088,7 +2108,7 @@ static void alc1220_fixup_clevo_p950(struct hda_codec *codec,
const struct hda_fixup *fix,
int action)
{
- hda_nid_t conn1[1] = { 0x0c };
+ static const hda_nid_t conn1[] = { 0x0c };
if (action != HDA_FIXUP_ACT_PRE_PROBE)
return;
@@ -2097,8 +2117,8 @@ static void alc1220_fixup_clevo_p950(struct hda_codec *codec,
/* We therefore want to make sure 0x14 (front headphone) and
* 0x1b (speakers) use the stereo DAC 0x02
*/
- snd_hda_override_conn_list(codec, 0x14, 1, conn1);
- snd_hda_override_conn_list(codec, 0x1b, 1, conn1);
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1);
}
static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
@@ -2438,6 +2458,13 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD),
SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE),
SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x1275, "MSI-GL63", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x1276, "MSI-GL73", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x1293, "MSI-GP65", ALC1220_FIXUP_CLEVO_P950),
SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD),
SND_PCI_QUIRK(0x1462, 0xda57, "MSI Z270-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS),
SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3),
@@ -2449,6 +2476,9 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x1558, 0x97e1, "Clevo P970[ER][CDFN]", ALC1220_FIXUP_CLEVO_P950),
SND_PCI_QUIRK(0x1558, 0x65d1, "Clevo PB51[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x50d3, "Clevo PC50[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x70d1, "Clevo PC70[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x7714, "Clevo X170", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD),
SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530),
@@ -2519,6 +2549,7 @@ static int patch_alc882(struct hda_codec *codec)
case 0x10ec0882:
case 0x10ec0885:
case 0x10ec0900:
+ case 0x10ec0b00:
case 0x10ec1220:
break;
default:
@@ -2919,6 +2950,7 @@ enum {
ALC269_TYPE_ALC225,
ALC269_TYPE_ALC294,
ALC269_TYPE_ALC300,
+ ALC269_TYPE_ALC623,
ALC269_TYPE_ALC700,
};
@@ -2954,6 +2986,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
case ALC269_TYPE_ALC225:
case ALC269_TYPE_ALC294:
case ALC269_TYPE_ALC300:
+ case ALC269_TYPE_ALC623:
case ALC269_TYPE_ALC700:
ssids = alc269_ssids;
break;
@@ -2965,6 +2998,107 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
return alc_parse_auto_config(codec, alc269_ignore, ssids);
}
+static const struct hda_jack_keymap alc_headset_btn_keymap[] = {
+ { SND_JACK_BTN_0, KEY_PLAYPAUSE },
+ { SND_JACK_BTN_1, KEY_VOICECOMMAND },
+ { SND_JACK_BTN_2, KEY_VOLUMEUP },
+ { SND_JACK_BTN_3, KEY_VOLUMEDOWN },
+ {}
+};
+
+static void alc_headset_btn_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ int report = 0;
+
+ if (jack->unsol_res & (7 << 13))
+ report |= SND_JACK_BTN_0;
+
+ if (jack->unsol_res & (1 << 16 | 3 << 8))
+ report |= SND_JACK_BTN_1;
+
+ /* Volume up key */
+ if (jack->unsol_res & (7 << 23))
+ report |= SND_JACK_BTN_2;
+
+ /* Volume down key */
+ if (jack->unsol_res & (7 << 10))
+ report |= SND_JACK_BTN_3;
+
+ jack->jack->button_state = report;
+}
+
+static void alc_disable_headset_jack_key(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (!spec->has_hs_key)
+ return;
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_write_coef_idx(codec, 0x48, 0x0);
+ alc_update_coef_idx(codec, 0x49, 0x0045, 0x0);
+ alc_update_coef_idx(codec, 0x44, 0x0045 << 8, 0x0);
+ break;
+ case 0x10ec0236:
+ case 0x10ec0256:
+ alc_write_coef_idx(codec, 0x48, 0x0);
+ alc_update_coef_idx(codec, 0x49, 0x0045, 0x0);
+ break;
+ }
+}
+
+static void alc_enable_headset_jack_key(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (!spec->has_hs_key)
+ return;
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_write_coef_idx(codec, 0x48, 0xd011);
+ alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045);
+ alc_update_coef_idx(codec, 0x44, 0x007f << 8, 0x0045 << 8);
+ break;
+ case 0x10ec0236:
+ case 0x10ec0256:
+ alc_write_coef_idx(codec, 0x48, 0xd011);
+ alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045);
+ break;
+ }
+}
+
+static void alc_fixup_headset_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->has_hs_key = 1;
+ snd_hda_jack_detect_enable_callback(codec, 0x55,
+ alc_headset_btn_callback);
+ snd_hda_jack_add_kctl(codec, 0x55, "Headset Jack", false,
+ SND_JACK_HEADSET, alc_headset_btn_keymap);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_enable_headset_jack_key(codec);
+ break;
+ }
+}
+
static void alc269vb_toggle_power_output(struct hda_codec *codec, int power_up)
{
alc_update_coef_idx(codec, 0x04, 1 << 11, power_up ? (1 << 11) : 0);
@@ -2983,7 +3117,7 @@ static void alc269_shutup(struct hda_codec *codec)
alc_shutup_pins(codec);
}
-static struct coef_fw alc282_coefs[] = {
+static const struct coef_fw alc282_coefs[] = {
WRITE_COEF(0x03, 0x0002), /* Power Down Control */
UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */
WRITE_COEF(0x07, 0x0200), /* DMIC control */
@@ -3095,7 +3229,7 @@ static void alc282_shutup(struct hda_codec *codec)
alc_write_coef_idx(codec, 0x78, coef78);
}
-static struct coef_fw alc283_coefs[] = {
+static const struct coef_fw alc283_coefs[] = {
WRITE_COEF(0x03, 0x0002), /* Power Down Control */
UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */
WRITE_COEF(0x07, 0x0200), /* DMIC control */
@@ -3252,7 +3386,13 @@ static void alc256_init(struct hda_codec *codec)
alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */
alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */
alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15);
- alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/
+ /*
+ * Expose headphone mic (or possibly Line In on some machines) instead
+ * of PC Beep on 1Ah, and disable 1Ah loopback for all outputs. See
+ * Documentation/sound/hd-audio/realtek-pc-beep.rst for details of
+ * this register.
+ */
+ alc_write_coef_idx(codec, 0x36, 0x5757);
}
static void alc256_shutup(struct hda_codec *codec)
@@ -3355,6 +3495,8 @@ static void alc225_shutup(struct hda_codec *codec)
if (!hp_pin)
hp_pin = 0x21;
+
+ alc_disable_headset_jack_key(codec);
/* 3k pull low control for Headset jack. */
alc_update_coef_idx(codec, 0x4a, 0, 3 << 10);
@@ -3394,6 +3536,9 @@ static void alc225_shutup(struct hda_codec *codec)
alc_update_coef_idx(codec, 0x4a, 3<<4, 2<<4);
msleep(30);
}
+
+ alc_update_coef_idx(codec, 0x4a, 3 << 10, 0);
+ alc_enable_headset_jack_key(codec);
}
static void alc_default_init(struct hda_codec *codec)
@@ -3588,8 +3733,8 @@ static void alc5505_dsp_init(struct hda_codec *codec)
}
#ifdef HALT_REALTEK_ALC5505
-#define alc5505_dsp_suspend(codec) /* NOP */
-#define alc5505_dsp_resume(codec) /* NOP */
+#define alc5505_dsp_suspend(codec) do { } while (0) /* NOP */
+#define alc5505_dsp_resume(codec) do { } while (0) /* NOP */
#else
#define alc5505_dsp_suspend(codec) alc5505_dsp_halt(codec)
#define alc5505_dsp_resume(codec) alc5505_dsp_back_from_halt(codec)
@@ -3625,7 +3770,7 @@ static int alc269_resume(struct hda_codec *codec)
msleep(200);
}
- regcache_sync(codec->core.regmap);
+ snd_hda_regmap_sync(codec);
hda_call_check_power_status(codec, 0x01);
/* on some machine, the BIOS will clear the codec gpio data when enter
@@ -3936,11 +4081,9 @@ static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec,
/* update LED status via GPIO */
static void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask,
- bool enabled)
+ int polarity, bool enabled)
{
- struct alc_spec *spec = codec->spec;
-
- if (spec->mute_led_polarity)
+ if (polarity)
enabled = !enabled;
alc_update_gpio_data(codec, mask, !enabled); /* muted -> LED on */
}
@@ -3951,7 +4094,8 @@ static void alc_fixup_gpio_mute_hook(void *private_data, int enabled)
struct hda_codec *codec = private_data;
struct alc_spec *spec = codec->spec;
- alc_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled);
+ alc_update_gpio_led(codec, spec->gpio_mute_led_mask,
+ spec->mute_led_polarity, enabled);
}
/* turn on/off mic-mute LED via GPIO per capture hook */
@@ -3960,6 +4104,7 @@ static void alc_gpio_micmute_update(struct hda_codec *codec)
struct alc_spec *spec = codec->spec;
alc_update_gpio_led(codec, spec->gpio_mic_led_mask,
+ spec->micmute_led_polarity,
spec->gen.micmute_led.led_value);
}
@@ -3991,6 +4136,12 @@ static void alc269_fixup_hp_gpio_led(struct hda_codec *codec,
alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10);
}
+static void alc285_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_hp_gpio_led(codec, action, 0x04, 0x00);
+}
+
static void alc286_fixup_hp_gpio_led(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -4045,6 +4196,111 @@ static void alc280_fixup_hp_gpio4(struct hda_codec *codec,
}
}
+/* update mute-LED according to the speaker mute state via COEF bit */
+static void alc_fixup_mute_led_coefbit_hook(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->mute_led_polarity)
+ enabled = !enabled;
+
+ /* temporarily power up/down for setting COEF bit */
+ enabled ? alc_update_coef_idx(codec, spec->mute_led_coef_idx,
+ spec->mute_led_coefbit_mask, spec->mute_led_coefbit_off) :
+ alc_update_coef_idx(codec, spec->mute_led_coef_idx,
+ spec->mute_led_coefbit_mask, spec->mute_led_coefbit_on);
+}
+
+static void alc285_fixup_hp_mute_led_coefbit(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_coef_idx = 0x0b;
+ spec->mute_led_coefbit_mask = 1<<3;
+ spec->mute_led_coefbit_on = 1<<3;
+ spec->mute_led_coefbit_off = 0;
+ spec->gen.vmaster_mute.hook = alc_fixup_mute_led_coefbit_hook;
+ spec->gen.vmaster_mute_enum = 1;
+ }
+}
+
+static void alc236_fixup_hp_mute_led_coefbit(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_coef_idx = 0x34;
+ spec->mute_led_coefbit_mask = 1<<5;
+ spec->mute_led_coefbit_on = 0;
+ spec->mute_led_coefbit_off = 1<<5;
+ spec->gen.vmaster_mute.hook = alc_fixup_mute_led_coefbit_hook;
+ spec->gen.vmaster_mute_enum = 1;
+ }
+}
+
+/* turn on/off mic-mute LED per capture hook by coef bit */
+static void alc_hp_cap_micmute_update(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->gen.micmute_led.led_value)
+ alc_update_coef_idx(codec, spec->mic_led_coef_idx,
+ spec->mic_led_coefbit_mask, spec->mic_led_coefbit_on);
+ else
+ alc_update_coef_idx(codec, spec->mic_led_coef_idx,
+ spec->mic_led_coefbit_mask, spec->mic_led_coefbit_off);
+}
+
+static void alc285_fixup_hp_coef_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mic_led_coef_idx = 0x19;
+ spec->mic_led_coefbit_mask = 1<<13;
+ spec->mic_led_coefbit_on = 1<<13;
+ spec->mic_led_coefbit_off = 0;
+ snd_hda_gen_add_micmute_led(codec, alc_hp_cap_micmute_update);
+ }
+}
+
+static void alc236_fixup_hp_coef_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mic_led_coef_idx = 0x35;
+ spec->mic_led_coefbit_mask = 3<<2;
+ spec->mic_led_coefbit_on = 2<<2;
+ spec->mic_led_coefbit_off = 1<<2;
+ snd_hda_gen_add_micmute_led(codec, alc_hp_cap_micmute_update);
+ }
+}
+
+static void alc285_fixup_hp_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc285_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc285_fixup_hp_coef_micmute_led(codec, fix, action);
+}
+
+static void alc236_fixup_hp_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc236_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc236_fixup_hp_coef_micmute_led(codec, fix, action);
+}
+
#if IS_REACHABLE(CONFIG_INPUT)
static void gpio2_mic_hotkey_event(struct hda_codec *codec,
struct hda_jack_callback *event)
@@ -4134,6 +4390,7 @@ static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec,
{
struct alc_spec *spec = codec->spec;
+ spec->micmute_led_polarity = 1;
alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->init_amp = ALC_INIT_DEFAULT;
@@ -4171,7 +4428,7 @@ static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
}
}
-static struct coef_fw alc225_pre_hsmode[] = {
+static const struct coef_fw alc225_pre_hsmode[] = {
UPDATE_COEF(0x4a, 1<<8, 0),
UPDATE_COEFEX(0x57, 0x05, 1<<14, 0),
UPDATE_COEF(0x63, 3<<14, 3<<14),
@@ -4184,7 +4441,7 @@ static struct coef_fw alc225_pre_hsmode[] = {
static void alc_headset_mode_unplugged(struct hda_codec *codec)
{
- static struct coef_fw coef0255[] = {
+ static const 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)*/
@@ -4192,7 +4449,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
WRITE_COEFEX(0x57, 0x03, 0x8aa6), /* Direct Drive HP Amp control */
{}
};
- static struct coef_fw coef0256[] = {
+ static const struct coef_fw coef0256[] = {
WRITE_COEF(0x1b, 0x0c4b), /* LDO and MISC control */
WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */
WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */
@@ -4200,7 +4457,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/
{}
};
- static struct coef_fw coef0233[] = {
+ static const struct coef_fw coef0233[] = {
WRITE_COEF(0x1b, 0x0c0b),
WRITE_COEF(0x45, 0xc429),
UPDATE_COEF(0x35, 0x4000, 0),
@@ -4210,7 +4467,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
WRITE_COEF(0x32, 0x42a3),
{}
};
- static struct coef_fw coef0288[] = {
+ static const struct coef_fw coef0288[] = {
UPDATE_COEF(0x4f, 0xfcc0, 0xc400),
UPDATE_COEF(0x50, 0x2000, 0x2000),
UPDATE_COEF(0x56, 0x0006, 0x0006),
@@ -4218,18 +4475,18 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
UPDATE_COEF(0x67, 0x2000, 0),
{}
};
- static struct coef_fw coef0298[] = {
+ static const struct coef_fw coef0298[] = {
UPDATE_COEF(0x19, 0x1300, 0x0300),
{}
};
- static struct coef_fw coef0292[] = {
+ static const struct coef_fw coef0292[] = {
WRITE_COEF(0x76, 0x000e),
WRITE_COEF(0x6c, 0x2400),
WRITE_COEF(0x18, 0x7308),
WRITE_COEF(0x6b, 0xc429),
{}
};
- static struct coef_fw coef0293[] = {
+ static const struct coef_fw coef0293[] = {
UPDATE_COEF(0x10, 7<<8, 6<<8), /* SET Line1 JD to 0 */
UPDATE_COEFEX(0x57, 0x05, 1<<15|1<<13, 0x0), /* SET charge pump by verb */
UPDATE_COEFEX(0x57, 0x03, 1<<10, 1<<10), /* SET EN_OSW to 1 */
@@ -4238,16 +4495,16 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */
{}
};
- static struct coef_fw coef0668[] = {
+ static const struct coef_fw coef0668[] = {
WRITE_COEF(0x15, 0x0d40),
WRITE_COEF(0xb7, 0x802b),
{}
};
- static struct coef_fw coef0225[] = {
+ static const struct coef_fw coef0225[] = {
UPDATE_COEF(0x63, 3<<14, 0),
{}
};
- static struct coef_fw coef0274[] = {
+ static const struct coef_fw coef0274[] = {
UPDATE_COEF(0x4a, 0x0100, 0),
UPDATE_COEFEX(0x57, 0x05, 0x4000, 0),
UPDATE_COEF(0x6b, 0xf000, 0x5000),
@@ -4312,25 +4569,25 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
hda_nid_t mic_pin)
{
- static struct coef_fw coef0255[] = {
+ static const struct coef_fw coef0255[] = {
WRITE_COEFEX(0x57, 0x03, 0x8aa6),
WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */
{}
};
- static struct coef_fw coef0256[] = {
+ static const struct coef_fw coef0256[] = {
UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14), /* Direct Drive HP Amp control(Set to verb control)*/
WRITE_COEFEX(0x57, 0x03, 0x09a3),
WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */
{}
};
- static struct coef_fw coef0233[] = {
+ static const struct coef_fw coef0233[] = {
UPDATE_COEF(0x35, 0, 1<<14),
WRITE_COEF(0x06, 0x2100),
WRITE_COEF(0x1a, 0x0021),
WRITE_COEF(0x26, 0x008c),
{}
};
- static struct coef_fw coef0288[] = {
+ static const struct coef_fw coef0288[] = {
UPDATE_COEF(0x4f, 0x00c0, 0),
UPDATE_COEF(0x50, 0x2000, 0),
UPDATE_COEF(0x56, 0x0006, 0),
@@ -4339,30 +4596,30 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
UPDATE_COEF(0x67, 0x2000, 0x2000),
{}
};
- static struct coef_fw coef0292[] = {
+ static const struct coef_fw coef0292[] = {
WRITE_COEF(0x19, 0xa208),
WRITE_COEF(0x2e, 0xacf0),
{}
};
- static struct coef_fw coef0293[] = {
+ static const struct coef_fw coef0293[] = {
UPDATE_COEFEX(0x57, 0x05, 0, 1<<15|1<<13), /* SET charge pump by verb */
UPDATE_COEFEX(0x57, 0x03, 1<<10, 0), /* SET EN_OSW to 0 */
UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */
{}
};
- static struct coef_fw coef0688[] = {
+ static const struct coef_fw coef0688[] = {
WRITE_COEF(0xb7, 0x802b),
WRITE_COEF(0xb5, 0x1040),
UPDATE_COEF(0xc3, 0, 1<<12),
{}
};
- static struct coef_fw coef0225[] = {
+ static const struct coef_fw coef0225[] = {
UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14),
UPDATE_COEF(0x4a, 3<<4, 2<<4),
UPDATE_COEF(0x63, 3<<14, 0),
{}
};
- static struct coef_fw coef0274[] = {
+ static const struct coef_fw coef0274[] = {
UPDATE_COEFEX(0x57, 0x05, 0x4000, 0x4000),
UPDATE_COEF(0x4a, 0x0010, 0),
UPDATE_COEF(0x6b, 0xf000, 0),
@@ -4448,7 +4705,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
static void alc_headset_mode_default(struct hda_codec *codec)
{
- static struct coef_fw coef0225[] = {
+ static const struct coef_fw coef0225[] = {
UPDATE_COEF(0x45, 0x3f<<10, 0x30<<10),
UPDATE_COEF(0x45, 0x3f<<10, 0x31<<10),
UPDATE_COEF(0x49, 3<<8, 0<<8),
@@ -4457,14 +4714,14 @@ static void alc_headset_mode_default(struct hda_codec *codec)
UPDATE_COEF(0x67, 0xf000, 0x3000),
{}
};
- static struct coef_fw coef0255[] = {
+ static const struct coef_fw coef0255[] = {
WRITE_COEF(0x45, 0xc089),
WRITE_COEF(0x45, 0xc489),
WRITE_COEFEX(0x57, 0x03, 0x8ea6),
WRITE_COEF(0x49, 0x0049),
{}
};
- static struct coef_fw coef0256[] = {
+ static const struct coef_fw coef0256[] = {
WRITE_COEF(0x45, 0xc489),
WRITE_COEFEX(0x57, 0x03, 0x0da3),
WRITE_COEF(0x49, 0x0049),
@@ -4472,12 +4729,12 @@ static void alc_headset_mode_default(struct hda_codec *codec)
WRITE_COEF(0x06, 0x6100),
{}
};
- static struct coef_fw coef0233[] = {
+ static const struct coef_fw coef0233[] = {
WRITE_COEF(0x06, 0x2100),
WRITE_COEF(0x32, 0x4ea3),
{}
};
- static struct coef_fw coef0288[] = {
+ static const struct coef_fw coef0288[] = {
UPDATE_COEF(0x4f, 0xfcc0, 0xc400), /* Set to TRS type */
UPDATE_COEF(0x50, 0x2000, 0x2000),
UPDATE_COEF(0x56, 0x0006, 0x0006),
@@ -4485,26 +4742,26 @@ static void alc_headset_mode_default(struct hda_codec *codec)
UPDATE_COEF(0x67, 0x2000, 0),
{}
};
- static struct coef_fw coef0292[] = {
+ static const struct coef_fw coef0292[] = {
WRITE_COEF(0x76, 0x000e),
WRITE_COEF(0x6c, 0x2400),
WRITE_COEF(0x6b, 0xc429),
WRITE_COEF(0x18, 0x7308),
{}
};
- static struct coef_fw coef0293[] = {
+ static const struct coef_fw coef0293[] = {
UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */
WRITE_COEF(0x45, 0xC429), /* Set to TRS type */
UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */
{}
};
- static struct coef_fw coef0688[] = {
+ static const struct coef_fw coef0688[] = {
WRITE_COEF(0x11, 0x0041),
WRITE_COEF(0x15, 0x0d40),
WRITE_COEF(0xb7, 0x802b),
{}
};
- static struct coef_fw coef0274[] = {
+ static const struct coef_fw coef0274[] = {
WRITE_COEF(0x45, 0x4289),
UPDATE_COEF(0x4a, 0x0010, 0x0010),
UPDATE_COEF(0x6b, 0x0f00, 0),
@@ -4567,53 +4824,53 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
{
int val;
- static struct coef_fw coef0255[] = {
+ static const struct coef_fw coef0255[] = {
WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */
WRITE_COEF(0x1b, 0x0c2b),
WRITE_COEFEX(0x57, 0x03, 0x8ea6),
{}
};
- static struct coef_fw coef0256[] = {
+ static const struct coef_fw coef0256[] = {
WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */
WRITE_COEF(0x1b, 0x0e6b),
{}
};
- static struct coef_fw coef0233[] = {
+ static const struct coef_fw coef0233[] = {
WRITE_COEF(0x45, 0xd429),
WRITE_COEF(0x1b, 0x0c2b),
WRITE_COEF(0x32, 0x4ea3),
{}
};
- static struct coef_fw coef0288[] = {
+ static const struct coef_fw coef0288[] = {
UPDATE_COEF(0x50, 0x2000, 0x2000),
UPDATE_COEF(0x56, 0x0006, 0x0006),
UPDATE_COEF(0x66, 0x0008, 0),
UPDATE_COEF(0x67, 0x2000, 0),
{}
};
- static struct coef_fw coef0292[] = {
+ static const struct coef_fw coef0292[] = {
WRITE_COEF(0x6b, 0xd429),
WRITE_COEF(0x76, 0x0008),
WRITE_COEF(0x18, 0x7388),
{}
};
- static struct coef_fw coef0293[] = {
+ static const struct coef_fw coef0293[] = {
WRITE_COEF(0x45, 0xd429), /* Set to ctia type */
UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */
{}
};
- static struct coef_fw coef0688[] = {
+ static const struct coef_fw coef0688[] = {
WRITE_COEF(0x11, 0x0001),
WRITE_COEF(0x15, 0x0d60),
WRITE_COEF(0xc3, 0x0000),
{}
};
- static struct coef_fw coef0225_1[] = {
+ static const struct coef_fw coef0225_1[] = {
UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10),
UPDATE_COEF(0x63, 3<<14, 2<<14),
{}
};
- static struct coef_fw coef0225_2[] = {
+ static const struct coef_fw coef0225_2[] = {
UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10),
UPDATE_COEF(0x63, 3<<14, 1<<14),
{}
@@ -4685,48 +4942,48 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
/* Nokia type */
static void alc_headset_mode_omtp(struct hda_codec *codec)
{
- static struct coef_fw coef0255[] = {
+ static const struct coef_fw coef0255[] = {
WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */
WRITE_COEF(0x1b, 0x0c2b),
WRITE_COEFEX(0x57, 0x03, 0x8ea6),
{}
};
- static struct coef_fw coef0256[] = {
+ static const struct coef_fw coef0256[] = {
WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */
WRITE_COEF(0x1b, 0x0e6b),
{}
};
- static struct coef_fw coef0233[] = {
+ static const struct coef_fw coef0233[] = {
WRITE_COEF(0x45, 0xe429),
WRITE_COEF(0x1b, 0x0c2b),
WRITE_COEF(0x32, 0x4ea3),
{}
};
- static struct coef_fw coef0288[] = {
+ static const struct coef_fw coef0288[] = {
UPDATE_COEF(0x50, 0x2000, 0x2000),
UPDATE_COEF(0x56, 0x0006, 0x0006),
UPDATE_COEF(0x66, 0x0008, 0),
UPDATE_COEF(0x67, 0x2000, 0),
{}
};
- static struct coef_fw coef0292[] = {
+ static const struct coef_fw coef0292[] = {
WRITE_COEF(0x6b, 0xe429),
WRITE_COEF(0x76, 0x0008),
WRITE_COEF(0x18, 0x7388),
{}
};
- static struct coef_fw coef0293[] = {
+ static const struct coef_fw coef0293[] = {
WRITE_COEF(0x45, 0xe429), /* Set to omtp type */
UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */
{}
};
- static struct coef_fw coef0688[] = {
+ static const struct coef_fw coef0688[] = {
WRITE_COEF(0x11, 0x0001),
WRITE_COEF(0x15, 0x0d50),
WRITE_COEF(0xc3, 0x0000),
{}
};
- static struct coef_fw coef0225[] = {
+ static const struct coef_fw coef0225[] = {
UPDATE_COEF(0x45, 0x3f<<10, 0x39<<10),
UPDATE_COEF(0x63, 3<<14, 2<<14),
{}
@@ -4786,17 +5043,17 @@ static void alc_determine_headset_type(struct hda_codec *codec)
int val;
bool is_ctia = false;
struct alc_spec *spec = codec->spec;
- static struct coef_fw coef0255[] = {
+ static const struct coef_fw coef0255[] = {
WRITE_COEF(0x45, 0xd089), /* combo jack auto switch control(Check type)*/
WRITE_COEF(0x49, 0x0149), /* combo jack auto switch control(Vref
conteol) */
{}
};
- static struct coef_fw coef0288[] = {
+ static const struct coef_fw coef0288[] = {
UPDATE_COEF(0x4f, 0xfcc0, 0xd400), /* Check Type */
{}
};
- static struct coef_fw coef0298[] = {
+ static const struct coef_fw coef0298[] = {
UPDATE_COEF(0x50, 0x2000, 0x2000),
UPDATE_COEF(0x56, 0x0006, 0x0006),
UPDATE_COEF(0x66, 0x0008, 0),
@@ -4804,19 +5061,19 @@ static void alc_determine_headset_type(struct hda_codec *codec)
UPDATE_COEF(0x19, 0x1300, 0x1300),
{}
};
- static struct coef_fw coef0293[] = {
+ static const struct coef_fw coef0293[] = {
UPDATE_COEF(0x4a, 0x000f, 0x0008), /* Combo Jack auto detect */
WRITE_COEF(0x45, 0xD429), /* Set to ctia type */
{}
};
- static struct coef_fw coef0688[] = {
+ static const struct coef_fw coef0688[] = {
WRITE_COEF(0x11, 0x0001),
WRITE_COEF(0xb7, 0x802b),
WRITE_COEF(0x15, 0x0d60),
WRITE_COEF(0xc3, 0x0c00),
{}
};
- static struct coef_fw coef0274[] = {
+ static const struct coef_fw coef0274[] = {
UPDATE_COEF(0x4a, 0x0010, 0),
UPDATE_COEF(0x4a, 0x8000, 0),
WRITE_COEF(0x45, 0xd289),
@@ -5103,7 +5360,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 alc255fw[] = {
+ static const struct coef_fw alc255fw[] = {
WRITE_COEF(0x1b, 0x880b),
WRITE_COEF(0x45, 0xd089),
WRITE_COEF(0x1b, 0x080b),
@@ -5111,7 +5368,7 @@ static void alc255_set_default_jack_type(struct hda_codec *codec)
WRITE_COEF(0x1b, 0x0c0b),
{}
};
- static struct coef_fw alc256fw[] = {
+ static const struct coef_fw alc256fw[] = {
WRITE_COEF(0x1b, 0x884b),
WRITE_COEF(0x45, 0xd089),
WRITE_COEF(0x1b, 0x084b),
@@ -5230,18 +5487,9 @@ static void alc_fixup_tpt470_dock(struct hda_codec *codec,
{ 0x19, 0x21a11010 }, /* dock mic */
{ }
};
- /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise
- * the speaker output becomes too low by some reason on Thinkpads with
- * ALC298 codec
- */
- static hda_nid_t preferred_pairs[] = {
- 0x14, 0x03, 0x17, 0x02, 0x21, 0x02,
- 0
- };
struct alc_spec *spec = codec->spec;
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->gen.preferred_dacs = preferred_pairs;
spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
snd_hda_apply_pincfgs(codec, pincfgs);
} else if (action == HDA_FIXUP_ACT_INIT) {
@@ -5254,6 +5502,23 @@ static void alc_fixup_tpt470_dock(struct hda_codec *codec,
}
}
+static void alc_fixup_tpt470_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise
+ * the speaker output becomes too low by some reason on Thinkpads with
+ * ALC298 codec
+ */
+ static const hda_nid_t preferred_pairs[] = {
+ 0x14, 0x03, 0x17, 0x02, 0x21, 0x02,
+ 0
+ };
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->gen.preferred_dacs = preferred_pairs;
+}
+
static void alc_shutup_dell_xps13(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -5495,9 +5760,9 @@ static void alc290_fixup_mono_speakers(struct hda_codec *codec,
/* DAC node 0x03 is giving mono output. We therefore want to
make sure 0x14 (front speaker) and 0x15 (headphones) use the
stereo DAC, while leaving 0x17 (bass speaker) for node 0x03. */
- hda_nid_t conn1[2] = { 0x0c };
- snd_hda_override_conn_list(codec, 0x14, 1, conn1);
- snd_hda_override_conn_list(codec, 0x15, 1, conn1);
+ static const hda_nid_t conn1[] = { 0x0c };
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1);
}
}
@@ -5512,8 +5777,8 @@ static void alc298_fixup_speaker_volume(struct hda_codec *codec,
Pin Complex), since Node 0x02 has Amp-out caps, we can adjust
speaker's volume now. */
- hda_nid_t conn1[1] = { 0x0c };
- snd_hda_override_conn_list(codec, 0x17, 1, conn1);
+ static const hda_nid_t conn1[] = { 0x0c };
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn1), conn1);
}
}
@@ -5522,8 +5787,18 @@ static void alc295_fixup_disable_dac3(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- hda_nid_t conn[2] = { 0x02, 0x03 };
- snd_hda_override_conn_list(codec, 0x17, 2, conn);
+ static const hda_nid_t conn[] = { 0x02, 0x03 };
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ }
+}
+
+/* force NID 0x17 (Bass Speaker) to DAC1 to share it with the main speaker */
+static void alc285_fixup_speaker2_to_dac1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ static const hda_nid_t conn[] = { 0x02 };
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
}
}
@@ -5535,7 +5810,8 @@ static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec,
snd_hda_gen_hp_automute(codec, jack);
/* mute_led_polarity is set to 0, so we pass inverted value here */
- alc_update_gpio_led(codec, 0x10, !spec->gen.hp_jack_present);
+ alc_update_gpio_led(codec, 0x10, spec->mute_led_polarity,
+ !spec->gen.hp_jack_present);
}
/* Manage GPIOs for HP EliteBook Folio 9480m.
@@ -5572,6 +5848,39 @@ static void alc275_fixup_gpio4_off(struct hda_codec *codec,
}
}
+/* Quirk for Thinkpad X1 7th and 8th Gen
+ * The following fixed routing needed
+ * DAC1 (NID 0x02) -> Speaker (NID 0x14); some eq applied secretly
+ * DAC2 (NID 0x03) -> Bass (NID 0x17) & Headphone (NID 0x21); sharing a DAC
+ * DAC3 (NID 0x06) -> Unused, due to the lack of volume amp
+ */
+static void alc285_fixup_thinkpad_x1_gen7(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */
+ static const hda_nid_t preferred_pairs[] = {
+ 0x14, 0x02, 0x17, 0x03, 0x21, 0x03, 0
+ };
+ struct alc_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ spec->gen.preferred_dacs = preferred_pairs;
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ /* The generic parser creates somewhat unintuitive volume ctls
+ * with the fixed routing above, and the shared DAC2 may be
+ * confusing for PA.
+ * Rename those to unique names so that PA doesn't touch them
+ * and use only Master volume.
+ */
+ rename_ctl(codec, "Front Playback Volume", "DAC1 Playback Volume");
+ rename_ctl(codec, "Bass Speaker Playback Volume", "DAC2 Playback Volume");
+ break;
+ }
+}
+
static void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec,
const struct hda_fixup *fix,
int action)
@@ -5596,12 +5905,21 @@ static void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec,
}
}
+static void alc225_fixup_s3_pop_noise(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ codec->power_save_node = 1;
+}
+
/* Forcibly assign NID 0x03 to HP/LO while NID 0x02 to SPK for EQ */
static void alc274_fixup_bind_dacs(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- static hda_nid_t preferred_pairs[] = {
+ static const hda_nid_t preferred_pairs[] = {
0x21, 0x03, 0x1b, 0x03, 0x16, 0x02,
0
};
@@ -5624,66 +5942,6 @@ static void alc285_fixup_invalidate_dacs(struct hda_codec *codec,
snd_hda_override_wcaps(codec, 0x03, 0);
}
-static const struct hda_jack_keymap alc_headset_btn_keymap[] = {
- { SND_JACK_BTN_0, KEY_PLAYPAUSE },
- { SND_JACK_BTN_1, KEY_VOICECOMMAND },
- { SND_JACK_BTN_2, KEY_VOLUMEUP },
- { SND_JACK_BTN_3, KEY_VOLUMEDOWN },
- {}
-};
-
-static void alc_headset_btn_callback(struct hda_codec *codec,
- struct hda_jack_callback *jack)
-{
- int report = 0;
-
- if (jack->unsol_res & (7 << 13))
- report |= SND_JACK_BTN_0;
-
- if (jack->unsol_res & (1 << 16 | 3 << 8))
- report |= SND_JACK_BTN_1;
-
- /* Volume up key */
- if (jack->unsol_res & (7 << 23))
- report |= SND_JACK_BTN_2;
-
- /* Volume down key */
- if (jack->unsol_res & (7 << 10))
- report |= SND_JACK_BTN_3;
-
- jack->jack->button_state = report;
-}
-
-static void alc_fixup_headset_jack(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
-
- switch (action) {
- case HDA_FIXUP_ACT_PRE_PROBE:
- snd_hda_jack_detect_enable_callback(codec, 0x55,
- alc_headset_btn_callback);
- snd_hda_jack_add_kctl(codec, 0x55, "Headset Jack", false,
- SND_JACK_HEADSET, alc_headset_btn_keymap);
- break;
- case HDA_FIXUP_ACT_INIT:
- switch (codec->core.vendor_id) {
- case 0x10ec0225:
- case 0x10ec0295:
- case 0x10ec0299:
- alc_write_coef_idx(codec, 0x48, 0xd011);
- alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045);
- alc_update_coef_idx(codec, 0x44, 0x007f << 8, 0x0045 << 8);
- break;
- case 0x10ec0236:
- case 0x10ec0256:
- alc_write_coef_idx(codec, 0x48, 0xd011);
- alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045);
- break;
- }
- break;
- }
-}
-
static void alc295_fixup_chromebook(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -5715,6 +5973,16 @@ static void alc_fixup_disable_mic_vref(struct hda_codec *codec,
snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ);
}
+static void alc285_fixup_hp_gpio_amp_init(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+
+ msleep(100);
+ alc_write_coef_idx(codec, 0x65, 0x0);
+}
+
/* for hda_fixup_thinkpad_acpi() */
#include "thinkpad_helper.c"
@@ -5760,6 +6028,7 @@ enum {
ALC269_FIXUP_HP_LINE1_MIC1_LED,
ALC269_FIXUP_INV_DMIC,
ALC269_FIXUP_LENOVO_DOCK,
+ ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST,
ALC269_FIXUP_NO_SHUTUP,
ALC286_FIXUP_SONY_MIC_NO_PRESENCE,
ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT,
@@ -5820,13 +6089,13 @@ enum {
ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE,
ALC275_FIXUP_DELL_XPS,
- ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE,
ALC293_FIXUP_LENOVO_SPK_NOISE,
ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
ALC255_FIXUP_DELL_SPK_NOISE,
ALC225_FIXUP_DISABLE_MIC_VREF,
ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC295_FIXUP_DISABLE_DAC3,
+ ALC285_FIXUP_SPEAKER2_TO_DAC1,
ALC280_FIXUP_HP_HEADSET_MIC,
ALC221_FIXUP_HP_FRONT_MIC,
ALC292_FIXUP_TPT460,
@@ -5843,9 +6112,11 @@ enum {
ALC233_FIXUP_ACER_HEADSET_MIC,
ALC294_FIXUP_LENOVO_MIC_LOCATION,
ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE,
+ ALC225_FIXUP_S3_POP_NOISE,
ALC700_FIXUP_INTEL_REFERENCE,
ALC274_FIXUP_DELL_BIND_DACS,
ALC274_FIXUP_DELL_AIO_LINEOUT_VERB,
+ ALC298_FIXUP_TPT470_DOCK_FIX,
ALC298_FIXUP_TPT470_DOCK,
ALC255_FIXUP_DUMMY_LINEOUT_VERB,
ALC255_FIXUP_DELL_HEADSET_MIC,
@@ -5871,8 +6142,32 @@ enum {
ALC256_FIXUP_ASUS_HEADSET_MIC,
ALC256_FIXUP_ASUS_MIC_NO_PRESENCE,
ALC299_FIXUP_PREDATOR_SPK,
- ALC294_FIXUP_ASUS_INTSPK_HEADSET_MIC,
ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE,
+ ALC289_FIXUP_DELL_SPK2,
+ ALC289_FIXUP_DUAL_SPK,
+ ALC294_FIXUP_SPK2_TO_DAC1,
+ ALC294_FIXUP_ASUS_DUAL_SPK,
+ ALC285_FIXUP_THINKPAD_X1_GEN7,
+ ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ ALC294_FIXUP_ASUS_HPE,
+ ALC294_FIXUP_ASUS_COEF_1B,
+ ALC285_FIXUP_HP_GPIO_LED,
+ ALC285_FIXUP_HP_MUTE_LED,
+ ALC236_FIXUP_HP_MUTE_LED,
+ ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET,
+ ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
+ ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS,
+ ALC269VC_FIXUP_ACER_HEADSET_MIC,
+ ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE,
+ ALC289_FIXUP_ASUS_GA401,
+ ALC289_FIXUP_ASUS_GA502,
+ ALC256_FIXUP_ACER_MIC_NO_PRESENCE,
+ ALC285_FIXUP_HP_GPIO_AMP_INIT,
+ ALC269_FIXUP_CZC_B20,
+ ALC269_FIXUP_CZC_TMI,
+ ALC269_FIXUP_CZC_L101,
+ ALC269_FIXUP_LEMOTE_A1802,
+ ALC269_FIXUP_LEMOTE_A190X,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -6070,6 +6365,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT
},
+ [ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LENOVO_DOCK,
+ },
[ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_pincfg_no_hp_to_lineout,
@@ -6538,17 +6839,6 @@ static const struct hda_fixup alc269_fixups[] = {
{}
}
},
- [ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE] = {
- .type = HDA_FIXUP_VERBS,
- .v.verbs = (const struct hda_verb[]) {
- /* Disable pass-through path for FRONT 14h */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x36},
- {0x20, AC_VERB_SET_PROC_COEF, 0x1737},
- {}
- },
- .chained = true,
- .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
- },
[ALC293_FIXUP_LENOVO_SPK_NOISE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_disable_aamix,
@@ -6611,6 +6901,12 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc295_fixup_disable_dac3,
},
+ [ALC285_FIXUP_SPEAKER2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
[ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
@@ -6711,6 +7007,12 @@ static const struct hda_fixup alc269_fixups[] = {
{ }
},
.chained = true,
+ .chain_id = ALC225_FIXUP_S3_POP_NOISE
+ },
+ [ALC225_FIXUP_S3_POP_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc225_fixup_s3_pop_noise,
+ .chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
},
[ALC700_FIXUP_INTEL_REFERENCE] = {
@@ -6743,12 +7045,18 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC274_FIXUP_DELL_BIND_DACS
},
- [ALC298_FIXUP_TPT470_DOCK] = {
+ [ALC298_FIXUP_TPT470_DOCK_FIX] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_tpt470_dock,
.chained = true,
.chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE
},
+ [ALC298_FIXUP_TPT470_DOCK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_tpt470_dacs,
+ .chained = true,
+ .chain_id = ALC298_FIXUP_TPT470_DOCK_FIX
+ },
[ALC255_FIXUP_DUMMY_LINEOUT_VERB] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
@@ -6809,7 +7117,7 @@ static const struct hda_fixup alc269_fixups[] = {
{ }
},
.chained = true,
- .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
},
[ALC294_FIXUP_ASUS_HEADSET_MIC] = {
.type = HDA_FIXUP_PINS,
@@ -6818,7 +7126,7 @@ static const struct hda_fixup alc269_fixups[] = {
{ }
},
.chained = true,
- .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
},
[ALC294_FIXUP_ASUS_SPK] = {
.type = HDA_FIXUP_VERBS,
@@ -6826,6 +7134,8 @@ static const struct hda_fixup alc269_fixups[] = {
/* Set EAPD high */
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x40 },
{ 0x20, AC_VERB_SET_PROC_COEF, 0x8800 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x7774 },
{ }
},
.chained = true,
@@ -6928,26 +7238,247 @@ static const struct hda_fixup alc269_fixups[] = {
{ }
}
},
- [ALC294_FIXUP_ASUS_INTSPK_HEADSET_MIC] = {
+ [ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
- { 0x14, 0x411111f0 }, /* disable confusing internal speaker */
- { 0x19, 0x04a11150 }, /* use as headset mic, without its own jack detect */
+ { 0x19, 0x04a11040 },
+ { 0x21, 0x04211020 },
{ }
},
.chained = true,
- .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
},
- [ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE] = {
+ [ALC289_FIXUP_DELL_SPK2] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
- { 0x19, 0x04a11040 },
- { 0x21, 0x04211020 },
+ { 0x17, 0x90170130 }, /* bass spk */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE
+ },
+ [ALC289_FIXUP_DUAL_SPK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC289_FIXUP_DELL_SPK2
+ },
+ [ALC294_FIXUP_SPK2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC
+ },
+ [ALC294_FIXUP_ASUS_DUAL_SPK] = {
+ .type = HDA_FIXUP_FUNC,
+ /* The GPIO must be pulled to initialize the AMP */
+ .v.func = alc_fixup_gpio4,
+ .chained = true,
+ .chain_id = ALC294_FIXUP_SPK2_TO_DAC1
+ },
+ [ALC285_FIXUP_THINKPAD_X1_GEN7] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_thinkpad_x1_gen7,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC285_FIXUP_THINKPAD_HEADSET_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_jack,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_X1_GEN7
+ },
+ [ALC294_FIXUP_ASUS_HPE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Set EAPD high */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x7774 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC
+ },
+ [ALC294_FIXUP_ASUS_COEF_1B] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Set bit 10 to correct noisy output after reboot from
+ * Windows 10 (due to pop noise reduction?)
+ */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x1b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x4e4b },
+ { }
+ },
+ },
+ [ALC285_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_gpio_led,
+ },
+ [ALC285_FIXUP_HP_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_mute_led,
+ },
+ [ALC236_FIXUP_HP_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc236_fixup_hp_mute_led,
+ },
+ [ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc5 },
+ { }
+ },
+ },
+ [ALC295_FIXUP_ASUS_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x90100120 }, /* use as internal speaker */
+ { 0x18, 0x02a111f0 }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01011020 }, /* use as line out */
+ { },
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC269VC_FIXUP_ACER_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x02a11030 }, /* use as headset mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a11130 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC289_FIXUP_ASUS_GA401] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11020 }, /* headset mic with jack detect */
+ { }
+ },
+ },
+ [ALC289_FIXUP_ASUS_GA502] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11020 }, /* headset mic with jack detect */
+ { }
+ },
+ },
+ [ALC256_FIXUP_ACER_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x02a11120 }, /* use as headset mic, without its own jack detect */
{ }
},
.chained = true,
.chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
},
+ [ALC285_FIXUP_HP_GPIO_AMP_INIT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_gpio_amp_init,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_GPIO_LED
+ },
+ [ALC269_FIXUP_CZC_B20] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x411111f0 },
+ { 0x14, 0x90170110 }, /* speaker */
+ { 0x15, 0x032f1020 }, /* HP out */
+ { 0x17, 0x411111f0 },
+ { 0x18, 0x03ab1040 }, /* mic */
+ { 0x19, 0xb7a7013f },
+ { 0x1a, 0x0181305f },
+ { 0x1b, 0x411111f0 },
+ { 0x1d, 0x411111f0 },
+ { 0x1e, 0x411111f0 },
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_CZC_TMI] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x4000c000 },
+ { 0x14, 0x90170110 }, /* speaker */
+ { 0x15, 0x0421401f }, /* HP out */
+ { 0x17, 0x411111f0 },
+ { 0x18, 0x04a19020 }, /* mic */
+ { 0x19, 0x411111f0 },
+ { 0x1a, 0x411111f0 },
+ { 0x1b, 0x411111f0 },
+ { 0x1d, 0x40448505 },
+ { 0x1e, 0x411111f0 },
+ { 0x20, 0x8000ffff },
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_CZC_L101] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x40000000 },
+ { 0x14, 0x01014010 }, /* speaker */
+ { 0x15, 0x411111f0 }, /* HP out */
+ { 0x16, 0x411111f0 },
+ { 0x18, 0x01a19020 }, /* mic */
+ { 0x19, 0x02a19021 },
+ { 0x1a, 0x0181302f },
+ { 0x1b, 0x0221401f },
+ { 0x1c, 0x411111f0 },
+ { 0x1d, 0x4044c601 },
+ { 0x1e, 0x411111f0 },
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_LEMOTE_A1802] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x40000000 },
+ { 0x14, 0x90170110 }, /* speaker */
+ { 0x17, 0x411111f0 },
+ { 0x18, 0x03a19040 }, /* mic1 */
+ { 0x19, 0x90a70130 }, /* mic2 */
+ { 0x1a, 0x411111f0 },
+ { 0x1b, 0x411111f0 },
+ { 0x1d, 0x40489d2d },
+ { 0x1e, 0x411111f0 },
+ { 0x20, 0x0003ffff },
+ { 0x21, 0x03214020 },
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_LEMOTE_A190X] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121401f }, /* HP out */
+ { 0x18, 0x01a19c20 }, /* rear mic */
+ { 0x19, 0x99a3092f }, /* front mic */
+ { 0x1b, 0x0201401f }, /* front lineout */
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -6963,16 +7494,20 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS),
SND_PCI_QUIRK(0x1025, 0x102b, "Acer Aspire C24-860", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1065, "Acer Aspire C20-820", ALC269VC_FIXUP_ACER_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK(0x1025, 0x1099, "Acer Aspire E5-523G", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1025, 0x110e, "Acer Aspire ES1-432", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1025, 0x1246, "Acer Predator Helios 500", ALC299_FIXUP_PREDATOR_SPK),
+ SND_PCI_QUIRK(0x1025, 0x1247, "Acer vCopperbox", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
+ SND_PCI_QUIRK(0x1025, 0x1248, "Acer Veriton N4660G", ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1025, 0x128f, "Acer Veriton Z6860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x1290, "Acer Veriton Z4860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x1291, "Acer Veriton Z4660G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x1308, "Acer Aspire Z24-890", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x132a, "Acer TravelMate B114-21", ALC233_FIXUP_ACER_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x1330, "Acer TravelMate X514-51T", ALC255_FIXUP_ACER_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS),
SND_PCI_QUIRK(0x1028, 0x05bd, "Dell Latitude E6440", ALC292_FIXUP_DELL_E7X),
@@ -7001,17 +7536,14 @@ 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 9350", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
SND_PCI_QUIRK(0x1028, 0x0706, "Dell Inspiron 7559", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER),
SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE),
SND_PCI_QUIRK(0x1028, 0x0738, "Dell Precision 5820", ALC269_FIXUP_NO_SHUTUP),
- SND_PCI_QUIRK(0x1028, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
SND_PCI_QUIRK(0x1028, 0x075c, "Dell XPS 27 7760", ALC298_FIXUP_SPK_VOLUME),
SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME),
SND_PCI_QUIRK(0x1028, 0x07b0, "Dell Precision 7520", ALC295_FIXUP_DISABLE_DAC3),
SND_PCI_QUIRK(0x1028, 0x0798, "Dell Inspiron 17 7000 Gaming", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER),
SND_PCI_QUIRK(0x1028, 0x080c, "Dell WYSE", ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE),
- SND_PCI_QUIRK(0x1028, 0x082a, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
SND_PCI_QUIRK(0x1028, 0x084b, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB),
SND_PCI_QUIRK(0x1028, 0x084e, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB),
SND_PCI_QUIRK(0x1028, 0x0871, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC),
@@ -7020,6 +7552,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x08ad, "Dell WYSE AIO", ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x08ae, "Dell WYSE NB", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0935, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB),
+ SND_PCI_QUIRK(0x1028, 0x097e, "Dell Precision", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x097d, "Dell Precision", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x098d, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x09bf, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE),
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),
@@ -7091,6 +7627,11 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3),
+ SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8729, "HP", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -7107,14 +7648,21 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK),
SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A),
SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
- SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_INTSPK_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_DUAL_SPK),
SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x194e, "ASUS UX563FD", ALC294_FIXUP_ASUS_HPE),
+ SND_PCI_QUIRK(0x1043, 0x19ce, "ASUS B9450FA", ALC294_FIXUP_ASUS_HPE),
+ SND_PCI_QUIRK(0x1043, 0x19e1, "ASUS UX581LV", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
SND_PCI_QUIRK(0x1043, 0x1a30, "ASUS X705UD", ALC256_FIXUP_ASUS_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B),
SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x1043, 0x1ccd, "ASUS X555UB", ALC256_FIXUP_ASUS_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502),
+ SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401),
SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2),
SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC),
@@ -7134,9 +7682,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x10cf, 0x1629, "Lifebook U7x7", ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC),
SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC),
SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE),
+ SND_PCI_QUIRK(0x10ec, 0x1230, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_HEADSET_MODE),
SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET),
+ SND_PCI_QUIRK(0x144d, 0xc176, "Samsung Notebook 9 Pro (NP930MBE-K04US)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET),
SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8),
+ SND_PCI_QUIRK(0x144d, 0xc812, "Samsung Notebook Pen S (NT950SBE-X58)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET),
SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC),
@@ -7146,12 +7698,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1558, 0x8560, "System76 Gazelle (gaze14)", ALC269_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1558, 0x8561, "System76 Gazelle (gaze14)", ALC269_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS),
+ SND_PCI_QUIRK(0x17aa, 0x1048, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE),
- SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST),
SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
@@ -7179,6 +7732,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK),
+ SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK),
SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
@@ -7187,6 +7742,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x312f, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
SND_PCI_QUIRK(0x17aa, 0x313c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
SND_PCI_QUIRK(0x17aa, 0x3151, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x3176, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
@@ -7211,8 +7768,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS),
+ SND_PCI_QUIRK(0x1b35, 0x1235, "CZC B20", ALC269_FIXUP_CZC_B20),
+ SND_PCI_QUIRK(0x1b35, 0x1236, "CZC TMI", ALC269_FIXUP_CZC_TMI),
+ SND_PCI_QUIRK(0x1b35, 0x1237, "CZC L101", ALC269_FIXUP_CZC_L101),
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
+ SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1c06, 0x2013, "Lemote A1802", ALC269_FIXUP_LEMOTE_A1802),
+ SND_PCI_QUIRK(0x1c06, 0x2015, "Lemote A190X", ALC269_FIXUP_LEMOTE_A190X),
#if 0
/* Below is a quirk table taken from the old code.
@@ -7284,6 +7847,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{.id = ALC269_FIXUP_HEADSET_MODE, .name = "headset-mode"},
{.id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, .name = "headset-mode-no-hp-mic"},
{.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"},
+ {.id = ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST, .name = "lenovo-dock-limit-boost"},
{.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"},
{.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic1-led"},
{.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"},
@@ -7295,6 +7859,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"},
{.id = ALC292_FIXUP_TPT440, .name = "tpt440"},
{.id = ALC292_FIXUP_TPT460, .name = "tpt460"},
+ {.id = ALC298_FIXUP_TPT470_DOCK_FIX, .name = "tpt470-dock-fix"},
{.id = ALC298_FIXUP_TPT470_DOCK, .name = "tpt470-dock"},
{.id = ALC233_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"},
{.id = ALC700_FIXUP_INTEL_REFERENCE, .name = "alc700-ref"},
@@ -7354,12 +7919,12 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{.id = ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc298-dell1"},
{.id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, .name = "alc298-dell-aio"},
{.id = ALC275_FIXUP_DELL_XPS, .name = "alc275-dell-xps"},
- {.id = ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE, .name = "alc256-dell-xps13"},
{.id = ALC293_FIXUP_LENOVO_SPK_NOISE, .name = "lenovo-spk-noise"},
{.id = ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, .name = "lenovo-hotkey"},
{.id = ALC255_FIXUP_DELL_SPK_NOISE, .name = "dell-spk-noise"},
{.id = ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc225-dell1"},
{.id = ALC295_FIXUP_DISABLE_DAC3, .name = "alc295-disable-dac3"},
+ {.id = ALC285_FIXUP_SPEAKER2_TO_DAC1, .name = "alc285-speaker2-to-dac1"},
{.id = ALC280_FIXUP_HP_HEADSET_MIC, .name = "alc280-hp-headset"},
{.id = ALC221_FIXUP_HP_FRONT_MIC, .name = "alc221-hp-mic"},
{.id = ALC298_FIXUP_SPK_VOLUME, .name = "alc298-spk-volume"},
@@ -7767,6 +8332,18 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x12, 0x90a60130},
{0x17, 0x90170110},
{0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x12, 0x90a60120},
+ {0x17, 0x90170110},
+ {0x21, 0x04211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+ {0x21, 0x03211020}),
SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
{0x14, 0x90170110},
{0x21, 0x04211020}),
@@ -7802,6 +8379,12 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
ALC225_STANDARD_PINS,
{0x12, 0xb7a60130},
{0x17, 0x90170110}),
+ SND_HDA_PIN_QUIRK(0x10ec0623, 0x17aa, "Lenovo", ALC283_FIXUP_HEADSET_MIC,
+ {0x14, 0x01014010},
+ {0x17, 0x90170120},
+ {0x18, 0x02a11030},
+ {0x19, 0x02a1103f},
+ {0x21, 0x0221101f}),
{}
};
@@ -7947,7 +8530,9 @@ static int patch_alc269(struct hda_codec *codec)
spec->gen.mixer_nid = 0;
break;
case 0x10ec0215:
+ case 0x10ec0245:
case 0x10ec0285:
+ case 0x10ec0287:
case 0x10ec0289:
spec->codec_variant = ALC269_TYPE_ALC215;
spec->shutup = alc225_shutup;
@@ -7974,6 +8559,9 @@ static int patch_alc269(struct hda_codec *codec)
spec->codec_variant = ALC269_TYPE_ALC300;
spec->gen.mixer_nid = 0; /* no loopback on ALC300 */
break;
+ case 0x10ec0623:
+ spec->codec_variant = ALC269_TYPE_ALC623;
+ break;
case 0x10ec0700:
case 0x10ec0701:
case 0x10ec0703:
@@ -8370,7 +8958,30 @@ static void alc662_fixup_usi_headset_mic(struct hda_codec *codec,
}
}
-static struct coef_fw alc668_coefs[] = {
+static void alc671_fixup_hp_headset_mic2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x19, 0x02a11040 }, /* use as headset mic, with its own jack detect */
+ { 0x1b, 0x0181304f },
+ { }
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.mixer_nid = 0;
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_write_coef_idx(codec, 0x19, 0xa054);
+ break;
+ }
+}
+
+static const struct coef_fw alc668_coefs[] = {
WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0),
WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80),
WRITE_COEF(0x08, 0x0031), WRITE_COEF(0x0a, 0x0060), WRITE_COEF(0x0b, 0x0),
@@ -8404,6 +9015,7 @@ enum {
ALC662_FIXUP_LED_GPIO1,
ALC662_FIXUP_IDEAPAD,
ALC272_FIXUP_MARIO,
+ ALC662_FIXUP_CZC_ET26,
ALC662_FIXUP_CZC_P10T,
ALC662_FIXUP_SKU_IGNORE,
ALC662_FIXUP_HP_RP5800,
@@ -8441,6 +9053,9 @@ enum {
ALC662_FIXUP_USI_FUNC,
ALC662_FIXUP_USI_HEADSET_MODE,
ALC662_FIXUP_LENOVO_MULTI_CODECS,
+ ALC671_FIXUP_HP_HEADSET_MIC2,
+ ALC662_FIXUP_ACER_X2660G_HEADSET_MODE,
+ ALC662_FIXUP_ACER_NITRO_HEADSET_MODE,
};
static const struct hda_fixup alc662_fixups[] = {
@@ -8468,6 +9083,25 @@ static const struct hda_fixup alc662_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc272_fixup_mario,
},
+ [ALC662_FIXUP_CZC_ET26] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x12, 0x403cc000},
+ {0x14, 0x90170110}, /* speaker */
+ {0x15, 0x411111f0},
+ {0x16, 0x411111f0},
+ {0x18, 0x01a19030}, /* mic */
+ {0x19, 0x90a7013f}, /* int-mic */
+ {0x1a, 0x01014020},
+ {0x1b, 0x0121401f},
+ {0x1c, 0x411111f0},
+ {0x1d, 0x411111f0},
+ {0x1e, 0x40478e35},
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
[ALC662_FIXUP_CZC_P10T] = {
.type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
@@ -8767,6 +9401,29 @@ static const struct hda_fixup alc662_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc233_alc662_fixup_lenovo_dual_codecs,
},
+ [ALC671_FIXUP_HP_HEADSET_MIC2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc671_fixup_hp_headset_mic2,
+ },
+ [ALC662_FIXUP_ACER_X2660G_HEADSET_MODE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x02a1113c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_USI_FUNC
+ },
+ [ALC662_FIXUP_ACER_NITRO_HEADSET_MODE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */
+ { 0x1b, 0x0221144f },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_USI_FUNC
+ },
};
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@@ -8778,6 +9435,8 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x034a, "Gateway LT27", ALC662_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0x123c, "Acer Nitro N50-600", ALC662_FIXUP_ACER_NITRO_HEADSET_MODE),
+ SND_PCI_QUIRK(0x1025, 0x124e, "Acer 2660G", ALC662_FIXUP_ACER_X2660G_HEADSET_MODE),
SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05fe, "Dell XPS 15", ALC668_FIXUP_DELL_XPS13),
@@ -8811,6 +9470,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO),
SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68),
SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON),
+ SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26),
SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T),
#if 0
@@ -8947,6 +9607,23 @@ static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = {
{0x12, 0x90a60130},
{0x14, 0x90170110},
{0x15, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2,
+ {0x14, 0x01014010},
+ {0x17, 0x90170150},
+ {0x19, 0x02a11060},
+ {0x1b, 0x01813030},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2,
+ {0x14, 0x01014010},
+ {0x18, 0x01a19040},
+ {0x1b, 0x01813030},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2,
+ {0x14, 0x01014020},
+ {0x17, 0x90170110},
+ {0x18, 0x01a19050},
+ {0x1b, 0x01813040},
+ {0x21, 0x02211030}),
{}
};
@@ -9072,6 +9749,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0236, "ALC236", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0245, "ALC245", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0257, "ALC257", patch_alc269),
@@ -9091,6 +9769,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0287, "ALC287", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0289, "ALC289", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269),
@@ -9101,6 +9780,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0299, "ALC299", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0300, "ALC300", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0623, "ALC623", patch_alc269),
HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),
HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861),
@@ -9133,6 +9813,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662),
HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882),
HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0b00, "ALCS1200A", patch_alc882),
HDA_CODEC_ENTRY(0x10ec1168, "ALC1220", patch_alc882),
HDA_CODEC_ENTRY(0x10ec1220, "ALC1220", patch_alc882),
{} /* terminator */
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 0d9b62768241..e02f58e0d650 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -795,7 +795,7 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
static bool has_builtin_speaker(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- hda_nid_t *nid_pin;
+ const hda_nid_t *nid_pin;
int nids, i;
if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) {
@@ -2191,7 +2191,7 @@ static void hp_envy_ts_fixup_dac_bind(struct hda_codec *codec,
int action)
{
struct sigmatel_spec *spec = codec->spec;
- static hda_nid_t preferred_pairs[] = {
+ static const hda_nid_t preferred_pairs[] = {
0xd, 0x13,
0
};
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 29dcdb8b36db..7ef8f3105cdb 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -396,7 +396,7 @@ static int via_resume(struct hda_codec *codec)
/* some delay here to make jack detection working (bko#98921) */
msleep(10);
codec->patch_ops.init(codec);
- regcache_sync(codec->core.regmap);
+ snd_hda_regmap_sync(codec);
return 0;
}
#endif
@@ -1038,8 +1038,8 @@ static const struct snd_pci_quirk vt2002p_fixups[] = {
*/
static void fix_vt1802_connections(struct hda_codec *codec)
{
- static hda_nid_t conn_24[] = { 0x14, 0x1c };
- static hda_nid_t conn_33[] = { 0x1c };
+ static const hda_nid_t conn_24[] = { 0x14, 0x1c };
+ static const hda_nid_t conn_33[] = { 0x1c };
snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 4b0dea7f7669..2654eebd5663 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -2360,7 +2360,8 @@ static int snd_ice1712_chip_init(struct snd_ice1712 *ice)
pci_write_config_byte(ice->pci, 0x61, ice->eeprom.data[ICE_EEP1_ACLINK]);
pci_write_config_byte(ice->pci, 0x62, ice->eeprom.data[ICE_EEP1_I2SID]);
pci_write_config_byte(ice->pci, 0x63, ice->eeprom.data[ICE_EEP1_SPDIF]);
- if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24) {
+ if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24 &&
+ ice->eeprom.subvendor != ICE1712_SUBDEVICE_STAUDIO_ADCIII) {
ice->gpio.write_mask = ice->eeprom.gpiomask;
ice->gpio.direction = ice->eeprom.gpiodir;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK,
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index e62c11816683..f360b33a1042 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -647,6 +647,7 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
unsigned long flags;
unsigned char mclk_change;
unsigned int i, old_rate;
+ bool call_set_rate = false;
if (rate > ice->hw_rates->list[ice->hw_rates->count - 1])
return -EINVAL;
@@ -670,7 +671,7 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
* setting clock rate for internal clock mode */
old_rate = ice->get_rate(ice);
if (force || (old_rate != rate))
- ice->set_rate(ice, rate);
+ call_set_rate = true;
else if (rate == ice->cur_rate) {
spin_unlock_irqrestore(&ice->reg_lock, flags);
return 0;
@@ -678,12 +679,14 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
}
ice->cur_rate = rate;
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+
+ if (call_set_rate)
+ ice->set_rate(ice, rate);
/* setting master clock */
mclk_change = ice->set_mclk(ice, rate);
- spin_unlock_irqrestore(&ice->reg_lock, flags);
-
if (mclk_change && ice->gpio.i2s_mclk_changed)
ice->gpio.i2s_mclk_changed(ice);
if (ice->gpio.set_pro_rate)
diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c
index 9d71e9d5c9a0..3cf41c11a405 100644
--- a/sound/pci/ice1712/prodigy_hifi.c
+++ b/sound/pci/ice1712/prodigy_hifi.c
@@ -536,7 +536,7 @@ static int wm_adc_mux_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
mutex_lock(&ice->gpio_mutex);
- ucontrol->value.integer.value[0] = wm_get(ice, WM_ADC_MUX) & 0x1f;
+ ucontrol->value.enumerated.item[0] = wm_get(ice, WM_ADC_MUX) & 0x1f;
mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -550,7 +550,7 @@ static int wm_adc_mux_enum_put(struct snd_kcontrol *kcontrol,
mutex_lock(&ice->gpio_mutex);
oval = wm_get(ice, WM_ADC_MUX);
- nval = (oval & 0xe0) | ucontrol->value.integer.value[0];
+ nval = (oval & 0xe0) | ucontrol->value.enumerated.item[0];
if (nval != oval) {
wm_put(ice, WM_ADC_MUX, nval);
change = 1;
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
index 52e9cfb4f819..8421b2f9c9f3 100644
--- a/sound/sh/aica.c
+++ b/sound/sh/aica.c
@@ -101,10 +101,10 @@ static void spu_memset(u32 toi, u32 what, int length)
}
/* spu_memload - write to SPU address space */
-static void spu_memload(u32 toi, void *from, int length)
+static void spu_memload(u32 toi, const void *from, int length)
{
unsigned long flags;
- u32 *froml = from;
+ const u32 *froml = from;
u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi);
int i;
u32 val;
diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c
index ed877a138965..7c46494466ff 100644
--- a/sound/sh/sh_dac_audio.c
+++ b/sound/sh/sh_dac_audio.c
@@ -175,7 +175,6 @@ static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream,
{
/* channel is not used (interleaved data) */
struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
if (copy_from_user_toio(chip->data_buffer + pos, src, count))
return -EFAULT;
@@ -195,7 +194,6 @@ static int snd_sh_dac_pcm_copy_kernel(struct snd_pcm_substream *substream,
{
/* channel is not used (interleaved data) */
struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
memcpy_toio(chip->data_buffer + pos, src, count);
chip->buffer_end = chip->data_buffer + pos + count;
@@ -214,7 +212,6 @@ static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream,
{
/* channel is not used (interleaved data) */
struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
memset_io(chip->data_buffer + pos, 0, count);
chip->buffer_end = chip->data_buffer + pos + count;
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 06c1d5ce642c..71f2d42188c4 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -10,27 +10,35 @@ config SND_ATMEL_SOC
if SND_ATMEL_SOC
config SND_ATMEL_SOC_PDC
- tristate
+ bool
depends on HAS_DMA
- default m if SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=m
- default y if SND_ATMEL_SOC_SSC_PDC=y || (SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=y)
-
-config SND_ATMEL_SOC_SSC_PDC
- tristate
config SND_ATMEL_SOC_DMA
- tristate
+ bool
select SND_SOC_GENERIC_DMAENGINE_PCM
- default m if SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=m
- default y if SND_ATMEL_SOC_SSC_DMA=y || (SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=y)
-
-config SND_ATMEL_SOC_SSC_DMA
- tristate
config SND_ATMEL_SOC_SSC
tristate
- default y if SND_ATMEL_SOC_SSC_DMA=y || SND_ATMEL_SOC_SSC_PDC=y
- default m if SND_ATMEL_SOC_SSC_DMA=m || SND_ATMEL_SOC_SSC_PDC=m
+ select SND_ATMEL_SOC_DMA
+ select SND_ATMEL_SOC_PDC
+
+config SND_ATMEL_SOC_SSC_PDC
+ tristate "SoC PCM DAI support for AT91 SSC controller using PDC"
+ depends on ATMEL_SSC
+ select SND_ATMEL_SOC_PDC
+ select SND_ATMEL_SOC_SSC
+ help
+ Say Y or M if you want to add support for Atmel SSC interface
+ in PDC mode configured using audio-graph-card in device-tree.
+
+config SND_ATMEL_SOC_SSC_DMA
+ tristate "SoC PCM DAI support for AT91 SSC controller using DMA"
+ depends on ATMEL_SSC
+ select SND_ATMEL_SOC_DMA
+ select SND_ATMEL_SOC_SSC
+ help
+ Say Y or M if you want to add support for Atmel SSC interface
+ in DMA mode configured using audio-graph-card in device-tree.
config SND_AT91_SOC_SAM9G20_WM8731
tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index 1f6890ed3738..c7d2989791be 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -6,8 +6,14 @@ snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
snd-soc-atmel-i2s-objs := atmel-i2s.o
snd-soc-mchp-i2s-mcc-objs := mchp-i2s-mcc.o
-obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
-obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o
+# pdc and dma need to both be built-in if any user of
+# ssc is built-in.
+ifdef CONFIG_SND_ATMEL_SOC_PDC
+obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel-pcm-pdc.o
+endif
+ifdef CONFIG_SND_ATMEL_SOC_DMA
+obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel-pcm-dma.o
+endif
obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
obj-$(CONFIG_SND_ATMEL_SOC_I2S) += snd-soc-atmel-i2s.o
obj-$(CONFIG_SND_MCHP_SOC_I2S_MCC) += snd-soc-mchp-i2s-mcc.o
diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
index 91242b6f8ea7..d78f4d856aaf 100644
--- a/sound/soc/codecs/hdac_hda.c
+++ b/sound/soc/codecs/hdac_hda.c
@@ -410,8 +410,8 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
return;
}
- snd_hdac_ext_bus_link_put(hdev->bus, hlink);
pm_runtime_disable(&hdev->dev);
+ snd_hdac_ext_bus_link_put(hdev->bus, hlink);
}
static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = {
@@ -498,7 +498,9 @@ static int hdac_hda_dev_remove(struct hdac_device *hdev)
struct hdac_hda_priv *hda_pvt;
hda_pvt = dev_get_drvdata(&hdev->dev);
- cancel_delayed_work_sync(&hda_pvt->codec.jackpoll_work);
+ if (hda_pvt && hda_pvt->codec.registered)
+ cancel_delayed_work_sync(&hda_pvt->codec.jackpoll_work);
+
return 0;
}
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index c9f9820968bb..0b05ec05748f 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -148,14 +148,14 @@ static struct hdac_hdmi_pcm *
hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi,
struct hdac_hdmi_cvt *cvt)
{
- struct hdac_hdmi_pcm *pcm = NULL;
+ struct hdac_hdmi_pcm *pcm;
list_for_each_entry(pcm, &hdmi->pcm_list, head) {
if (pcm->cvt == cvt)
- break;
+ return pcm;
}
- return pcm;
+ return NULL;
}
static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm,
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index f6bf4cfbea23..45da2b51543e 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -2103,10 +2103,8 @@ static void max98090_pll_det_disable_work(struct work_struct *work)
M98090_IULK_MASK, 0);
}
-static void max98090_pll_work(struct work_struct *work)
+static void max98090_pll_work(struct max98090_priv *max98090)
{
- struct max98090_priv *max98090 =
- container_of(work, struct max98090_priv, pll_work);
struct snd_soc_component *component = max98090->component;
if (!snd_soc_component_is_active(component))
@@ -2259,7 +2257,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
if (active & M98090_ULK_MASK) {
dev_dbg(component->dev, "M98090_ULK_MASK\n");
- schedule_work(&max98090->pll_work);
+ max98090_pll_work(max98090);
}
if (active & M98090_JDET_MASK) {
@@ -2422,7 +2420,6 @@ static int max98090_probe(struct snd_soc_component *component)
max98090_pll_det_enable_work);
INIT_WORK(&max98090->pll_det_disable_work,
max98090_pll_det_disable_work);
- INIT_WORK(&max98090->pll_work, max98090_pll_work);
/* Enable jack detection */
snd_soc_component_write(component, M98090_REG_JACK_DETECT,
@@ -2475,7 +2472,6 @@ static void max98090_remove(struct snd_soc_component *component)
cancel_delayed_work_sync(&max98090->jack_work);
cancel_delayed_work_sync(&max98090->pll_det_enable_work);
cancel_work_sync(&max98090->pll_det_disable_work);
- cancel_work_sync(&max98090->pll_work);
max98090->component = NULL;
}
diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h
index 57965cd678b4..a197114b0dad 100644
--- a/sound/soc/codecs/max98090.h
+++ b/sound/soc/codecs/max98090.h
@@ -1530,7 +1530,6 @@ struct max98090_priv {
struct delayed_work jack_work;
struct delayed_work pll_det_enable_work;
struct work_struct pll_det_disable_work;
- struct work_struct pll_work;
struct snd_soc_jack *jack;
unsigned int dai_fmt;
int tdm_slots;
diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c
index 528695cd6a1c..569f7ca227e6 100644
--- a/sound/soc/codecs/max98373.c
+++ b/sound/soc/codecs/max98373.c
@@ -843,8 +843,8 @@ static int max98373_resume(struct device *dev)
{
struct max98373_priv *max98373 = dev_get_drvdata(dev);
- max98373_reset(max98373, dev);
regcache_cache_only(max98373->regmap, false);
+ max98373_reset(max98373, dev);
regcache_sync(max98373->regmap);
return 0;
}
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
index 8600c5439e1e..2e4aa23b5a60 100644
--- a/sound/soc/codecs/max9867.c
+++ b/sound/soc/codecs/max9867.c
@@ -46,13 +46,13 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_micboost_tlv,
static const struct snd_kcontrol_new max9867_snd_controls[] = {
SOC_DOUBLE_R_TLV("Master Playback Volume", MAX9867_LEFTVOL,
- MAX9867_RIGHTVOL, 0, 41, 1, max9867_master_tlv),
+ MAX9867_RIGHTVOL, 0, 40, 1, max9867_master_tlv),
SOC_DOUBLE_R_TLV("Line Capture Volume", MAX9867_LEFTLINELVL,
MAX9867_RIGHTLINELVL, 0, 15, 1, max9867_line_tlv),
SOC_DOUBLE_R_TLV("Mic Capture Volume", MAX9867_LEFTMICGAIN,
MAX9867_RIGHTMICGAIN, 0, 20, 1, max9867_mic_tlv),
SOC_DOUBLE_R_TLV("Mic Boost Capture Volume", MAX9867_LEFTMICGAIN,
- MAX9867_RIGHTMICGAIN, 5, 4, 0, max9867_micboost_tlv),
+ MAX9867_RIGHTMICGAIN, 5, 3, 0, max9867_micboost_tlv),
SOC_SINGLE("Digital Sidetone Volume", MAX9867_SIDETONE, 0, 31, 1),
SOC_SINGLE_TLV("Digital Playback Volume", MAX9867_DACLEVEL, 0, 15, 1,
max9867_dac_tlv),
diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
index 368b6c09474b..c820d5a386f6 100644
--- a/sound/soc/codecs/msm8916-wcd-analog.c
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -306,7 +306,7 @@ struct pm8916_wcd_analog_priv {
};
static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" };
-static const char *const rdac2_mux_text[] = { "ZERO", "RX2", "RX1" };
+static const char *const rdac2_mux_text[] = { "RX1", "RX2" };
static const char *const hph_text[] = { "ZERO", "Switch", };
static const struct soc_enum hph_enum = SOC_ENUM_SINGLE_VIRT(
@@ -321,7 +321,7 @@ static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE_VIRT(
/* RDAC2 MUX */
static const struct soc_enum rdac2_mux_enum = SOC_ENUM_SINGLE(
- CDC_D_CDC_CONN_HPHR_DAC_CTL, 0, 3, rdac2_mux_text);
+ CDC_D_CDC_CONN_HPHR_DAC_CTL, 0, 2, rdac2_mux_text);
static const struct snd_kcontrol_new spkr_switch[] = {
SOC_DAPM_SINGLE("Switch", CDC_A_SPKR_DAC_CTL, 7, 1, 0)
@@ -391,9 +391,6 @@ static int pm8916_wcd_analog_enable_micbias_int(struct snd_soc_component
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_component_update_bits(component, CDC_A_MICB_1_INT_RBIAS,
- MICB_1_INT_TX2_INT_RBIAS_EN_MASK,
- MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE);
snd_soc_component_update_bits(component, reg, MICB_1_EN_PULL_DOWN_EN_MASK, 0);
snd_soc_component_update_bits(component, CDC_A_MICB_1_EN,
MICB_1_EN_OPA_STG2_TAIL_CURR_MASK,
@@ -443,6 +440,14 @@ static int pm8916_wcd_analog_enable_micbias_int1(struct
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_INT_RBIAS,
+ MICB_1_INT_TX1_INT_RBIAS_EN_MASK,
+ MICB_1_INT_TX1_INT_RBIAS_EN_ENABLE);
+ break;
+ }
+
return pm8916_wcd_analog_enable_micbias_int(component, event, w->reg,
wcd->micbias1_cap_mode);
}
@@ -553,6 +558,11 @@ static int pm8916_wcd_analog_enable_micbias_int2(struct
struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component);
switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, CDC_A_MICB_1_INT_RBIAS,
+ MICB_1_INT_TX2_INT_RBIAS_EN_MASK,
+ MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE);
+ break;
case SND_SOC_DAPM_POST_PMU:
pm8916_mbhc_configure_bias(wcd, true);
break;
@@ -888,10 +898,10 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("MIC BIAS External1", CDC_A_MICB_1_EN, 7, 0,
pm8916_wcd_analog_enable_micbias_ext1,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("MIC BIAS External2", CDC_A_MICB_2_EN, 7, 0,
pm8916_wcd_analog_enable_micbias_ext2,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_ADC_E("ADC1", NULL, CDC_A_TX_1_EN, 7, 0,
pm8916_wcd_analog_enable_adc,
diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c
index a63961861e55..1d56370fae69 100644
--- a/sound/soc/codecs/msm8916-wcd-digital.c
+++ b/sound/soc/codecs/msm8916-wcd-digital.c
@@ -206,6 +206,10 @@ static const char *const rx_mix1_text[] = {
"ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3"
};
+static const char * const rx_mix2_text[] = {
+ "ZERO", "IIR1", "IIR2"
+};
+
static const char *const dec_mux_text[] = {
"ZERO", "ADC1", "ADC2", "ADC3", "DMIC1", "DMIC2"
};
@@ -233,6 +237,16 @@ static const struct soc_enum rx3_mix1_inp_enum[] = {
SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B2_CTL, 0, 6, rx_mix1_text),
};
+/* RX1 MIX2 */
+static const struct soc_enum rx_mix2_inp1_chain_enum =
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B3_CTL,
+ 0, 3, rx_mix2_text);
+
+/* RX2 MIX2 */
+static const struct soc_enum rx2_mix2_inp1_chain_enum =
+ SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B3_CTL,
+ 0, 3, rx_mix2_text);
+
/* DEC */
static const struct soc_enum dec1_mux_enum = SOC_ENUM_SINGLE(
LPASS_CDC_CONN_TX_B1_CTL, 0, 6, dec_mux_text);
@@ -272,6 +286,10 @@ static const struct snd_kcontrol_new rx3_mix1_inp2_mux = SOC_DAPM_ENUM(
"RX3 MIX1 INP2 Mux", rx3_mix1_inp_enum[1]);
static const struct snd_kcontrol_new rx3_mix1_inp3_mux = SOC_DAPM_ENUM(
"RX3 MIX1 INP3 Mux", rx3_mix1_inp_enum[2]);
+static const struct snd_kcontrol_new rx1_mix2_inp1_mux = SOC_DAPM_ENUM(
+ "RX1 MIX2 INP1 Mux", rx_mix2_inp1_chain_enum);
+static const struct snd_kcontrol_new rx2_mix2_inp1_mux = SOC_DAPM_ENUM(
+ "RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum);
/* Digital Gain control -38.4 dB to +38.4 dB in 0.3 dB steps */
static const DECLARE_TLV_DB_SCALE(digital_gain, -3840, 30, 0);
@@ -338,6 +356,12 @@ static int msm8916_wcd_digital_enable_interpolator(
snd_soc_component_write(component, rx_gain_reg[w->shift],
snd_soc_component_read32(component, rx_gain_reg[w->shift]));
break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, LPASS_CDC_CLK_RX_RESET_CTL,
+ 1 << w->shift, 1 << w->shift);
+ snd_soc_component_update_bits(component, LPASS_CDC_CLK_RX_RESET_CTL,
+ 1 << w->shift, 0x0);
+ break;
}
return 0;
}
@@ -492,6 +516,10 @@ static const struct snd_soc_dapm_widget msm8916_wcd_digital_dapm_widgets[] = {
&rx3_mix1_inp2_mux),
SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0,
&rx3_mix1_inp3_mux),
+ SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+ &rx1_mix2_inp1_mux),
+ SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+ &rx2_mix2_inp1_mux),
SND_SOC_DAPM_MUX("CIC1 MUX", SND_SOC_NOPM, 0, 0, &cic1_mux),
SND_SOC_DAPM_MUX("CIC2 MUX", SND_SOC_NOPM, 0, 0, &cic2_mux),
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index ca568b9bf0f2..3966f8e25293 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -21,8 +21,7 @@
#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_3LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE)
+ SNDRV_PCM_FMTBIT_S24_LE)
#define PCM3168A_FMT_I2S 0x0
#define PCM3168A_FMT_LEFT_J 0x1
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
index 861210f6bf4f..4cbef9affffd 100644
--- a/sound/soc/codecs/pcm512x.c
+++ b/sound/soc/codecs/pcm512x.c
@@ -1564,13 +1564,15 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
}
pcm512x->sclk = devm_clk_get(dev, NULL);
- if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto err;
+ }
if (!IS_ERR(pcm512x->sclk)) {
ret = clk_prepare_enable(pcm512x->sclk);
if (ret != 0) {
dev_err(dev, "Failed to enable SCLK: %d\n", ret);
- return ret;
+ goto err;
}
}
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index adbae1f36a8a..747ca248bf10 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -2432,6 +2432,13 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component)
{
struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ /*
+ * soc_remove_component() force-disables jack and thus rt5640->jack
+ * could be NULL at the time of driver's module unloading.
+ */
+ if (!rt5640->jack)
+ return;
+
disable_irq(rt5640->irq);
rt5640_cancel_work(rt5640);
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 1c06b3b9218c..c83f7f5da96b 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -3270,6 +3270,9 @@ static void rt5645_jack_detect_work(struct work_struct *work)
snd_soc_jack_report(rt5645->mic_jack,
report, SND_JACK_MICROPHONE);
return;
+ case 4:
+ val = snd_soc_component_read32(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020;
+ break;
default: /* read rt5645 jd1_1 status */
val = snd_soc_component_read32(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000;
break;
@@ -3603,7 +3606,7 @@ static const struct rt5645_platform_data intel_braswell_platform_data = {
static const struct rt5645_platform_data buddy_platform_data = {
.dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
.dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
- .jd_mode = 3,
+ .jd_mode = 4,
.level_trigger_irq = true,
};
@@ -3622,6 +3625,12 @@ static const struct rt5645_platform_data asus_t100ha_platform_data = {
.inv_jd1_1 = true,
};
+static const struct rt5645_platform_data asus_t101ha_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC_DATA_IN2N,
+ .dmic2_data_pin = RT5645_DMIC2_DISABLE,
+ .jd_mode = 3,
+};
+
static const struct rt5645_platform_data lenovo_ideapad_miix_310_pdata = {
.jd_mode = 3,
.in2_diff = true,
@@ -3700,6 +3709,14 @@ static const struct dmi_system_id dmi_platform_data[] = {
.driver_data = (void *)&asus_t100ha_platform_data,
},
{
+ .ident = "ASUS T101HA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "T101HA"),
+ },
+ .driver_data = (void *)&asus_t101ha_platform_data,
+ },
+ {
.ident = "MINIX Z83-4",
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MINIX"),
@@ -3999,6 +4016,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
RT5645_JD1_MODE_1);
break;
case 3:
+ case 4:
regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
RT5645_JD1_MODE_MASK,
RT5645_JD1_MODE_2);
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index 762595de956c..c506c9305043 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -1770,6 +1770,9 @@ static int rt5651_detect_headset(struct snd_soc_component *component)
static bool rt5651_support_button_press(struct rt5651_priv *rt5651)
{
+ if (!rt5651->hp_jack)
+ return false;
+
/* Button press support only works with internal jack-detection */
return (rt5651->hp_jack->status & SND_JACK_MICROPHONE) &&
rt5651->gpiod_hp_det == NULL;
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index 70fee6849ab0..f21181734170 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -31,18 +31,19 @@
#include "rt5670.h"
#include "rt5670-dsp.h"
-#define RT5670_DEV_GPIO BIT(0)
-#define RT5670_IN2_DIFF BIT(1)
-#define RT5670_DMIC_EN BIT(2)
-#define RT5670_DMIC1_IN2P BIT(3)
-#define RT5670_DMIC1_GPIO6 BIT(4)
-#define RT5670_DMIC1_GPIO7 BIT(5)
-#define RT5670_DMIC2_INR BIT(6)
-#define RT5670_DMIC2_GPIO8 BIT(7)
-#define RT5670_DMIC3_GPIO5 BIT(8)
-#define RT5670_JD_MODE1 BIT(9)
-#define RT5670_JD_MODE2 BIT(10)
-#define RT5670_JD_MODE3 BIT(11)
+#define RT5670_DEV_GPIO BIT(0)
+#define RT5670_IN2_DIFF BIT(1)
+#define RT5670_DMIC_EN BIT(2)
+#define RT5670_DMIC1_IN2P BIT(3)
+#define RT5670_DMIC1_GPIO6 BIT(4)
+#define RT5670_DMIC1_GPIO7 BIT(5)
+#define RT5670_DMIC2_INR BIT(6)
+#define RT5670_DMIC2_GPIO8 BIT(7)
+#define RT5670_DMIC3_GPIO5 BIT(8)
+#define RT5670_JD_MODE1 BIT(9)
+#define RT5670_JD_MODE2 BIT(10)
+#define RT5670_JD_MODE3 BIT(11)
+#define RT5670_GPIO1_IS_EXT_SPK_EN BIT(12)
static unsigned long rt5670_quirk;
static unsigned int quirk_override;
@@ -1447,6 +1448,33 @@ static int rt5670_hp_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int rt5670_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
+
+ if (!rt5670->pdata.gpio1_is_ext_spk_en)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2,
+ RT5670_GP1_OUT_MASK, RT5670_GP1_OUT_HI);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2,
+ RT5670_GP1_OUT_MASK, RT5670_GP1_OUT_LO);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
static int rt5670_bst1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -1860,7 +1888,9 @@ static const struct snd_soc_dapm_widget rt5670_specific_dapm_widgets[] = {
};
static const struct snd_soc_dapm_widget rt5672_specific_dapm_widgets[] = {
- SND_SOC_DAPM_PGA("SPO Amp", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("SPO Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt5670_spk_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_OUTPUT("SPOLP"),
SND_SOC_DAPM_OUTPUT("SPOLN"),
SND_SOC_DAPM_OUTPUT("SPORP"),
@@ -2857,14 +2887,14 @@ static const struct dmi_system_id dmi_platform_intel_quirks[] = {
},
{
.callback = rt5670_quirk_cb,
- .ident = "Lenovo Thinkpad Tablet 10",
+ .ident = "Lenovo Miix 2 10",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"),
},
.driver_data = (unsigned long *)(RT5670_DMIC_EN |
RT5670_DMIC1_IN2P |
- RT5670_DEV_GPIO |
+ RT5670_GPIO1_IS_EXT_SPK_EN |
RT5670_JD_MODE2),
},
{
@@ -2924,6 +2954,10 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
rt5670->pdata.dev_gpio = true;
dev_info(&i2c->dev, "quirk dev_gpio\n");
}
+ if (rt5670_quirk & RT5670_GPIO1_IS_EXT_SPK_EN) {
+ rt5670->pdata.gpio1_is_ext_spk_en = true;
+ dev_info(&i2c->dev, "quirk GPIO1 is external speaker enable\n");
+ }
if (rt5670_quirk & RT5670_IN2_DIFF) {
rt5670->pdata.in2_diff = true;
dev_info(&i2c->dev, "quirk IN2_DIFF\n");
@@ -3023,6 +3057,13 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT);
}
+ if (rt5670->pdata.gpio1_is_ext_spk_en) {
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1,
+ RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_GPIO1);
+ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2,
+ RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT);
+ }
+
if (rt5670->pdata.jd_mode) {
regmap_update_bits(rt5670->regmap, RT5670_GLB_CLK,
RT5670_SCLK_SRC_MASK, RT5670_SCLK_SRC_RCCLK);
diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h
index a8c3e44770b8..de0203369b7c 100644
--- a/sound/soc/codecs/rt5670.h
+++ b/sound/soc/codecs/rt5670.h
@@ -757,7 +757,7 @@
#define RT5670_PWR_VREF2_BIT 4
#define RT5670_PWR_FV2 (0x1 << 3)
#define RT5670_PWR_FV2_BIT 3
-#define RT5670_LDO_SEL_MASK (0x3)
+#define RT5670_LDO_SEL_MASK (0x7)
#define RT5670_LDO_SEL_SFT 0
/* Power Management for Analog 2 (0x64) */
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index ba24b0c52aa8..93aa5669487f 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -294,6 +294,7 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
case RT5677_I2C_MASTER_CTRL7:
case RT5677_I2C_MASTER_CTRL8:
case RT5677_HAP_GENE_CTRL2:
+ case RT5677_PWR_ANLG2: /* Modified by DSP firmware */
case RT5677_PWR_DSP_ST:
case RT5677_PRIV_DATA:
case RT5677_ASRC_22:
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
index 78409dd11488..a0cce7452b45 100644
--- a/sound/soc/codecs/rt5682.c
+++ b/sound/soc/codecs/rt5682.c
@@ -72,6 +72,7 @@ struct rt5682_priv {
static const struct reg_sequence patch_list[] = {
{RT5682_HP_IMP_SENS_CTRL_19, 0x1000},
{RT5682_DAC_ADC_DIG_VOL1, 0xa020},
+ {RT5682_I2C_CTRL, 0x000f},
};
static const struct reg_default rt5682_reg[] = {
@@ -995,6 +996,16 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component,
{
struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
+ rt5682->hs_jack = hs_jack;
+
+ if (!hs_jack) {
+ regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
+ RT5682_JD1_EN_MASK, RT5682_JD1_DIS);
+ regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL,
+ RT5682_POW_JDH | RT5682_POW_JDL, 0);
+ return 0;
+ }
+
switch (rt5682->pdata.jd_src) {
case RT5682_JD1:
snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_2,
@@ -1032,8 +1043,6 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component,
break;
}
- rt5682->hs_jack = hs_jack;
-
return 0;
}
@@ -2473,6 +2482,7 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682)
mutex_lock(&rt5682->calibrate_mutex);
rt5682_reset(rt5682->regmap);
+ regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f);
regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2af);
usleep_range(15000, 20000);
regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2af);
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 8e5e48f6a24b..c58ad2a13e4e 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1343,7 +1343,8 @@ static int sgtl5000_set_power_regs(struct snd_soc_component *component)
* if vddio == vdda the source of charge pump should be
* assigned manually to VDDIO
*/
- if (vddio == vdda) {
+ if (regulator_is_equal(sgtl5000->supplies[VDDA].consumer,
+ sgtl5000->supplies[VDDIO].consumer)) {
lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD;
lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO <<
SGTL5000_VDDC_MAN_ASSN_SHIFT;
@@ -1643,6 +1644,40 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
dev_err(&client->dev,
"Error %d initializing CHIP_CLK_CTRL\n", ret);
+ /* Mute everything to avoid pop from the following power-up */
+ ret = regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_CTRL,
+ SGTL5000_CHIP_ANA_CTRL_DEFAULT);
+ if (ret) {
+ dev_err(&client->dev,
+ "Error %d muting outputs via CHIP_ANA_CTRL\n", ret);
+ goto disable_clk;
+ }
+
+ /*
+ * If VAG is powered-on (e.g. from previous boot), it would be disabled
+ * by the write to ANA_POWER in later steps of the probe code. This
+ * may create a loud pop even with all outputs muted. The proper way
+ * to circumvent this is disabling the bit first and waiting the proper
+ * cool-down time.
+ */
+ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, &value);
+ if (ret) {
+ dev_err(&client->dev, "Failed to read ANA_POWER: %d\n", ret);
+ goto disable_clk;
+ }
+ if (value & SGTL5000_VAG_POWERUP) {
+ ret = regmap_update_bits(sgtl5000->regmap,
+ SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_VAG_POWERUP,
+ 0);
+ if (ret) {
+ dev_err(&client->dev, "Error %d disabling VAG\n", ret);
+ goto disable_clk;
+ }
+
+ msleep(SGTL5000_VAG_POWERDOWN_DELAY);
+ }
+
/* Follow section 2.2.1.1 of AN3663 */
ana_pwr = SGTL5000_ANA_POWER_DEFAULT;
if (sgtl5000->num_supplies <= VDDD) {
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
index 18cae08bbd3a..066517e352a7 100644
--- a/sound/soc/codecs/sgtl5000.h
+++ b/sound/soc/codecs/sgtl5000.h
@@ -233,6 +233,7 @@
/*
* SGTL5000_CHIP_ANA_CTRL
*/
+#define SGTL5000_CHIP_ANA_CTRL_DEFAULT 0x0133
#define SGTL5000_LINE_OUT_MUTE 0x0100
#define SGTL5000_HP_SEL_MASK 0x0040
#define SGTL5000_HP_SEL_SHIFT 6
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
index 20798fa2988a..be4fd16bcad7 100644
--- a/sound/soc/codecs/tas571x.c
+++ b/sound/soc/codecs/tas571x.c
@@ -820,8 +820,10 @@ static int tas571x_i2c_probe(struct i2c_client *client,
priv->regmap = devm_regmap_init(dev, NULL, client,
priv->chip->regmap_config);
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ goto disable_regs;
+ }
priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW);
if (IS_ERR(priv->pdn_gpio)) {
@@ -845,7 +847,7 @@ static int tas571x_i2c_probe(struct i2c_client *client,
ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0);
if (ret)
- return ret;
+ goto disable_regs;
usleep_range(50000, 60000);
@@ -861,12 +863,20 @@ static int tas571x_i2c_probe(struct i2c_client *client,
*/
ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0);
if (ret)
- return ret;
+ goto disable_regs;
}
- return devm_snd_soc_register_component(&client->dev,
+ ret = devm_snd_soc_register_component(&client->dev,
&priv->component_driver,
&tas571x_dai, 1);
+ if (ret)
+ goto disable_regs;
+
+ return ret;
+
+disable_regs:
+ regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies);
+ return ret;
}
static int tas571x_i2c_remove(struct i2c_client *client)
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index cf64e109c658..7b087d94141b 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -2410,6 +2410,8 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
err_pm_runtime:
pm_runtime_disable(&i2c->dev);
+ if (i2c->irq)
+ free_irq(i2c->irq, wm2200);
err_reset:
if (wm2200->pdata.reset)
gpio_set_value_cansleep(wm2200->pdata.reset, 0);
@@ -2426,12 +2428,15 @@ static int wm2200_i2c_remove(struct i2c_client *i2c)
{
struct wm2200_priv *wm2200 = i2c_get_clientdata(i2c);
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm2200);
if (wm2200->pdata.reset)
gpio_set_value_cansleep(wm2200->pdata.reset, 0);
if (wm2200->pdata.ldo_ena)
gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
+ regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
+ wm2200->core_supplies);
return 0;
}
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 4af0e519e623..91cc63c5a51f 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2617,6 +2617,7 @@ static int wm5100_i2c_probe(struct i2c_client *i2c,
return ret;
err_reset:
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm5100);
wm5100_free_gpio(i2c);
@@ -2640,6 +2641,7 @@ static int wm5100_i2c_remove(struct i2c_client *i2c)
{
struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c);
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm5100);
wm5100_free_gpio(i2c);
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 5ebdd1d9afde..036b5c214320 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -1903,6 +1903,7 @@ static int wm8904_set_bias_level(struct snd_soc_component *component,
snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0,
WM8904_BIAS_ENA, 0);
+ snd_soc_component_write(component, WM8904_SW_RESET_AND_ID, 0);
regcache_cache_only(wm8904->regmap, true);
regcache_mark_dirty(wm8904->regmap);
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 55112c1bba5e..6cf0f6612bda 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -860,8 +860,7 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
wm8960->is_stream_in_use[tx] = true;
- if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON &&
- !wm8960->is_stream_in_use[!tx])
+ if (!wm8960->is_stream_in_use[!tx])
return wm8960_configure_clocking(component);
return 0;
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 3e5c69fbc33a..d9d59f45833f 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -2788,7 +2788,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
if (target % Fref == 0) {
fll_div->theta = 0;
- fll_div->lambda = 0;
+ fll_div->lambda = 1;
} else {
gcd_fll = gcd(target, fratio * Fref);
@@ -2858,7 +2858,7 @@ static int wm8962_set_fll(struct snd_soc_component *component, int fll_id, int s
return -EINVAL;
}
- if (fll_div.theta || fll_div.lambda)
+ if (fll_div.theta)
fll1 |= WM8962_FLL_FRAC;
/* Stop the FLL while we reconfigure */
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index c3d06e8bc54f..d5fb7f5dd551 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -533,13 +533,10 @@ static SOC_ENUM_SINGLE_DECL(dac_osr,
static SOC_ENUM_SINGLE_DECL(adc_osr,
WM8994_OVERSAMPLING, 1, osr_text);
-static const struct snd_kcontrol_new wm8994_snd_controls[] = {
+static const struct snd_kcontrol_new wm8994_common_snd_controls[] = {
SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME,
WM8994_AIF1_ADC1_RIGHT_VOLUME,
1, 119, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("AIF1ADC2 Volume", WM8994_AIF1_ADC2_LEFT_VOLUME,
- WM8994_AIF1_ADC2_RIGHT_VOLUME,
- 1, 119, 0, digital_tlv),
SOC_DOUBLE_R_TLV("AIF2ADC Volume", WM8994_AIF2_ADC_LEFT_VOLUME,
WM8994_AIF2_ADC_RIGHT_VOLUME,
1, 119, 0, digital_tlv),
@@ -556,8 +553,6 @@ SOC_ENUM("AIF2DACR Source", aif2dacr_src),
SOC_DOUBLE_R_TLV("AIF1DAC1 Volume", WM8994_AIF1_DAC1_LEFT_VOLUME,
WM8994_AIF1_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("AIF1DAC2 Volume", WM8994_AIF1_DAC2_LEFT_VOLUME,
- WM8994_AIF1_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
SOC_DOUBLE_R_TLV("AIF2DAC Volume", WM8994_AIF2_DAC_LEFT_VOLUME,
WM8994_AIF2_DAC_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
@@ -565,17 +560,12 @@ SOC_SINGLE_TLV("AIF1 Boost Volume", WM8994_AIF1_CONTROL_2, 10, 3, 0, aif_tlv),
SOC_SINGLE_TLV("AIF2 Boost Volume", WM8994_AIF2_CONTROL_2, 10, 3, 0, aif_tlv),
SOC_SINGLE("AIF1DAC1 EQ Switch", WM8994_AIF1_DAC1_EQ_GAINS_1, 0, 1, 0),
-SOC_SINGLE("AIF1DAC2 EQ Switch", WM8994_AIF1_DAC2_EQ_GAINS_1, 0, 1, 0),
SOC_SINGLE("AIF2 EQ Switch", WM8994_AIF2_EQ_GAINS_1, 0, 1, 0),
WM8994_DRC_SWITCH("AIF1DAC1 DRC Switch", WM8994_AIF1_DRC1_1, 2),
WM8994_DRC_SWITCH("AIF1ADC1L DRC Switch", WM8994_AIF1_DRC1_1, 1),
WM8994_DRC_SWITCH("AIF1ADC1R DRC Switch", WM8994_AIF1_DRC1_1, 0),
-WM8994_DRC_SWITCH("AIF1DAC2 DRC Switch", WM8994_AIF1_DRC2_1, 2),
-WM8994_DRC_SWITCH("AIF1ADC2L DRC Switch", WM8994_AIF1_DRC2_1, 1),
-WM8994_DRC_SWITCH("AIF1ADC2R DRC Switch", WM8994_AIF1_DRC2_1, 0),
-
WM8994_DRC_SWITCH("AIF2DAC DRC Switch", WM8994_AIF2_DRC_1, 2),
WM8994_DRC_SWITCH("AIF2ADCL DRC Switch", WM8994_AIF2_DRC_1, 1),
WM8994_DRC_SWITCH("AIF2ADCR DRC Switch", WM8994_AIF2_DRC_1, 0),
@@ -594,9 +584,6 @@ SOC_SINGLE("Sidetone HPF Switch", WM8994_SIDETONE, 6, 1, 0),
SOC_ENUM("AIF1ADC1 HPF Mode", aif1adc1_hpf),
SOC_DOUBLE("AIF1ADC1 HPF Switch", WM8994_AIF1_ADC1_FILTERS, 12, 11, 1, 0),
-SOC_ENUM("AIF1ADC2 HPF Mode", aif1adc2_hpf),
-SOC_DOUBLE("AIF1ADC2 HPF Switch", WM8994_AIF1_ADC2_FILTERS, 12, 11, 1, 0),
-
SOC_ENUM("AIF2ADC HPF Mode", aif2adc_hpf),
SOC_DOUBLE("AIF2ADC HPF Switch", WM8994_AIF2_ADC_FILTERS, 12, 11, 1, 0),
@@ -637,6 +624,24 @@ SOC_SINGLE("AIF2DAC 3D Stereo Switch", WM8994_AIF2_DAC_FILTERS_2,
8, 1, 0),
};
+/* Controls not available on WM1811 */
+static const struct snd_kcontrol_new wm8994_snd_controls[] = {
+SOC_DOUBLE_R_TLV("AIF1ADC2 Volume", WM8994_AIF1_ADC2_LEFT_VOLUME,
+ WM8994_AIF1_ADC2_RIGHT_VOLUME,
+ 1, 119, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF1DAC2 Volume", WM8994_AIF1_DAC2_LEFT_VOLUME,
+ WM8994_AIF1_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+
+SOC_SINGLE("AIF1DAC2 EQ Switch", WM8994_AIF1_DAC2_EQ_GAINS_1, 0, 1, 0),
+
+WM8994_DRC_SWITCH("AIF1DAC2 DRC Switch", WM8994_AIF1_DRC2_1, 2),
+WM8994_DRC_SWITCH("AIF1ADC2L DRC Switch", WM8994_AIF1_DRC2_1, 1),
+WM8994_DRC_SWITCH("AIF1ADC2R DRC Switch", WM8994_AIF1_DRC2_1, 0),
+
+SOC_ENUM("AIF1ADC2 HPF Mode", aif1adc2_hpf),
+SOC_DOUBLE("AIF1ADC2 HPF Switch", WM8994_AIF1_ADC2_FILTERS, 12, 11, 1, 0),
+};
+
static const struct snd_kcontrol_new wm8994_eq_controls[] = {
SOC_SINGLE_TLV("AIF1DAC1 EQ1 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 11, 31, 0,
eq_tlv),
@@ -4258,13 +4263,15 @@ static int wm8994_component_probe(struct snd_soc_component *component)
wm8994_handle_pdata(wm8994);
wm_hubs_add_analogue_controls(component);
- snd_soc_add_component_controls(component, wm8994_snd_controls,
- ARRAY_SIZE(wm8994_snd_controls));
+ snd_soc_add_component_controls(component, wm8994_common_snd_controls,
+ ARRAY_SIZE(wm8994_common_snd_controls));
snd_soc_dapm_new_controls(dapm, wm8994_dapm_widgets,
ARRAY_SIZE(wm8994_dapm_widgets));
switch (control->type) {
case WM8994:
+ snd_soc_add_component_controls(component, wm8994_snd_controls,
+ ARRAY_SIZE(wm8994_snd_controls));
snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets,
ARRAY_SIZE(wm8994_specific_dapm_widgets));
if (control->revision < 4) {
@@ -4284,8 +4291,10 @@ static int wm8994_component_probe(struct snd_soc_component *component)
}
break;
case WM8958:
+ snd_soc_add_component_controls(component, wm8994_snd_controls,
+ ARRAY_SIZE(wm8994_snd_controls));
snd_soc_add_component_controls(component, wm8958_snd_controls,
- ARRAY_SIZE(wm8958_snd_controls));
+ ARRAY_SIZE(wm8958_snd_controls));
snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
ARRAY_SIZE(wm8958_dapm_widgets));
if (control->revision < 1) {
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 40ba71d00c71..c1ec559c1e96 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -1282,8 +1282,7 @@ static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
}
if (in) {
- if (in & WMFW_CTL_FLAG_READABLE)
- out |= rd;
+ out |= rd;
if (in & WMFW_CTL_FLAG_WRITEABLE)
out |= wr;
if (in & WMFW_CTL_FLAG_VOLATILE)
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index 01052a0808b0..5aee6b8366d2 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -241,6 +241,7 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
ret = dmaengine_slave_config(pair->dma_chan[dir], &config_be);
if (ret) {
dev_err(dev, "failed to config DMA channel for Back-End\n");
+ dma_release_channel(pair->dma_chan[dir]);
return ret;
}
diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c
index 3897a54a11fe..fe54b32b1eb0 100644
--- a/sound/soc/fsl/fsl_audmix.c
+++ b/sound/soc/fsl/fsl_audmix.c
@@ -286,6 +286,7 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned long lock_flags;
/* Capture stream shall not be handled */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
@@ -295,12 +296,16 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ spin_lock_irqsave(&priv->lock, lock_flags);
priv->tdms |= BIT(dai->driver->id);
+ spin_unlock_irqrestore(&priv->lock, lock_flags);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ spin_lock_irqsave(&priv->lock, lock_flags);
priv->tdms &= ~BIT(dai->driver->id);
+ spin_unlock_irqrestore(&priv->lock, lock_flags);
break;
default:
return -EINVAL;
@@ -493,6 +498,7 @@ static int fsl_audmix_probe(struct platform_device *pdev)
return PTR_ERR(priv->ipg_clk);
}
+ spin_lock_init(&priv->lock);
platform_set_drvdata(pdev, priv);
pm_runtime_enable(dev);
@@ -501,15 +507,20 @@ static int fsl_audmix_probe(struct platform_device *pdev)
ARRAY_SIZE(fsl_audmix_dai));
if (ret) {
dev_err(dev, "failed to register ASoC DAI\n");
- return ret;
+ goto err_disable_pm;
}
priv->pdev = platform_device_register_data(dev, mdrv, 0, NULL, 0);
if (IS_ERR(priv->pdev)) {
ret = PTR_ERR(priv->pdev);
dev_err(dev, "failed to register platform %s: %d\n", mdrv, ret);
+ goto err_disable_pm;
}
+ return 0;
+
+err_disable_pm:
+ pm_runtime_disable(dev);
return ret;
}
@@ -517,6 +528,8 @@ static int fsl_audmix_remove(struct platform_device *pdev)
{
struct fsl_audmix *priv = dev_get_drvdata(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
if (priv->pdev)
platform_device_unregister(priv->pdev);
diff --git a/sound/soc/fsl/fsl_audmix.h b/sound/soc/fsl/fsl_audmix.h
index 7812ffec45c5..479f05695d53 100644
--- a/sound/soc/fsl/fsl_audmix.h
+++ b/sound/soc/fsl/fsl_audmix.h
@@ -96,6 +96,7 @@ struct fsl_audmix {
struct platform_device *pdev;
struct regmap *regmap;
struct clk *ipg_clk;
+ spinlock_t lock; /* Protect tdms */
u8 tdms;
};
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 8593269156bd..5e8078ba0a5d 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -907,12 +907,24 @@ static int fsl_sai_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
&fsl_sai_dai, 1);
if (ret)
- return ret;
+ goto err_pm_disable;
- if (sai->sai_on_imx)
- return imx_pcm_dma_init(pdev, IMX_SAI_DMABUF_SIZE);
- else
- return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (sai->sai_on_imx) {
+ ret = imx_pcm_dma_init(pdev, IMX_SAI_DMABUF_SIZE);
+ if (ret)
+ goto err_pm_disable;
+ } else {
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ return ret;
+
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
}
static int fsl_sai_remove(struct platform_device *pdev)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index d83be26d6446..0e2bdad373d6 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -678,8 +678,9 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
struct regmap *regs = ssi->regs;
u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i;
unsigned long clkrate, baudrate, tmprate;
- unsigned int slots = params_channels(hw_params);
- unsigned int slot_width = 32;
+ unsigned int channels = params_channels(hw_params);
+ unsigned int slot_width = params_width(hw_params);
+ unsigned int slots = 2;
u64 sub, savesub = 100000;
unsigned int freq;
bool baudclk_is_used;
@@ -688,10 +689,14 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
/* Override slots and slot_width if being specifically set... */
if (ssi->slots)
slots = ssi->slots;
- /* ...but keep 32 bits if slots is 2 -- I2S Master mode */
- if (ssi->slot_width && slots != 2)
+ if (ssi->slot_width)
slot_width = ssi->slot_width;
+ /* ...but force 32 bits for stereo audio using I2S Master Mode */
+ if (channels == 2 &&
+ (ssi->i2s_net & SSI_SCR_I2S_MODE_MASK) == SSI_SCR_I2S_MODE_MASTER)
+ slot_width = 32;
+
/* Generate bit clock based on the slot number and slot width */
freq = slots * slot_width * params_rate(hw_params);
diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c
index 9aaf3e5b45b9..a0f5c4a37ceb 100644
--- a/sound/soc/fsl/imx-audmix.c
+++ b/sound/soc/fsl/imx-audmix.c
@@ -300,14 +300,14 @@ static int imx_audmix_probe(struct platform_device *pdev)
priv->card.num_configs = priv->num_dai_conf;
priv->card.dapm_routes = priv->dapm_routes;
priv->card.num_dapm_routes = priv->num_dapm_routes;
- priv->card.dev = pdev->dev.parent;
+ priv->card.dev = &pdev->dev;
priv->card.owner = THIS_MODULE;
priv->card.name = "imx-audmix";
platform_set_drvdata(pdev, &priv->card);
snd_soc_card_set_drvdata(&priv->card, priv);
- ret = devm_snd_soc_register_card(pdev->dev.parent, &priv->card);
+ ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed\n");
return ret;
diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c
index fdd2c73fd2fa..869fe0068cbd 100644
--- a/sound/soc/img/img-i2s-in.c
+++ b/sound/soc/img/img-i2s-in.c
@@ -482,6 +482,7 @@ static int img_i2s_in_probe(struct platform_device *pdev)
if (IS_ERR(rst)) {
if (PTR_ERR(rst) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
+ pm_runtime_put(&pdev->dev);
goto err_suspend;
}
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 1f868da106b7..48f2b91b3bed 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -59,6 +59,9 @@ config SND_SOC_INTEL_HASWELL
If you have a Intel Haswell or Broadwell platform connected to
an I2S codec, then enable this option by saying Y or m. This is
typically used for Chromebooks. This is a recommended option.
+ This option is mutually exclusive with the SOF support on
+ Broadwell. If you want to enable SOF on Broadwell, you need to
+ deselect this option first.
config SND_SOC_INTEL_BAYTRAIL
tristate "Baytrail (legacy) Platforms"
diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c
index baef461a99f1..df8f7994d3b7 100644
--- a/sound/soc/intel/atom/sst-atom-controls.c
+++ b/sound/soc/intel/atom/sst-atom-controls.c
@@ -966,7 +966,9 @@ static int sst_set_be_modules(struct snd_soc_dapm_widget *w,
dev_dbg(c->dev, "Enter: widget=%s\n", w->name);
if (SND_SOC_DAPM_EVENT_ON(event)) {
+ mutex_lock(&drv->lock);
ret = sst_send_slot_map(drv);
+ mutex_unlock(&drv->lock);
if (ret)
return ret;
ret = sst_send_pipe_module_params(w, k);
@@ -1333,7 +1335,7 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
dai->capture_widget->name);
w = dai->capture_widget;
snd_soc_dapm_widget_for_each_source_path(w, p) {
- if (p->connected && !p->connected(w, p->sink))
+ if (p->connected && !p->connected(w, p->source))
continue;
if (p->connect && p->source->power &&
diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c
index d952719bc098..5862fe968083 100644
--- a/sound/soc/intel/atom/sst/sst_pci.c
+++ b/sound/soc/intel/atom/sst/sst_pci.c
@@ -99,7 +99,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram);
do_release_regions:
pci_release_regions(pci);
- return 0;
+ return ret;
}
/*
diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c
index 771df36fbbaf..82d4fdacfcf7 100644
--- a/sound/soc/intel/boards/bxt_rt298.c
+++ b/sound/soc/intel/boards/bxt_rt298.c
@@ -537,6 +537,7 @@ static int bxt_card_late_probe(struct snd_soc_card *card)
/* broxton audio machine driver for SPT + RT298S */
static struct snd_soc_card broxton_rt298 = {
.name = "broxton-rt298",
+ .owner = THIS_MODULE,
.dai_link = broxton_rt298_dais,
.num_links = ARRAY_SIZE(broxton_rt298_dais),
.controls = broxton_controls,
@@ -552,6 +553,7 @@ static struct snd_soc_card broxton_rt298 = {
static struct snd_soc_card geminilake_rt298 = {
.name = "geminilake-rt298",
+ .owner = THIS_MODULE,
.dai_link = broxton_rt298_dais,
.num_links = ARRAY_SIZE(broxton_rt298_dais),
.controls = broxton_controls,
diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c
index c360ebc3ccc7..1d2fe84bd3d7 100644
--- a/sound/soc/intel/boards/bytcht_es8316.c
+++ b/sound/soc/intel/boards/bytcht_es8316.c
@@ -441,7 +441,8 @@ static const struct dmi_system_id byt_cht_es8316_quirk_table[] = {
DMI_MATCH(DMI_SYS_VENDOR, "IRBIS"),
DMI_MATCH(DMI_PRODUCT_NAME, "NB41"),
},
- .driver_data = (void *)(BYT_CHT_ES8316_INTMIC_IN2_MAP
+ .driver_data = (void *)(BYT_CHT_ES8316_SSP0
+ | BYT_CHT_ES8316_INTMIC_IN2_MAP
| BYT_CHT_ES8316_JD_INVERTED),
},
{ /* Teclast X98 Plus II */
@@ -546,8 +547,10 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
if (cnt) {
ret = device_add_properties(codec_dev, props);
- if (ret)
+ if (ret) {
+ put_device(codec_dev);
return ret;
+ }
}
devm_acpi_dev_add_driver_gpios(codec_dev, byt_cht_es8316_gpios);
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index b906cfd5f97d..be73a54c1bf3 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -405,10 +405,12 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"),
},
- .driver_data = (void *)(BYT_RT5640_IN1_MAP |
- BYT_RT5640_MCLK_EN |
- BYT_RT5640_SSP0_AIF1),
-
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
},
{
.matches = {
@@ -589,6 +591,17 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_SSP0_AIF1 |
BYT_RT5640_MCLK_EN),
},
+ {
+ /* MPMAN MPWIN895CL */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MPMAN"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MPWIN8900CL"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
{ /* MSI S100 tablet */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."),
@@ -705,13 +718,17 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_MCLK_EN),
},
{
+ /* Teclast X89 */
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"),
DMI_MATCH(DMI_BOARD_NAME, "tPAD"),
},
.driver_data = (void *)(BYT_RT5640_IN3_MAP |
- BYT_RT5640_MCLK_EN |
- BYT_RT5640_SSP0_AIF1),
+ BYT_RT5640_JD_SRC_JD1_IN4P |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_1P0 |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
},
{ /* Toshiba Satellite Click Mini L9W-B */
.matches = {
@@ -725,6 +742,30 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_SSP0_AIF1 |
BYT_RT5640_MCLK_EN),
},
+ { /* Toshiba Encore WT8-A */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TOSHIBA WT8-A"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_JD_NOT_INV |
+ BYT_RT5640_MCLK_EN),
+ },
+ { /* Toshiba Encore WT10-A */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TOSHIBA WT10-A-103"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD1_IN4P |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF2 |
+ BYT_RT5640_MCLK_EN),
+ },
{ /* Catch-all for generic Insyde tablets, must be last */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 2c07ec8b42ae..c406528e4c9c 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -48,6 +48,7 @@ struct cht_mc_private {
#define CHT_RT5645_SSP2_AIF2 BIT(16) /* default is using AIF1 */
#define CHT_RT5645_SSP0_AIF1 BIT(17)
#define CHT_RT5645_SSP0_AIF2 BIT(18)
+#define CHT_RT5645_PMC_PLT_CLK_0 BIT(19)
static unsigned long cht_rt5645_quirk = 0;
@@ -59,6 +60,8 @@ static void log_quirks(struct device *dev)
dev_info(dev, "quirk SSP0_AIF1 enabled");
if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)
dev_info(dev, "quirk SSP0_AIF2 enabled");
+ if (cht_rt5645_quirk & CHT_RT5645_PMC_PLT_CLK_0)
+ dev_info(dev, "quirk PMC_PLT_CLK_0 enabled");
}
static int platform_clock_control(struct snd_soc_dapm_widget *w,
@@ -226,16 +229,22 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-/* uncomment when we have a real quirk
static int cht_rt5645_quirk_cb(const struct dmi_system_id *id)
{
cht_rt5645_quirk = (unsigned long)id->driver_data;
return 1;
}
-*/
static const struct dmi_system_id cht_rt5645_quirk_table[] = {
{
+ /* Strago family Chromebooks */
+ .callback = cht_rt5645_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_Strago"),
+ },
+ .driver_data = (void *)CHT_RT5645_PMC_PLT_CLK_0,
+ },
+ {
},
};
@@ -530,6 +539,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
int dai_index = 0;
int ret_val = 0;
int i;
+ const char *mclk_name;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
@@ -665,11 +675,15 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
if (ret_val)
return ret_val;
- drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
+ if (cht_rt5645_quirk & CHT_RT5645_PMC_PLT_CLK_0)
+ mclk_name = "pmc_plt_clk_0";
+ else
+ mclk_name = "pmc_plt_clk_3";
+
+ drv->mclk = devm_clk_get(&pdev->dev, mclk_name);
if (IS_ERR(drv->mclk)) {
- dev_err(&pdev->dev,
- "Failed to get MCLK from pmc_plt_clk_3: %ld\n",
- PTR_ERR(drv->mclk));
+ dev_err(&pdev->dev, "Failed to get MCLK from %s: %ld\n",
+ mclk_name, PTR_ERR(drv->mclk));
return PTR_ERR(drv->mclk);
}
diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
index 39988b26a434..0090baae1b2d 100644
--- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
+++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
@@ -414,6 +414,9 @@ static int kabylake_dmic_startup(struct snd_pcm_substream *substream)
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
dmic_constraints);
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
+
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
}
@@ -621,7 +624,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card)
* kabylake audio machine driver for MAX98927 + RT5514 + RT5663
*/
static struct snd_soc_card kabylake_audio_card = {
- .name = "kbl_r5514_5663_max",
+ .name = "kbl-r5514-5663-max",
.owner = THIS_MODULE,
.dai_link = kabylake_dais,
.num_links = ARRAY_SIZE(kabylake_dais),
diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c
index fc52d3a32354..5d5b8ca1002f 100644
--- a/sound/soc/intel/boards/skl_hda_dsp_generic.c
+++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c
@@ -86,7 +86,7 @@ skl_hda_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link)
}
static struct snd_soc_card hda_soc_card = {
- .name = "skl_hda_card",
+ .name = "hda-dsp",
.owner = THIS_MODULE,
.dai_link = skl_hda_be_dai_links,
.dapm_widgets = skl_hda_widgets,
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
index 3343dbcd506f..5a6e593b0962 100644
--- a/sound/soc/intel/boards/sof_rt5682.c
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -292,7 +292,7 @@ static int speaker_codec_init(struct snd_soc_pcm_runtime *rtd)
/* sof audio machine driver for rt5682 codec */
static struct snd_soc_card sof_audio_card_rt5682 = {
- .name = "sof_rt5682",
+ .name = "rt5682", /* the sof- prefix is added by the core */
.owner = THIS_MODULE,
.controls = sof_controls,
.num_controls = ARRAY_SIZE(sof_controls),
@@ -498,6 +498,14 @@ static int sof_audio_probe(struct platform_device *pdev)
if (!ctx)
return -ENOMEM;
+ mach = (&pdev->dev)->platform_data;
+
+ /* A speaker amp might not be present when the quirk claims one is.
+ * Detect this via whether the machine driver match includes quirk_data.
+ */
+ if ((sof_rt5682_quirk & SOF_SPEAKER_AMP_PRESENT) && !mach->quirk_data)
+ sof_rt5682_quirk &= ~SOF_SPEAKER_AMP_PRESENT;
+
if (x86_match_cpu(legacy_cpi_ids)) {
is_legacy_cpu = 1;
dmic_num = 0;
@@ -533,7 +541,6 @@ static int sof_audio_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
sof_audio_card_rt5682.dev = &pdev->dev;
- mach = (&pdev->dev)->platform_data;
/* set platform name for each dailink */
ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_rt5682,
@@ -547,8 +554,24 @@ static int sof_audio_probe(struct platform_device *pdev)
&sof_audio_card_rt5682);
}
+static int sof_rt5682_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct snd_soc_component *component = NULL;
+
+ for_each_card_components(card, component) {
+ if (!strcmp(component->name, rt5682_component[0].name)) {
+ snd_soc_component_set_jack(component, NULL, NULL);
+ break;
+ }
+ }
+
+ return 0;
+}
+
static struct platform_driver sof_audio = {
.probe = sof_audio_probe,
+ .remove = sof_rt5682_remove,
.driver = {
.name = "sof_rt5682",
.pm = &snd_soc_pm_ops,
diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c
index bffc6a9619fc..48970349b01c 100644
--- a/sound/soc/intel/skylake/skl-debug.c
+++ b/sound/soc/intel/skylake/skl-debug.c
@@ -34,8 +34,8 @@ static ssize_t skl_print_pins(struct skl_module_pin *m_pin, char *buf,
int i;
ssize_t ret = 0;
- for (i = 0; i < max_pin; i++)
- ret += snprintf(buf + size, MOD_BUF - size,
+ for (i = 0; i < max_pin; i++) {
+ ret += scnprintf(buf + size, MOD_BUF - size,
"%s %d\n\tModule %d\n\tInstance %d\n\t"
"In-used %s\n\tType %s\n"
"\tState %d\n\tIndex %d\n",
@@ -45,13 +45,15 @@ static ssize_t skl_print_pins(struct skl_module_pin *m_pin, char *buf,
m_pin[i].in_use ? "Used" : "Unused",
m_pin[i].is_dynamic ? "Dynamic" : "Static",
m_pin[i].pin_state, i);
+ size += ret;
+ }
return ret;
}
static ssize_t skl_print_fmt(struct skl_module_fmt *fmt, char *buf,
ssize_t size, bool direction)
{
- return snprintf(buf + size, MOD_BUF - size,
+ return scnprintf(buf + size, MOD_BUF - size,
"%s\n\tCh %d\n\tFreq %d\n\tBit depth %d\n\t"
"Valid bit depth %d\n\tCh config %#x\n\tInterleaving %d\n\t"
"Sample Type %d\n\tCh Map %#x\n",
@@ -73,16 +75,16 @@ static ssize_t module_read(struct file *file, char __user *user_buf,
if (!buf)
return -ENOMEM;
- ret = snprintf(buf, MOD_BUF, "Module:\n\tUUID %pUL\n\tModule id %d\n"
+ ret = scnprintf(buf, MOD_BUF, "Module:\n\tUUID %pUL\n\tModule id %d\n"
"\tInstance id %d\n\tPvt_id %d\n", mconfig->guid,
mconfig->id.module_id, mconfig->id.instance_id,
mconfig->id.pvt_id);
- ret += snprintf(buf + ret, MOD_BUF - ret,
+ ret += scnprintf(buf + ret, MOD_BUF - ret,
"Resources:\n\tMCPS %#x\n\tIBS %#x\n\tOBS %#x\t\n",
mconfig->mcps, mconfig->ibs, mconfig->obs);
- ret += snprintf(buf + ret, MOD_BUF - ret,
+ ret += scnprintf(buf + ret, MOD_BUF - ret,
"Module data:\n\tCore %d\n\tIn queue %d\n\t"
"Out queue %d\n\tType %s\n",
mconfig->core_id, mconfig->max_in_queue,
@@ -92,38 +94,38 @@ static ssize_t module_read(struct file *file, char __user *user_buf,
ret += skl_print_fmt(mconfig->in_fmt, buf, ret, true);
ret += skl_print_fmt(mconfig->out_fmt, buf, ret, false);
- ret += snprintf(buf + ret, MOD_BUF - ret,
+ ret += scnprintf(buf + ret, MOD_BUF - ret,
"Fixup:\n\tParams %#x\n\tConverter %#x\n",
mconfig->params_fixup, mconfig->converter);
- ret += snprintf(buf + ret, MOD_BUF - ret,
+ ret += scnprintf(buf + ret, MOD_BUF - ret,
"Module Gateway:\n\tType %#x\n\tVbus %#x\n\tHW conn %#x\n\tSlot %#x\n",
mconfig->dev_type, mconfig->vbus_id,
mconfig->hw_conn_type, mconfig->time_slot);
- ret += snprintf(buf + ret, MOD_BUF - ret,
+ ret += scnprintf(buf + ret, MOD_BUF - ret,
"Pipeline:\n\tID %d\n\tPriority %d\n\tConn Type %d\n\t"
"Pages %#x\n", mconfig->pipe->ppl_id,
mconfig->pipe->pipe_priority, mconfig->pipe->conn_type,
mconfig->pipe->memory_pages);
- ret += snprintf(buf + ret, MOD_BUF - ret,
+ ret += scnprintf(buf + ret, MOD_BUF - ret,
"\tParams:\n\t\tHost DMA %d\n\t\tLink DMA %d\n",
mconfig->pipe->p_params->host_dma_id,
mconfig->pipe->p_params->link_dma_id);
- ret += snprintf(buf + ret, MOD_BUF - ret,
+ ret += scnprintf(buf + ret, MOD_BUF - ret,
"\tPCM params:\n\t\tCh %d\n\t\tFreq %d\n\t\tFormat %d\n",
mconfig->pipe->p_params->ch,
mconfig->pipe->p_params->s_freq,
mconfig->pipe->p_params->s_fmt);
- ret += snprintf(buf + ret, MOD_BUF - ret,
+ ret += scnprintf(buf + ret, MOD_BUF - ret,
"\tLink %#x\n\tStream %#x\n",
mconfig->pipe->p_params->linktype,
mconfig->pipe->p_params->stream);
- ret += snprintf(buf + ret, MOD_BUF - ret,
+ ret += scnprintf(buf + ret, MOD_BUF - ret,
"\tState %d\n\tPassthru %s\n",
mconfig->pipe->state,
mconfig->pipe->passthru ? "true" : "false");
@@ -133,7 +135,7 @@ static ssize_t module_read(struct file *file, char __user *user_buf,
ret += skl_print_pins(mconfig->m_out_pin, buf,
mconfig->max_out_queue, ret, false);
- ret += snprintf(buf + ret, MOD_BUF - ret,
+ ret += scnprintf(buf + ret, MOD_BUF - ret,
"Other:\n\tDomain %d\n\tHomogeneous Input %s\n\t"
"Homogeneous Output %s\n\tIn Queue Mask %d\n\t"
"Out Queue Mask %d\n\tDMA ID %d\n\tMem Pages %d\n\t"
@@ -191,7 +193,7 @@ static ssize_t fw_softreg_read(struct file *file, char __user *user_buf,
__ioread32_copy(d->fw_read_buff, fw_reg_addr, w0_stat_sz >> 2);
for (offset = 0; offset < FW_REG_SIZE; offset += 16) {
- ret += snprintf(tmp + ret, FW_REG_BUF - ret, "%#.4x: ", offset);
+ ret += scnprintf(tmp + ret, FW_REG_BUF - ret, "%#.4x: ", offset);
hex_dump_to_buffer(d->fw_read_buff + offset, 16, 16, 4,
tmp + ret, FW_REG_BUF - ret, 0);
ret += strlen(tmp + ret);
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index 13408de34055..0bbd86390be5 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -83,7 +83,7 @@
#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
#define JZ_AIC_CLK_DIV_MASK 0xf
-#define I2SDIV_DV_SHIFT 8
+#define I2SDIV_DV_SHIFT 0
#define I2SDIV_DV_MASK (0xf << I2SDIV_DV_SHIFT)
#define I2SDIV_IDV_SHIFT 8
#define I2SDIV_IDV_MASK (0xf << I2SDIV_IDV_SHIFT)
diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c
index 3446a113f482..eb38cdb37f0e 100644
--- a/sound/soc/kirkwood/kirkwood-i2s.c
+++ b/sound/soc/kirkwood/kirkwood-i2s.c
@@ -559,10 +559,6 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
return PTR_ERR(priv->clk);
}
- err = clk_prepare_enable(priv->clk);
- if (err < 0)
- return err;
-
priv->extclk = devm_clk_get(&pdev->dev, "extclk");
if (IS_ERR(priv->extclk)) {
if (PTR_ERR(priv->extclk) == -EPROBE_DEFER)
@@ -578,6 +574,10 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
}
}
+ err = clk_prepare_enable(priv->clk);
+ if (err < 0)
+ return err;
+
/* Some sensible defaults - this reflects the powerup values */
priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24;
priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24;
@@ -591,7 +591,7 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128;
}
- err = devm_snd_soc_register_component(&pdev->dev, &kirkwood_soc_component,
+ err = snd_soc_register_component(&pdev->dev, &kirkwood_soc_component,
soc_dai, 2);
if (err) {
dev_err(&pdev->dev, "snd_soc_register_component failed\n");
@@ -614,6 +614,7 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev)
{
struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
if (!IS_ERR(priv->extclk))
clk_disable_unprepare(priv->extclk);
clk_disable_unprepare(priv->clk);
diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c
index 01c1c7db2510..db4f2363b822 100644
--- a/sound/soc/meson/axg-fifo.c
+++ b/sound/soc/meson/axg-fifo.c
@@ -226,7 +226,7 @@ static int axg_fifo_pcm_open(struct snd_pcm_substream *ss)
/* Enable pclk to access registers and clock the fifo ip */
ret = clk_prepare_enable(fifo->pclk);
if (ret)
- return ret;
+ goto free_irq;
/* Setup status2 so it reports the memory pointer */
regmap_update_bits(fifo->map, FIFO_CTRL1,
@@ -246,8 +246,14 @@ static int axg_fifo_pcm_open(struct snd_pcm_substream *ss)
/* Take memory arbitror out of reset */
ret = reset_control_deassert(fifo->arb);
if (ret)
- clk_disable_unprepare(fifo->pclk);
+ goto free_clk;
+
+ return 0;
+free_clk:
+ clk_disable_unprepare(fifo->pclk);
+free_irq:
+ free_irq(fifo->irq, ss);
return ret;
}
diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c
index 0c6cce5c5773..9bcaf4b8b57e 100644
--- a/sound/soc/meson/axg-tdm-formatter.c
+++ b/sound/soc/meson/axg-tdm-formatter.c
@@ -68,7 +68,7 @@ EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks);
static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter)
{
struct axg_tdm_stream *ts = formatter->stream;
- bool invert = formatter->drv->quirks->invert_sclk;
+ bool invert;
int ret;
/* Do nothing if the formatter is already enabled */
@@ -76,11 +76,12 @@ static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter)
return 0;
/*
- * If sclk is inverted, invert it back and provide the inversion
- * required by the formatter
+ * If sclk is inverted, it means the bit should latched on the
+ * rising edge which is what our HW expects. If not, we need to
+ * invert it before the formatter.
*/
- invert ^= axg_tdm_sclk_invert(ts->iface->fmt);
- ret = clk_set_phase(formatter->sclk, invert ? 180 : 0);
+ invert = axg_tdm_sclk_invert(ts->iface->fmt);
+ ret = clk_set_phase(formatter->sclk, invert ? 0 : 180);
if (ret)
return ret;
diff --git a/sound/soc/meson/axg-tdm-formatter.h b/sound/soc/meson/axg-tdm-formatter.h
index 9ef98e955cb2..a1f0dcc0ff13 100644
--- a/sound/soc/meson/axg-tdm-formatter.h
+++ b/sound/soc/meson/axg-tdm-formatter.h
@@ -16,7 +16,6 @@ struct snd_kcontrol;
struct axg_tdm_formatter_hw {
unsigned int skew_offset;
- bool invert_sclk;
};
struct axg_tdm_formatter_ops {
diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c
index 585ce030b79b..702595715f94 100644
--- a/sound/soc/meson/axg-tdm-interface.c
+++ b/sound/soc/meson/axg-tdm-interface.c
@@ -119,18 +119,25 @@ static int axg_tdm_iface_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
- /* These modes are not supported */
- if (fmt & (SND_SOC_DAIFMT_CBS_CFM | SND_SOC_DAIFMT_CBM_CFS)) {
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ if (!iface->mclk) {
+ dev_err(dai->dev, "cpu clock master: mclk missing\n");
+ return -ENODEV;
+ }
+ break;
+
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
dev_err(dai->dev, "only CBS_CFS and CBM_CFM are supported\n");
+ /* Fall-through */
+ default:
return -EINVAL;
}
- /* If the TDM interface is the clock master, it requires mclk */
- if (!iface->mclk && (fmt & SND_SOC_DAIFMT_CBS_CFS)) {
- dev_err(dai->dev, "cpu clock master: mclk missing\n");
- return -ENODEV;
- }
-
iface->fmt = fmt;
return 0;
}
@@ -319,7 +326,8 @@ static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream,
if (ret)
return ret;
- if (iface->fmt & SND_SOC_DAIFMT_CBS_CFS) {
+ if ((iface->fmt & SND_SOC_DAIFMT_MASTER_MASK) ==
+ SND_SOC_DAIFMT_CBS_CFS) {
ret = axg_tdm_iface_set_sclk(dai, params);
if (ret)
return ret;
diff --git a/sound/soc/meson/axg-tdmin.c b/sound/soc/meson/axg-tdmin.c
index a790f925a4ef..396a8201001b 100644
--- a/sound/soc/meson/axg-tdmin.c
+++ b/sound/soc/meson/axg-tdmin.c
@@ -208,15 +208,29 @@ static const struct axg_tdm_formatter_driver axg_tdmin_drv = {
.regmap_cfg = &axg_tdmin_regmap_cfg,
.ops = &axg_tdmin_ops,
.quirks = &(const struct axg_tdm_formatter_hw) {
- .invert_sclk = false,
.skew_offset = 2,
},
};
+static const struct axg_tdm_formatter_driver g12a_tdmin_drv = {
+ .component_drv = &axg_tdmin_component_drv,
+ .regmap_cfg = &axg_tdmin_regmap_cfg,
+ .ops = &axg_tdmin_ops,
+ .quirks = &(const struct axg_tdm_formatter_hw) {
+ .skew_offset = 3,
+ },
+};
+
static const struct of_device_id axg_tdmin_of_match[] = {
{
.compatible = "amlogic,axg-tdmin",
.data = &axg_tdmin_drv,
+ }, {
+ .compatible = "amlogic,g12a-tdmin",
+ .data = &g12a_tdmin_drv,
+ }, {
+ .compatible = "amlogic,sm1-tdmin",
+ .data = &g12a_tdmin_drv,
}, {}
};
MODULE_DEVICE_TABLE(of, axg_tdmin_of_match);
diff --git a/sound/soc/meson/axg-tdmout.c b/sound/soc/meson/axg-tdmout.c
index 527bfc4487e0..3ceabddae629 100644
--- a/sound/soc/meson/axg-tdmout.c
+++ b/sound/soc/meson/axg-tdmout.c
@@ -24,6 +24,7 @@
#define TDMOUT_CTRL1 0x04
#define TDMOUT_CTRL1_TYPE_MASK GENMASK(6, 4)
#define TDMOUT_CTRL1_TYPE(x) ((x) << 4)
+#define SM1_TDMOUT_CTRL1_GAIN_EN 7
#define TDMOUT_CTRL1_MSB_POS_MASK GENMASK(12, 8)
#define TDMOUT_CTRL1_MSB_POS(x) ((x) << 8)
#define TDMOUT_CTRL1_SEL_SHIFT 24
@@ -51,25 +52,6 @@ static const struct regmap_config axg_tdmout_regmap_cfg = {
.max_register = TDMOUT_MASK_VAL,
};
-static const struct snd_kcontrol_new axg_tdmout_controls[] = {
- SOC_DOUBLE("Lane 0 Volume", TDMOUT_GAIN0, 0, 8, 255, 0),
- SOC_DOUBLE("Lane 1 Volume", TDMOUT_GAIN0, 16, 24, 255, 0),
- SOC_DOUBLE("Lane 2 Volume", TDMOUT_GAIN1, 0, 8, 255, 0),
- SOC_DOUBLE("Lane 3 Volume", TDMOUT_GAIN1, 16, 24, 255, 0),
- SOC_SINGLE("Gain Enable Switch", TDMOUT_CTRL1,
- TDMOUT_CTRL1_GAIN_EN, 1, 0),
-};
-
-static const char * const tdmout_sel_texts[] = {
- "IN 0", "IN 1", "IN 2",
-};
-
-static SOC_ENUM_SINGLE_DECL(axg_tdmout_sel_enum, TDMOUT_CTRL1,
- TDMOUT_CTRL1_SEL_SHIFT, tdmout_sel_texts);
-
-static const struct snd_kcontrol_new axg_tdmout_in_mux =
- SOC_DAPM_ENUM("Input Source", axg_tdmout_sel_enum);
-
static struct snd_soc_dai *
axg_tdmout_get_be(struct snd_soc_dapm_widget *w)
{
@@ -137,7 +119,6 @@ static int axg_tdmout_prepare(struct regmap *map,
break;
case SND_SOC_DAIFMT_LEFT_J:
- case SND_SOC_DAIFMT_RIGHT_J:
case SND_SOC_DAIFMT_DSP_B:
skew += 1;
break;
@@ -198,6 +179,25 @@ static int axg_tdmout_prepare(struct regmap *map,
return axg_tdm_formatter_set_channel_masks(map, ts, TDMOUT_MASK0);
}
+static const struct snd_kcontrol_new axg_tdmout_controls[] = {
+ SOC_DOUBLE("Lane 0 Volume", TDMOUT_GAIN0, 0, 8, 255, 0),
+ SOC_DOUBLE("Lane 1 Volume", TDMOUT_GAIN0, 16, 24, 255, 0),
+ SOC_DOUBLE("Lane 2 Volume", TDMOUT_GAIN1, 0, 8, 255, 0),
+ SOC_DOUBLE("Lane 3 Volume", TDMOUT_GAIN1, 16, 24, 255, 0),
+ SOC_SINGLE("Gain Enable Switch", TDMOUT_CTRL1,
+ TDMOUT_CTRL1_GAIN_EN, 1, 0),
+};
+
+static const char * const axg_tdmout_sel_texts[] = {
+ "IN 0", "IN 1", "IN 2",
+};
+
+static SOC_ENUM_SINGLE_DECL(axg_tdmout_sel_enum, TDMOUT_CTRL1,
+ TDMOUT_CTRL1_SEL_SHIFT, axg_tdmout_sel_texts);
+
+static const struct snd_kcontrol_new axg_tdmout_in_mux =
+ SOC_DAPM_ENUM("Input Source", axg_tdmout_sel_enum);
+
static const struct snd_soc_dapm_widget axg_tdmout_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0),
@@ -238,7 +238,6 @@ static const struct axg_tdm_formatter_driver axg_tdmout_drv = {
.regmap_cfg = &axg_tdmout_regmap_cfg,
.ops = &axg_tdmout_ops,
.quirks = &(const struct axg_tdm_formatter_hw) {
- .invert_sclk = true,
.skew_offset = 1,
},
};
@@ -248,7 +247,66 @@ static const struct axg_tdm_formatter_driver g12a_tdmout_drv = {
.regmap_cfg = &axg_tdmout_regmap_cfg,
.ops = &axg_tdmout_ops,
.quirks = &(const struct axg_tdm_formatter_hw) {
- .invert_sclk = true,
+ .skew_offset = 2,
+ },
+};
+
+static const struct snd_kcontrol_new sm1_tdmout_controls[] = {
+ SOC_DOUBLE("Lane 0 Volume", TDMOUT_GAIN0, 0, 8, 255, 0),
+ SOC_DOUBLE("Lane 1 Volume", TDMOUT_GAIN0, 16, 24, 255, 0),
+ SOC_DOUBLE("Lane 2 Volume", TDMOUT_GAIN1, 0, 8, 255, 0),
+ SOC_DOUBLE("Lane 3 Volume", TDMOUT_GAIN1, 16, 24, 255, 0),
+ SOC_SINGLE("Gain Enable Switch", TDMOUT_CTRL1,
+ SM1_TDMOUT_CTRL1_GAIN_EN, 1, 0),
+};
+
+static const char * const sm1_tdmout_sel_texts[] = {
+ "IN 0", "IN 1", "IN 2", "IN 3", "IN 4",
+};
+
+static SOC_ENUM_SINGLE_DECL(sm1_tdmout_sel_enum, TDMOUT_CTRL1,
+ TDMOUT_CTRL1_SEL_SHIFT, sm1_tdmout_sel_texts);
+
+static const struct snd_kcontrol_new sm1_tdmout_in_mux =
+ SOC_DAPM_ENUM("Input Source", sm1_tdmout_sel_enum);
+
+static const struct snd_soc_dapm_widget sm1_tdmout_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &sm1_tdmout_in_mux),
+ SND_SOC_DAPM_PGA_E("ENC", SND_SOC_NOPM, 0, 0, NULL, 0,
+ axg_tdm_formatter_event,
+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)),
+ SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route sm1_tdmout_dapm_routes[] = {
+ { "SRC SEL", "IN 0", "IN 0" },
+ { "SRC SEL", "IN 1", "IN 1" },
+ { "SRC SEL", "IN 2", "IN 2" },
+ { "SRC SEL", "IN 3", "IN 3" },
+ { "SRC SEL", "IN 4", "IN 4" },
+ { "ENC", NULL, "SRC SEL" },
+ { "OUT", NULL, "ENC" },
+};
+
+static const struct snd_soc_component_driver sm1_tdmout_component_drv = {
+ .controls = sm1_tdmout_controls,
+ .num_controls = ARRAY_SIZE(sm1_tdmout_controls),
+ .dapm_widgets = sm1_tdmout_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sm1_tdmout_dapm_widgets),
+ .dapm_routes = sm1_tdmout_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sm1_tdmout_dapm_routes),
+};
+
+static const struct axg_tdm_formatter_driver sm1_tdmout_drv = {
+ .component_drv = &sm1_tdmout_component_drv,
+ .regmap_cfg = &axg_tdmout_regmap_cfg,
+ .ops = &axg_tdmout_ops,
+ .quirks = &(const struct axg_tdm_formatter_hw) {
.skew_offset = 2,
},
};
@@ -260,6 +318,9 @@ static const struct of_device_id axg_tdmout_of_match[] = {
}, {
.compatible = "amlogic,g12a-tdmout",
.data = &g12a_tdmout_drv,
+ }, {
+ .compatible = "amlogic,sm1-tdmout",
+ .data = &sm1_tdmout_drv,
}, {}
};
MODULE_DEVICE_TABLE(of, axg_tdmout_of_match);
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 8e3e86619b35..a0e94f3f7faf 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -72,7 +72,7 @@ config SND_SOC_QDSP6_ASM_DAI
config SND_SOC_QDSP6
tristate "SoC ALSA audio driver for QDSP6"
- depends on QCOM_APR && HAS_DMA
+ depends on QCOM_APR
select SND_SOC_QDSP6_COMMON
select SND_SOC_QDSP6_CORE
select SND_SOC_QDSP6_AFE
diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c
index 5661025e8cec..de9e2f865b42 100644
--- a/sound/soc/qcom/common.c
+++ b/sound/soc/qcom/common.c
@@ -4,6 +4,7 @@
#include <linux/module.h>
#include "common.h"
+#include "qdsp6/q6afe.h"
int qcom_snd_parse_of(struct snd_soc_card *card)
{
@@ -83,11 +84,22 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
}
link->no_pcm = 1;
link->ignore_pmdown_time = 1;
+
+ if (q6afe_is_rx_port(link->id)) {
+ link->dpcm_playback = 1;
+ link->dpcm_capture = 0;
+ } else {
+ link->dpcm_playback = 0;
+ link->dpcm_capture = 1;
+ }
+
} else {
link->platform_of_node = link->cpu_of_node;
link->codec_dai_name = "snd-soc-dummy-dai";
link->codec_name = "snd-soc-dummy";
link->dynamic = 1;
+ link->dpcm_playback = 1;
+ link->dpcm_capture = 1;
}
link->ignore_suspend = 1;
@@ -97,8 +109,6 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
goto err;
}
- link->dpcm_playback = 1;
- link->dpcm_capture = 1;
link->stream_name = link->name;
link++;
diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c
index dc645ba4d8d0..175d6d9b5031 100644
--- a/sound/soc/qcom/qdsp6/q6afe-dai.c
+++ b/sound/soc/qcom/qdsp6/q6afe-dai.c
@@ -901,6 +901,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
@@ -916,6 +918,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
@@ -930,6 +934,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
@@ -945,6 +951,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
@@ -959,6 +967,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
@@ -974,6 +984,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
@@ -988,6 +1000,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
@@ -1003,6 +1017,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 1,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c
index e0945f7a58c8..0ce4eb60f984 100644
--- a/sound/soc/qcom/qdsp6/q6afe.c
+++ b/sound/soc/qcom/qdsp6/q6afe.c
@@ -800,6 +800,14 @@ int q6afe_get_port_id(int index)
}
EXPORT_SYMBOL_GPL(q6afe_get_port_id);
+int q6afe_is_rx_port(int index)
+{
+ if (index < 0 || index >= AFE_PORT_MAX)
+ return -EINVAL;
+
+ return port_maps[index].is_rx;
+}
+EXPORT_SYMBOL_GPL(q6afe_is_rx_port);
static int afe_apr_send_pkt(struct q6afe *afe, struct apr_pkt *pkt,
struct q6afe_port *port)
{
diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h
index c7ed5422baff..1a0f80a14afe 100644
--- a/sound/soc/qcom/qdsp6/q6afe.h
+++ b/sound/soc/qcom/qdsp6/q6afe.h
@@ -198,6 +198,7 @@ int q6afe_port_start(struct q6afe_port *port);
int q6afe_port_stop(struct q6afe_port *port);
void q6afe_port_put(struct q6afe_port *port);
int q6afe_get_port_id(int index);
+int q6afe_is_rx_port(int index);
void q6afe_hdmi_port_prepare(struct q6afe_port *port,
struct q6afe_hdmi_cfg *cfg);
void q6afe_slim_port_prepare(struct q6afe_port *port,
diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c
index 548eb4fa2da6..9f0ffdcef637 100644
--- a/sound/soc/qcom/qdsp6/q6asm-dai.c
+++ b/sound/soc/qcom/qdsp6/q6asm-dai.c
@@ -171,7 +171,7 @@ static const struct snd_compr_codec_caps q6asm_compr_caps = {
};
static void event_handler(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
+ void *payload, void *priv)
{
struct q6asm_dai_rtd *prtd = priv;
struct snd_pcm_substream *substream = prtd->substream;
@@ -494,7 +494,7 @@ static struct snd_pcm_ops q6asm_dai_ops = {
};
static void compress_event_handler(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
+ void *payload, void *priv)
{
struct q6asm_dai_rtd *prtd = priv;
struct snd_compr_stream *substream = prtd->cstream;
diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c
index 4f85cb19a309..9cb014aa2fb7 100644
--- a/sound/soc/qcom/qdsp6/q6asm.c
+++ b/sound/soc/qcom/qdsp6/q6asm.c
@@ -25,6 +25,7 @@
#define ASM_STREAM_CMD_FLUSH 0x00010BCE
#define ASM_SESSION_CMD_PAUSE 0x00010BD3
#define ASM_DATA_CMD_EOS 0x00010BDB
+#define ASM_DATA_EVENT_RENDERED_EOS 0x00010C1C
#define ASM_NULL_POPP_TOPOLOGY 0x00010C68
#define ASM_STREAM_CMD_FLUSH_READBUFS 0x00010C09
#define ASM_STREAM_CMD_SET_ENCDEC_PARAM 0x00010C10
@@ -546,9 +547,6 @@ static int32_t q6asm_stream_callback(struct apr_device *adev,
case ASM_SESSION_CMD_SUSPEND:
client_event = ASM_CLIENT_EVENT_CMD_SUSPEND_DONE;
break;
- case ASM_DATA_CMD_EOS:
- client_event = ASM_CLIENT_EVENT_CMD_EOS_DONE;
- break;
case ASM_STREAM_CMD_FLUSH:
client_event = ASM_CLIENT_EVENT_CMD_FLUSH_DONE;
break;
@@ -652,6 +650,9 @@ static int32_t q6asm_stream_callback(struct apr_device *adev,
}
break;
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ client_event = ASM_CLIENT_EVENT_CMD_EOS_DONE;
+ break;
}
if (ac->cb)
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index 28a80c1cb41d..b43657e6e655 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -20,6 +20,7 @@ config SND_SOC_ROCKCHIP_PDM
tristate "Rockchip PDM Controller Driver"
depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
select SND_SOC_GENERIC_DMAENGINE_PCM
+ select RATIONAL
help
Say Y or M if you want to add support for PDM driver for
Rockchip PDM Controller. The Controller supports up to maximum of
diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
index 88ebaf6e1880..a0506e554c98 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -674,7 +674,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
ret = rockchip_pcm_platform_register(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM\n");
- return ret;
+ goto err_suspend;
}
return 0;
diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c
index 7cd42fcfcf38..1707414cfa92 100644
--- a/sound/soc/rockchip/rockchip_pdm.c
+++ b/sound/soc/rockchip/rockchip_pdm.c
@@ -590,8 +590,10 @@ static int rockchip_pdm_resume(struct device *dev)
int ret;
ret = pm_runtime_get_sync(dev);
- if (ret < 0)
+ if (ret < 0) {
+ pm_runtime_put(dev);
return ret;
+ }
ret = regcache_sync(pdm->regmap);
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 4579827ea7c7..3e49a22f18ec 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -376,6 +376,17 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
*/
u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
{
+ static const u32 dalign_values[8][2] = {
+ {0x76543210, 0x67452301},
+ {0x00000032, 0x00000023},
+ {0x00007654, 0x00006745},
+ {0x00000076, 0x00000067},
+ {0xfedcba98, 0xefcdab89},
+ {0x000000ba, 0x000000ab},
+ {0x0000fedc, 0x0000efcd},
+ {0x000000fe, 0x000000ef},
+ };
+ int id = 0, inv;
struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io);
struct rsnd_mod *target;
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
@@ -411,13 +422,18 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
target = cmd ? cmd : ssiu;
}
+ if (mod == ssiu)
+ id = rsnd_mod_id_sub(mod);
+
/* Non target mod or non 16bit needs normal DALIGN */
if ((snd_pcm_format_width(runtime->format) != 16) ||
(mod != target))
- return 0x76543210;
+ inv = 0;
/* Target mod needs inverted DALIGN when 16bit */
else
- return 0x67452301;
+ inv = 1;
+
+ return dalign_values[id][inv];
}
u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod)
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
index 0324a5c39619..28f65eba2bb4 100644
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/sh/rcar/dma.c
@@ -508,10 +508,10 @@ static struct rsnd_mod_ops rsnd_dmapp_ops = {
#define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8)
#define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc)
-#define RDMA_SSIU_I_N(addr, i, j) (addr ##_reg - 0x00441000 + (0x1000 * (i)) + (((j) / 4) * 0xA000) + (((j) % 4) * 0x400))
+#define RDMA_SSIU_I_N(addr, i, j) (addr ##_reg - 0x00441000 + (0x1000 * (i)) + (((j) / 4) * 0xA000) + (((j) % 4) * 0x400) - (0x4000 * ((i) / 9) * ((j) / 4)))
#define RDMA_SSIU_O_N(addr, i, j) RDMA_SSIU_I_N(addr, i, j)
-#define RDMA_SSIU_I_P(addr, i, j) (addr ##_reg - 0x00141000 + (0x1000 * (i)) + (((j) / 4) * 0xA000) + (((j) % 4) * 0x400))
+#define RDMA_SSIU_I_P(addr, i, j) (addr ##_reg - 0x00141000 + (0x1000 * (i)) + (((j) / 4) * 0xA000) + (((j) % 4) * 0x400) - (0x4000 * ((i) / 9) * ((j) / 4)))
#define RDMA_SSIU_O_P(addr, i, j) RDMA_SSIU_I_P(addr, i, j)
#define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i))
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index af19010b9d88..8bd49c8a9517 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -224,6 +224,14 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
RSND_GEN_S_REG(SSI_SYS_STATUS5, 0x884),
RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888),
RSND_GEN_S_REG(SSI_SYS_STATUS7, 0x88c),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE0, 0x850),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE1, 0x854),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE2, 0x858),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE3, 0x85c),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE4, 0x890),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE5, 0x894),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE6, 0x898),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE7, 0x89c),
RSND_GEN_S_REG(HDMI0_SEL, 0x9e0),
RSND_GEN_S_REG(HDMI1_SEL, 0x9e4),
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 7727add3eb1a..dd7ea04c689f 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -189,6 +189,14 @@ enum rsnd_reg {
SSI_SYS_STATUS5,
SSI_SYS_STATUS6,
SSI_SYS_STATUS7,
+ SSI_SYS_INT_ENABLE0,
+ SSI_SYS_INT_ENABLE1,
+ SSI_SYS_INT_ENABLE2,
+ SSI_SYS_INT_ENABLE3,
+ SSI_SYS_INT_ENABLE4,
+ SSI_SYS_INT_ENABLE5,
+ SSI_SYS_INT_ENABLE6,
+ SSI_SYS_INT_ENABLE7,
HDMI0_SEL,
HDMI1_SEL,
SSI9_BUSIF0_MODE,
@@ -237,6 +245,7 @@ enum rsnd_reg {
#define SSI9_BUSIF_ADINR(i) (SSI9_BUSIF0_ADINR + (i))
#define SSI9_BUSIF_DALIGN(i) (SSI9_BUSIF0_DALIGN + (i))
#define SSI_SYS_STATUS(i) (SSI_SYS_STATUS0 + (i))
+#define SSI_SYS_INT_ENABLE(i) (SSI_SYS_INT_ENABLE0 + (i))
struct rsnd_priv;
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 44bda210256e..2664220f3302 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -372,6 +372,9 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod,
u32 wsr = ssi->wsr;
int width;
int is_tdm, is_tdm_split;
+ int id = rsnd_mod_id(mod);
+ int i;
+ u32 sys_int_enable = 0;
is_tdm = rsnd_runtime_is_tdm(io);
is_tdm_split = rsnd_runtime_is_tdm_split(io);
@@ -447,6 +450,38 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod,
cr_mode = DIEN; /* PIO : enable Data interrupt */
}
+ /* enable busif buffer over/under run interrupt. */
+ if (is_tdm || is_tdm_split) {
+ switch (id) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ for (i = 0; i < 4; i++) {
+ sys_int_enable = rsnd_mod_read(mod,
+ SSI_SYS_INT_ENABLE(i * 2));
+ sys_int_enable |= 0xf << (id * 4);
+ rsnd_mod_write(mod,
+ SSI_SYS_INT_ENABLE(i * 2),
+ sys_int_enable);
+ }
+
+ break;
+ case 9:
+ for (i = 0; i < 4; i++) {
+ sys_int_enable = rsnd_mod_read(mod,
+ SSI_SYS_INT_ENABLE((i * 2) + 1));
+ sys_int_enable |= 0xf << 4;
+ rsnd_mod_write(mod,
+ SSI_SYS_INT_ENABLE((i * 2) + 1),
+ sys_int_enable);
+ }
+
+ break;
+ }
+ }
+
init_end:
ssi->cr_own = cr_own;
ssi->cr_mode = cr_mode;
@@ -496,6 +531,13 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct device *dev = rsnd_priv_to_dev(priv);
+ int is_tdm, is_tdm_split;
+ int id = rsnd_mod_id(mod);
+ int i;
+ u32 sys_int_enable = 0;
+
+ is_tdm = rsnd_runtime_is_tdm(io);
+ is_tdm_split = rsnd_runtime_is_tdm_split(io);
if (!rsnd_ssi_is_run_mods(mod, io))
return 0;
@@ -517,6 +559,38 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
ssi->wsr = 0;
}
+ /* disable busif buffer over/under run interrupt. */
+ if (is_tdm || is_tdm_split) {
+ switch (id) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ for (i = 0; i < 4; i++) {
+ sys_int_enable = rsnd_mod_read(mod,
+ SSI_SYS_INT_ENABLE(i * 2));
+ sys_int_enable &= ~(0xf << (id * 4));
+ rsnd_mod_write(mod,
+ SSI_SYS_INT_ENABLE(i * 2),
+ sys_int_enable);
+ }
+
+ break;
+ case 9:
+ for (i = 0; i < 4; i++) {
+ sys_int_enable = rsnd_mod_read(mod,
+ SSI_SYS_INT_ENABLE((i * 2) + 1));
+ sys_int_enable &= ~(0xf << 4);
+ rsnd_mod_write(mod,
+ SSI_SYS_INT_ENABLE((i * 2) + 1),
+ sys_int_enable);
+ }
+
+ break;
+ }
+ }
+
return 0;
}
@@ -594,10 +668,16 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod,
* Capture: It might not receave data. Do nothing
*/
if (rsnd_io_is_play(io)) {
- rsnd_mod_write(mod, SSICR, cr | EN);
+ rsnd_mod_write(mod, SSICR, cr | ssi->cr_en);
rsnd_ssi_status_check(mod, DIRQ);
}
+ /* In multi-SSI mode, stop is performed by setting ssi0129 in
+ * SSI_CONTROL to 0 (in rsnd_ssio_stop_gen2). Do nothing here.
+ */
+ if (rsnd_ssi_multi_slaves_runtime(io))
+ return 0;
+
/*
* disable SSI,
* and, wait idle state
@@ -616,6 +696,11 @@ static int rsnd_ssi_irq(struct rsnd_mod *mod,
int enable)
{
u32 val = 0;
+ int is_tdm, is_tdm_split;
+ int id = rsnd_mod_id(mod);
+
+ is_tdm = rsnd_runtime_is_tdm(io);
+ is_tdm_split = rsnd_runtime_is_tdm_split(io);
if (rsnd_is_gen1(priv))
return 0;
@@ -629,6 +714,19 @@ static int rsnd_ssi_irq(struct rsnd_mod *mod,
if (enable)
val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000;
+ if (is_tdm || is_tdm_split) {
+ switch (id) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 9:
+ val |= 0x0000ff00;
+ break;
+ }
+ }
+
rsnd_mod_write(mod, SSI_INT_ENABLE, val);
return 0;
@@ -645,6 +743,12 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
u32 status;
bool elapsed = false;
bool stop = false;
+ int id = rsnd_mod_id(mod);
+ int i;
+ int is_tdm, is_tdm_split;
+
+ is_tdm = rsnd_runtime_is_tdm(io);
+ is_tdm_split = rsnd_runtime_is_tdm_split(io);
spin_lock(&priv->lock);
@@ -666,6 +770,53 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
stop = true;
}
+ status = 0;
+
+ if (is_tdm || is_tdm_split) {
+ switch (id) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ for (i = 0; i < 4; i++) {
+ status = rsnd_mod_read(mod,
+ SSI_SYS_STATUS(i * 2));
+ status &= 0xf << (id * 4);
+
+ if (status) {
+ rsnd_dbg_irq_status(dev,
+ "%s err status : 0x%08x\n",
+ rsnd_mod_name(mod), status);
+ rsnd_mod_write(mod,
+ SSI_SYS_STATUS(i * 2),
+ 0xf << (id * 4));
+ stop = true;
+ break;
+ }
+ }
+ break;
+ case 9:
+ for (i = 0; i < 4; i++) {
+ status = rsnd_mod_read(mod,
+ SSI_SYS_STATUS((i * 2) + 1));
+ status &= 0xf << 4;
+
+ if (status) {
+ rsnd_dbg_irq_status(dev,
+ "%s err status : 0x%08x\n",
+ rsnd_mod_name(mod), status);
+ rsnd_mod_write(mod,
+ SSI_SYS_STATUS((i * 2) + 1),
+ 0xf << 4);
+ stop = true;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
rsnd_ssi_status_clear(mod);
rsnd_ssi_interrupt_out:
spin_unlock(&priv->lock);
@@ -737,6 +888,9 @@ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
if (!rsnd_rdai_is_clk_master(rdai))
return;
+ if (rsnd_ssi_is_multi_slave(mod, io))
+ return;
+
switch (rsnd_mod_id(mod)) {
case 1:
case 2:
diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c
index 2347f3404c06..e0ac791338a6 100644
--- a/sound/soc/sh/rcar/ssiu.c
+++ b/sound/soc/sh/rcar/ssiu.c
@@ -225,7 +225,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
i;
for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) {
- shift = (i * 4) + 16;
+ shift = (i * 4) + 20;
val = (val & ~(0xF << shift)) |
rsnd_mod_id(pos) << shift;
}
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 6aeba0d66ec5..d0c2d78f3c19 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -2008,7 +2008,25 @@ match:
dai_link->platforms->name = component->name;
/* convert non BE into BE */
- dai_link->no_pcm = 1;
+ if (!dai_link->no_pcm) {
+ dai_link->no_pcm = 1;
+
+ if (dai_link->dpcm_playback)
+ dev_warn(card->dev,
+ "invalid configuration, dailink %s has flags no_pcm=0 and dpcm_playback=1\n",
+ dai_link->name);
+ if (dai_link->dpcm_capture)
+ dev_warn(card->dev,
+ "invalid configuration, dailink %s has flags no_pcm=0 and dpcm_capture=1\n",
+ dai_link->name);
+
+ /* convert normal link into DPCM one */
+ if (!(dai_link->dpcm_playback ||
+ dai_link->dpcm_capture)) {
+ dai_link->dpcm_playback = !dai_link->capture_only;
+ dai_link->dpcm_capture = !dai_link->playback_only;
+ }
+ }
/* override any BE fixups */
dai_link->be_hw_params_fixup =
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index f40adb604c25..60212214fc5d 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -423,7 +423,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
memset(&template, 0, sizeof(template));
template.reg = e->reg;
- template.mask = e->mask << e->shift_l;
+ template.mask = e->mask;
template.shift = e->shift_l;
template.off_val = snd_soc_enum_item_to_val(e, 0);
template.on_val = template.off_val;
@@ -545,8 +545,22 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
if (data->value == value)
return false;
- if (data->widget)
- data->widget->on_val = value;
+ if (data->widget) {
+ switch (dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->id) {
+ case snd_soc_dapm_switch:
+ case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
+ data->widget->on_val = value & data->widget->mask;
+ break;
+ case snd_soc_dapm_demux:
+ case snd_soc_dapm_mux:
+ data->widget->on_val = value >> data->widget->shift;
+ break;
+ default:
+ data->widget->on_val = value;
+ break;
+ }
+ }
data->value = value;
@@ -801,7 +815,13 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i,
val = max - val;
p->connect = !!val;
} else {
- p->connect = 0;
+ /* since a virtual mixer has no backing registers to
+ * decide which path to connect, it will try to match
+ * with initial state. This is to ensure
+ * that the default mixer choice will be
+ * correctly powered up during initialization.
+ */
+ p->connect = invert;
}
}
@@ -4680,7 +4700,7 @@ static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm)
continue;
if (w->power) {
dapm_seq_insert(w, &down_list, false);
- w->power = 0;
+ w->new_power = 0;
powerdown = 1;
}
}
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index c7b990abdbaa..2a528e73bad2 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -100,10 +100,9 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
unsigned int sync = 0;
int enable;
- trace_snd_soc_jack_report(jack, mask, status);
-
if (!jack)
return;
+ trace_snd_soc_jack_report(jack, mask, status);
dapm = &jack->card->dapm;
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index f4dc3d445aae..95fc24580f85 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -832,7 +832,7 @@ int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
unsigned int regbase = mc->regbase;
unsigned int regcount = mc->regcount;
unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
- unsigned int regwmask = (1<<regwshift)-1;
+ unsigned int regwmask = (1UL<<regwshift)-1;
unsigned int invert = mc->invert;
unsigned long mask = (1UL<<mc->nbits)-1;
long min = mc->min;
@@ -881,7 +881,7 @@ int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
unsigned int regbase = mc->regbase;
unsigned int regcount = mc->regcount;
unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
- unsigned int regwmask = (1<<regwshift)-1;
+ unsigned int regwmask = (1UL<<regwshift)-1;
unsigned int invert = mc->invert;
unsigned long mask = (1UL<<mc->nbits)-1;
long max = mc->max;
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index c46ad0f66292..67eede91c695 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -931,6 +931,11 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
int i, ret = 0;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+ ret = soc_pcm_params_symmetry(substream, params);
+ if (ret)
+ goto out;
+
if (rtd->dai_link->ops->hw_params) {
ret = rtd->dai_link->ops->hw_params(substream, params);
if (ret < 0) {
@@ -1015,9 +1020,6 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
snd_soc_dapm_update_dai(substream, params, cpu_dai);
- ret = soc_pcm_params_symmetry(substream, params);
- if (ret)
- goto component_err;
out:
mutex_unlock(&rtd->pcm_mutex);
return ret;
@@ -1465,6 +1467,7 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
struct snd_soc_dapm_widget *widget;
struct snd_soc_dai *dai;
int prune = 0;
+ int do_prune;
/* Destroy any old FE <--> BE connections */
for_each_dpcm_be(fe, stream, dpcm) {
@@ -1478,13 +1481,16 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
continue;
/* is there a valid CODEC DAI widget for this BE */
+ do_prune = 1;
for_each_rtd_codec_dai(dpcm->be, i, dai) {
widget = dai_get_widget(dai, stream);
/* prune the BE if it's no longer in our active list */
if (widget && widget_in_list(list, widget))
- continue;
+ do_prune = 0;
}
+ if (!do_prune)
+ continue;
dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n",
stream ? "capture" : "playback",
@@ -2294,7 +2300,8 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
- (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
continue;
ret = dpcm_do_trigger(dpcm, be_substream, cmd);
@@ -2324,7 +2331,8 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
break;
case SNDRV_PCM_TRIGGER_STOP:
- if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
continue;
if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
@@ -2369,42 +2377,81 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
}
EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
+static int dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream,
+ int cmd, bool fe_first)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ int ret;
+
+ /* call trigger on the frontend before the backend. */
+ if (fe_first) {
+ dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n",
+ fe->dai_link->name, cmd);
+
+ ret = soc_pcm_trigger(substream, cmd);
+ if (ret < 0)
+ return ret;
+
+ ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
+ return ret;
+ }
+
+ /* call trigger on the frontend after the backend. */
+ ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
+ fe->dai_link->name, cmd);
+
+ ret = soc_pcm_trigger(substream, cmd);
+
+ return ret;
+}
+
static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *fe = substream->private_data;
- int stream = substream->stream, ret;
+ int stream = substream->stream;
+ int ret = 0;
enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
switch (trigger) {
case SND_SOC_DPCM_TRIGGER_PRE:
- /* call trigger on the frontend before the backend. */
-
- dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n",
- fe->dai_link->name, cmd);
-
- ret = soc_pcm_trigger(substream, cmd);
- if (ret < 0) {
- dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
- goto out;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = dpcm_dai_trigger_fe_be(substream, cmd, true);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = dpcm_dai_trigger_fe_be(substream, cmd, false);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
}
-
- ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
break;
case SND_SOC_DPCM_TRIGGER_POST:
- /* call trigger on the frontend after the backend. */
-
- ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
- if (ret < 0) {
- dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
- goto out;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = dpcm_dai_trigger_fe_be(substream, cmd, false);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = dpcm_dai_trigger_fe_be(substream, cmd, true);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
}
-
- dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
- fe->dai_link->name, cmd);
-
- ret = soc_pcm_trigger(substream, cmd);
break;
case SND_SOC_DPCM_TRIGGER_BESPOKE:
/* bespoke trigger() - handles both FE and BEs */
@@ -2413,10 +2460,6 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
fe->dai_link->name, cmd);
ret = soc_pcm_bespoke_trigger(substream, cmd);
- if (ret < 0) {
- dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
- goto out;
- }
break;
default:
dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd,
@@ -2425,6 +2468,12 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
goto out;
}
+ if (ret < 0) {
+ dev_err(fe->dev, "ASoC: trigger FE cmd: %d failed: %d\n",
+ cmd, ret);
+ goto out;
+ }
+
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -3358,16 +3407,16 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
unsigned long flags;
/* FE state */
- offset += snprintf(buf + offset, size - offset,
+ offset += scnprintf(buf + offset, size - offset,
"[%s - %s]\n", fe->dai_link->name,
stream ? "Capture" : "Playback");
- offset += snprintf(buf + offset, size - offset, "State: %s\n",
+ offset += scnprintf(buf + offset, size - offset, "State: %s\n",
dpcm_state_string(fe->dpcm[stream].state));
if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
(fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
- offset += snprintf(buf + offset, size - offset,
+ offset += scnprintf(buf + offset, size - offset,
"Hardware Params: "
"Format = %s, Channels = %d, Rate = %d\n",
snd_pcm_format_name(params_format(params)),
@@ -3375,10 +3424,10 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
params_rate(params));
/* BEs state */
- offset += snprintf(buf + offset, size - offset, "Backends:\n");
+ offset += scnprintf(buf + offset, size - offset, "Backends:\n");
if (list_empty(&fe->dpcm[stream].be_clients)) {
- offset += snprintf(buf + offset, size - offset,
+ offset += scnprintf(buf + offset, size - offset,
" No active DSP links\n");
goto out;
}
@@ -3388,16 +3437,16 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
struct snd_soc_pcm_runtime *be = dpcm->be;
params = &dpcm->hw_params;
- offset += snprintf(buf + offset, size - offset,
+ offset += scnprintf(buf + offset, size - offset,
"- %s\n", be->dai_link->name);
- offset += snprintf(buf + offset, size - offset,
+ offset += scnprintf(buf + offset, size - offset,
" State: %s\n",
dpcm_state_string(be->dpcm[stream].state));
if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
(be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
- offset += snprintf(buf + offset, size - offset,
+ offset += scnprintf(buf + offset, size - offset,
" Hardware Params: "
"Format = %s, Channels = %d, Rate = %d\n",
snd_pcm_format_name(params_format(params)),
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 3299ebb48c1a..b14bea746875 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -366,7 +366,7 @@ static int soc_tplg_add_kcontrol(struct soc_tplg *tplg,
struct snd_soc_component *comp = tplg->comp;
return soc_tplg_add_dcontrol(comp->card->snd_card,
- comp->dev, k, NULL, comp, kcontrol);
+ comp->dev, k, comp->name_prefix, comp, kcontrol);
}
/* remove a mixer kcontrol */
@@ -558,12 +558,12 @@ static void remove_link(struct snd_soc_component *comp,
if (dobj->ops && dobj->ops->link_unload)
dobj->ops->link_unload(comp, dobj);
+ list_del(&dobj->list);
+ snd_soc_remove_dai_link(comp->card, link);
+
kfree(link->name);
kfree(link->stream_name);
kfree(link->cpu_dai_name);
-
- list_del(&dobj->list);
- snd_soc_remove_dai_link(comp->card, link);
kfree(link);
}
@@ -614,9 +614,11 @@ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr,
ext_ops = tplg->bytes_ext_ops;
num_ops = tplg->bytes_ext_ops_count;
for (i = 0; i < num_ops; i++) {
- if (!sbe->put && ext_ops[i].id == be->ext_ops.put)
+ if (!sbe->put &&
+ ext_ops[i].id == le32_to_cpu(be->ext_ops.put))
sbe->put = ext_ops[i].put;
- if (!sbe->get && ext_ops[i].id == be->ext_ops.get)
+ if (!sbe->get &&
+ ext_ops[i].id == le32_to_cpu(be->ext_ops.get))
sbe->get = ext_ops[i].get;
}
@@ -631,11 +633,11 @@ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr,
num_ops = tplg->io_ops_count;
for (i = 0; i < num_ops; i++) {
- if (k->put == NULL && ops[i].id == hdr->ops.put)
+ if (k->put == NULL && ops[i].id == le32_to_cpu(hdr->ops.put))
k->put = ops[i].put;
- if (k->get == NULL && ops[i].id == hdr->ops.get)
+ if (k->get == NULL && ops[i].id == le32_to_cpu(hdr->ops.get))
k->get = ops[i].get;
- if (k->info == NULL && ops[i].id == hdr->ops.info)
+ if (k->info == NULL && ops[i].id == le32_to_cpu(hdr->ops.info))
k->info = ops[i].info;
}
@@ -648,11 +650,11 @@ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr,
num_ops = ARRAY_SIZE(io_ops);
for (i = 0; i < num_ops; i++) {
- if (k->put == NULL && ops[i].id == hdr->ops.put)
+ if (k->put == NULL && ops[i].id == le32_to_cpu(hdr->ops.put))
k->put = ops[i].put;
- if (k->get == NULL && ops[i].id == hdr->ops.get)
+ if (k->get == NULL && ops[i].id == le32_to_cpu(hdr->ops.get))
k->get = ops[i].get;
- if (k->info == NULL && ops[i].id == hdr->ops.info)
+ if (k->info == NULL && ops[i].id == le32_to_cpu(hdr->ops.info))
k->info = ops[i].info;
}
@@ -901,7 +903,13 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count,
}
/* create any TLV data */
- soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
+ err = soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to create TLV %s\n",
+ mc->hdr.name);
+ kfree(sm);
+ continue;
+ }
/* pass control to driver for optional further init */
err = soc_tplg_init_kcontrol(tplg, &kc,
@@ -941,7 +949,7 @@ static int soc_tplg_denum_create_texts(struct soc_enum *se,
if (se->dobj.control.dtexts == NULL)
return -ENOMEM;
- for (i = 0; i < ec->items; i++) {
+ for (i = 0; i < le32_to_cpu(ec->items); i++) {
if (strnlen(ec->texts[i], SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
SNDRV_CTL_ELEM_ID_NAME_MAXLEN) {
@@ -1112,6 +1120,7 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
struct snd_soc_tplg_hdr *hdr)
{
struct snd_soc_tplg_ctl_hdr *control_hdr;
+ int ret;
int i;
if (tplg->pass != SOC_TPLG_PASS_MIXER) {
@@ -1140,25 +1149,30 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
case SND_SOC_TPLG_CTL_RANGE:
case SND_SOC_TPLG_DAPM_CTL_VOLSW:
case SND_SOC_TPLG_DAPM_CTL_PIN:
- soc_tplg_dmixer_create(tplg, 1,
- le32_to_cpu(hdr->payload_size));
+ ret = soc_tplg_dmixer_create(tplg, 1,
+ le32_to_cpu(hdr->payload_size));
break;
case SND_SOC_TPLG_CTL_ENUM:
case SND_SOC_TPLG_CTL_ENUM_VALUE:
case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
- soc_tplg_denum_create(tplg, 1,
- le32_to_cpu(hdr->payload_size));
+ ret = soc_tplg_denum_create(tplg, 1,
+ le32_to_cpu(hdr->payload_size));
break;
case SND_SOC_TPLG_CTL_BYTES:
- soc_tplg_dbytes_create(tplg, 1,
- le32_to_cpu(hdr->payload_size));
+ ret = soc_tplg_dbytes_create(tplg, 1,
+ le32_to_cpu(hdr->payload_size));
break;
default:
soc_bind_err(tplg, control_hdr, i);
return -EINVAL;
}
+ if (ret < 0) {
+ dev_err(tplg->dev, "ASoC: invalid control\n");
+ return ret;
+ }
+
}
return 0;
@@ -1266,16 +1280,30 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg,
routes[i]->dobj.index = tplg->index;
list_add(&routes[i]->dobj.list, &tplg->comp->dobj_list);
- soc_tplg_add_route(tplg, routes[i]);
+ ret = soc_tplg_add_route(tplg, routes[i]);
+ if (ret < 0) {
+ /*
+ * this route was added to the list, it will
+ * be freed in remove_route() so increment the
+ * counter to skip it in the error handling
+ * below.
+ */
+ i++;
+ break;
+ }
/* add route, but keep going if some fail */
snd_soc_dapm_add_routes(dapm, routes[i], 1);
}
- /* free memory allocated for all dapm routes in case of error */
- if (ret < 0)
- for (i = 0; i < count ; i++)
- kfree(routes[i]);
+ /*
+ * free memory allocated for all dapm routes not added to the
+ * list in case of error
+ */
+ if (ret < 0) {
+ while (i < count)
+ kfree(routes[i++]);
+ }
/*
* free pointer to array of dapm routes as this is no longer needed.
@@ -1321,7 +1349,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
goto err_str;
kc[i].private_value = (long)sm;
kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc[i].access = mc->hdr.access;
+ kc[i].access = le32_to_cpu(mc->hdr.access);
/* we only support FL/FR channel mapping atm */
sm->reg = tplc_chan_get_reg(tplg, mc->channel,
@@ -1333,10 +1361,10 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
sm->rshift = tplc_chan_get_shift(tplg, mc->channel,
SNDRV_CHMAP_FR);
- sm->max = mc->max;
- sm->min = mc->min;
- sm->invert = mc->invert;
- sm->platform_max = mc->platform_max;
+ sm->max = le32_to_cpu(mc->max);
+ sm->min = le32_to_cpu(mc->min);
+ sm->invert = le32_to_cpu(mc->invert);
+ sm->platform_max = le32_to_cpu(mc->platform_max);
sm->dobj.index = tplg->index;
INIT_LIST_HEAD(&sm->dobj.list);
@@ -1349,7 +1377,13 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
}
/* create any TLV data */
- soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr);
+ err = soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to create TLV %s\n",
+ mc->hdr.name);
+ kfree(sm);
+ continue;
+ }
/* pass control to driver for optional further init */
err = soc_tplg_init_kcontrol(tplg, &kc[i],
@@ -1399,7 +1433,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create(
goto err;
tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
- ec->priv.size);
+ le32_to_cpu(ec->priv.size));
dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n",
ec->hdr.name);
@@ -1411,7 +1445,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create(
}
kc[i].private_value = (long)se;
kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc[i].access = ec->hdr.access;
+ kc[i].access = le32_to_cpu(ec->hdr.access);
/* we only support FL/FR channel mapping atm */
se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
@@ -1420,8 +1454,8 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create(
se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
SNDRV_CHMAP_FR);
- se->items = ec->items;
- se->mask = ec->mask;
+ se->items = le32_to_cpu(ec->items);
+ se->mask = le32_to_cpu(ec->mask);
se->dobj.index = tplg->index;
switch (le32_to_cpu(ec->hdr.ops.info)) {
@@ -1528,9 +1562,9 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create(
}
kc[i].private_value = (long)sbe;
kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc[i].access = be->hdr.access;
+ kc[i].access = le32_to_cpu(be->hdr.access);
- sbe->max = be->max;
+ sbe->max = le32_to_cpu(be->max);
INIT_LIST_HEAD(&sbe->dobj.list);
/* map standard io handlers and check for external handlers */
@@ -1588,7 +1622,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
/* map user to kernel widget ID */
template.id = get_widget_id(le32_to_cpu(w->id));
- if (template.id < 0)
+ if ((int)template.id < 0)
return template.id;
/* strings are allocated here, but used and freed by the widget */
@@ -1885,6 +1919,10 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
if (link == NULL)
return -ENOMEM;
+ link->dobj.index = tplg->index;
+ link->dobj.ops = tplg->ops;
+ link->dobj.type = SND_SOC_DOBJ_DAI_LINK;
+
if (strlen(pcm->pcm_name)) {
link->name = kstrdup(pcm->pcm_name, GFP_KERNEL);
link->stream_name = kstrdup(pcm->pcm_name, GFP_KERNEL);
@@ -1910,20 +1948,24 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
ret = soc_tplg_dai_link_load(tplg, link, NULL);
if (ret < 0) {
dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n");
- kfree(link->name);
- kfree(link->stream_name);
- kfree(link->cpu_dai_name);
- kfree(link);
- return ret;
+ goto err;
+ }
+
+ ret = snd_soc_add_dai_link(tplg->comp->card, link);
+ if (ret < 0) {
+ dev_err(tplg->comp->dev, "ASoC: adding FE link failed\n");
+ goto err;
}
- link->dobj.index = tplg->index;
- link->dobj.ops = tplg->ops;
- link->dobj.type = SND_SOC_DOBJ_DAI_LINK;
list_add(&link->dobj.list, &tplg->comp->dobj_list);
- snd_soc_add_dai_link(tplg->comp->card, link);
return 0;
+err:
+ kfree(link->name);
+ kfree(link->stream_name);
+ kfree(link->cpu_dai_name);
+ kfree(link);
+ return ret;
}
/* create a FE DAI and DAI link from the PCM object */
@@ -2016,6 +2058,7 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
int size;
int i;
bool abi_match;
+ int ret;
count = le32_to_cpu(hdr->count);
@@ -2053,11 +2096,18 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
_pcm = pcm;
} else {
abi_match = false;
- pcm_new_ver(tplg, pcm, &_pcm);
+ ret = pcm_new_ver(tplg, pcm, &_pcm);
+ if (ret < 0)
+ return ret;
}
/* create the FE DAIs and DAI links */
- soc_tplg_pcm_create(tplg, _pcm);
+ ret = soc_tplg_pcm_create(tplg, _pcm);
+ if (ret < 0) {
+ if (!abi_match)
+ kfree(_pcm);
+ return ret;
+ }
/* offset by version-specific struct size and
* real priv data size
@@ -2296,8 +2346,11 @@ static int soc_tplg_link_elems_load(struct soc_tplg *tplg,
}
ret = soc_tplg_link_config(tplg, _link);
- if (ret < 0)
+ if (ret < 0) {
+ if (!abi_match)
+ kfree(_link);
return ret;
+ }
/* offset by version-specific struct size and
* real priv data size
@@ -2382,7 +2435,7 @@ static int soc_tplg_dai_elems_load(struct soc_tplg *tplg,
{
struct snd_soc_tplg_dai *dai;
int count;
- int i;
+ int i, ret;
count = le32_to_cpu(hdr->count);
@@ -2397,7 +2450,12 @@ static int soc_tplg_dai_elems_load(struct soc_tplg *tplg,
return -EINVAL;
}
- soc_tplg_dai_config(tplg, dai);
+ ret = soc_tplg_dai_config(tplg, dai);
+ if (ret < 0) {
+ dev_err(tplg->dev, "ASoC: failed to configure DAI\n");
+ return ret;
+ }
+
tplg->pos += (sizeof(*dai) + le32_to_cpu(dai->priv.size));
}
@@ -2461,7 +2519,7 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg,
{
struct snd_soc_tplg_manifest *manifest, *_manifest;
bool abi_match;
- int err;
+ int ret = 0;
if (tplg->pass != SOC_TPLG_PASS_MANIFEST)
return 0;
@@ -2474,19 +2532,19 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg,
_manifest = manifest;
} else {
abi_match = false;
- err = manifest_new_ver(tplg, manifest, &_manifest);
- if (err < 0)
- return err;
+ ret = manifest_new_ver(tplg, manifest, &_manifest);
+ if (ret < 0)
+ return ret;
}
/* pass control to component driver for optional further init */
if (tplg->comp && tplg->ops && tplg->ops->manifest)
- return tplg->ops->manifest(tplg->comp, tplg->index, _manifest);
+ ret = tplg->ops->manifest(tplg->comp, tplg->index, _manifest);
if (!abi_match) /* free the duplicated one */
kfree(_manifest);
- return 0;
+ return ret;
}
/* validate header magic, size and type */
@@ -2505,7 +2563,7 @@ static int soc_valid_header(struct soc_tplg *tplg,
}
/* big endian firmware objects not supported atm */
- if (hdr->magic == SOC_TPLG_MAGIC_BIG_ENDIAN) {
+ if (le32_to_cpu(hdr->magic) == SOC_TPLG_MAGIC_BIG_ENDIAN) {
dev_err(tplg->dev,
"ASoC: pass %d big endian not supported header got %x at offset 0x%lx size 0x%zx.\n",
tplg->pass, hdr->magic,
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 5beda47cdf9f..74cd1989157b 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -288,6 +288,46 @@ static int sof_machine_check(struct snd_sof_dev *sdev)
#endif
}
+/*
+ * FW Boot State Transition Diagram
+ *
+ * +-----------------------------------------------------------------------+
+ * | |
+ * ------------------ ------------------ |
+ * | | | | |
+ * | BOOT_FAILED | | READY_FAILED |-------------------------+ |
+ * | | | | | |
+ * ------------------ ------------------ | |
+ * ^ ^ | |
+ * | | | |
+ * (FW Boot Timeout) (FW_READY FAIL) | |
+ * | | | |
+ * | | | |
+ * ------------------ | ------------------ | |
+ * | | | | | | |
+ * | IN_PROGRESS |---------------+------------->| COMPLETE | | |
+ * | | (FW Boot OK) (FW_READY OK) | | | |
+ * ------------------ ------------------ | |
+ * ^ | | |
+ * | | | |
+ * (FW Loading OK) (System Suspend/Runtime Suspend)
+ * | | | |
+ * | | | |
+ * ------------------ ------------------ | | |
+ * | | | |<-----+ | |
+ * | PREPARE | | NOT_STARTED |<---------------------+ |
+ * | | | |<---------------------------+
+ * ------------------ ------------------
+ * | ^ | ^
+ * | | | |
+ * | +-----------------------+ |
+ * | (DSP Probe OK) |
+ * | |
+ * | |
+ * +------------------------------------+
+ * (System Suspend/Runtime Suspend)
+ */
+
static int sof_probe_continue(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *plat_data = sdev->pdata;
@@ -303,6 +343,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
return ret;
}
+ sdev->fw_state = SOF_FW_BOOT_PREPARE;
+
/* check machine info */
ret = sof_machine_check(sdev);
if (ret < 0) {
@@ -330,6 +372,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
/* init the IPC */
sdev->ipc = snd_sof_ipc_init(sdev);
if (!sdev->ipc) {
+ ret = -ENOMEM;
dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret);
goto ipc_err;
}
@@ -342,7 +385,12 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
goto fw_load_err;
}
- /* boot the firmware */
+ sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS;
+
+ /*
+ * Boot the firmware. The FW boot status will be modified
+ * in snd_sof_run_firmware() depending on the outcome.
+ */
ret = snd_sof_run_firmware(sdev);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n",
@@ -368,7 +416,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
if (ret < 0) {
dev_err(sdev->dev,
"error: failed to register DSP DAI driver %d\n", ret);
- goto fw_run_err;
+ goto fw_trace_err;
}
drv_name = plat_data->machine->drv_name;
@@ -382,7 +430,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
if (IS_ERR(plat_data->pdev_mach)) {
ret = PTR_ERR(plat_data->pdev_mach);
- goto fw_run_err;
+ goto fw_trace_err;
}
dev_dbg(sdev->dev, "created machine %s\n",
@@ -393,7 +441,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
return 0;
-#if !IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)
+fw_trace_err:
+ snd_sof_free_trace(sdev);
fw_run_err:
snd_sof_fw_unload(sdev);
fw_load_err:
@@ -402,21 +451,10 @@ ipc_err:
snd_sof_free_debug(sdev);
dbg_err:
snd_sof_remove(sdev);
-#else
-
- /*
- * when the probe_continue is handled in a work queue, the
- * probe does not fail so we don't release resources here.
- * They will be released with an explicit call to
- * snd_sof_device_remove() when the PCI/ACPI device is removed
- */
-fw_run_err:
-fw_load_err:
-ipc_err:
-dbg_err:
-
-#endif
+ /* all resources freed, update state to match */
+ sdev->fw_state = SOF_FW_BOOT_NOT_STARTED;
+ sdev->first_boot = true;
return ret;
}
@@ -447,6 +485,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
sdev->pdata = plat_data;
sdev->first_boot = true;
+ sdev->fw_state = SOF_FW_BOOT_NOT_STARTED;
dev_set_drvdata(dev, sdev);
/* check all mandatory ops */
@@ -494,10 +533,12 @@ int snd_sof_device_remove(struct device *dev)
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
cancel_work_sync(&sdev->probe_work);
- snd_sof_fw_unload(sdev);
- snd_sof_ipc_free(sdev);
- snd_sof_free_debug(sdev);
- snd_sof_free_trace(sdev);
+ if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) {
+ snd_sof_fw_unload(sdev);
+ snd_sof_ipc_free(sdev);
+ snd_sof_free_debug(sdev);
+ snd_sof_free_trace(sdev);
+ }
/*
* Unregister machine driver. This will unbind the snd_card which
@@ -513,7 +554,8 @@ int snd_sof_device_remove(struct device *dev)
* scheduled on, when they are unloaded. Therefore, the DSP must be
* removed only after the topology has been unloaded.
*/
- snd_sof_remove(sdev);
+ if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED)
+ snd_sof_remove(sdev);
/* release firmware */
release_firmware(pdata->fw);
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index b86b5f9783fd..3045a3a37889 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -72,10 +72,18 @@ config SND_SOC_SOF_BAYTRAIL
config SND_SOC_SOF_BROADWELL_SUPPORT
bool "SOF support for Broadwell"
+ depends on SND_SOC_INTEL_HASWELL=n
help
This adds support for Sound Open Firmware for Intel(R) platforms
using the Broadwell processors.
- Say Y if you have such a device.
+ This option is mutually exclusive with the Haswell/Broadwell legacy
+ driver. If you want to enable SOF on Broadwell you need to deselect
+ the legacy driver first.
+ SOF does fully support Broadwell yet, so this option is not
+ recommended for distros. At some point all legacy drivers will be
+ deprecated but not before all userspace firmware/topology/UCM files
+ are made available to downstream distros.
+ Say Y if you want to enable SOF on Broadwell
If unsure select "N".
config SND_SOC_SOF_BROADWELL
@@ -209,6 +217,16 @@ config SND_SOC_SOF_HDA_AUDIO_CODEC
Say Y if you want to enable HDAudio codecs with SOF.
If unsure select "N".
+config SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1
+ bool "SOF enable DMI Link L1"
+ help
+ This option enables DMI L1 for both playback and capture
+ and disables known workarounds for specific HDaudio platforms.
+ Only use to look into power optimizations on platforms not
+ affected by DMI L1 issues. This option is not recommended.
+ Say Y if you want to enable DMI Link L1
+ If unsure, select "N".
+
endif ## SND_SOC_SOF_HDA_COMMON
config SND_SOC_SOF_HDA_LINK_BASELINE
diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c
index 70d524ef9bc0..0ca3c1b55eeb 100644
--- a/sound/soc/sof/intel/bdw.c
+++ b/sound/soc/sof/intel/bdw.c
@@ -37,6 +37,7 @@
#define MBOX_SIZE 0x1000
#define MBOX_DUMP_SIZE 0x30
#define EXCEPT_OFFSET 0x800
+#define EXCEPT_MAX_HDR_SIZE 0x400
/* DSP peripherals */
#define DMAC0_OFFSET 0xFE000
@@ -228,6 +229,11 @@ static void bdw_get_registers(struct snd_sof_dev *sdev,
/* note: variable AR register array is not read */
/* then get panic info */
+ if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
+ dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",
+ xoops->arch_hdr.totalsize);
+ return;
+ }
offset += xoops->arch_hdr.totalsize;
sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
@@ -588,6 +594,7 @@ static int bdw_probe(struct snd_sof_dev *sdev)
/* TODO: add offsets */
sdev->mmio_bar = BDW_DSP_BAR;
sdev->mailbox_bar = BDW_DSP_BAR;
+ sdev->dsp_oops_offset = MBOX_OFFSET;
/* PCI base */
mmio = platform_get_resource(pdev, IORESOURCE_MEM,
diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c
index 39d1ae01c45d..7c09a619f238 100644
--- a/sound/soc/sof/intel/byt.c
+++ b/sound/soc/sof/intel/byt.c
@@ -24,10 +24,12 @@
#define DRAM_OFFSET 0x100000
#define DRAM_SIZE (160 * 1024)
#define SHIM_OFFSET 0x140000
-#define SHIM_SIZE 0x100
+#define SHIM_SIZE_BYT 0x100
+#define SHIM_SIZE_CHT 0x118
#define MBOX_OFFSET 0x144000
#define MBOX_SIZE 0x1000
#define EXCEPT_OFFSET 0x800
+#define EXCEPT_MAX_HDR_SIZE 0x400
/* DSP peripherals */
#define DMAC0_OFFSET 0x098000
@@ -74,7 +76,7 @@ static const struct snd_sof_debugfs_map byt_debugfs[] = {
SOF_DEBUGFS_ACCESS_D0_ONLY},
{"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
SOF_DEBUGFS_ACCESS_D0_ONLY},
- {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE,
+ {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_BYT,
SOF_DEBUGFS_ACCESS_ALWAYS},
};
@@ -101,7 +103,7 @@ static const struct snd_sof_debugfs_map cht_debugfs[] = {
SOF_DEBUGFS_ACCESS_D0_ONLY},
{"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
SOF_DEBUGFS_ACCESS_D0_ONLY},
- {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE,
+ {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_CHT,
SOF_DEBUGFS_ACCESS_ALWAYS},
};
@@ -273,6 +275,11 @@ static void byt_get_registers(struct snd_sof_dev *sdev,
/* note: variable AR register array is not read */
/* then get panic info */
+ if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
+ dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",
+ xoops->arch_hdr.totalsize);
+ return;
+ }
offset += xoops->arch_hdr.totalsize;
sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c
index 0d8437b080bf..2aa98b0de002 100644
--- a/sound/soc/sof/intel/hda-codec.c
+++ b/sound/soc/sof/intel/hda-codec.c
@@ -23,19 +23,18 @@
#define IDISP_VID_INTEL 0x80860000
/* load the legacy HDA codec driver */
-#ifdef MODULE
-static void hda_codec_load_module(struct hda_codec *codec)
+static int hda_codec_load_module(struct hda_codec *codec)
{
+#ifdef MODULE
char alias[MODULE_NAME_LEN];
const char *module = alias;
snd_hdac_codec_modalias(&codec->core, alias, sizeof(alias));
dev_dbg(&codec->core.dev, "loading codec module: %s\n", module);
request_module(module);
-}
-#else
-static void hda_codec_load_module(struct hda_codec *codec) {}
#endif
+ return device_attach(hda_codec_dev(codec));
+}
#endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */
@@ -76,10 +75,16 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address)
/* use legacy bus only for HDA codecs, idisp uses ext bus */
if ((resp & 0xFFFF0000) != IDISP_VID_INTEL) {
hdev->type = HDA_DEV_LEGACY;
- hda_codec_load_module(&hda_priv->codec);
+ ret = hda_codec_load_module(&hda_priv->codec);
+ /*
+ * handle ret==0 (no driver bound) as an error, but pass
+ * other return codes without modification
+ */
+ if (ret == 0)
+ ret = -ENOENT;
}
- return 0;
+ return ret;
#else
hdev = devm_kzalloc(sdev->dev, sizeof(*hdev), GFP_KERNEL);
if (!hdev)
diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c
index 07bc123112c9..5946c7e289d1 100644
--- a/sound/soc/sof/intel/hda-ctrl.c
+++ b/sound/soc/sof/intel/hda-ctrl.c
@@ -139,20 +139,16 @@ void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable)
*/
int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable)
{
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- struct hdac_bus *bus = sof_to_bus(sdev);
-#endif
u32 val;
/* enable/disable audio dsp clock gating */
val = enable ? PCI_CGCTL_ADSPDCGE : 0;
snd_sof_pci_update_bits(sdev, PCI_CGCTL, PCI_CGCTL_ADSPDCGE, val);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
- /* enable/disable L1 support */
- val = enable ? SOF_HDA_VS_EM2_L1SEN : 0;
- snd_hdac_chip_updatel(bus, VS_EM2, SOF_HDA_VS_EM2_L1SEN, val);
-#endif
+ /* enable/disable DMI Link L1 support */
+ val = enable ? HDA_VS_INTEL_EM2_L1SEN : 0;
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
+ HDA_VS_INTEL_EM2_L1SEN, val);
/* enable/disable audio dsp power gating */
val = enable ? 0 : PCI_PGCTL_ADSPPGD;
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index e1decf25aeac..2c748c1b361f 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -341,6 +341,10 @@ struct snd_soc_dai_driver skl_dai[] = {
.ops = &hda_link_dai_ops,
},
{
+ .name = "iDisp4 Pin",
+ .ops = &hda_link_dai_ops,
+},
+{
.name = "Analog CPU DAI",
.ops = &hda_link_dai_ops,
},
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index 6427f0b3a2f1..356bb134ae93 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -44,6 +44,7 @@ static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format,
return -ENODEV;
}
hstream = &dsp_stream->hstream;
+ hstream->substream = NULL;
/* allocate DMA buffer */
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, &pci->dev, size, dmab);
@@ -277,7 +278,6 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
/* init for booting wait */
init_waitqueue_head(&sdev->boot_wait);
- sdev->boot_complete = false;
/* prepare DMA for code loader stream */
tag = cl_stream_prepare(sdev, 0x40, stripped_firmware.size,
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index c92006f89499..6b879702e202 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -177,6 +177,17 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction)
direction == SNDRV_PCM_STREAM_PLAYBACK ?
"playback" : "capture");
+ /*
+ * Disable DMI Link L1 entry when capture stream is opened.
+ * Workaround to address a known issue with host DMA that results
+ * in xruns during pause/release in capture scenarios.
+ */
+ if (!IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1))
+ if (stream && direction == SNDRV_PCM_STREAM_CAPTURE)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
+ HDA_VS_INTEL_EM2,
+ HDA_VS_INTEL_EM2_L1SEN, 0);
+
return stream;
}
@@ -185,23 +196,43 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
{
struct hdac_bus *bus = sof_to_bus(sdev);
struct hdac_stream *s;
+ bool active_capture_stream = false;
+ bool found = false;
spin_lock_irq(&bus->reg_lock);
- /* find used stream */
+ /*
+ * close stream matching the stream tag
+ * and check if there are any open capture streams.
+ */
list_for_each_entry(s, &bus->stream_list, list) {
- if (s->direction == direction &&
- s->opened && s->stream_tag == stream_tag) {
+ if (!s->opened)
+ continue;
+
+ if (s->direction == direction && s->stream_tag == stream_tag) {
s->opened = false;
- spin_unlock_irq(&bus->reg_lock);
- return 0;
+ found = true;
+ } else if (s->direction == SNDRV_PCM_STREAM_CAPTURE) {
+ active_capture_stream = true;
}
}
spin_unlock_irq(&bus->reg_lock);
- dev_dbg(sdev->dev, "stream_tag %d not opened!\n", stream_tag);
- return -ENODEV;
+ /* Enable DMI L1 entry if there are no capture streams open */
+ if (!IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1))
+ if (!active_capture_stream)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
+ HDA_VS_INTEL_EM2,
+ HDA_VS_INTEL_EM2_L1SEN,
+ HDA_VS_INTEL_EM2_L1SEN);
+
+ if (!found) {
+ dev_dbg(sdev->dev, "stream_tag %d not opened!\n", stream_tag);
+ return -ENODEV;
+ }
+
+ return 0;
}
int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index faf1a8ada091..35c9055ea439 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -32,6 +32,8 @@
/* platform specific devices */
#include "shim.h"
+#define EXCEPT_MAX_HDR_SIZE 0x400
+
/*
* Debug
*/
@@ -116,6 +118,11 @@ static void hda_dsp_get_registers(struct snd_sof_dev *sdev,
/* note: variable AR register array is not read */
/* then get panic info */
+ if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
+ dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",
+ xoops->arch_hdr.totalsize);
+ return;
+ }
offset += xoops->arch_hdr.totalsize;
sof_block_read(sdev, sdev->mmio_bar, offset,
panic_info, sizeof(*panic_info));
@@ -144,7 +151,7 @@ void hda_dsp_dump_skl(struct snd_sof_dev *sdev, u32 flags)
panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
HDA_ADSP_ERROR_CODE_SKL + 0x4);
- if (sdev->boot_complete) {
+ if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
hda_dsp_get_registers(sdev, &xoops, &panic_info, stack,
HDA_DSP_STACK_DUMP_SIZE);
snd_sof_get_status(sdev, status, panic, &xoops, &panic_info,
@@ -171,7 +178,7 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
HDA_DSP_SRAM_REG_FW_STATUS);
panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_TRACEP);
- if (sdev->boot_complete) {
+ if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
hda_dsp_get_registers(sdev, &xoops, &panic_info, stack,
HDA_DSP_STACK_DUMP_SIZE);
snd_sof_get_status(sdev, status, panic, &xoops, &panic_info,
@@ -219,6 +226,7 @@ static int hda_init(struct snd_sof_dev *sdev)
sof_hda_bus_init(bus, &pci->dev, ext_ops);
bus->use_posbuf = 1;
bus->bdl_pos_adj = 0;
+ bus->sync_write = 1;
mutex_init(&hbus->prepare_mutex);
hbus->pci = pci;
@@ -293,10 +301,23 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
if (bus->ppcap)
dev_dbg(sdev->dev, "PP capability, will probe DSP later.\n");
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ /* init i915 and HDMI codecs */
+ ret = hda_codec_i915_init(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: init i915 and HDMI codec failed\n");
+ return ret;
+ }
+#endif
+
+ /* Init HDA controller after i915 init */
ret = hda_dsp_ctrl_init_chip(sdev, true);
if (ret < 0) {
dev_err(bus->dev, "error: init chip failed with ret: %d\n",
ret);
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+ hda_codec_i915_exit(sdev);
+#endif
return ret;
}
@@ -304,13 +325,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
if (bus->mlcap)
snd_hdac_ext_bus_get_ml_capabilities(bus);
- /* init i915 and HDMI codecs */
- ret = hda_codec_i915_init(sdev);
- if (ret < 0) {
- dev_err(sdev->dev, "error: no HDMI audio devices found\n");
- return ret;
- }
-
/* codec detection */
if (!bus->codec_mask) {
dev_info(bus->dev, "no hda codecs found!\n");
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 92d45c43b4b1..54475ac93b4d 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -39,7 +39,6 @@
#define SOF_HDA_WAKESTS 0x0E
#define SOF_HDA_WAKESTS_INT_MASK ((1 << 8) - 1)
#define SOF_HDA_RIRBSTS 0x5d
-#define SOF_HDA_VS_EM2_L1SEN BIT(13)
/* SOF_HDA_GCTL register bist */
#define SOF_HDA_GCTL_RESET BIT(0)
@@ -221,6 +220,10 @@
#define HDA_DSP_REG_HIPCIE (HDA_DSP_IPC_BASE + 0x0C)
#define HDA_DSP_REG_HIPCCTL (HDA_DSP_IPC_BASE + 0x10)
+/* Intel Vendor Specific Registers */
+#define HDA_VS_INTEL_EM2 0x1030
+#define HDA_VS_INTEL_EM2_L1SEN BIT(13)
+
/* HIPCI */
#define HDA_DSP_REG_HIPCI_BUSY BIT(31)
#define HDA_DSP_REG_HIPCI_MSG_MASK 0x7FFFFFFF
@@ -326,7 +329,7 @@
/* Number of DAIs */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-#define SOF_SKL_NUM_DAIS 14
+#define SOF_SKL_NUM_DAIS 15
#else
#define SOF_SKL_NUM_DAIS 8
#endif
diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c
index 666f7fe98693..b3bff871863b 100644
--- a/sound/soc/sof/ipc.c
+++ b/sound/soc/sof/ipc.c
@@ -344,19 +344,12 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
break;
case SOF_IPC_FW_READY:
/* check for FW boot completion */
- if (!sdev->boot_complete) {
+ if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) {
err = sof_ops(sdev)->fw_ready(sdev, cmd);
- if (err < 0) {
- /*
- * this indicates a mismatch in ABI
- * between the driver and fw
- */
- dev_err(sdev->dev, "error: ABI mismatch %d\n",
- err);
- } else {
- /* firmware boot completed OK */
- sdev->boot_complete = true;
- }
+ if (err < 0)
+ sdev->fw_state = SOF_FW_BOOT_READY_FAILED;
+ else
+ sdev->fw_state = SOF_FW_BOOT_COMPLETE;
/* wake up firmware loader */
wake_up(&sdev->boot_wait);
@@ -500,7 +493,7 @@ int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev,
/* send IPC to the DSP */
err = sof_ipc_tx_message(sdev->ipc,
- stream.hdr.cmd, &stream, sizeof(stream), &posn,
+ stream.hdr.cmd, &stream, sizeof(stream), posn,
sizeof(*posn));
if (err < 0) {
dev_err(sdev->dev, "error: failed to get stream %d position\n",
@@ -830,6 +823,9 @@ void snd_sof_ipc_free(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc *ipc = sdev->ipc;
+ if (!ipc)
+ return;
+
/* disable sending of ipc's */
mutex_lock(&ipc->tx_mutex);
ipc->disable_ipc_tx = true;
diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c
index 628fae552442..4cd6e2934922 100644
--- a/sound/soc/sof/loader.c
+++ b/sound/soc/sof/loader.c
@@ -68,6 +68,8 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset)
ret = get_ext_windows(sdev, ext_hdr);
break;
default:
+ dev_warn(sdev->dev, "warning: unknown ext header type %d size 0x%x\n",
+ ext_hdr->type, ext_hdr->hdr.size);
break;
}
@@ -335,7 +337,6 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
int init_core_mask;
init_waitqueue_head(&sdev->boot_wait);
- sdev->boot_complete = false;
/* create fw_version debugfs to store boot version info */
if (sdev->first_boot) {
@@ -367,19 +368,27 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
init_core_mask = ret;
- /* now wait for the DSP to boot */
- ret = wait_event_timeout(sdev->boot_wait, sdev->boot_complete,
+ /*
+ * now wait for the DSP to boot. There are 3 possible outcomes:
+ * 1. Boot wait times out indicating FW boot failure.
+ * 2. FW boots successfully and fw_ready op succeeds.
+ * 3. FW boots but fw_ready op fails.
+ */
+ ret = wait_event_timeout(sdev->boot_wait,
+ sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS,
msecs_to_jiffies(sdev->boot_timeout));
if (ret == 0) {
dev_err(sdev->dev, "error: firmware boot failure\n");
- /* after this point FW_READY msg should be ignored */
- sdev->boot_complete = true;
snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX |
SOF_DBG_TEXT | SOF_DBG_PCI);
+ sdev->fw_state = SOF_FW_BOOT_FAILED;
return -EIO;
}
- dev_info(sdev->dev, "firmware boot complete\n");
+ if (sdev->fw_state == SOF_FW_BOOT_COMPLETE)
+ dev_info(sdev->dev, "firmware boot complete\n");
+ else
+ return -EIO; /* FW boots but fw_ready op failed */
/* perform post fw run operations */
ret = snd_sof_dsp_post_fw_run(sdev);
diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c
index f84b4344dcc3..906cd6bdd54f 100644
--- a/sound/soc/sof/nocodec.c
+++ b/sound/soc/sof/nocodec.c
@@ -14,6 +14,7 @@
static struct snd_soc_card sof_nocodec_card = {
.name = "nocodec", /* the sof- prefix is added by the core */
+ .owner = THIS_MODULE
};
static int sof_nocodec_bes_setup(struct device *dev,
@@ -39,8 +40,10 @@ static int sof_nocodec_bes_setup(struct device *dev,
links[i].platform_name = dev_name(dev);
links[i].codec_dai_name = "snd-soc-dummy-dai";
links[i].codec_name = "snd-soc-dummy";
- links[i].dpcm_playback = 1;
- links[i].dpcm_capture = 1;
+ if (ops->drv[i].playback.channels_min)
+ links[i].dpcm_playback = 1;
+ if (ops->drv[i].capture.channels_min)
+ links[i].dpcm_capture = 1;
}
card->dai_link = links;
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 8ef1d51025d8..62a98ca18da9 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -257,7 +257,14 @@ static int sof_resume(struct device *dev, bool runtime_resume)
int ret;
/* do nothing if dsp resume callbacks are not set */
- if (!sof_ops(sdev)->resume || !sof_ops(sdev)->runtime_resume)
+ if (!runtime_resume && !sof_ops(sdev)->resume)
+ return 0;
+
+ if (runtime_resume && !sof_ops(sdev)->runtime_resume)
+ return 0;
+
+ /* DSP was never successfully started, nothing to resume */
+ if (sdev->first_boot)
return 0;
/*
@@ -274,6 +281,8 @@ static int sof_resume(struct device *dev, bool runtime_resume)
return ret;
}
+ sdev->fw_state = SOF_FW_BOOT_PREPARE;
+
/* load the firmware */
ret = snd_sof_load_firmware(sdev);
if (ret < 0) {
@@ -283,7 +292,12 @@ static int sof_resume(struct device *dev, bool runtime_resume)
return ret;
}
- /* boot the firmware */
+ sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS;
+
+ /*
+ * Boot the firmware. The FW boot status will be modified
+ * in snd_sof_run_firmware() depending on the outcome.
+ */
ret = snd_sof_run_firmware(sdev);
if (ret < 0) {
dev_err(sdev->dev,
@@ -326,9 +340,15 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
int ret;
/* do nothing if dsp suspend callback is not set */
- if (!sof_ops(sdev)->suspend)
+ if (!runtime_suspend && !sof_ops(sdev)->suspend)
return 0;
+ if (runtime_suspend && !sof_ops(sdev)->runtime_suspend)
+ return 0;
+
+ if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
+ goto power_down;
+
/* release trace */
snd_sof_release_trace(sdev);
@@ -350,6 +370,12 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
return ret;
}
+power_down:
+
+ /* return if the DSP was not probed successfully */
+ if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED)
+ return 0;
+
/* power down all DSP cores */
if (runtime_suspend)
ret = snd_sof_dsp_runtime_suspend(sdev, 0);
@@ -360,6 +386,9 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
"error: failed to power down DSP during suspend %d\n",
ret);
+ /* reset FW state */
+ sdev->fw_state = SOF_FW_BOOT_NOT_STARTED;
+
return ret;
}
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 1e85d6f9c5c3..05fde6f3cbc3 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -337,6 +337,15 @@ struct snd_sof_dai {
struct list_head list; /* list in sdev dai list */
};
+enum snd_sof_fw_state {
+ SOF_FW_BOOT_NOT_STARTED = 0,
+ SOF_FW_BOOT_PREPARE,
+ SOF_FW_BOOT_IN_PROGRESS,
+ SOF_FW_BOOT_FAILED,
+ SOF_FW_BOOT_READY_FAILED, /* firmware booted but fw_ready op failed */
+ SOF_FW_BOOT_COMPLETE,
+};
+
/*
* SOF Device Level.
*/
@@ -353,7 +362,7 @@ struct snd_sof_dev {
/* DSP firmware boot */
wait_queue_head_t boot_wait;
- u32 boot_complete;
+ enum snd_sof_fw_state fw_state;
u32 first_boot;
/* work queue in case the probe is implemented in two steps */
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index c88afa872a58..5fbb25f9ccb5 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -531,15 +531,16 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp,
struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value;
int max_size = sbe->max;
- if (le32_to_cpu(control->priv.size) > max_size) {
+ /* init the get/put bytes data */
+ scontrol->size = sizeof(struct sof_ipc_ctrl_data) +
+ le32_to_cpu(control->priv.size);
+
+ if (scontrol->size > max_size) {
dev_err(sdev->dev, "err: bytes data size %d exceeds max %d.\n",
- control->priv.size, max_size);
+ scontrol->size, max_size);
return -EINVAL;
}
- /* init the get/put bytes data */
- scontrol->size = sizeof(struct sof_ipc_ctrl_data) +
- le32_to_cpu(control->priv.size);
scontrol->control_data = kzalloc(max_size, GFP_KERNEL);
cdata = scontrol->control_data;
if (!scontrol->control_data)
@@ -901,7 +902,9 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp,
for (j = 0; j < count; j++) {
/* match token type */
if (!(tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD ||
- tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT))
+ tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT ||
+ tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_BYTE ||
+ tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_BOOL))
continue;
/* match token id */
@@ -2717,6 +2720,10 @@ static int sof_link_load(struct snd_soc_component *scomp, int index,
if (!link->no_pcm) {
link->nonatomic = true;
+ /* set trigger order */
+ link->trigger[0] = SND_SOC_DPCM_TRIGGER_POST;
+ link->trigger[1] = SND_SOC_DPCM_TRIGGER_POST;
+
/* nothing more to do for FE dai links */
return 0;
}
diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c
index d588e4b70fad..39bafe319556 100644
--- a/sound/soc/sof/trace.c
+++ b/sound/soc/sof/trace.c
@@ -291,7 +291,10 @@ void snd_sof_free_trace(struct snd_sof_dev *sdev)
{
snd_sof_release_trace(sdev);
- snd_dma_free_pages(&sdev->dmatb);
- snd_dma_free_pages(&sdev->dmatp);
+ if (sdev->dma_trace_pages) {
+ snd_dma_free_pages(&sdev->dmatb);
+ snd_dma_free_pages(&sdev->dmatp);
+ sdev->dma_trace_pages = 0;
+ }
}
EXPORT_SYMBOL(snd_sof_free_trace);
diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c
index 48ea915b24ba..2ed92c990b97 100644
--- a/sound/soc/sti/uniperif_player.c
+++ b/sound/soc/sti/uniperif_player.c
@@ -226,7 +226,6 @@ static void uni_player_set_channel_status(struct uniperif *player,
* sampling frequency. If no sample rate is already specified, then
* set one.
*/
- mutex_lock(&player->ctrl_lock);
if (runtime) {
switch (runtime->rate) {
case 22050:
@@ -303,7 +302,6 @@ static void uni_player_set_channel_status(struct uniperif *player,
player->stream_settings.iec958.status[3 + (n * 4)] << 24;
SET_UNIPERIF_CHANNEL_STA_REGN(player, n, status);
}
- mutex_unlock(&player->ctrl_lock);
/* Update the channel status */
if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
@@ -365,8 +363,10 @@ static int uni_player_prepare_iec958(struct uniperif *player,
SET_UNIPERIF_CTRL_ZERO_STUFF_HW(player);
+ mutex_lock(&player->ctrl_lock);
/* Update the channel status */
uni_player_set_channel_status(player, runtime);
+ mutex_unlock(&player->ctrl_lock);
/* Clear the user validity user bits */
SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(player, 0);
@@ -598,7 +598,6 @@ static int uni_player_ctl_iec958_put(struct snd_kcontrol *kcontrol,
iec958->status[1] = ucontrol->value.iec958.status[1];
iec958->status[2] = ucontrol->value.iec958.status[2];
iec958->status[3] = ucontrol->value.iec958.status[3];
- mutex_unlock(&player->ctrl_lock);
spin_lock_irqsave(&player->irq_lock, flags);
if (player->substream && player->substream->runtime)
@@ -608,6 +607,8 @@ static int uni_player_ctl_iec958_put(struct snd_kcontrol *kcontrol,
uni_player_set_channel_status(player, NULL);
spin_unlock_irqrestore(&player->irq_lock, flags);
+ mutex_unlock(&player->ctrl_lock);
+
return 0;
}
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c
index 25c9cb67d6dd..79e6e18a0bde 100644
--- a/sound/soc/stm/stm32_sai_sub.c
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -184,6 +184,56 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg)
}
}
+static int stm32_sai_sub_reg_up(struct stm32_sai_sub_data *sai,
+ unsigned int reg, unsigned int mask,
+ unsigned int val)
+{
+ int ret;
+
+ ret = clk_enable(sai->pdata->pclk);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(sai->regmap, reg, mask, val);
+
+ clk_disable(sai->pdata->pclk);
+
+ return ret;
+}
+
+static int stm32_sai_sub_reg_wr(struct stm32_sai_sub_data *sai,
+ unsigned int reg, unsigned int mask,
+ unsigned int val)
+{
+ int ret;
+
+ ret = clk_enable(sai->pdata->pclk);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write_bits(sai->regmap, reg, mask, val);
+
+ clk_disable(sai->pdata->pclk);
+
+ return ret;
+}
+
+static int stm32_sai_sub_reg_rd(struct stm32_sai_sub_data *sai,
+ unsigned int reg, unsigned int *val)
+{
+ int ret;
+
+ ret = clk_enable(sai->pdata->pclk);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(sai->regmap, reg, val);
+
+ clk_disable(sai->pdata->pclk);
+
+ return ret;
+}
+
static const struct regmap_config stm32_sai_sub_regmap_config_f4 = {
.reg_bits = 32,
.reg_stride = 4,
@@ -295,7 +345,7 @@ static int stm32_sai_set_clk_div(struct stm32_sai_sub_data *sai,
mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version));
cr1 = SAI_XCR1_MCKDIV_SET(div);
- ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1);
+ ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, mask, cr1);
if (ret < 0)
dev_err(&sai->pdev->dev, "Failed to update CR1 register\n");
@@ -372,8 +422,8 @@ static int stm32_sai_mclk_enable(struct clk_hw *hw)
dev_dbg(&sai->pdev->dev, "Enable master clock\n");
- return regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
- SAI_XCR1_MCKEN, SAI_XCR1_MCKEN);
+ return stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX,
+ SAI_XCR1_MCKEN, SAI_XCR1_MCKEN);
}
static void stm32_sai_mclk_disable(struct clk_hw *hw)
@@ -383,7 +433,7 @@ static void stm32_sai_mclk_disable(struct clk_hw *hw)
dev_dbg(&sai->pdev->dev, "Disable master clock\n");
- regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_MCKEN, 0);
+ stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, SAI_XCR1_MCKEN, 0);
}
static const struct clk_ops mclk_ops = {
@@ -446,15 +496,15 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid)
unsigned int sr, imr, flags;
snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING;
- regmap_read(sai->regmap, STM_SAI_IMR_REGX, &imr);
- regmap_read(sai->regmap, STM_SAI_SR_REGX, &sr);
+ stm32_sai_sub_reg_rd(sai, STM_SAI_IMR_REGX, &imr);
+ stm32_sai_sub_reg_rd(sai, STM_SAI_SR_REGX, &sr);
flags = sr & imr;
if (!flags)
return IRQ_NONE;
- regmap_write_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK,
- SAI_XCLRFR_MASK);
+ stm32_sai_sub_reg_wr(sai, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK,
+ SAI_XCLRFR_MASK);
if (!sai->substream) {
dev_err(&pdev->dev, "Device stopped. Spurious IRQ 0x%x\n", sr);
@@ -503,8 +553,8 @@ static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai,
int ret;
if (dir == SND_SOC_CLOCK_OUT && sai->sai_mclk) {
- ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
- SAI_XCR1_NODIV,
+ ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX,
+ SAI_XCR1_NODIV,
(unsigned int)~SAI_XCR1_NODIV);
if (ret < 0)
return ret;
@@ -573,7 +623,7 @@ static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
slotr_mask |= SAI_XSLOTR_SLOTEN_MASK;
- regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, slotr_mask, slotr);
+ stm32_sai_sub_reg_up(sai, STM_SAI_SLOTR_REGX, slotr_mask, slotr);
sai->slot_width = slot_width;
sai->slots = slots;
@@ -655,7 +705,7 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
cr1_mask |= SAI_XCR1_CKSTR;
frcr_mask |= SAI_XFRCR_FSPOL;
- regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr);
+ stm32_sai_sub_reg_up(sai, STM_SAI_FRCR_REGX, frcr_mask, frcr);
/* DAI clock master masks */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -683,7 +733,7 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
cr1_mask |= SAI_XCR1_SLAVE;
conf_update:
- ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1);
+ ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, cr1_mask, cr1);
if (ret < 0) {
dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
return ret;
@@ -720,12 +770,12 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream,
}
/* Enable ITs */
- regmap_write_bits(sai->regmap, STM_SAI_CLRFR_REGX,
- SAI_XCLRFR_MASK, SAI_XCLRFR_MASK);
+ stm32_sai_sub_reg_wr(sai, STM_SAI_CLRFR_REGX,
+ SAI_XCLRFR_MASK, SAI_XCLRFR_MASK);
imr = SAI_XIMR_OVRUDRIE;
if (STM_SAI_IS_CAPTURE(sai)) {
- regmap_read(sai->regmap, STM_SAI_CR2_REGX, &cr2);
+ stm32_sai_sub_reg_rd(sai, STM_SAI_CR2_REGX, &cr2);
if (cr2 & SAI_XCR2_MUTECNT_MASK)
imr |= SAI_XIMR_MUTEDETIE;
}
@@ -735,8 +785,8 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream,
else
imr |= SAI_XIMR_AFSDETIE | SAI_XIMR_LFSDETIE;
- regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX,
- SAI_XIMR_MASK, imr);
+ stm32_sai_sub_reg_up(sai, STM_SAI_IMR_REGX,
+ SAI_XIMR_MASK, imr);
return 0;
}
@@ -753,10 +803,10 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai,
* SAI fifo threshold is set to half fifo, to keep enough space
* for DMA incoming bursts.
*/
- regmap_write_bits(sai->regmap, STM_SAI_CR2_REGX,
- SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK,
- SAI_XCR2_FFLUSH |
- SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF));
+ stm32_sai_sub_reg_wr(sai, STM_SAI_CR2_REGX,
+ SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK,
+ SAI_XCR2_FFLUSH |
+ SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF));
/* DS bits in CR1 not set for SPDIF (size forced to 24 bits).*/
if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) {
@@ -785,7 +835,7 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai,
if ((sai->slots == 2) && (params_channels(params) == 1))
cr1 |= SAI_XCR1_MONO;
- ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1);
+ ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, cr1_mask, cr1);
if (ret < 0) {
dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
return ret;
@@ -799,7 +849,7 @@ static int stm32_sai_set_slots(struct snd_soc_dai *cpu_dai)
struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
int slotr, slot_sz;
- regmap_read(sai->regmap, STM_SAI_SLOTR_REGX, &slotr);
+ stm32_sai_sub_reg_rd(sai, STM_SAI_SLOTR_REGX, &slotr);
/*
* If SLOTSZ is set to auto in SLOTR, align slot width on data size
@@ -821,16 +871,16 @@ static int stm32_sai_set_slots(struct snd_soc_dai *cpu_dai)
sai->slots = 2;
/* The number of slots in the audio frame is equal to NBSLOT[3:0] + 1*/
- regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX,
- SAI_XSLOTR_NBSLOT_MASK,
- SAI_XSLOTR_NBSLOT_SET((sai->slots - 1)));
+ stm32_sai_sub_reg_up(sai, STM_SAI_SLOTR_REGX,
+ SAI_XSLOTR_NBSLOT_MASK,
+ SAI_XSLOTR_NBSLOT_SET((sai->slots - 1)));
/* Set default slots mask if not already set from DT */
if (!(slotr & SAI_XSLOTR_SLOTEN_MASK)) {
sai->slot_mask = (1 << sai->slots) - 1;
- regmap_update_bits(sai->regmap,
- STM_SAI_SLOTR_REGX, SAI_XSLOTR_SLOTEN_MASK,
- SAI_XSLOTR_SLOTEN_SET(sai->slot_mask));
+ stm32_sai_sub_reg_up(sai,
+ STM_SAI_SLOTR_REGX, SAI_XSLOTR_SLOTEN_MASK,
+ SAI_XSLOTR_SLOTEN_SET(sai->slot_mask));
}
dev_dbg(cpu_dai->dev, "Slots %d, slot width %d\n",
@@ -860,14 +910,14 @@ static void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai)
dev_dbg(cpu_dai->dev, "Frame length %d, frame active %d\n",
sai->fs_length, fs_active);
- regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr);
+ stm32_sai_sub_reg_up(sai, STM_SAI_FRCR_REGX, frcr_mask, frcr);
if ((sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_LSB) {
offset = sai->slot_width - sai->data_size;
- regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX,
- SAI_XSLOTR_FBOFF_MASK,
- SAI_XSLOTR_FBOFF_SET(offset));
+ stm32_sai_sub_reg_up(sai, STM_SAI_SLOTR_REGX,
+ SAI_XSLOTR_FBOFF_MASK,
+ SAI_XSLOTR_FBOFF_SET(offset));
}
}
@@ -984,9 +1034,9 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
return -EINVAL;
}
- regmap_update_bits(sai->regmap,
- STM_SAI_CR1_REGX,
- SAI_XCR1_OSR, cr1);
+ stm32_sai_sub_reg_up(sai,
+ STM_SAI_CR1_REGX,
+ SAI_XCR1_OSR, cr1);
div = stm32_sai_get_clk_div(sai, sai_clk_rate,
sai->mclk_rate);
@@ -1048,12 +1098,12 @@ static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
dev_dbg(cpu_dai->dev, "Enable DMA and SAI\n");
- regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
- SAI_XCR1_DMAEN, SAI_XCR1_DMAEN);
+ stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX,
+ SAI_XCR1_DMAEN, SAI_XCR1_DMAEN);
/* Enable SAI */
- ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
- SAI_XCR1_SAIEN, SAI_XCR1_SAIEN);
+ ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX,
+ SAI_XCR1_SAIEN, SAI_XCR1_SAIEN);
if (ret < 0)
dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
break;
@@ -1062,16 +1112,16 @@ static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_STOP:
dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n");
- regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX,
- SAI_XIMR_MASK, 0);
+ stm32_sai_sub_reg_up(sai, STM_SAI_IMR_REGX,
+ SAI_XIMR_MASK, 0);
- regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
- SAI_XCR1_SAIEN,
- (unsigned int)~SAI_XCR1_SAIEN);
+ stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX,
+ SAI_XCR1_SAIEN,
+ (unsigned int)~SAI_XCR1_SAIEN);
- ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
- SAI_XCR1_DMAEN,
- (unsigned int)~SAI_XCR1_DMAEN);
+ ret = stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX,
+ SAI_XCR1_DMAEN,
+ (unsigned int)~SAI_XCR1_DMAEN);
if (ret < 0)
dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
@@ -1091,7 +1141,7 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream,
struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
unsigned long flags;
- regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0);
+ stm32_sai_sub_reg_up(sai, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0);
regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_NODIV,
SAI_XCR1_NODIV);
@@ -1166,7 +1216,7 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai)
cr1_mask |= SAI_XCR1_SYNCEN_MASK;
cr1 |= SAI_XCR1_SYNCEN_SET(sai->sync);
- return regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1);
+ return stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, cr1_mask, cr1);
}
static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = {
@@ -1215,6 +1265,16 @@ static int stm32_sai_pcm_process_spdif(struct snd_pcm_substream *substream,
return 0;
}
+/* No support of mmap in S/PDIF mode */
+static const struct snd_pcm_hardware stm32_sai_pcm_hw_spdif = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED,
+ .buffer_bytes_max = 8 * PAGE_SIZE,
+ .period_bytes_min = 1024,
+ .period_bytes_max = PAGE_SIZE,
+ .periods_min = 2,
+ .periods_max = 8,
+};
+
static const struct snd_pcm_hardware stm32_sai_pcm_hw = {
.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP,
.buffer_bytes_max = 8 * PAGE_SIZE,
@@ -1267,7 +1327,7 @@ static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = {
};
static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config_spdif = {
- .pcm_hardware = &stm32_sai_pcm_hw,
+ .pcm_hardware = &stm32_sai_pcm_hw_spdif,
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
.process = stm32_sai_pcm_process_spdif,
};
@@ -1309,8 +1369,13 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev,
if (STM_SAI_IS_H7(sai->pdata) && STM_SAI_IS_SUB_A(sai))
sai->regmap_config = &stm32_sai_sub_regmap_config_h7;
- sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai_ck",
- base, sai->regmap_config);
+ /*
+ * Do not manage peripheral clock through regmap framework as this
+ * can lead to circular locking issue with sai master clock provider.
+ * Manage peripheral clock directly in driver instead.
+ */
+ sai->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ sai->regmap_config);
if (IS_ERR(sai->regmap)) {
dev_err(&pdev->dev, "Failed to initialize MMIO\n");
return PTR_ERR(sai->regmap);
@@ -1407,6 +1472,10 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev,
return PTR_ERR(sai->sai_ck);
}
+ ret = clk_prepare(sai->pdata->pclk);
+ if (ret < 0)
+ return ret;
+
if (STM_SAI_IS_F4(sai->pdata))
return 0;
@@ -1471,20 +1540,31 @@ static int stm32_sai_sub_probe(struct platform_device *pdev)
return ret;
}
- ret = devm_snd_soc_register_component(&pdev->dev, &stm32_component,
- &sai->cpu_dai_drv, 1);
- if (ret)
- return ret;
-
if (STM_SAI_PROTOCOL_IS_SPDIF(sai))
conf = &stm32_sai_pcm_config_spdif;
- ret = devm_snd_dmaengine_pcm_register(&pdev->dev, conf, 0);
+ ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0);
if (ret) {
dev_err(&pdev->dev, "Could not register pcm dma\n");
return ret;
}
+ ret = snd_soc_register_component(&pdev->dev, &stm32_component,
+ &sai->cpu_dai_drv, 1);
+ if (ret)
+ snd_dmaengine_pcm_unregister(&pdev->dev);
+
+ return ret;
+}
+
+static int stm32_sai_sub_remove(struct platform_device *pdev)
+{
+ struct stm32_sai_sub_data *sai = dev_get_drvdata(&pdev->dev);
+
+ clk_unprepare(sai->pdata->pclk);
+ snd_dmaengine_pcm_unregister(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
+
return 0;
}
@@ -1492,18 +1572,35 @@ static int stm32_sai_sub_probe(struct platform_device *pdev)
static int stm32_sai_sub_suspend(struct device *dev)
{
struct stm32_sai_sub_data *sai = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_enable(sai->pdata->pclk);
+ if (ret < 0)
+ return ret;
regcache_cache_only(sai->regmap, true);
regcache_mark_dirty(sai->regmap);
+
+ clk_disable(sai->pdata->pclk);
+
return 0;
}
static int stm32_sai_sub_resume(struct device *dev)
{
struct stm32_sai_sub_data *sai = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_enable(sai->pdata->pclk);
+ if (ret < 0)
+ return ret;
regcache_cache_only(sai->regmap, false);
- return regcache_sync(sai->regmap);
+ ret = regcache_sync(sai->regmap);
+
+ clk_disable(sai->pdata->pclk);
+
+ return ret;
}
#endif /* CONFIG_PM_SLEEP */
@@ -1518,6 +1615,7 @@ static struct platform_driver stm32_sai_sub_driver = {
.pm = &stm32_sai_sub_pm_ops,
},
.probe = stm32_sai_sub_probe,
+ .remove = stm32_sai_sub_remove,
};
module_platform_driver(stm32_sai_sub_driver);
diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c
index 56d79695577c..ac9ae434237a 100644
--- a/sound/soc/stm/stm32_spdifrx.c
+++ b/sound/soc/stm/stm32_spdifrx.c
@@ -11,7 +11,6 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_platform.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/regmap.h>
#include <linux/reset.h>
@@ -204,6 +203,7 @@
* @slave_config: dma slave channel runtime config pointer
* @phys_addr: SPDIFRX registers physical base address
* @lock: synchronization enabling lock
+ * @irq_lock: prevent race condition with IRQ on stream state
* @cs: channel status buffer
* @ub: user data buffer
* @irq: SPDIFRX interrupt line
@@ -224,6 +224,7 @@ struct stm32_spdifrx_data {
struct dma_slave_config slave_config;
dma_addr_t phys_addr;
spinlock_t lock; /* Sync enabling lock */
+ spinlock_t irq_lock; /* Prevent race condition on stream state */
unsigned char cs[SPDIFRX_CS_BYTES_NB];
unsigned char ub[SPDIFRX_UB_BYTES_NB];
int irq;
@@ -304,6 +305,7 @@ static void stm32_spdifrx_dma_ctrl_stop(struct stm32_spdifrx_data *spdifrx)
static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx)
{
int cr, cr_mask, imr, ret;
+ unsigned long flags;
/* Enable IRQs */
imr = SPDIFRX_IMR_IFEIE | SPDIFRX_IMR_SYNCDIE | SPDIFRX_IMR_PERRIE;
@@ -311,7 +313,7 @@ static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx)
if (ret)
return ret;
- spin_lock(&spdifrx->lock);
+ spin_lock_irqsave(&spdifrx->lock, flags);
spdifrx->refcount++;
@@ -344,7 +346,7 @@ static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx)
"Failed to start synchronization\n");
}
- spin_unlock(&spdifrx->lock);
+ spin_unlock_irqrestore(&spdifrx->lock, flags);
return ret;
}
@@ -352,11 +354,12 @@ static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx)
static void stm32_spdifrx_stop(struct stm32_spdifrx_data *spdifrx)
{
int cr, cr_mask, reg;
+ unsigned long flags;
- spin_lock(&spdifrx->lock);
+ spin_lock_irqsave(&spdifrx->lock, flags);
if (--spdifrx->refcount) {
- spin_unlock(&spdifrx->lock);
+ spin_unlock_irqrestore(&spdifrx->lock, flags);
return;
}
@@ -375,7 +378,7 @@ static void stm32_spdifrx_stop(struct stm32_spdifrx_data *spdifrx)
regmap_read(spdifrx->regmap, STM32_SPDIFRX_DR, &reg);
regmap_read(spdifrx->regmap, STM32_SPDIFRX_CSR, &reg);
- spin_unlock(&spdifrx->lock);
+ spin_unlock_irqrestore(&spdifrx->lock, flags);
}
static int stm32_spdifrx_dma_ctrl_register(struct device *dev,
@@ -462,8 +465,6 @@ static int stm32_spdifrx_get_ctrl_data(struct stm32_spdifrx_data *spdifrx)
memset(spdifrx->cs, 0, SPDIFRX_CS_BYTES_NB);
memset(spdifrx->ub, 0, SPDIFRX_UB_BYTES_NB);
- pinctrl_pm_select_default_state(&spdifrx->pdev->dev);
-
ret = stm32_spdifrx_dma_ctrl_start(spdifrx);
if (ret < 0)
return ret;
@@ -495,7 +496,6 @@ static int stm32_spdifrx_get_ctrl_data(struct stm32_spdifrx_data *spdifrx)
end:
clk_disable_unprepare(spdifrx->kclk);
- pinctrl_pm_select_sleep_state(&spdifrx->pdev->dev);
return ret;
}
@@ -643,7 +643,6 @@ static const struct regmap_config stm32_h7_spdifrx_regmap_conf = {
static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
{
struct stm32_spdifrx_data *spdifrx = (struct stm32_spdifrx_data *)devid;
- struct snd_pcm_substream *substream = spdifrx->substream;
struct platform_device *pdev = spdifrx->pdev;
unsigned int cr, mask, sr, imr;
unsigned int flags;
@@ -711,14 +710,19 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR,
SPDIFRX_CR_SPDIFEN_MASK, cr);
- if (substream)
- snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
+ spin_lock(&spdifrx->irq_lock);
+ if (spdifrx->substream)
+ snd_pcm_stop(spdifrx->substream,
+ SNDRV_PCM_STATE_DISCONNECTED);
+ spin_unlock(&spdifrx->irq_lock);
return IRQ_HANDLED;
}
- if (err_xrun && substream)
- snd_pcm_stop_xrun(substream);
+ spin_lock(&spdifrx->irq_lock);
+ if (err_xrun && spdifrx->substream)
+ snd_pcm_stop_xrun(spdifrx->substream);
+ spin_unlock(&spdifrx->irq_lock);
return IRQ_HANDLED;
}
@@ -727,9 +731,12 @@ static int stm32_spdifrx_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned long flags;
int ret;
+ spin_lock_irqsave(&spdifrx->irq_lock, flags);
spdifrx->substream = substream;
+ spin_unlock_irqrestore(&spdifrx->irq_lock, flags);
ret = clk_prepare_enable(spdifrx->kclk);
if (ret)
@@ -805,8 +812,12 @@ static void stm32_spdifrx_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned long flags;
+ spin_lock_irqsave(&spdifrx->irq_lock, flags);
spdifrx->substream = NULL;
+ spin_unlock_irqrestore(&spdifrx->irq_lock, flags);
+
clk_disable_unprepare(spdifrx->kclk);
}
@@ -911,6 +922,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev)
spdifrx->pdev = pdev;
init_completion(&spdifrx->cs_completion);
spin_lock_init(&spdifrx->lock);
+ spin_lock_init(&spdifrx->irq_lock);
platform_set_drvdata(pdev, spdifrx);
diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 0e0e8ebaa571..f9603c14ab57 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -80,6 +80,7 @@
#define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK GENMASK(15, 12)
#define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK GENMASK(11, 8)
+#define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK GENMASK(3, 2)
#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK GENMASK(5, 4)
#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK GENMASK(8, 6)
#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK GENMASK(12, 9)
@@ -241,7 +242,7 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
- BIT(SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT),
+ SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK,
value << SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT);
return 0;
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index 4c94c39f14d6..054b863a6301 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -159,6 +159,7 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_component *component = codec_dai->component;
struct snd_soc_card *card = rtd->card;
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ int shrt = 0;
if (gpio_is_valid(machine->gpio_hp_det)) {
tegra_wm8903_hp_jack_gpio.gpio = machine->gpio_hp_det;
@@ -171,12 +172,15 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
&tegra_wm8903_hp_jack_gpio);
}
+ if (of_property_read_bool(card->dev->of_node, "nvidia,headset"))
+ shrt = SND_JACK_MICROPHONE;
+
snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE,
&tegra_wm8903_mic_jack,
tegra_wm8903_mic_jack_pins,
ARRAY_SIZE(tegra_wm8903_mic_jack_pins));
wm8903_mic_detect(component, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE,
- 0);
+ shrt);
snd_soc_dapm_force_enable_pin(&card->dapm, "MICBIAS");
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c
index 56009d147208..e6fa200e822b 100644
--- a/sound/soc/ti/davinci-mcasp.c
+++ b/sound/soc/ti/davinci-mcasp.c
@@ -1807,8 +1807,10 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp)
PTR_ERR(chan));
return PTR_ERR(chan);
}
- if (WARN_ON(!chan->device || !chan->device->dev))
+ if (WARN_ON(!chan->device || !chan->device->dev)) {
+ dma_release_channel(chan);
return -EINVAL;
+ }
if (chan->device->dev->of_node)
ret = of_property_read_string(chan->device->dev->of_node,
@@ -2260,7 +2262,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
ret = edma_pcm_platform_register(&pdev->dev);
break;
case PCM_SDMA:
- ret = sdma_pcm_platform_register(&pdev->dev, NULL, NULL);
+ ret = sdma_pcm_platform_register(&pdev->dev, "tx", "rx");
break;
default:
dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret);
diff --git a/sound/soc/ti/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c
index 1ab3c7df4f8b..3273b317fa3b 100644
--- a/sound/soc/ti/omap-mcbsp.c
+++ b/sound/soc/ti/omap-mcbsp.c
@@ -686,7 +686,7 @@ static int omap_mcbsp_init(struct platform_device *pdev)
mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp,
SNDRV_PCM_STREAM_CAPTURE);
- mcbsp->fclk = clk_get(&pdev->dev, "fck");
+ mcbsp->fclk = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(mcbsp->fclk)) {
ret = PTR_ERR(mcbsp->fclk);
dev_err(mcbsp->dev, "unable to get fck: %d\n", ret);
@@ -711,7 +711,7 @@ static int omap_mcbsp_init(struct platform_device *pdev)
if (ret) {
dev_err(mcbsp->dev,
"Unable to create additional controls\n");
- goto err_thres;
+ return ret;
}
}
@@ -724,8 +724,6 @@ static int omap_mcbsp_init(struct platform_device *pdev)
err_st:
if (mcbsp->pdata->buffer_size)
sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group);
-err_thres:
- clk_put(mcbsp->fclk);
return ret;
}
@@ -1424,7 +1422,7 @@ static int asoc_mcbsp_probe(struct platform_device *pdev)
if (ret)
return ret;
- return sdma_pcm_platform_register(&pdev->dev, NULL, NULL);
+ return sdma_pcm_platform_register(&pdev->dev, "tx", "rx");
}
static int asoc_mcbsp_remove(struct platform_device *pdev)
@@ -1442,8 +1440,6 @@ static int asoc_mcbsp_remove(struct platform_device *pdev)
omap_mcbsp_st_cleanup(pdev);
- clk_put(mcbsp->fclk);
-
return 0;
}
diff --git a/sound/soc/ti/sdma-pcm.c b/sound/soc/ti/sdma-pcm.c
index a236350beb10..2b0bc234e1b6 100644
--- a/sound/soc/ti/sdma-pcm.c
+++ b/sound/soc/ti/sdma-pcm.c
@@ -62,7 +62,7 @@ int sdma_pcm_platform_register(struct device *dev,
config->chan_names[0] = txdmachan;
config->chan_names[1] = rxdmachan;
- return devm_snd_dmaengine_pcm_register(dev, config, 0);
+ return devm_snd_dmaengine_pcm_register(dev, config, flags);
}
EXPORT_SYMBOL_GPL(sdma_pcm_platform_register);
diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c
index 759c635412a2..5315adc1134b 100644
--- a/sound/soc/ux500/mop500.c
+++ b/sound/soc/ux500/mop500.c
@@ -59,10 +59,11 @@ static void mop500_of_node_put(void)
{
int i;
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < 2; i++)
of_node_put(mop500_dai_links[i].cpu_of_node);
- of_node_put(mop500_dai_links[i].codec_of_node);
- }
+
+ /* Both links use the same codec, which is refcounted only once */
+ of_node_put(mop500_dai_links[0].codec_of_node);
}
static int mop500_of_probe(struct platform_device *pdev,
@@ -77,7 +78,9 @@ static int mop500_of_probe(struct platform_device *pdev,
if (!(msp_np[0] && msp_np[1] && codec_np)) {
dev_err(&pdev->dev, "Phandle missing or invalid\n");
- mop500_of_node_put();
+ for (i = 0; i < 2; i++)
+ of_node_put(msp_np[i]);
+ of_node_put(codec_np);
return -EINVAL;
}
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index e1ce257ab705..d27a21b0ff9c 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -16,7 +16,8 @@ snd-usb-audio-objs := card.o \
power.o \
proc.o \
quirks.o \
- stream.o
+ stream.o \
+ validate.o
snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.o
diff --git a/sound/usb/card.c b/sound/usb/card.c
index db91dc76cc91..230d862cfa3a 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -597,6 +597,10 @@ static int usb_audio_probe(struct usb_interface *intf,
}
}
if (! chip) {
+ err = snd_usb_apply_boot_quirk_once(dev, intf, quirk, id);
+ if (err < 0)
+ goto __error;
+
/* it's a fresh one.
* now look for an empty slot and create a new card instance
*/
@@ -655,10 +659,14 @@ static int usb_audio_probe(struct usb_interface *intf,
goto __error;
}
- /* we are allowed to call snd_card_register() many times */
- err = snd_card_register(chip->card);
- if (err < 0)
- goto __error;
+ /* we are allowed to call snd_card_register() many times, but first
+ * check to see if a device needs to skip it or do anything special
+ */
+ if (!snd_usb_registration_quirk(chip, ifnum)) {
+ err = snd_card_register(chip->card);
+ if (err < 0)
+ goto __error;
+ }
if (quirk && quirk->shares_media_device) {
/* don't want to fail when snd_media_device_create() fails */
@@ -806,9 +814,6 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
if (chip == (void *)-1L)
return 0;
- chip->autosuspended = !!PMSG_IS_AUTO(message);
- if (!chip->autosuspended)
- snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
if (!chip->num_suspended_intf++) {
list_for_each_entry(as, &chip->pcm_list, list) {
snd_usb_pcm_suspend(as);
@@ -821,6 +826,11 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
snd_usb_mixer_suspend(mixer);
}
+ if (!PMSG_IS_AUTO(message) && !chip->system_suspend) {
+ snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
+ chip->system_suspend = chip->num_suspended_intf;
+ }
+
return 0;
}
@@ -834,10 +844,10 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)
if (chip == (void *)-1L)
return 0;
- if (--chip->num_suspended_intf)
- return 0;
atomic_inc(&chip->active); /* avoid autopm */
+ if (chip->num_suspended_intf > 1)
+ goto out;
list_for_each_entry(as, &chip->pcm_list, list) {
err = snd_usb_pcm_resume(as);
@@ -859,9 +869,12 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)
snd_usbmidi_resume(p);
}
- if (!chip->autosuspended)
+ out:
+ if (chip->num_suspended_intf == chip->system_suspend) {
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
- chip->autosuspended = 0;
+ chip->system_suspend = 0;
+ }
+ chip->num_suspended_intf--;
err_out:
atomic_dec(&chip->active); /* allow autopm after this point */
diff --git a/sound/usb/card.h b/sound/usb/card.h
index 2991b9986f66..5351d7183b1b 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -84,6 +84,10 @@ struct snd_usb_endpoint {
dma_addr_t sync_dma; /* DMA address of syncbuf */
unsigned int pipe; /* the data i/o pipe */
+ unsigned int packsize[2]; /* small/large packet sizes in samples */
+ unsigned int sample_rem; /* remainder from division fs/pps */
+ unsigned int sample_accum; /* sample accumulator */
+ unsigned int pps; /* packets per second */
unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */
unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */
int freqshift; /* how much to shift the feedback value to get Q16.16 */
@@ -104,6 +108,7 @@ struct snd_usb_endpoint {
int iface, altsetting;
int skip_packets; /* quirks for devices to ignore the first n packets
in a stream */
+ bool is_implicit_feedback; /* This endpoint is used as implicit feedback */
spinlock_t lock;
struct list_head list;
@@ -132,6 +137,7 @@ struct snd_usb_substream {
unsigned int tx_length_quirk:1; /* add length specifier to transfers */
unsigned int fmt_type; /* USB audio format type (1-3) */
unsigned int pkt_offset_adj; /* Bytes to drop from beginning of packets (for non-compliant devices) */
+ unsigned int stream_offset_adj; /* Bytes to drop from beginning of stream (for non-compliant devices) */
unsigned int running: 1; /* running status */
@@ -145,6 +151,7 @@ struct snd_usb_substream {
struct snd_usb_endpoint *sync_endpoint;
unsigned long flags;
bool need_setup_ep; /* (re)configure EP at prepare? */
+ bool need_setup_fmt; /* (re)configure fmt after resume? */
unsigned int speed; /* USB_SPEED_XXX */
u64 formats; /* format bitmasks (all or'ed) */
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 72e9bdf76115..b118cf97607f 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -38,39 +38,37 @@ static void *find_uac_clock_desc(struct usb_host_interface *iface, int id,
static bool validate_clock_source_v2(void *p, int id)
{
struct uac_clock_source_descriptor *cs = p;
- return cs->bLength == sizeof(*cs) && cs->bClockID == id;
+ return cs->bClockID == id;
}
static bool validate_clock_source_v3(void *p, int id)
{
struct uac3_clock_source_descriptor *cs = p;
- return cs->bLength == sizeof(*cs) && cs->bClockID == id;
+ return cs->bClockID == id;
}
static bool validate_clock_selector_v2(void *p, int id)
{
struct uac_clock_selector_descriptor *cs = p;
- return cs->bLength >= sizeof(*cs) && cs->bClockID == id &&
- cs->bLength == 7 + cs->bNrInPins;
+ return cs->bClockID == id;
}
static bool validate_clock_selector_v3(void *p, int id)
{
struct uac3_clock_selector_descriptor *cs = p;
- return cs->bLength >= sizeof(*cs) && cs->bClockID == id &&
- cs->bLength == 11 + cs->bNrInPins;
+ return cs->bClockID == id;
}
static bool validate_clock_multiplier_v2(void *p, int id)
{
struct uac_clock_multiplier_descriptor *cs = p;
- return cs->bLength == sizeof(*cs) && cs->bClockID == id;
+ return cs->bClockID == id;
}
static bool validate_clock_multiplier_v3(void *p, int id)
{
struct uac3_clock_multiplier_descriptor *cs = p;
- return cs->bLength == sizeof(*cs) && cs->bClockID == id;
+ return cs->bClockID == id;
}
#define DEFINE_FIND_HELPER(name, obj, validator, type) \
@@ -153,8 +151,71 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i
return ret;
}
+static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip,
+ struct audioformat *fmt,
+ int source_id)
+{
+ bool ret = false;
+ int count;
+ unsigned char data;
+ struct usb_device *dev = chip->dev;
+
+ if (fmt->protocol == UAC_VERSION_2) {
+ struct uac_clock_source_descriptor *cs_desc =
+ snd_usb_find_clock_source(chip->ctrl_intf, source_id);
+
+ if (!cs_desc)
+ return false;
+
+ /*
+ * Assume the clock is valid if clock source supports only one
+ * single sample rate, the terminal is connected directly to it
+ * (there is no clock selector) and clock type is internal.
+ * This is to deal with some Denon DJ controllers that always
+ * reports that clock is invalid.
+ */
+ if (fmt->nr_rates == 1 &&
+ (fmt->clock & 0xff) == cs_desc->bClockID &&
+ (cs_desc->bmAttributes & 0x3) !=
+ UAC_CLOCK_SOURCE_TYPE_EXT)
+ return true;
+ }
+
+ /*
+ * MOTU MicroBook IIc
+ * Sample rate changes takes more than 2 seconds for this device. Clock
+ * validity request returns false during that period.
+ */
+ if (chip->usb_id == USB_ID(0x07fd, 0x0004)) {
+ count = 0;
+
+ while ((!ret) && (count < 50)) {
+ int err;
+
+ msleep(100);
+
+ err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+ UAC2_CS_CONTROL_CLOCK_VALID << 8,
+ snd_usb_ctrl_intf(chip) | (source_id << 8),
+ &data, sizeof(data));
+ if (err < 0) {
+ dev_warn(&dev->dev,
+ "%s(): cannot get clock validity for id %d\n",
+ __func__, source_id);
+ return false;
+ }
+
+ ret = !!data;
+ count++;
+ }
+ }
+
+ return ret;
+}
+
static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
- int protocol,
+ struct audioformat *fmt,
int source_id)
{
int err;
@@ -162,26 +223,26 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
struct usb_device *dev = chip->dev;
u32 bmControls;
- if (protocol == UAC_VERSION_3) {
+ if (fmt->protocol == UAC_VERSION_3) {
struct uac3_clock_source_descriptor *cs_desc =
snd_usb_find_clock_source_v3(chip->ctrl_intf, source_id);
if (!cs_desc)
- return 0;
+ return false;
bmControls = le32_to_cpu(cs_desc->bmControls);
} else { /* UAC_VERSION_1/2 */
struct uac_clock_source_descriptor *cs_desc =
snd_usb_find_clock_source(chip->ctrl_intf, source_id);
if (!cs_desc)
- return 0;
+ return false;
bmControls = cs_desc->bmControls;
}
/* If a clock source can't tell us whether it's valid, we assume it is */
if (!uac_v2v3_control_is_readable(bmControls,
UAC2_CS_CONTROL_CLOCK_VALID))
- return 1;
+ return true;
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
@@ -193,13 +254,17 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
dev_warn(&dev->dev,
"%s(): cannot get clock validity for id %d\n",
__func__, source_id);
- return 0;
+ return false;
}
- return !!data;
+ if (data)
+ return true;
+ else
+ return uac_clock_source_is_valid_quirk(chip, fmt, source_id);
}
-static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id,
+static int __uac_clock_find_source(struct snd_usb_audio *chip,
+ struct audioformat *fmt, int entity_id,
unsigned long *visited, bool validate)
{
struct uac_clock_source_descriptor *source;
@@ -219,7 +284,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id,
source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
if (source) {
entity_id = source->bClockID;
- if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_2,
+ if (validate && !uac_clock_source_is_valid(chip, fmt,
entity_id)) {
usb_audio_err(chip,
"clock source %d is not valid, cannot use\n",
@@ -250,8 +315,9 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id,
}
cur = ret;
- ret = __uac_clock_find_source(chip, selector->baCSourceID[ret - 1],
- visited, validate);
+ ret = __uac_clock_find_source(chip, fmt,
+ selector->baCSourceID[ret - 1],
+ visited, validate);
if (!validate || ret > 0 || !chip->autoclock)
return ret;
@@ -262,8 +328,9 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id,
if (i == cur)
continue;
- ret = __uac_clock_find_source(chip, selector->baCSourceID[i - 1],
- visited, true);
+ ret = __uac_clock_find_source(chip, fmt,
+ selector->baCSourceID[i - 1],
+ visited, true);
if (ret < 0)
continue;
@@ -283,14 +350,16 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id,
/* FIXME: multipliers only act as pass-thru element for now */
multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id);
if (multiplier)
- return __uac_clock_find_source(chip, multiplier->bCSourceID,
- visited, validate);
+ return __uac_clock_find_source(chip, fmt,
+ multiplier->bCSourceID,
+ visited, validate);
return -EINVAL;
}
-static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
- unsigned long *visited, bool validate)
+static int __uac3_clock_find_source(struct snd_usb_audio *chip,
+ struct audioformat *fmt, int entity_id,
+ unsigned long *visited, bool validate)
{
struct uac3_clock_source_descriptor *source;
struct uac3_clock_selector_descriptor *selector;
@@ -309,7 +378,7 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
source = snd_usb_find_clock_source_v3(chip->ctrl_intf, entity_id);
if (source) {
entity_id = source->bClockID;
- if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_3,
+ if (validate && !uac_clock_source_is_valid(chip, fmt,
entity_id)) {
usb_audio_err(chip,
"clock source %d is not valid, cannot use\n",
@@ -340,7 +409,8 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
}
cur = ret;
- ret = __uac3_clock_find_source(chip, selector->baCSourceID[ret - 1],
+ ret = __uac3_clock_find_source(chip, fmt,
+ selector->baCSourceID[ret - 1],
visited, validate);
if (!validate || ret > 0 || !chip->autoclock)
return ret;
@@ -352,8 +422,9 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
if (i == cur)
continue;
- ret = __uac3_clock_find_source(chip, selector->baCSourceID[i - 1],
- visited, true);
+ ret = __uac3_clock_find_source(chip, fmt,
+ selector->baCSourceID[i - 1],
+ visited, true);
if (ret < 0)
continue;
@@ -374,7 +445,8 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf,
entity_id);
if (multiplier)
- return __uac3_clock_find_source(chip, multiplier->bCSourceID,
+ return __uac3_clock_find_source(chip, fmt,
+ multiplier->bCSourceID,
visited, validate);
return -EINVAL;
@@ -391,18 +463,18 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id,
*
* Returns the clock source UnitID (>=0) on success, or an error.
*/
-int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol,
- int entity_id, bool validate)
+int snd_usb_clock_find_source(struct snd_usb_audio *chip,
+ struct audioformat *fmt, bool validate)
{
DECLARE_BITMAP(visited, 256);
memset(visited, 0, sizeof(visited));
- switch (protocol) {
+ switch (fmt->protocol) {
case UAC_VERSION_2:
- return __uac_clock_find_source(chip, entity_id, visited,
+ return __uac_clock_find_source(chip, fmt, fmt->clock, visited,
validate);
case UAC_VERSION_3:
- return __uac3_clock_find_source(chip, entity_id, visited,
+ return __uac3_clock_find_source(chip, fmt, fmt->clock, visited,
validate);
default:
return -EINVAL;
@@ -503,8 +575,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
* automatic clock selection if the current clock is not
* valid.
*/
- clock = snd_usb_clock_find_source(chip, fmt->protocol,
- fmt->clock, true);
+ clock = snd_usb_clock_find_source(chip, fmt, true);
if (clock < 0) {
/* We did not find a valid clock, but that might be
* because the current sample rate does not match an
@@ -512,8 +583,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
* and we will do another validation after setting the
* rate.
*/
- clock = snd_usb_clock_find_source(chip, fmt->protocol,
- fmt->clock, false);
+ clock = snd_usb_clock_find_source(chip, fmt, false);
if (clock < 0)
return clock;
}
@@ -579,7 +649,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
validation:
/* validate clock after rate change */
- if (!uac_clock_source_is_valid(chip, fmt->protocol, clock))
+ if (!uac_clock_source_is_valid(chip, fmt, clock))
return -ENXIO;
return 0;
}
diff --git a/sound/usb/clock.h b/sound/usb/clock.h
index 076e31b79ee0..68df0fbe09d0 100644
--- a/sound/usb/clock.h
+++ b/sound/usb/clock.h
@@ -6,7 +6,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt, int rate);
-int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol,
- int entity_id, bool validate);
+int snd_usb_clock_find_source(struct snd_usb_audio *chip,
+ struct audioformat *fmt, bool validate);
#endif /* __USBAUDIO_CLOCK_H */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index a2ab8e8d3a93..88760268fb55 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -124,12 +124,12 @@ int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep)
/*
* For streaming based on information derived from sync endpoints,
- * prepare_outbound_urb_sizes() will call next_packet_size() to
+ * prepare_outbound_urb_sizes() will call slave_next_packet_size() to
* determine the number of samples to be sent in the next packet.
*
- * For implicit feedback, next_packet_size() is unused.
+ * For implicit feedback, slave_next_packet_size() is unused.
*/
-int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep)
+int snd_usb_endpoint_slave_next_packet_size(struct snd_usb_endpoint *ep)
{
unsigned long flags;
int ret;
@@ -146,6 +146,29 @@ int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep)
return ret;
}
+/*
+ * For adaptive and synchronous endpoints, prepare_outbound_urb_sizes()
+ * will call next_packet_size() to determine the number of samples to be
+ * sent in the next packet.
+ */
+int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep)
+{
+ int ret;
+
+ if (ep->fill_max)
+ return ep->maxframesize;
+
+ ep->sample_accum += ep->sample_rem;
+ if (ep->sample_accum >= ep->pps) {
+ ep->sample_accum -= ep->pps;
+ ret = ep->packsize[1];
+ } else {
+ ret = ep->packsize[0];
+ }
+
+ return ret;
+}
+
static void retire_outbound_urb(struct snd_usb_endpoint *ep,
struct snd_urb_ctx *urb_ctx)
{
@@ -190,6 +213,8 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep,
if (ctx->packet_size[i])
counts = ctx->packet_size[i];
+ else if (ep->sync_master)
+ counts = snd_usb_endpoint_slave_next_packet_size(ep);
else
counts = snd_usb_endpoint_next_packet_size(ep);
@@ -321,17 +346,17 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
ep->next_packet_read_pos %= MAX_URBS;
/* take URB out of FIFO */
- if (!list_empty(&ep->ready_playback_urbs))
+ if (!list_empty(&ep->ready_playback_urbs)) {
ctx = list_first_entry(&ep->ready_playback_urbs,
struct snd_urb_ctx, ready_list);
+ list_del_init(&ctx->ready_list);
+ }
}
spin_unlock_irqrestore(&ep->lock, flags);
if (ctx == NULL)
return;
- list_del_init(&ctx->ready_list);
-
/* copy over the length information */
for (i = 0; i < packet->packets; i++)
ctx->packet_size[i] = packet->packet_size[i];
@@ -388,6 +413,9 @@ static void snd_complete_urb(struct urb *urb)
}
prepare_outbound_urb(ep, ctx);
+ /* can be stopped during prepare callback */
+ if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+ goto exit_clear;
} else {
retire_inbound_urb(ep, ctx);
/* can be stopped during retire callback */
@@ -494,6 +522,8 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
list_add_tail(&ep->list, &chip->ep_list);
+ ep->is_implicit_feedback = 0;
+
__exit_unlock:
mutex_unlock(&chip->mutex);
@@ -594,6 +624,178 @@ static void release_urbs(struct snd_usb_endpoint *ep, int force)
}
/*
+ * Check data endpoint for format differences
+ */
+static bool check_ep_params(struct snd_usb_endpoint *ep,
+ snd_pcm_format_t pcm_format,
+ unsigned int channels,
+ unsigned int period_bytes,
+ unsigned int frames_per_period,
+ unsigned int periods_per_buffer,
+ struct audioformat *fmt,
+ struct snd_usb_endpoint *sync_ep)
+{
+ unsigned int maxsize, minsize, packs_per_ms, max_packs_per_urb;
+ unsigned int max_packs_per_period, urbs_per_period, urb_packs;
+ unsigned int max_urbs;
+ int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels;
+ int tx_length_quirk = (ep->chip->tx_length_quirk &&
+ usb_pipeout(ep->pipe));
+ bool ret = 1;
+
+ if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) {
+ /*
+ * When operating in DSD DOP mode, the size of a sample frame
+ * in hardware differs from the actual physical format width
+ * because we need to make room for the DOP markers.
+ */
+ frame_bits += channels << 3;
+ }
+
+ ret = ret && (ep->datainterval == fmt->datainterval);
+ ret = ret && (ep->stride == frame_bits >> 3);
+
+ switch (pcm_format) {
+ case SNDRV_PCM_FORMAT_U8:
+ ret = ret && (ep->silence_value == 0x80);
+ break;
+ case SNDRV_PCM_FORMAT_DSD_U8:
+ case SNDRV_PCM_FORMAT_DSD_U16_LE:
+ case SNDRV_PCM_FORMAT_DSD_U32_LE:
+ case SNDRV_PCM_FORMAT_DSD_U16_BE:
+ case SNDRV_PCM_FORMAT_DSD_U32_BE:
+ ret = ret && (ep->silence_value == 0x69);
+ break;
+ default:
+ ret = ret && (ep->silence_value == 0);
+ }
+
+ /* assume max. frequency is 50% higher than nominal */
+ ret = ret && (ep->freqmax == ep->freqn + (ep->freqn >> 1));
+ /* Round up freqmax to nearest integer in order to calculate maximum
+ * packet size, which must represent a whole number of frames.
+ * This is accomplished by adding 0x0.ffff before converting the
+ * Q16.16 format into integer.
+ * In order to accurately calculate the maximum packet size when
+ * the data interval is more than 1 (i.e. ep->datainterval > 0),
+ * multiply by the data interval prior to rounding. For instance,
+ * a freqmax of 41 kHz will result in a max packet size of 6 (5.125)
+ * frames with a data interval of 1, but 11 (10.25) frames with a
+ * data interval of 2.
+ * (ep->freqmax << ep->datainterval overflows at 8.192 MHz for the
+ * maximum datainterval value of 3, at USB full speed, higher for
+ * USB high speed, noting that ep->freqmax is in units of
+ * frames per packet in Q16.16 format.)
+ */
+ maxsize = (((ep->freqmax << ep->datainterval) + 0xffff) >> 16) *
+ (frame_bits >> 3);
+ if (tx_length_quirk)
+ maxsize += sizeof(__le32); /* Space for length descriptor */
+ /* but wMaxPacketSize might reduce this */
+ if (ep->maxpacksize && ep->maxpacksize < maxsize) {
+ /* whatever fits into a max. size packet */
+ unsigned int data_maxsize = maxsize = ep->maxpacksize;
+
+ if (tx_length_quirk)
+ /* Need to remove the length descriptor to calc freq */
+ data_maxsize -= sizeof(__le32);
+ ret = ret && (ep->freqmax == (data_maxsize / (frame_bits >> 3))
+ << (16 - ep->datainterval));
+ }
+
+ if (ep->fill_max)
+ ret = ret && (ep->curpacksize == ep->maxpacksize);
+ else
+ ret = ret && (ep->curpacksize == maxsize);
+
+ if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) {
+ packs_per_ms = 8 >> ep->datainterval;
+ max_packs_per_urb = MAX_PACKS_HS;
+ } else {
+ packs_per_ms = 1;
+ max_packs_per_urb = MAX_PACKS;
+ }
+ if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep))
+ max_packs_per_urb = min(max_packs_per_urb,
+ 1U << sync_ep->syncinterval);
+ max_packs_per_urb = max(1u, max_packs_per_urb >> ep->datainterval);
+
+ /*
+ * Capture endpoints need to use small URBs because there's no way
+ * to tell in advance where the next period will end, and we don't
+ * want the next URB to complete much after the period ends.
+ *
+ * Playback endpoints with implicit sync much use the same parameters
+ * as their corresponding capture endpoint.
+ */
+ if (usb_pipein(ep->pipe) ||
+ snd_usb_endpoint_implicit_feedback_sink(ep)) {
+
+ urb_packs = packs_per_ms;
+ /*
+ * Wireless devices can poll at a max rate of once per 4ms.
+ * For dataintervals less than 5, increase the packet count to
+ * allow the host controller to use bursting to fill in the
+ * gaps.
+ */
+ if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_WIRELESS) {
+ int interval = ep->datainterval;
+
+ while (interval < 5) {
+ urb_packs <<= 1;
+ ++interval;
+ }
+ }
+ /* make capture URBs <= 1 ms and smaller than a period */
+ urb_packs = min(max_packs_per_urb, urb_packs);
+ while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
+ urb_packs >>= 1;
+ ret = ret && (ep->nurbs == MAX_URBS);
+
+ /*
+ * Playback endpoints without implicit sync are adjusted so that
+ * a period fits as evenly as possible in the smallest number of
+ * URBs. The total number of URBs is adjusted to the size of the
+ * ALSA buffer, subject to the MAX_URBS and MAX_QUEUE limits.
+ */
+ } else {
+ /* determine how small a packet can be */
+ minsize = (ep->freqn >> (16 - ep->datainterval)) *
+ (frame_bits >> 3);
+ /* with sync from device, assume it can be 12% lower */
+ if (sync_ep)
+ minsize -= minsize >> 3;
+ minsize = max(minsize, 1u);
+
+ /* how many packets will contain an entire ALSA period? */
+ max_packs_per_period = DIV_ROUND_UP(period_bytes, minsize);
+
+ /* how many URBs will contain a period? */
+ urbs_per_period = DIV_ROUND_UP(max_packs_per_period,
+ max_packs_per_urb);
+ /* how many packets are needed in each URB? */
+ urb_packs = DIV_ROUND_UP(max_packs_per_period, urbs_per_period);
+
+ /* limit the number of frames in a single URB */
+ ret = ret && (ep->max_urb_frames ==
+ DIV_ROUND_UP(frames_per_period, urbs_per_period));
+
+ /* try to use enough URBs to contain an entire ALSA buffer */
+ max_urbs = min((unsigned) MAX_URBS,
+ MAX_QUEUE * packs_per_ms / urb_packs);
+ ret = ret && (ep->nurbs == min(max_urbs,
+ urbs_per_period * periods_per_buffer));
+ }
+
+ ret = ret && (ep->datainterval == fmt->datainterval);
+ ret = ret && (ep->maxpacksize == fmt->maxpacksize);
+ ret = ret &&
+ (ep->fill_max == !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX));
+
+ return ret;
+}
+
+/*
* configure a data endpoint
*/
static int data_ep_set_params(struct snd_usb_endpoint *ep,
@@ -858,10 +1060,23 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
int err;
if (ep->use_count != 0) {
- usb_audio_warn(ep->chip,
- "Unable to change format on ep #%x: already in use\n",
- ep->ep_num);
- return -EBUSY;
+ bool check = ep->is_implicit_feedback &&
+ check_ep_params(ep, pcm_format,
+ channels, period_bytes,
+ period_frames, buffer_periods,
+ fmt, sync_ep);
+
+ if (!check) {
+ usb_audio_warn(ep->chip,
+ "Unable to change format on ep #%x: already in use\n",
+ ep->ep_num);
+ return -EBUSY;
+ }
+
+ usb_audio_dbg(ep->chip,
+ "Ep #%x already in use as implicit feedback but format not changed\n",
+ ep->ep_num);
+ return 0;
}
/* release old buffers, if any */
@@ -871,10 +1086,17 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
ep->maxpacksize = fmt->maxpacksize;
ep->fill_max = !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX);
- if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL)
+ if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL) {
ep->freqn = get_usb_full_speed_rate(rate);
- else
+ ep->pps = 1000 >> ep->datainterval;
+ } else {
ep->freqn = get_usb_high_speed_rate(rate);
+ ep->pps = 8000 >> ep->datainterval;
+ }
+
+ ep->sample_rem = rate % ep->pps;
+ ep->packsize[0] = rate / ep->pps;
+ ep->packsize[1] = (rate + (ep->pps - 1)) / ep->pps;
/* calculate the frequency in 16.16 format */
ep->freqm = ep->freqn;
@@ -933,6 +1155,7 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
ep->active_mask = 0;
ep->unlink_mask = 0;
ep->phase = 0;
+ ep->sample_accum = 0;
snd_usb_endpoint_start_quirk(ep);
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index 63a39d4fa8d8..d23fa0a8c11b 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -28,6 +28,7 @@ void snd_usb_endpoint_release(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_free(struct snd_usb_endpoint *ep);
int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
+int snd_usb_endpoint_slave_next_packet_size(struct snd_usb_endpoint *ep);
int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep);
void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
diff --git a/sound/usb/format.c b/sound/usb/format.c
index c02b51a82775..f4f0cf3deaf0 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -285,6 +285,36 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
return nr_rates;
}
+/* Line6 Helix series don't support the UAC2_CS_RANGE usb function
+ * call. Return a static table of known clock rates.
+ */
+static int line6_parse_audio_format_rates_quirk(struct snd_usb_audio *chip,
+ struct audioformat *fp)
+{
+ switch (chip->usb_id) {
+ case USB_ID(0x0E41, 0x4241): /* Line6 Helix */
+ case USB_ID(0x0E41, 0x4242): /* Line6 Helix Rack */
+ case USB_ID(0x0E41, 0x4244): /* Line6 Helix LT */
+ case USB_ID(0x0E41, 0x4246): /* Line6 HX-Stomp */
+ case USB_ID(0x0E41, 0x4248): /* Line6 Helix >= fw 2.82 */
+ case USB_ID(0x0E41, 0x4249): /* Line6 Helix Rack >= fw 2.82 */
+ case USB_ID(0x0E41, 0x424a): /* Line6 Helix LT >= fw 2.82 */
+ /* supported rates: 48Khz */
+ kfree(fp->rate_table);
+ fp->rate_table = kmalloc(sizeof(int), GFP_KERNEL);
+ if (!fp->rate_table)
+ return -ENOMEM;
+ fp->nr_rates = 1;
+ fp->rate_min = 48000;
+ fp->rate_max = 48000;
+ fp->rates = SNDRV_PCM_RATE_48000;
+ fp->rate_table[0] = 48000;
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
/*
* parse the format descriptor and stores the possible sample rates
* on the audioformat table (audio class v2 and v3).
@@ -294,9 +324,8 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
{
struct usb_device *dev = chip->dev;
unsigned char tmp[2], *data;
- int nr_triplets, data_size, ret = 0;
- int clock = snd_usb_clock_find_source(chip, fp->protocol,
- fp->clock, false);
+ int nr_triplets, data_size, ret = 0, ret_l6;
+ int clock = snd_usb_clock_find_source(chip, fp, false);
if (clock < 0) {
dev_err(&dev->dev,
@@ -313,9 +342,22 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
tmp, sizeof(tmp));
if (ret < 0) {
- dev_err(&dev->dev,
- "%s(): unable to retrieve number of sample rates (clock %d)\n",
+ /* line6 helix devices don't support UAC2_CS_CONTROL_SAM_FREQ call */
+ ret_l6 = line6_parse_audio_format_rates_quirk(chip, fp);
+ if (ret_l6 == -ENODEV) {
+ /* no line6 device found continue showing the error */
+ dev_err(&dev->dev,
+ "%s(): unable to retrieve number of sample rates (clock %d)\n",
+ __func__, clock);
+ goto err;
+ }
+ if (ret_l6 == 0) {
+ dev_info(&dev->dev,
+ "%s(): unable to retrieve number of sample rates: set it to a predefined value (clock %d).\n",
__func__, clock);
+ return 0;
+ }
+ ret = ret_l6;
goto err;
}
diff --git a/sound/usb/helper.h b/sound/usb/helper.h
index 6afb70156ec4..5e8a18b4e7b9 100644
--- a/sound/usb/helper.h
+++ b/sound/usb/helper.h
@@ -31,4 +31,8 @@ static inline int snd_usb_ctrl_intf(struct snd_usb_audio *chip)
return get_iface_desc(chip->ctrl_intf)->bInterfaceNumber;
}
+/* in validate.c */
+bool snd_usb_validate_audio_desc(void *p, int protocol);
+bool snd_usb_validate_midi_desc(void *p);
+
#endif /* __USBAUDIO_HELPER_H */
diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index 82abef3fe90d..4b6e99e055dc 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -287,6 +287,8 @@ int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
urb->interval = LINE6_ISO_INTERVAL;
urb->error_count = 0;
urb->complete = audio_in_callback;
+ if (usb_urb_ep_type_check(urb))
+ return -EINVAL;
}
return 0;
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index e63a2451c88f..9eac35587f9d 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -316,7 +316,7 @@ static void line6_data_received(struct urb *urb)
line6_midibuf_read(mb, line6->buffer_message,
LINE6_MIDI_MESSAGE_MAXLEN);
- if (done == 0)
+ if (done <= 0)
break;
line6->message_length = done;
@@ -831,7 +831,7 @@ void line6_disconnect(struct usb_interface *interface)
if (WARN_ON(usbdev != line6->usbdev))
return;
- cancel_delayed_work(&line6->startup_work);
+ cancel_delayed_work_sync(&line6->startup_work);
if (line6->urb_listen != NULL)
line6_stop_listen(line6);
diff --git a/sound/usb/line6/midibuf.c b/sound/usb/line6/midibuf.c
index 8d6eefa0d936..6a70463f82c4 100644
--- a/sound/usb/line6/midibuf.c
+++ b/sound/usb/line6/midibuf.c
@@ -159,7 +159,7 @@ int line6_midibuf_read(struct midi_buffer *this, unsigned char *data,
int midi_length_prev =
midibuf_message_length(this->command_prev);
- if (midi_length_prev > 0) {
+ if (midi_length_prev > 1) {
midi_length = midi_length_prev - 1;
repeat = 1;
} else
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 2e8ead3f9bc2..797ced329b79 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -432,6 +432,8 @@ int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
urb->interval = LINE6_ISO_INTERVAL;
urb->error_count = 0;
urb->complete = audio_out_callback;
+ if (usb_urb_ep_type_check(urb))
+ return -EINVAL;
}
return 0;
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 53b53a9a4c6f..db2395a95e3f 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -31,8 +31,7 @@ enum {
enum {
LINE6_PODHD300,
LINE6_PODHD400,
- LINE6_PODHD500_0,
- LINE6_PODHD500_1,
+ LINE6_PODHD500,
LINE6_PODX3,
LINE6_PODX3LIVE,
LINE6_PODHD500X,
@@ -374,8 +373,7 @@ static const struct usb_device_id podhd_id_table[] = {
/* TODO: no need to alloc data interfaces when only audio is used */
{ LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 },
{ LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 },
- { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 },
- { LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 },
+ { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500 },
{ LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 },
{ LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE },
{ LINE6_IF_NUM(0x4159, 0), .driver_info = LINE6_PODHD500X },
@@ -408,23 +406,13 @@ static const struct line6_properties podhd_properties_table[] = {
.ep_audio_r = 0x82,
.ep_audio_w = 0x01,
},
- [LINE6_PODHD500_0] = {
+ [LINE6_PODHD500] = {
.id = "PODHD500",
.name = "POD HD500",
- .capabilities = LINE6_CAP_PCM
- | LINE6_CAP_HWMON,
- .altsetting = 0,
- .ep_ctrl_r = 0x81,
- .ep_ctrl_w = 0x01,
- .ep_audio_r = 0x86,
- .ep_audio_w = 0x02,
- },
- [LINE6_PODHD500_1] = {
- .id = "PODHD500",
- .name = "POD HD500",
- .capabilities = LINE6_CAP_PCM
+ .capabilities = LINE6_CAP_PCM | LINE6_CAP_CONTROL
| LINE6_CAP_HWMON,
.altsetting = 1,
+ .ctrl_if = 1,
.ep_ctrl_r = 0x81,
.ep_ctrl_w = 0x01,
.ep_audio_r = 0x86,
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index b737f0ec77d0..0cb4142b05f6 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -1499,6 +1499,8 @@ void snd_usbmidi_disconnect(struct list_head *p)
spin_unlock_irq(&umidi->disc_lock);
up_write(&umidi->disc_rwsem);
+ del_timer_sync(&umidi->error_timer);
+
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
struct snd_usb_midi_endpoint *ep = &umidi->endpoints[i];
if (ep->out)
@@ -1525,7 +1527,6 @@ void snd_usbmidi_disconnect(struct list_head *p)
ep->in = NULL;
}
}
- del_timer_sync(&umidi->error_timer);
}
EXPORT_SYMBOL(snd_usbmidi_disconnect);
@@ -2282,16 +2283,22 @@ void snd_usbmidi_input_stop(struct list_head *p)
}
EXPORT_SYMBOL(snd_usbmidi_input_stop);
-static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint *ep)
+static void snd_usbmidi_input_start_ep(struct snd_usb_midi *umidi,
+ struct snd_usb_midi_in_endpoint *ep)
{
unsigned int i;
+ unsigned long flags;
if (!ep)
return;
for (i = 0; i < INPUT_URBS; ++i) {
struct urb *urb = ep->urbs[i];
- urb->dev = ep->umidi->dev;
- snd_usbmidi_submit_urb(urb, GFP_KERNEL);
+ spin_lock_irqsave(&umidi->disc_lock, flags);
+ if (!atomic_read(&urb->use_count)) {
+ urb->dev = ep->umidi->dev;
+ snd_usbmidi_submit_urb(urb, GFP_ATOMIC);
+ }
+ spin_unlock_irqrestore(&umidi->disc_lock, flags);
}
}
@@ -2307,7 +2314,7 @@ void snd_usbmidi_input_start(struct list_head *p)
if (umidi->input_running || !umidi->opened[1])
return;
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
- snd_usbmidi_input_start_ep(umidi->endpoints[i].in);
+ snd_usbmidi_input_start_ep(umidi, umidi->endpoints[i].in);
umidi->input_running = 1;
}
EXPORT_SYMBOL(snd_usbmidi_input_start);
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index eceab19766db..ff8d29bf601f 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -576,8 +576,9 @@ static int check_matrix_bitmap(unsigned char *bmap,
* if failed, give up and free the control instance.
*/
-int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list,
- struct snd_kcontrol *kctl)
+int snd_usb_mixer_add_list(struct usb_mixer_elem_list *list,
+ struct snd_kcontrol *kctl,
+ bool is_std_info)
{
struct usb_mixer_interface *mixer = list->mixer;
int err;
@@ -591,6 +592,7 @@ int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list,
return err;
}
list->kctl = kctl;
+ list->is_std_info = is_std_info;
list->next_id_elem = mixer->id_elems[list->id];
mixer->id_elems[list->id] = list;
return 0;
@@ -740,13 +742,6 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state,
{
int mu_channels;
- if (desc->bLength < sizeof(*desc))
- return -EINVAL;
- if (!desc->bNrInPins)
- return -EINVAL;
- if (desc->bLength < sizeof(*desc) + desc->bNrInPins)
- return -EINVAL;
-
switch (state->mixer->protocol) {
case UAC_VERSION_1:
case UAC_VERSION_2:
@@ -765,222 +760,250 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state,
}
/*
- * parse the source unit recursively until it reaches to a terminal
- * or a branched unit.
+ * Parse Input Terminal Unit
*/
static int __check_input_term(struct mixer_build *state, int id,
- struct usb_audio_term *term)
+ struct usb_audio_term *term);
+
+static int parse_term_uac1_iterm_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
{
- int protocol = state->mixer->protocol;
+ struct uac_input_terminal_descriptor *d = p1;
+
+ term->type = le16_to_cpu(d->wTerminalType);
+ term->channels = d->bNrChannels;
+ term->chconfig = le16_to_cpu(d->wChannelConfig);
+ term->name = d->iTerminal;
+ return 0;
+}
+
+static int parse_term_uac2_iterm_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac2_input_terminal_descriptor *d = p1;
int err;
- void *p1;
- unsigned char *hdr;
- memset(term, 0, sizeof(*term));
- for (;;) {
- /* a loop in the terminal chain? */
- if (test_and_set_bit(id, state->termbitmap))
- return -EINVAL;
+ /* call recursively to verify the referenced clock entity */
+ err = __check_input_term(state, d->bCSourceID, term);
+ if (err < 0)
+ return err;
- p1 = find_audio_control_unit(state, id);
- if (!p1)
- break;
+ /* save input term properties after recursion,
+ * to ensure they are not overriden by the recursion calls
+ */
+ term->id = id;
+ term->type = le16_to_cpu(d->wTerminalType);
+ term->channels = d->bNrChannels;
+ term->chconfig = le32_to_cpu(d->bmChannelConfig);
+ term->name = d->iTerminal;
+ return 0;
+}
- hdr = p1;
- term->id = id;
+static int parse_term_uac3_iterm_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac3_input_terminal_descriptor *d = p1;
+ int err;
- if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
- switch (hdr[2]) {
- case UAC_INPUT_TERMINAL:
- if (protocol == UAC_VERSION_1) {
- struct uac_input_terminal_descriptor *d = p1;
-
- term->type = le16_to_cpu(d->wTerminalType);
- term->channels = d->bNrChannels;
- term->chconfig = le16_to_cpu(d->wChannelConfig);
- term->name = d->iTerminal;
- } else { /* UAC_VERSION_2 */
- struct uac2_input_terminal_descriptor *d = p1;
-
- /* call recursively to verify that the
- * referenced clock entity is valid */
- err = __check_input_term(state, d->bCSourceID, term);
- if (err < 0)
- return err;
+ /* call recursively to verify the referenced clock entity */
+ err = __check_input_term(state, d->bCSourceID, term);
+ if (err < 0)
+ return err;
- /* save input term properties after recursion,
- * to ensure they are not overriden by the
- * recursion calls */
- term->id = id;
- term->type = le16_to_cpu(d->wTerminalType);
- term->channels = d->bNrChannels;
- term->chconfig = le32_to_cpu(d->bmChannelConfig);
- term->name = d->iTerminal;
- }
- return 0;
- case UAC_FEATURE_UNIT: {
- /* the header is the same for v1 and v2 */
- struct uac_feature_unit_descriptor *d = p1;
+ /* save input term properties after recursion,
+ * to ensure they are not overriden by the recursion calls
+ */
+ term->id = id;
+ term->type = le16_to_cpu(d->wTerminalType);
- id = d->bSourceID;
- break; /* continue to parse */
- }
- case UAC_MIXER_UNIT: {
- struct uac_mixer_unit_descriptor *d = p1;
-
- term->type = UAC3_MIXER_UNIT << 16; /* virtual type */
- term->channels = uac_mixer_unit_bNrChannels(d);
- term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol);
- term->name = uac_mixer_unit_iMixer(d);
- return 0;
- }
- case UAC_SELECTOR_UNIT:
- case UAC2_CLOCK_SELECTOR: {
- struct uac_selector_unit_descriptor *d = p1;
- /* call recursively to retrieve the channel info */
- err = __check_input_term(state, d->baSourceID[0], term);
- if (err < 0)
- return err;
- term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
- term->id = id;
- term->name = uac_selector_unit_iSelector(d);
- return 0;
- }
- case UAC1_PROCESSING_UNIT:
- /* UAC2_EFFECT_UNIT */
- if (protocol == UAC_VERSION_1)
- term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
- else /* UAC_VERSION_2 */
- term->type = UAC3_EFFECT_UNIT << 16; /* virtual type */
- /* fall through */
- case UAC1_EXTENSION_UNIT:
- /* UAC2_PROCESSING_UNIT_V2 */
- if (protocol == UAC_VERSION_1 && !term->type)
- term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */
- else if (protocol == UAC_VERSION_2 && !term->type)
- term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
- /* fall through */
- case UAC2_EXTENSION_UNIT_V2: {
- struct uac_processing_unit_descriptor *d = p1;
-
- if (protocol == UAC_VERSION_2 &&
- hdr[2] == UAC2_EFFECT_UNIT) {
- /* UAC2/UAC1 unit IDs overlap here in an
- * uncompatible way. Ignore this unit for now.
- */
- return 0;
- }
+ err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID));
+ if (err < 0)
+ return err;
+ term->channels = err;
- if (d->bNrInPins) {
- id = d->baSourceID[0];
- break; /* continue to parse */
- }
- if (!term->type)
- term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */
+ /* REVISIT: UAC3 IT doesn't have channels cfg */
+ term->chconfig = 0;
- term->channels = uac_processing_unit_bNrChannels(d);
- term->chconfig = uac_processing_unit_wChannelConfig(d, protocol);
- term->name = uac_processing_unit_iProcessing(d, protocol);
- return 0;
- }
- case UAC2_CLOCK_SOURCE: {
- struct uac_clock_source_descriptor *d = p1;
+ term->name = le16_to_cpu(d->wTerminalDescrStr);
+ return 0;
+}
- term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
- term->id = id;
- term->name = d->iClockSource;
- return 0;
- }
- default:
- return -ENODEV;
- }
- } else { /* UAC_VERSION_3 */
- switch (hdr[2]) {
- case UAC_INPUT_TERMINAL: {
- struct uac3_input_terminal_descriptor *d = p1;
-
- /* call recursively to verify that the
- * referenced clock entity is valid */
- err = __check_input_term(state, d->bCSourceID, term);
- if (err < 0)
- return err;
+static int parse_term_mixer_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac_mixer_unit_descriptor *d = p1;
+ int protocol = state->mixer->protocol;
+ int err;
- /* save input term properties after recursion,
- * to ensure they are not overriden by the
- * recursion calls */
- term->id = id;
- term->type = le16_to_cpu(d->wTerminalType);
+ err = uac_mixer_unit_get_channels(state, d);
+ if (err <= 0)
+ return err;
- err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID));
- if (err < 0)
- return err;
- term->channels = err;
+ term->type = UAC3_MIXER_UNIT << 16; /* virtual type */
+ term->channels = err;
+ if (protocol != UAC_VERSION_3) {
+ term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol);
+ term->name = uac_mixer_unit_iMixer(d);
+ }
+ return 0;
+}
- /* REVISIT: UAC3 IT doesn't have channels cfg */
- term->chconfig = 0;
+static int parse_term_selector_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac_selector_unit_descriptor *d = p1;
+ int err;
- term->name = le16_to_cpu(d->wTerminalDescrStr);
- return 0;
- }
- case UAC3_FEATURE_UNIT: {
- struct uac3_feature_unit_descriptor *d = p1;
+ /* call recursively to retrieve the channel info */
+ err = __check_input_term(state, d->baSourceID[0], term);
+ if (err < 0)
+ return err;
+ term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
+ term->id = id;
+ if (state->mixer->protocol != UAC_VERSION_3)
+ term->name = uac_selector_unit_iSelector(d);
+ return 0;
+}
- id = d->bSourceID;
- break; /* continue to parse */
- }
- case UAC3_CLOCK_SOURCE: {
- struct uac3_clock_source_descriptor *d = p1;
+static int parse_term_proc_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id, int vtype)
+{
+ struct uac_processing_unit_descriptor *d = p1;
+ int protocol = state->mixer->protocol;
+ int err;
- term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
- term->id = id;
- term->name = le16_to_cpu(d->wClockSourceStr);
- return 0;
- }
- case UAC3_MIXER_UNIT: {
- struct uac_mixer_unit_descriptor *d = p1;
+ if (d->bNrInPins) {
+ /* call recursively to retrieve the channel info */
+ err = __check_input_term(state, d->baSourceID[0], term);
+ if (err < 0)
+ return err;
+ }
- err = uac_mixer_unit_get_channels(state, d);
- if (err <= 0)
- return err;
+ term->type = vtype << 16; /* virtual type */
+ term->id = id;
- term->channels = err;
- term->type = UAC3_MIXER_UNIT << 16; /* virtual type */
+ if (protocol == UAC_VERSION_3)
+ return 0;
- return 0;
- }
- case UAC3_SELECTOR_UNIT:
- case UAC3_CLOCK_SELECTOR: {
- struct uac_selector_unit_descriptor *d = p1;
- /* call recursively to retrieve the channel info */
- err = __check_input_term(state, d->baSourceID[0], term);
- if (err < 0)
- return err;
- term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
- term->id = id;
- term->name = 0; /* TODO: UAC3 Class-specific strings */
+ if (!term->channels) {
+ term->channels = uac_processing_unit_bNrChannels(d);
+ term->chconfig = uac_processing_unit_wChannelConfig(d, protocol);
+ }
+ term->name = uac_processing_unit_iProcessing(d, protocol);
+ return 0;
+}
- return 0;
- }
- case UAC3_PROCESSING_UNIT: {
- struct uac_processing_unit_descriptor *d = p1;
+static int parse_term_effect_unit(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ term->type = UAC3_EFFECT_UNIT << 16; /* virtual type */
+ term->id = id;
+ return 0;
+}
+
+static int parse_term_uac2_clock_source(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac_clock_source_descriptor *d = p1;
- if (!d->bNrInPins)
- return -EINVAL;
+ term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
+ term->id = id;
+ term->name = d->iClockSource;
+ return 0;
+}
- /* call recursively to retrieve the channel info */
- err = __check_input_term(state, d->baSourceID[0], term);
- if (err < 0)
- return err;
+static int parse_term_uac3_clock_source(struct mixer_build *state,
+ struct usb_audio_term *term,
+ void *p1, int id)
+{
+ struct uac3_clock_source_descriptor *d = p1;
- term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
- term->id = id;
- term->name = 0; /* TODO: UAC3 Class-specific strings */
+ term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
+ term->id = id;
+ term->name = le16_to_cpu(d->wClockSourceStr);
+ return 0;
+}
- return 0;
- }
- default:
- return -ENODEV;
- }
+#define PTYPE(a, b) ((a) << 8 | (b))
+
+/*
+ * parse the source unit recursively until it reaches to a terminal
+ * or a branched unit.
+ */
+static int __check_input_term(struct mixer_build *state, int id,
+ struct usb_audio_term *term)
+{
+ int protocol = state->mixer->protocol;
+ void *p1;
+ unsigned char *hdr;
+
+ for (;;) {
+ /* a loop in the terminal chain? */
+ if (test_and_set_bit(id, state->termbitmap))
+ return -EINVAL;
+
+ p1 = find_audio_control_unit(state, id);
+ if (!p1)
+ break;
+ if (!snd_usb_validate_audio_desc(p1, protocol))
+ break; /* bad descriptor */
+
+ hdr = p1;
+ term->id = id;
+
+ switch (PTYPE(protocol, hdr[2])) {
+ case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT): {
+ /* the header is the same for all versions */
+ struct uac_feature_unit_descriptor *d = p1;
+
+ id = d->bSourceID;
+ break; /* continue to parse */
+ }
+ case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL):
+ return parse_term_uac1_iterm_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL):
+ return parse_term_uac2_iterm_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL):
+ return parse_term_uac3_iterm_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_1, UAC_MIXER_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_MIXER_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_MIXER_UNIT):
+ return parse_term_mixer_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR):
+ case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR):
+ return parse_term_selector_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_1, UAC1_PROCESSING_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2):
+ case PTYPE(UAC_VERSION_3, UAC3_PROCESSING_UNIT):
+ return parse_term_proc_unit(state, term, p1, id,
+ UAC3_PROCESSING_UNIT);
+ case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT):
+ return parse_term_effect_unit(state, term, p1, id);
+ case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2):
+ case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT):
+ return parse_term_proc_unit(state, term, p1, id,
+ UAC3_EXTENSION_UNIT);
+ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE):
+ return parse_term_uac2_clock_source(state, term, p1, id);
+ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE):
+ return parse_term_uac3_clock_source(state, term, p1, id);
+ default:
+ return -ENODEV;
}
}
return -ENODEV;
@@ -1024,10 +1047,15 @@ static struct usb_feature_control_info audio_feature_info[] = {
{ UAC2_FU_PHASE_INVERTER, "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 },
};
+static void usb_mixer_elem_info_free(struct usb_mixer_elem_info *cval)
+{
+ kfree(cval);
+}
+
/* private_free callback */
void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl)
{
- kfree(kctl->private_data);
+ usb_mixer_elem_info_free(kctl->private_data);
kctl->private_data = NULL;
}
@@ -1145,6 +1173,14 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
cval->res = 384;
}
break;
+ case USB_ID(0x0495, 0x3042): /* ESS Technology Asus USB DAC */
+ if ((strstr(kctl->id.name, "Playback Volume") != NULL) ||
+ strstr(kctl->id.name, "Capture Volume") != NULL) {
+ cval->min >>= 8;
+ cval->max = 0;
+ cval->res = 1;
+ }
+ break;
}
}
@@ -1211,7 +1247,8 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
if (cval->min + cval->res < cval->max) {
int last_valid_res = cval->res;
int saved, test, check;
- get_cur_mix_raw(cval, minchn, &saved);
+ if (get_cur_mix_raw(cval, minchn, &saved) < 0)
+ goto no_res_check;
for (;;) {
test = saved;
if (test < cval->max)
@@ -1231,6 +1268,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
snd_usb_set_cur_mix_value(cval, minchn, 0, saved);
}
+no_res_check:
cval->initialized = 1;
}
@@ -1418,7 +1456,7 @@ error:
usb_audio_err(chip,
"cannot get connectors status: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
UAC_GET_CUR, validx, idx, cval->val_type);
- return ret;
+ return filter_error(cval, ret);
}
ucontrol->value.integer.value[0] = val;
@@ -1550,7 +1588,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
ctl_info = get_feature_control_info(control);
if (!ctl_info) {
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return;
}
if (mixer->protocol == UAC_VERSION_1)
@@ -1583,7 +1621,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
if (!kctl) {
usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return;
}
kctl->private_free = snd_usb_mixer_elem_free;
@@ -1722,10 +1760,16 @@ static void get_connector_control_name(struct usb_mixer_interface *mixer,
/* Build a mixer control for a UAC connector control (jack-detect) */
static void build_connector_control(struct usb_mixer_interface *mixer,
+ const struct usbmix_name_map *imap,
struct usb_audio_term *term, bool is_input)
{
struct snd_kcontrol *kctl;
struct usb_mixer_elem_info *cval;
+ const struct usbmix_name_map *map;
+
+ map = find_map(imap, term->id, 0);
+ if (check_ignored_ctl(map))
+ return;
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
if (!cval)
@@ -1753,11 +1797,15 @@ static void build_connector_control(struct usb_mixer_interface *mixer,
kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval);
if (!kctl) {
usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return;
}
- get_connector_control_name(mixer, term, is_input, kctl->id.name,
- sizeof(kctl->id.name));
+
+ if (check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)))
+ strlcat(kctl->id.name, " Jack", sizeof(kctl->id.name));
+ else
+ get_connector_control_name(mixer, term, is_input, kctl->id.name,
+ sizeof(kctl->id.name));
kctl->private_free = snd_usb_mixer_elem_free;
snd_usb_mixer_add_control(&cval->head, kctl);
}
@@ -1774,13 +1822,6 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
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.
@@ -1806,7 +1847,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval);
if (!kctl) {
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return -ENOMEM;
}
@@ -1839,62 +1880,20 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
__u8 *bmaControls;
if (state->mixer->protocol == UAC_VERSION_1) {
- if (hdr->bLength < 7) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
csize = hdr->bControlSize;
- if (!csize) {
- usb_audio_dbg(state->chip,
- "unit %u: invalid bControlSize == 0\n",
- unitid);
- return -EINVAL;
- }
channels = (hdr->bLength - 7) / csize - 1;
bmaControls = hdr->bmaControls;
- if (hdr->bLength < 7 + csize) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
} else if (state->mixer->protocol == UAC_VERSION_2) {
struct uac2_feature_unit_descriptor *ftr = _ftr;
- if (hdr->bLength < 6) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
csize = 4;
channels = (hdr->bLength - 6) / 4 - 1;
bmaControls = ftr->bmaControls;
- if (hdr->bLength < 6 + csize) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
} else { /* UAC_VERSION_3 */
struct uac3_feature_unit_descriptor *ftr = _ftr;
- if (hdr->bLength < 7) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
csize = 4;
channels = (ftr->bLength - 7) / 4 - 1;
bmaControls = ftr->bmaControls;
- if (hdr->bLength < 7 + csize) {
- usb_audio_err(state->chip,
- "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
- unitid);
- return -EINVAL;
- }
}
/* parse the source unit */
@@ -2068,7 +2067,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
if (!kctl) {
usb_audio_err(state->chip, "cannot malloc kcontrol\n");
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return;
}
kctl->private_free = snd_usb_mixer_elem_free;
@@ -2094,15 +2093,11 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid,
if (state->mixer->protocol == UAC_VERSION_2) {
struct uac2_input_terminal_descriptor *d_v2 = raw_desc;
- if (d_v2->bLength < sizeof(*d_v2))
- return -EINVAL;
control = UAC2_TE_CONNECTOR;
term_id = d_v2->bTerminalID;
bmctls = le16_to_cpu(d_v2->bmControls);
} else if (state->mixer->protocol == UAC_VERSION_3) {
struct uac3_input_terminal_descriptor *d_v3 = raw_desc;
- if (d_v3->bLength < sizeof(*d_v3))
- return -EINVAL;
control = UAC3_TE_INSERTION;
term_id = d_v3->bTerminalID;
bmctls = le32_to_cpu(d_v3->bmControls);
@@ -2113,8 +2108,9 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid,
check_input_term(state, term_id, &iterm);
/* Check for jack detection. */
- if (uac_v2v3_control_is_readable(bmctls, control))
- build_connector_control(state->mixer, &iterm, true);
+ if ((iterm.type & 0xff00) != 0x0100 &&
+ uac_v2v3_control_is_readable(bmctls, control))
+ build_connector_control(state->mixer, state->map, &iterm, true);
return 0;
}
@@ -2364,18 +2360,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
const char *name = extension_unit ?
"Extension Unit" : "Processing Unit";
- if (desc->bLength < 13) {
- usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid);
- return -EINVAL;
- }
-
num_ins = desc->bNrInPins;
- if (desc->bLength < 13 + num_ins ||
- desc->bLength < num_ins + uac_processing_unit_bControlSize(desc, state->mixer->protocol)) {
- usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid);
- return -EINVAL;
- }
-
for (i = 0; i < num_ins; i++) {
err = parse_audio_unit(state, desc->baSourceID[i]);
if (err < 0)
@@ -2466,7 +2451,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
kctl = snd_ctl_new1(&mixer_procunit_ctl, cval);
if (!kctl) {
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
return -ENOMEM;
}
kctl->private_free = snd_usb_mixer_elem_free;
@@ -2604,7 +2589,7 @@ static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl)
if (kctl->private_data) {
struct usb_mixer_elem_info *cval = kctl->private_data;
num_ins = cval->max;
- kfree(cval);
+ usb_mixer_elem_info_free(cval);
kctl->private_data = NULL;
}
if (kctl->private_value) {
@@ -2630,13 +2615,6 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
const struct usbmix_name_map *map;
char **namelist;
- if (desc->bLength < 5 || !desc->bNrInPins ||
- desc->bLength < 5 + desc->bNrInPins) {
- usb_audio_err(state->chip,
- "invalid SELECTOR UNIT descriptor %d\n", unitid);
- return -EINVAL;
- }
-
for (i = 0; i < desc->bNrInPins; i++) {
err = parse_audio_unit(state, desc->baSourceID[i]);
if (err < 0)
@@ -2676,10 +2654,10 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
break;
}
- namelist = kmalloc_array(desc->bNrInPins, sizeof(char *), GFP_KERNEL);
+ namelist = kcalloc(desc->bNrInPins, sizeof(char *), GFP_KERNEL);
if (!namelist) {
- kfree(cval);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto error_cval;
}
#define MAX_ITEM_NAME_LEN 64
for (i = 0; i < desc->bNrInPins; i++) {
@@ -2687,11 +2665,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
len = 0;
namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL);
if (!namelist[i]) {
- while (i--)
- kfree(namelist[i]);
- kfree(namelist);
- kfree(cval);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto error_name;
}
len = check_mapped_selector_name(state, unitid, i, namelist[i],
MAX_ITEM_NAME_LEN);
@@ -2705,11 +2680,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval);
if (! kctl) {
usb_audio_err(state->chip, "cannot malloc kcontrol\n");
- for (i = 0; i < desc->bNrInPins; i++)
- kfree(namelist[i]);
- kfree(namelist);
- kfree(cval);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto error_name;
}
kctl->private_value = (unsigned long)namelist;
kctl->private_free = usb_mixer_selector_elem_free;
@@ -2755,6 +2727,14 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
usb_audio_dbg(state->chip, "[%d] SU [%s] items = %d\n",
cval->head.id, kctl->id.name, desc->bNrInPins);
return snd_usb_mixer_add_control(&cval->head, kctl);
+
+ error_name:
+ for (i = 0; i < desc->bNrInPins; i++)
+ kfree(namelist[i]);
+ kfree(namelist);
+ error_cval:
+ usb_mixer_elem_info_free(cval);
+ return err;
}
/*
@@ -2775,62 +2755,49 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
return -EINVAL;
}
- if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
- switch (p1[2]) {
- case UAC_INPUT_TERMINAL:
- return parse_audio_input_terminal(state, unitid, p1);
- 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);
- case UAC_FEATURE_UNIT:
- return parse_audio_feature_unit(state, unitid, p1);
- case UAC1_PROCESSING_UNIT:
- /* UAC2_EFFECT_UNIT has the same value */
- if (protocol == UAC_VERSION_1)
- return parse_audio_processing_unit(state, unitid, p1);
- else
- return 0; /* FIXME - effect units not implemented yet */
- case UAC1_EXTENSION_UNIT:
- /* UAC2_PROCESSING_UNIT_V2 has the same value */
- if (protocol == UAC_VERSION_1)
- return parse_audio_extension_unit(state, unitid, p1);
- else /* UAC_VERSION_2 */
- return parse_audio_processing_unit(state, unitid, p1);
- case UAC2_EXTENSION_UNIT_V2:
- return parse_audio_extension_unit(state, unitid, p1);
- default:
- usb_audio_err(state->chip,
- "unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
- return -EINVAL;
- }
- } else { /* UAC_VERSION_3 */
- switch (p1[2]) {
- case UAC_INPUT_TERMINAL:
- return parse_audio_input_terminal(state, unitid, p1);
- case UAC3_MIXER_UNIT:
- return parse_audio_mixer_unit(state, unitid, p1);
- case UAC3_CLOCK_SOURCE:
- return parse_clock_source_unit(state, unitid, p1);
- case UAC3_SELECTOR_UNIT:
- case UAC3_CLOCK_SELECTOR:
- return parse_audio_selector_unit(state, unitid, p1);
- case UAC3_FEATURE_UNIT:
- return parse_audio_feature_unit(state, unitid, p1);
- case UAC3_EFFECT_UNIT:
- return 0; /* FIXME - effect units not implemented yet */
- case UAC3_PROCESSING_UNIT:
- return parse_audio_processing_unit(state, unitid, p1);
- case UAC3_EXTENSION_UNIT:
- return parse_audio_extension_unit(state, unitid, p1);
- default:
- usb_audio_err(state->chip,
- "unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
- return -EINVAL;
- }
+ if (!snd_usb_validate_audio_desc(p1, protocol)) {
+ usb_audio_dbg(state->chip, "invalid unit %d\n", unitid);
+ return 0; /* skip invalid unit */
+ }
+
+ switch (PTYPE(protocol, p1[2])) {
+ case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL):
+ case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL):
+ case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL):
+ return parse_audio_input_terminal(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC_MIXER_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_MIXER_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_MIXER_UNIT):
+ return parse_audio_mixer_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE):
+ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE):
+ return parse_clock_source_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR):
+ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR):
+ return parse_audio_selector_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT):
+ return parse_audio_feature_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC1_PROCESSING_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2):
+ case PTYPE(UAC_VERSION_3, UAC3_PROCESSING_UNIT):
+ return parse_audio_processing_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT):
+ case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2):
+ case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT):
+ return parse_audio_extension_unit(state, unitid, p1);
+ case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT):
+ case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT):
+ return 0; /* FIXME - effect units not implemented yet */
+ default:
+ usb_audio_err(state->chip,
+ "unit %u: unexpected type 0x%02x\n",
+ unitid, p1[2]);
+ return -EINVAL;
}
}
@@ -2992,6 +2959,9 @@ static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer,
continue;
iface = usb_ifnum_to_if(dev, intf);
+ if (!iface)
+ continue;
+
num = iface->num_altsetting;
if (num < 2)
@@ -3101,13 +3071,13 @@ static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer,
memset(&iterm, 0, sizeof(iterm));
iterm.id = UAC3_BADD_IT_ID4;
iterm.type = UAC_BIDIR_TERMINAL_HEADSET;
- build_connector_control(mixer, &iterm, true);
+ build_connector_control(mixer, map->map, &iterm, true);
/* Output Term - Insertion control */
memset(&oterm, 0, sizeof(oterm));
oterm.id = UAC3_BADD_OT_ID3;
oterm.type = UAC_BIDIR_TERMINAL_HEADSET;
- build_connector_control(mixer, &oterm, false);
+ build_connector_control(mixer, map->map, &oterm, false);
}
return 0;
@@ -3136,7 +3106,8 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
if (map->id == state.chip->usb_id) {
state.map = map->map;
state.selector_map = map->selector_map;
- mixer->ignore_ctl_error = map->ignore_ctl_error;
+ mixer->connector_map = map->connector_map;
+ mixer->ignore_ctl_error |= map->ignore_ctl_error;
break;
}
}
@@ -3145,11 +3116,12 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
mixer->hostif->extralen,
p, UAC_OUTPUT_TERMINAL)) != NULL) {
+ if (!snd_usb_validate_audio_desc(p, mixer->protocol))
+ continue; /* skip invalid descriptor */
+
if (mixer->protocol == UAC_VERSION_1) {
struct uac1_output_terminal_descriptor *desc = p;
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
/* mark terminal ID as visited */
set_bit(desc->bTerminalID, state.unitbitmap);
state.oterm.id = desc->bTerminalID;
@@ -3161,8 +3133,6 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
} else if (mixer->protocol == UAC_VERSION_2) {
struct uac2_output_terminal_descriptor *desc = p;
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
/* mark terminal ID as visited */
set_bit(desc->bTerminalID, state.unitbitmap);
state.oterm.id = desc->bTerminalID;
@@ -3180,16 +3150,15 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
if (err < 0 && err != -EINVAL)
return err;
- if (uac_v2v3_control_is_readable(le16_to_cpu(desc->bmControls),
+ if ((state.oterm.type & 0xff00) != 0x0100 &&
+ uac_v2v3_control_is_readable(le16_to_cpu(desc->bmControls),
UAC2_TE_CONNECTOR)) {
- build_connector_control(state.mixer, &state.oterm,
- false);
+ build_connector_control(state.mixer, state.map,
+ &state.oterm, false);
}
} else { /* UAC_VERSION_3 */
struct uac3_output_terminal_descriptor *desc = p;
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
/* mark terminal ID as visited */
set_bit(desc->bTerminalID, state.unitbitmap);
state.oterm.id = desc->bTerminalID;
@@ -3207,10 +3176,11 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
if (err < 0 && err != -EINVAL)
return err;
- if (uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls),
+ if ((state.oterm.type & 0xff00) != 0x0100 &&
+ uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls),
UAC3_TE_INSERTION)) {
- build_connector_control(state.mixer, &state.oterm,
- false);
+ build_connector_control(state.mixer, state.map,
+ &state.oterm, false);
}
}
}
@@ -3218,13 +3188,38 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
return 0;
}
+static int delegate_notify(struct usb_mixer_interface *mixer, int unitid,
+ u8 *control, u8 *channel)
+{
+ const struct usbmix_connector_map *map = mixer->connector_map;
+
+ if (!map)
+ return unitid;
+
+ for (; map->id; map++) {
+ if (map->id == unitid) {
+ if (control && map->control)
+ *control = map->control;
+ if (channel && map->channel)
+ *channel = map->channel;
+ return map->delegated_id;
+ }
+ }
+ return unitid;
+}
+
void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid)
{
struct usb_mixer_elem_list *list;
+ unitid = delegate_notify(mixer, unitid, NULL, NULL);
+
for_each_mixer_elem(list, mixer, unitid) {
- struct usb_mixer_elem_info *info =
- mixer_elem_list_to_info(list);
+ struct usb_mixer_elem_info *info;
+
+ if (!list->is_std_info)
+ continue;
+ info = mixer_elem_list_to_info(list);
/* invalidate cache, so the value is read from the device */
info->cached = 0;
snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
@@ -3291,6 +3286,8 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
return;
}
+ unitid = delegate_notify(mixer, unitid, &control, &channel);
+
for_each_mixer_elem(list, mixer, unitid)
count++;
@@ -3302,6 +3299,8 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
if (!list->kctl)
continue;
+ if (!list->is_std_info)
+ continue;
info = mixer_elem_list_to_info(list);
if (count > 1 && info->control != control)
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 394cd9107507..2d494d1646b6 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -6,6 +6,13 @@
struct media_mixer_ctl;
+struct usbmix_connector_map {
+ u8 id;
+ u8 delegated_id;
+ u8 control;
+ u8 channel;
+};
+
struct usb_mixer_interface {
struct snd_usb_audio *chip;
struct usb_host_interface *hostif;
@@ -18,6 +25,9 @@ struct usb_mixer_interface {
/* the usb audio specification version this interface complies to */
int protocol;
+ /* optional connector delegation map */
+ const struct usbmix_connector_map *connector_map;
+
/* Sound Blaster remote control stuff */
const struct rc_config *rc_cfg;
u32 rc_code;
@@ -52,6 +62,7 @@ struct usb_mixer_elem_list {
struct usb_mixer_elem_list *next_id_elem; /* list of controls with same id */
struct snd_kcontrol *kctl;
unsigned int id;
+ bool is_std_info;
usb_mixer_elem_dump_func_t dump;
usb_mixer_elem_resume_func_t resume;
};
@@ -89,8 +100,12 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid);
int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
int request, int validx, int value_set);
-int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list,
- struct snd_kcontrol *kctl);
+int snd_usb_mixer_add_list(struct usb_mixer_elem_list *list,
+ struct snd_kcontrol *kctl,
+ bool is_std_info);
+
+#define snd_usb_mixer_add_control(list, kctl) \
+ snd_usb_mixer_add_list(list, kctl, true)
void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list,
struct usb_mixer_interface *mixer,
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index 73baf398c84a..ac84f0b2b0bc 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -27,6 +27,7 @@ struct usbmix_ctl_map {
u32 id;
const struct usbmix_name_map *map;
const struct usbmix_selector_map *selector_map;
+ const struct usbmix_connector_map *connector_map;
int ignore_ctl_error;
};
@@ -349,6 +350,58 @@ static const struct usbmix_name_map dell_alc4020_map[] = {
{ 0 }
};
+/* Some mobos shipped with a dummy HD-audio show the invalid GET_MIN/GET_MAX
+ * response for Input Gain Pad (id=19, control=12) and the connector status
+ * for SPDIF terminal (id=18). Skip them.
+ */
+static const struct usbmix_name_map asus_rog_map[] = {
+ { 18, NULL }, /* OT, connector control */
+ { 19, NULL, 12 }, /* FU, Input Gain Pad */
+ {}
+};
+
+/* TRX40 mobos with Realtek ALC1220-VB */
+static const struct usbmix_name_map trx40_mobo_map[] = {
+ { 18, NULL }, /* OT, IEC958 - broken response, disabled */
+ { 19, NULL, 12 }, /* FU, Input Gain Pad - broken response, disabled */
+ { 16, "Speaker" }, /* OT */
+ { 22, "Speaker Playback" }, /* FU */
+ { 7, "Line" }, /* IT */
+ { 19, "Line Capture" }, /* FU */
+ { 17, "Front Headphone" }, /* OT */
+ { 23, "Front Headphone Playback" }, /* FU */
+ { 8, "Mic" }, /* IT */
+ { 20, "Mic Capture" }, /* FU */
+ { 9, "Front Mic" }, /* IT */
+ { 21, "Front Mic Capture" }, /* FU */
+ { 24, "IEC958 Playback" }, /* FU */
+ {}
+};
+
+static const struct usbmix_connector_map trx40_mobo_connector_map[] = {
+ { 10, 16 }, /* (Back) Speaker */
+ { 11, 17 }, /* Front Headphone */
+ { 13, 7 }, /* Line */
+ { 14, 8 }, /* Mic */
+ { 15, 9 }, /* Front Mic */
+ {}
+};
+
+/* Rear panel + front mic on Gigabyte TRX40 Aorus Master with ALC1220-VB */
+static const struct usbmix_name_map aorus_master_alc1220vb_map[] = {
+ { 17, NULL }, /* OT, IEC958?, disabled */
+ { 19, NULL, 12 }, /* FU, Input Gain Pad - broken response, disabled */
+ { 16, "Line Out" }, /* OT */
+ { 22, "Line Out Playback" }, /* FU */
+ { 7, "Line" }, /* IT */
+ { 19, "Line Capture" }, /* FU */
+ { 8, "Mic" }, /* IT */
+ { 20, "Mic Capture" }, /* FU */
+ { 9, "Front Mic" }, /* IT */
+ { 21, "Front Mic Capture" }, /* FU */
+ {}
+};
+
/*
* Control map entries
*/
@@ -468,6 +521,38 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
.id = USB_ID(0x05a7, 0x1020),
.map = bose_companion5_map,
},
+ { /* Gigabyte TRX40 Aorus Master (rear panel + front mic) */
+ .id = USB_ID(0x0414, 0xa001),
+ .map = aorus_master_alc1220vb_map,
+ },
+ { /* Gigabyte TRX40 Aorus Pro WiFi */
+ .id = USB_ID(0x0414, 0xa002),
+ .map = trx40_mobo_map,
+ .connector_map = trx40_mobo_connector_map,
+ },
+ { /* ASUS ROG Zenith II */
+ .id = USB_ID(0x0b05, 0x1916),
+ .map = asus_rog_map,
+ },
+ { /* ASUS ROG Strix */
+ .id = USB_ID(0x0b05, 0x1917),
+ .map = asus_rog_map,
+ },
+ { /* MSI TRX40 Creator */
+ .id = USB_ID(0x0db0, 0x0d64),
+ .map = trx40_mobo_map,
+ .connector_map = trx40_mobo_connector_map,
+ },
+ { /* MSI TRX40 */
+ .id = USB_ID(0x0db0, 0x543d),
+ .map = trx40_mobo_map,
+ .connector_map = trx40_mobo_connector_map,
+ },
+ { /* Asrock TRX40 Creator */
+ .id = USB_ID(0x26ce, 0x0a01),
+ .map = trx40_mobo_map,
+ .connector_map = trx40_mobo_connector_map,
+ },
{ 0 } /* terminator */
};
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 27dcb3743690..c48104208fed 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -156,7 +156,8 @@ static int add_single_ctl_with_resume(struct usb_mixer_interface *mixer,
return -ENOMEM;
}
kctl->private_free = snd_usb_mixer_elem_free;
- return snd_usb_mixer_add_control(list, kctl);
+ /* don't use snd_usb_mixer_add_control() here, this is a special list element */
+ return snd_usb_mixer_add_list(list, kctl, false);
}
/*
@@ -182,6 +183,7 @@ static const struct rc_config {
{ USB_ID(0x041e, 0x3042), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 */
{ USB_ID(0x041e, 0x30df), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */
{ USB_ID(0x041e, 0x3237), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */
+ { USB_ID(0x041e, 0x3263), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */
{ USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */
};
@@ -1507,11 +1509,15 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
/* use known values for that card: interface#1 altsetting#1 */
iface = usb_ifnum_to_if(chip->dev, 1);
- if (!iface || iface->num_altsetting < 2)
- return -EINVAL;
+ if (!iface || iface->num_altsetting < 2) {
+ err = -EINVAL;
+ goto end;
+ }
alts = &iface->altsetting[1];
- if (get_iface_desc(alts)->bNumEndpoints < 1)
- return -EINVAL;
+ if (get_iface_desc(alts)->bNumEndpoints < 1) {
+ err = -EINVAL;
+ goto end;
+ }
ep = get_endpoint(alts, 0)->bEndpointAddress;
err = snd_usb_ctl_msg(chip->dev,
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index ff5ab24f3bd1..e837ce55f6ad 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -344,10 +344,22 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
ep = 0x81;
ifnum = 1;
goto add_sync_ep_from_ifnum;
- case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II */
+ case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II/IIc */
+ /* MicroBook IIc */
+ if (altsd->bInterfaceClass == USB_CLASS_AUDIO)
+ return 0;
+
+ /* MicroBook II */
ep = 0x84;
ifnum = 0;
goto add_sync_ep_from_ifnum;
+ case USB_ID(0x07fd, 0x0008): /* MOTU M Series */
+ case USB_ID(0x31e9, 0x0001): /* Solid State Logic SSL2 */
+ case USB_ID(0x31e9, 0x0002): /* Solid State Logic SSL2+ */
+ case USB_ID(0x0d9a, 0x00df): /* RTX6001 */
+ ep = 0x81;
+ ifnum = 2;
+ goto add_sync_ep_from_ifnum;
case USB_ID(0x0582, 0x01d8): /* BOSS Katana */
/* BOSS Katana amplifiers do not need quirks */
return 0;
@@ -370,7 +382,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
add_sync_ep_from_ifnum:
iface = usb_ifnum_to_if(dev, ifnum);
- if (!iface || iface->num_altsetting == 0)
+ if (!iface || iface->num_altsetting < 2)
return -EINVAL;
alts = &iface->altsetting[1];
@@ -382,6 +394,8 @@ add_sync_ep:
if (!subs->sync_endpoint)
return -EINVAL;
+ subs->sync_endpoint->is_implicit_feedback = 1;
+
subs->data_endpoint->sync_master = subs->sync_endpoint;
return 1;
@@ -480,12 +494,15 @@ static int set_sync_endpoint(struct snd_usb_substream *subs,
implicit_fb ?
SND_USB_ENDPOINT_TYPE_DATA :
SND_USB_ENDPOINT_TYPE_SYNC);
+
if (!subs->sync_endpoint) {
if (is_playback && attr == USB_ENDPOINT_SYNC_NONE)
return 0;
return -EINVAL;
}
+ subs->sync_endpoint->is_implicit_feedback = implicit_fb;
+
subs->data_endpoint->sync_master = subs->sync_endpoint;
return 0;
@@ -506,15 +523,15 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
if (WARN_ON(!iface))
return -EINVAL;
alts = usb_altnum_to_altsetting(iface, fmt->altsetting);
- altsd = get_iface_desc(alts);
- if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting))
+ if (WARN_ON(!alts))
return -EINVAL;
+ altsd = get_iface_desc(alts);
- if (fmt == subs->cur_audiofmt)
+ if (fmt == subs->cur_audiofmt && !subs->need_setup_fmt)
return 0;
/* close the old interface */
- if (subs->interface >= 0 && subs->interface != fmt->iface) {
+ if (subs->interface >= 0 && (subs->interface != fmt->iface || subs->need_setup_fmt)) {
if (!subs->stream->chip->keep_iface) {
err = usb_set_interface(subs->dev, subs->interface, 0);
if (err < 0) {
@@ -528,6 +545,9 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
subs->altset_idx = 0;
}
+ if (subs->need_setup_fmt)
+ subs->need_setup_fmt = false;
+
/* set interface */
if (iface->cur_altsetting != alts) {
err = snd_usb_select_mode_quirk(subs, fmt);
@@ -1397,6 +1417,12 @@ static void retire_capture_urb(struct snd_usb_substream *subs,
// continue;
}
bytes = urb->iso_frame_desc[i].actual_length;
+ if (subs->stream_offset_adj > 0) {
+ unsigned int adj = min(subs->stream_offset_adj, bytes);
+ cp += adj;
+ bytes -= adj;
+ subs->stream_offset_adj -= adj;
+ }
frames = bytes / stride;
if (!subs->txfr_quirk)
bytes = frames * stride;
@@ -1568,6 +1594,8 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
for (i = 0; i < ctx->packets; i++) {
if (ctx->packet_size[i])
counts = ctx->packet_size[i];
+ else if (ep->sync_master)
+ counts = snd_usb_endpoint_slave_next_packet_size(ep);
else
counts = snd_usb_endpoint_next_packet_size(ep);
@@ -1727,6 +1755,7 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea
return 0;
case SNDRV_PCM_TRIGGER_STOP:
stop_endpoints(subs, false);
+ subs->data_endpoint->retire_data_urb = NULL;
subs->running = 0;
return 0;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
@@ -1735,6 +1764,13 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea
subs->data_endpoint->retire_data_urb = retire_playback_urb;
subs->running = 0;
return 0;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (subs->stream->chip->setup_fmt_after_resume_quirk) {
+ stop_endpoints(subs, true);
+ subs->need_setup_fmt = true;
+ return 0;
+ }
+ break;
}
return -EINVAL;
@@ -1767,6 +1803,13 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream
subs->data_endpoint->retire_data_urb = retire_capture_urb;
subs->running = 1;
return 0;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (subs->stream->chip->setup_fmt_after_resume_quirk) {
+ stop_endpoints(subs, true);
+ subs->need_setup_fmt = true;
+ return 0;
+ }
+ break;
}
return -EINVAL;
diff --git a/sound/usb/power.c b/sound/usb/power.c
index bd303a1ba1b7..606a2cb23eab 100644
--- a/sound/usb/power.c
+++ b/sound/usb/power.c
@@ -31,6 +31,8 @@ snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface,
struct uac3_power_domain_descriptor *pd_desc = p;
int i;
+ if (!snd_usb_validate_audio_desc(p, UAC_VERSION_3))
+ continue;
for (i = 0; i < pd_desc->bNrEntities; i++) {
if (pd_desc->baEntityID[i] == id) {
pd->pd_id = pd_desc->bPowerDomainID;
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 9e049f60e80e..f55b605cfeb7 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -25,6 +25,26 @@
.idProduct = prod, \
.bInterfaceClass = USB_CLASS_VENDOR_SPEC
+/* HP Thunderbolt Dock Audio Headset */
+{
+ USB_DEVICE(0x03f0, 0x0269),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "HP",
+ .product_name = "Thunderbolt Dock Audio Headset",
+ .profile_name = "HP-Thunderbolt-Dock-Audio-Headset",
+ .ifnum = QUIRK_NO_INTERFACE
+ }
+},
+/* HP Thunderbolt Dock Audio Module */
+{
+ USB_DEVICE(0x03f0, 0x0567),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "HP",
+ .product_name = "Thunderbolt Dock Audio Module",
+ .profile_name = "HP-Thunderbolt-Dock-Audio-Module",
+ .ifnum = QUIRK_NO_INTERFACE
+ }
+},
/* FTDI devices */
{
USB_DEVICE(0x0403, 0xb8d8),
@@ -2756,90 +2776,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.type = QUIRK_MIDI_NOVATION
}
},
-{
- /*
- * Focusrite Scarlett Solo 2nd generation
- * Reports that playback should use Synch: Synchronous
- * while still providing a feedback endpoint. Synchronous causes
- * snapping on some sample rates.
- * Force it to use Synch: Asynchronous.
- */
- USB_DEVICE(0x1235, 0x8205),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
- .formats = SNDRV_PCM_FMTBIT_S32_LE,
- .channels = 2,
- .iface = 1,
- .altsetting = 1,
- .altset_idx = 1,
- .attributes = 0,
- .endpoint = 0x01,
- .ep_attr = USB_ENDPOINT_XFER_ISOC |
- USB_ENDPOINT_SYNC_ASYNC,
- .protocol = UAC_VERSION_2,
- .rates = SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_88200 |
- SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_176400 |
- SNDRV_PCM_RATE_192000,
- .rate_min = 44100,
- .rate_max = 192000,
- .nr_rates = 6,
- .rate_table = (unsigned int[]) {
- 44100, 48000, 88200,
- 96000, 176400, 192000
- },
- .clock = 41
- }
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
- .formats = SNDRV_PCM_FMTBIT_S32_LE,
- .channels = 2,
- .iface = 2,
- .altsetting = 1,
- .altset_idx = 1,
- .attributes = 0,
- .endpoint = 0x82,
- .ep_attr = USB_ENDPOINT_XFER_ISOC |
- USB_ENDPOINT_SYNC_ASYNC |
- USB_ENDPOINT_USAGE_IMPLICIT_FB,
- .protocol = UAC_VERSION_2,
- .rates = SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_88200 |
- SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_176400 |
- SNDRV_PCM_RATE_192000,
- .rate_min = 44100,
- .rate_max = 192000,
- .nr_rates = 6,
- .rate_table = (unsigned int[]) {
- 44100, 48000, 88200,
- 96000, 176400, 192000
- },
- .clock = 41
- }
- },
- {
- .ifnum = 3,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = -1
- }
- }
- }
-},
/* Access Music devices */
{
@@ -3466,12 +3402,13 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
.vendor_name = "Dell",
.product_name = "WD19 Dock",
.profile_name = "Dell-WD15-Dock",
- .ifnum = QUIRK_NO_INTERFACE
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_SETUP_FMT_AFTER_RESUME
}
},
/* MOTU Microbook II */
{
- USB_DEVICE(0x07fd, 0x0004),
+ USB_DEVICE_VENDOR_SPEC(0x07fd, 0x0004),
.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
.vendor_name = "MOTU",
.product_name = "MicroBookII",
@@ -3534,5 +3471,201 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
}
}
},
+{
+ /*
+ * Pioneer DJ DJM-250MK2
+ * PCM is 8 channels out @ 48 fixed (endpoints 0x01).
+ * The output from computer to the mixer is usable.
+ *
+ * The input (phono or line to computer) is not working.
+ * It should be at endpoint 0x82 and probably also 8 channels,
+ * but it seems that it works only with Pioneer proprietary software.
+ * Even on officially supported OS, the Audacity was unable to record
+ * and Mixxx to recognize the control vinyls.
+ */
+ USB_DEVICE_VENDOR_SPEC(0x2b73, 0x0017),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 8, // outputs
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x01,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC|
+ USB_ENDPOINT_SYNC_ASYNC,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 48000 }
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+{
+ /*
+ * PIONEER DJ DDJ-RB
+ * PCM is 4 channels out, 2 dummy channels in @ 44.1 fixed
+ * The feedback for the output is the dummy input.
+ */
+ USB_DEVICE_VENDOR_SPEC(0x2b73, 0x000e),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 4,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x01,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC|
+ USB_ENDPOINT_SYNC_ASYNC,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 44100 }
+ }
+ },
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 2,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x82,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC|
+ USB_ENDPOINT_SYNC_ASYNC|
+ USB_ENDPOINT_USAGE_IMPLICIT_FB,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 44100 }
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+
+#define ALC1220_VB_DESKTOP(vend, prod) { \
+ USB_DEVICE(vend, prod), \
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \
+ .vendor_name = "Realtek", \
+ .product_name = "ALC1220-VB-DT", \
+ .profile_name = "Realtek-ALC1220-VB-Desktop", \
+ .ifnum = QUIRK_NO_INTERFACE \
+ } \
+}
+ALC1220_VB_DESKTOP(0x0414, 0xa002), /* Gigabyte TRX40 Aorus Pro WiFi */
+ALC1220_VB_DESKTOP(0x0db0, 0x0d64), /* MSI TRX40 Creator */
+ALC1220_VB_DESKTOP(0x0db0, 0x543d), /* MSI TRX40 */
+ALC1220_VB_DESKTOP(0x26ce, 0x0a01), /* Asrock TRX40 Creator */
+#undef ALC1220_VB_DESKTOP
+
+/* Two entries for Gigabyte TRX40 Aorus Master:
+ * TRX40 Aorus Master has two USB-audio devices, one for the front headphone
+ * with ESS SABRE9218 DAC chip, while another for the rest I/O (the rear
+ * panel and the front mic) with Realtek ALC1220-VB.
+ * Here we provide two distinct names for making UCM profiles easier.
+ */
+{
+ USB_DEVICE(0x0414, 0xa000),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "Gigabyte",
+ .product_name = "Aorus Master Front Headphone",
+ .profile_name = "Gigabyte-Aorus-Master-Front-Headphone",
+ .ifnum = QUIRK_NO_INTERFACE
+ }
+},
+{
+ USB_DEVICE(0x0414, 0xa001),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "Gigabyte",
+ .product_name = "Aorus Master Main Audio",
+ .profile_name = "Gigabyte-Aorus-Master-Main-Audio",
+ .ifnum = QUIRK_NO_INTERFACE
+ }
+},
+
+/*
+ * MacroSilicon MS2109 based HDMI capture cards
+ *
+ * These claim 96kHz 1ch in the descriptors, but are actually 48kHz 2ch.
+ * They also need QUIRK_AUDIO_ALIGN_TRANSFER, which makes one wonder if
+ * they pretend to be 96kHz mono as a workaround for stereo being broken
+ * by that...
+ *
+ * They also have an issue with initial stream alignment that causes the
+ * channels to be swapped and out of phase, which is dealt with in quirks.c.
+ */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+ USB_DEVICE_ID_MATCH_INT_CLASS |
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+ .idVendor = 0x534d,
+ .idProduct = 0x2109,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "MacroSilicon",
+ .product_name = "MS2109",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = &(const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_ALIGN_TRANSFER,
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_STANDARD_MIXER,
+ },
+ {
+ .ifnum = 3,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels = 2,
+ .iface = 3,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = 0,
+ .endpoint = 0x82,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC |
+ USB_ENDPOINT_SYNC_ASYNC,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
#undef USB_DEVICE_VENDOR_SPEC
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index b6f7b13768a1..a756f50d9f07 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -248,6 +248,9 @@ static int create_yamaha_midi_quirk(struct snd_usb_audio *chip,
NULL, USB_MS_MIDI_OUT_JACK);
if (!injd && !outjd)
return -ENODEV;
+ if ((injd && !snd_usb_validate_midi_desc(injd)) ||
+ (outjd && !snd_usb_validate_midi_desc(outjd)))
+ return -ENODEV;
if (injd && (injd->bLength < 5 ||
(injd->bJackType != USB_MS_EMBEDDED &&
injd->bJackType != USB_MS_EXTERNAL)))
@@ -505,6 +508,16 @@ static int create_standard_mixer_quirk(struct snd_usb_audio *chip,
return snd_usb_create_mixer(chip, quirk->ifnum, 0);
}
+
+static int setup_fmt_after_resume_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver,
+ const struct snd_usb_audio_quirk *quirk)
+{
+ chip->setup_fmt_after_resume_quirk = 1;
+ return 1; /* Continue with creating streams and mixer */
+}
+
/*
* audio-interface quirks
*
@@ -543,6 +556,7 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip,
[QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
[QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk,
[QUIRK_AUDIO_STANDARD_MIXER] = create_standard_mixer_quirk,
+ [QUIRK_SETUP_FMT_AFTER_RESUME] = setup_fmt_after_resume_quirk,
};
if (quirk->type < QUIRK_TYPE_COUNT) {
@@ -1099,6 +1113,31 @@ free_buf:
return err;
}
+static int snd_usb_motu_m_series_boot_quirk(struct usb_device *dev)
+{
+ int ret;
+
+ if (snd_usb_pipe_sanity_check(dev, usb_sndctrlpipe(dev, 0)))
+ return -EINVAL;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 1, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0, 0, NULL, 0, 1000);
+
+ if (ret < 0)
+ return ret;
+
+ msleep(2000);
+
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 1, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x20, 0, NULL, 0, 1000);
+
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
/*
* Setup quirks
*/
@@ -1277,7 +1316,28 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx 3 */
return snd_usb_axefx3_boot_quirk(dev);
case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II */
- return snd_usb_motu_microbookii_boot_quirk(dev);
+ /*
+ * For some reason interface 3 with vendor-spec class is
+ * detected on MicroBook IIc.
+ */
+ if (get_iface_desc(intf->altsetting)->bInterfaceClass ==
+ USB_CLASS_VENDOR_SPEC &&
+ get_iface_desc(intf->altsetting)->bInterfaceNumber < 3)
+ return snd_usb_motu_microbookii_boot_quirk(dev);
+ break;
+ }
+
+ return 0;
+}
+
+int snd_usb_apply_boot_quirk_once(struct usb_device *dev,
+ struct usb_interface *intf,
+ const struct snd_usb_audio_quirk *quirk,
+ unsigned int id)
+{
+ switch (id) {
+ case USB_ID(0x07fd, 0x0008): /* MOTU M Series */
+ return snd_usb_motu_m_series_boot_quirk(dev);
}
return 0;
@@ -1372,6 +1432,9 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
case USB_ID(0x041e, 0x3f19): /* E-Mu 0204 USB */
set_format_emu_quirk(subs, fmt);
break;
+ case USB_ID(0x534d, 0x2109): /* MacroSilicon MS2109 */
+ subs->stream_offset_adj = 2;
+ break;
}
}
@@ -1383,10 +1446,12 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */
case USB_ID(0x0556, 0x0014): /* Phoenix Audio TMX320VC */
case USB_ID(0x05A3, 0x9420): /* ELP HD USB Camera */
+ case USB_ID(0x05a7, 0x1020): /* Bose Companion 5 */
case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */
case USB_ID(0x1395, 0x740a): /* Sennheiser DECT */
case USB_ID(0x1901, 0x0191): /* GE B850V3 CP2114 audio interface */
case USB_ID(0x21B4, 0x0081): /* AudioQuest DragonFly */
+ case USB_ID(0x2912, 0x30c8): /* Audioengine D1 */
return true;
}
@@ -1407,6 +1472,7 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
static bool is_itf_usb_dsd_dac(unsigned int id)
{
switch (id) {
+ case USB_ID(0x154e, 0x1002): /* Denon DCD-1500RE */
case USB_ID(0x154e, 0x1003): /* Denon DA-300USB */
case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */
case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */
@@ -1538,15 +1604,24 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
&& (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
msleep(20);
- /* Zoom R16/24, Logitech H650e, Jabra 550a needs a tiny delay here,
- * otherwise requests like get/set frequency return as failed despite
- * actually succeeding.
+ /* Zoom R16/24, Logitech H650e, Jabra 550a, Kingston HyperX needs a tiny
+ * delay here, otherwise requests like get/set frequency return as
+ * failed despite actually succeeding.
*/
if ((chip->usb_id == USB_ID(0x1686, 0x00dd) ||
chip->usb_id == USB_ID(0x046d, 0x0a46) ||
- chip->usb_id == USB_ID(0x0b0e, 0x0349)) &&
+ chip->usb_id == USB_ID(0x0b0e, 0x0349) ||
+ chip->usb_id == USB_ID(0x0951, 0x16ad)) &&
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
usleep_range(1000, 2000);
+
+ /*
+ * Samsung USBC Headset (AKG) need a tiny delay after each
+ * class compliant request. (Model number: AAM625R or AAM627R)
+ */
+ if (chip->usb_id == USB_ID(0x04e8, 0xa051) &&
+ (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
+ usleep_range(5000, 6000);
}
/*
@@ -1563,7 +1638,8 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
struct usb_interface *iface;
/* Playback Designs */
- if (USB_ID_VENDOR(chip->usb_id) == 0x23ba) {
+ if (USB_ID_VENDOR(chip->usb_id) == 0x23ba &&
+ USB_ID_PRODUCT(chip->usb_id) < 0x0110) {
switch (fp->altsetting) {
case 1:
fp->dsd_dop = true;
@@ -1580,9 +1656,6 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
/* XMOS based USB DACs */
switch (chip->usb_id) {
case USB_ID(0x1511, 0x0037): /* AURALiC VEGA */
- case USB_ID(0x22d9, 0x0416): /* OPPO HA-1 */
- case USB_ID(0x22d9, 0x0436): /* OPPO Sonica */
- case USB_ID(0x22d9, 0x0461): /* OPPO UDP-205 */
case USB_ID(0x2522, 0x0012): /* LH Labs VI DAC Infinity */
case USB_ID(0x2772, 0x0230): /* Pro-Ject Pre Box S2 Digital */
if (fp->altsetting == 2)
@@ -1591,12 +1664,11 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
case USB_ID(0x0d8c, 0x0316): /* Hegel HD12 DSD */
case USB_ID(0x10cb, 0x0103): /* The Bit Opus #3; with fp->dsd_raw */
- case USB_ID(0x16b0, 0x06b2): /* NuPrime DAC-10 */
+ case USB_ID(0x16d0, 0x06b2): /* NuPrime DAC-10 */
case USB_ID(0x16d0, 0x09dd): /* Encore mDSD */
case USB_ID(0x16d0, 0x0733): /* Furutech ADL Stratos */
case USB_ID(0x16d0, 0x09db): /* NuPrime Audio DAC-9 */
case USB_ID(0x1db5, 0x0003): /* Bryston BDA3 */
- case USB_ID(0x22d9, 0x0426): /* OPPO HA-2 */
case USB_ID(0x22e1, 0xca01): /* HDTA Serenade DSD */
case USB_ID(0x249c, 0x9326): /* M2Tech Young MkIII */
case USB_ID(0x2616, 0x0106): /* PS Audio NuWave DAC */
@@ -1651,9 +1723,13 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
* from XMOS/Thesycon
*/
switch (USB_ID_VENDOR(chip->usb_id)) {
- case 0x20b1: /* XMOS based devices */
case 0x152a: /* Thesycon devices */
+ case 0x20b1: /* XMOS based devices */
+ case 0x22d9: /* Oppo */
+ case 0x23ba: /* Playback Designs */
case 0x25ce: /* Mytek devices */
+ case 0x278b: /* Rotel? */
+ case 0x292b: /* Gustard/Ess based devices */
case 0x2ab6: /* T+A devices */
case 0x3842: /* EVGA */
case 0xc502: /* HiBy devices */
@@ -1699,5 +1775,62 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
else
fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
break;
+ case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook IIc */
+ /*
+ * MaxPacketsOnly attribute is erroneously set in endpoint
+ * descriptors. As a result this card produces noise with
+ * all sample rates other than 96 KHz.
+ */
+ fp->attributes &= ~UAC_EP_CS_ATTR_FILL_MAX;
+ break;
+ case USB_ID(0x1235, 0x8202): /* Focusrite Scarlett 2i2 2nd gen */
+ case USB_ID(0x1235, 0x8205): /* Focusrite Scarlett Solo 2nd gen */
+ /*
+ * Reports that playback should use Synch: Synchronous
+ * while still providing a feedback endpoint.
+ * Synchronous causes snapping on some sample rates.
+ * Force it to use Synch: Asynchronous.
+ */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE;
+ fp->ep_attr |= USB_ENDPOINT_SYNC_ASYNC;
+ }
+ break;
}
}
+
+/*
+ * registration quirk:
+ * the registration is skipped if a device matches with the given ID,
+ * unless the interface reaches to the defined one. This is for delaying
+ * the registration until the last known interface, so that the card and
+ * devices appear at the same time.
+ */
+
+struct registration_quirk {
+ unsigned int usb_id; /* composed via USB_ID() */
+ unsigned int interface; /* the interface to trigger register */
+};
+
+#define REG_QUIRK_ENTRY(vendor, product, iface) \
+ { .usb_id = USB_ID(vendor, product), .interface = (iface) }
+
+static const struct registration_quirk registration_quirks[] = {
+ REG_QUIRK_ENTRY(0x0951, 0x16d8, 2), /* Kingston HyperX AMP */
+ REG_QUIRK_ENTRY(0x0951, 0x16ed, 2), /* Kingston HyperX Cloud Alpha S */
+ REG_QUIRK_ENTRY(0x0951, 0x16ea, 2), /* Kingston HyperX Cloud Flight S */
+ { 0 } /* terminator */
+};
+
+/* return true if skipping registration */
+bool snd_usb_registration_quirk(struct snd_usb_audio *chip, int iface)
+{
+ const struct registration_quirk *q;
+
+ for (q = registration_quirks; q->usb_id; q++)
+ if (chip->usb_id == q->usb_id)
+ return iface != q->interface;
+
+ /* Register as normal */
+ return false;
+}
diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h
index a80e0ddd0736..c76cf24a640a 100644
--- a/sound/usb/quirks.h
+++ b/sound/usb/quirks.h
@@ -20,6 +20,11 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
const struct snd_usb_audio_quirk *quirk,
unsigned int usb_id);
+int snd_usb_apply_boot_quirk_once(struct usb_device *dev,
+ struct usb_interface *intf,
+ const struct snd_usb_audio_quirk *quirk,
+ unsigned int usb_id);
+
void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
struct audioformat *fmt);
@@ -46,4 +51,6 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
struct audioformat *fp,
int stream);
+bool snd_usb_registration_quirk(struct snd_usb_audio *chip, int iface);
+
#endif /* __USBAUDIO_QUIRKS_H */
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index e852c7fd6109..3a17c4c53f87 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -89,6 +89,7 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
subs->tx_length_quirk = as->chip->tx_length_quirk;
subs->speed = snd_usb_get_speed(subs->dev);
subs->pkt_offset_adj = 0;
+ subs->stream_offset_adj = 0;
snd_usb_set_pcm_ops(as->pcm, stream);
@@ -627,16 +628,14 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
*/
static void *
snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
- int terminal_id, bool uac23)
+ int terminal_id, int protocol)
{
struct uac2_input_terminal_descriptor *term = NULL;
- size_t minlen = uac23 ? sizeof(struct uac2_input_terminal_descriptor) :
- sizeof(struct uac_input_terminal_descriptor);
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
ctrl_iface->extralen,
term, UAC_INPUT_TERMINAL))) {
- if (term->bLength < minlen)
+ if (!snd_usb_validate_audio_desc(term, protocol))
continue;
if (term->bTerminalID == terminal_id)
return term;
@@ -647,7 +646,7 @@ snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
static void *
snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
- int terminal_id)
+ int terminal_id, int protocol)
{
/* OK to use with both UAC2 and UAC3 */
struct uac2_output_terminal_descriptor *term = NULL;
@@ -655,8 +654,9 @@ snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
ctrl_iface->extralen,
term, UAC_OUTPUT_TERMINAL))) {
- if (term->bLength >= sizeof(*term) &&
- term->bTerminalID == terminal_id)
+ if (!snd_usb_validate_audio_desc(term, protocol))
+ continue;
+ if (term->bTerminalID == terminal_id)
return term;
}
@@ -731,7 +731,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
- false);
+ protocol);
if (iterm) {
num_channels = iterm->bNrChannels;
chconfig = le16_to_cpu(iterm->wChannelConfig);
@@ -767,7 +767,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
*/
input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
- true);
+ protocol);
if (input_term) {
clock = input_term->bCSourceID;
if (!chconfig && (num_channels == input_term->bNrChannels))
@@ -776,7 +776,8 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
}
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
- as->bTerminalLink);
+ as->bTerminalLink,
+ protocol);
if (output_term) {
clock = output_term->bCSourceID;
goto found_clock;
@@ -1002,14 +1003,15 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
*/
input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
- true);
+ UAC_VERSION_3);
if (input_term) {
clock = input_term->bCSourceID;
goto found_clock;
}
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
- as->bTerminalLink);
+ as->bTerminalLink,
+ UAC_VERSION_3);
if (output_term) {
clock = output_term->bCSourceID;
goto found_clock;
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index feb30f9c1716..55a2119c2411 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -26,14 +26,14 @@ struct snd_usb_audio {
struct usb_interface *pm_intf;
u32 usb_id;
struct mutex mutex;
- unsigned int autosuspended:1;
+ unsigned int system_suspend;
atomic_t active;
atomic_t shutdown;
atomic_t usage_count;
wait_queue_head_t shutdown_wait;
unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */
unsigned int tx_length_quirk:1; /* Put length specifier in transfers */
-
+ unsigned int setup_fmt_after_resume_quirk:1; /* setup the format to interface after resume */
int num_interfaces;
int num_suspended_intf;
int sample_rate_read_error;
@@ -98,6 +98,7 @@ enum quirk_type {
QUIRK_AUDIO_EDIROL_UAXX,
QUIRK_AUDIO_ALIGN_TRANSFER,
QUIRK_AUDIO_STANDARD_MIXER,
+ QUIRK_SETUP_FMT_AFTER_RESUME,
QUIRK_TYPE_COUNT
};
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index d1caa8ed9e68..9985fc139487 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -119,7 +119,7 @@ static int snd_usX2Y_hwdep_dsp_status(struct snd_hwdep *hw,
info->num_dsps = 2; // 0: Prepad Data, 1: FPGA Code
if (us428->chip_status & USX2Y_STAT_CHIP_INIT)
info->chip_ready = 1;
- info->version = USX2Y_DRIVER_VERSION;
+ info->version = USX2Y_DRIVER_VERSION;
return 0;
}
diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
index 89fa287678fc..e0bace4d1c40 100644
--- a/sound/usb/usx2y/usbusx2yaudio.c
+++ b/sound/usb/usx2y/usbusx2yaudio.c
@@ -681,6 +681,8 @@ static int usX2Y_rate_set(struct usX2Ydev *usX2Y, int rate)
us->submitted = 2*NOOF_SETRATE_URBS;
for (i = 0; i < NOOF_SETRATE_URBS; ++i) {
struct urb *urb = us->urb[i];
+ if (!urb)
+ continue;
if (urb->status) {
if (!err)
err = -ENODEV;
diff --git a/sound/usb/validate.c b/sound/usb/validate.c
new file mode 100644
index 000000000000..079f1713e6d1
--- /dev/null
+++ b/sound/usb/validate.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Validation of USB-audio class descriptors
+//
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
+#include <linux/usb/midi.h>
+#include "usbaudio.h"
+#include "helper.h"
+
+struct usb_desc_validator {
+ unsigned char protocol;
+ unsigned char type;
+ bool (*func)(const void *p, const struct usb_desc_validator *v);
+ size_t size;
+};
+
+#define UAC_VERSION_ALL (unsigned char)(-1)
+
+/* UAC1 only */
+static bool validate_uac1_header(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac1_ac_header_descriptor *d = p;
+
+ return d->bLength >= sizeof(*d) &&
+ d->bLength >= sizeof(*d) + d->bInCollection;
+}
+
+/* for mixer unit; covering all UACs */
+static bool validate_mixer_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac_mixer_unit_descriptor *d = p;
+ size_t len;
+
+ if (d->bLength < sizeof(*d) || !d->bNrInPins)
+ return false;
+ len = sizeof(*d) + d->bNrInPins;
+ /* We can't determine the bitmap size only from this unit descriptor,
+ * so just check with the remaining length.
+ * The actual bitmap is checked at mixer unit parser.
+ */
+ switch (v->protocol) {
+ case UAC_VERSION_1:
+ default:
+ len += 2 + 1; /* wChannelConfig, iChannelNames */
+ /* bmControls[n*m] */
+ len += 1; /* iMixer */
+ break;
+ case UAC_VERSION_2:
+ len += 4 + 1; /* bmChannelConfig, iChannelNames */
+ /* bmMixerControls[n*m] */
+ len += 1 + 1; /* bmControls, iMixer */
+ break;
+ case UAC_VERSION_3:
+ len += 2; /* wClusterDescrID */
+ /* bmMixerControls[n*m] */
+ break;
+ }
+ return d->bLength >= len;
+}
+
+/* both for processing and extension units; covering all UACs */
+static bool validate_processing_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac_processing_unit_descriptor *d = p;
+ const unsigned char *hdr = p;
+ size_t len, m;
+
+ if (d->bLength < sizeof(*d))
+ return false;
+ len = sizeof(*d) + d->bNrInPins;
+ if (d->bLength < len)
+ return false;
+ switch (v->protocol) {
+ case UAC_VERSION_1:
+ default:
+ /* bNrChannels, wChannelConfig, iChannelNames */
+ len += 1 + 2 + 1;
+ if (d->bLength < len + 1) /* bControlSize */
+ return false;
+ m = hdr[len];
+ len += 1 + m + 1; /* bControlSize, bmControls, iProcessing */
+ break;
+ case UAC_VERSION_2:
+ /* bNrChannels, bmChannelConfig, iChannelNames */
+ len += 1 + 4 + 1;
+ if (v->type == UAC2_PROCESSING_UNIT_V2)
+ len += 2; /* bmControls -- 2 bytes for PU */
+ else
+ len += 1; /* bmControls -- 1 byte for EU */
+ len += 1; /* iProcessing */
+ break;
+ case UAC_VERSION_3:
+ /* wProcessingDescrStr, bmControls */
+ len += 2 + 4;
+ break;
+ }
+ if (d->bLength < len)
+ return false;
+
+ switch (v->protocol) {
+ case UAC_VERSION_1:
+ default:
+ if (v->type == UAC1_EXTENSION_UNIT)
+ return true; /* OK */
+ switch (le16_to_cpu(d->wProcessType)) {
+ case UAC_PROCESS_UP_DOWNMIX:
+ case UAC_PROCESS_DOLBY_PROLOGIC:
+ if (d->bLength < len + 1) /* bNrModes */
+ return false;
+ m = hdr[len];
+ len += 1 + m * 2; /* bNrModes, waModes(n) */
+ break;
+ default:
+ break;
+ }
+ break;
+ case UAC_VERSION_2:
+ if (v->type == UAC2_EXTENSION_UNIT_V2)
+ return true; /* OK */
+ switch (le16_to_cpu(d->wProcessType)) {
+ case UAC2_PROCESS_UP_DOWNMIX:
+ case UAC2_PROCESS_DOLBY_PROLOCIC: /* SiC! */
+ if (d->bLength < len + 1) /* bNrModes */
+ return false;
+ m = hdr[len];
+ len += 1 + m * 4; /* bNrModes, daModes(n) */
+ break;
+ default:
+ break;
+ }
+ break;
+ case UAC_VERSION_3:
+ if (v->type == UAC3_EXTENSION_UNIT) {
+ len += 2; /* wClusterDescrID */
+ break;
+ }
+ switch (le16_to_cpu(d->wProcessType)) {
+ case UAC3_PROCESS_UP_DOWNMIX:
+ if (d->bLength < len + 1) /* bNrModes */
+ return false;
+ m = hdr[len];
+ len += 1 + m * 2; /* bNrModes, waClusterDescrID(n) */
+ break;
+ case UAC3_PROCESS_MULTI_FUNCTION:
+ len += 2 + 4; /* wClusterDescrID, bmAlgorighms */
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ if (d->bLength < len)
+ return false;
+
+ return true;
+}
+
+/* both for selector and clock selector units; covering all UACs */
+static bool validate_selector_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac_selector_unit_descriptor *d = p;
+ size_t len;
+
+ if (d->bLength < sizeof(*d))
+ return false;
+ len = sizeof(*d) + d->bNrInPins;
+ switch (v->protocol) {
+ case UAC_VERSION_1:
+ default:
+ len += 1; /* iSelector */
+ break;
+ case UAC_VERSION_2:
+ len += 1 + 1; /* bmControls, iSelector */
+ break;
+ case UAC_VERSION_3:
+ len += 4 + 2; /* bmControls, wSelectorDescrStr */
+ break;
+ }
+ return d->bLength >= len;
+}
+
+static bool validate_uac1_feature_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac_feature_unit_descriptor *d = p;
+
+ if (d->bLength < sizeof(*d) || !d->bControlSize)
+ return false;
+ /* at least bmaControls(0) for master channel + iFeature */
+ return d->bLength >= sizeof(*d) + d->bControlSize + 1;
+}
+
+static bool validate_uac2_feature_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac2_feature_unit_descriptor *d = p;
+
+ if (d->bLength < sizeof(*d))
+ return false;
+ /* at least bmaControls(0) for master channel + iFeature */
+ return d->bLength >= sizeof(*d) + 4 + 1;
+}
+
+static bool validate_uac3_feature_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac3_feature_unit_descriptor *d = p;
+
+ if (d->bLength < sizeof(*d))
+ return false;
+ /* at least bmaControls(0) for master channel + wFeatureDescrStr */
+ return d->bLength >= sizeof(*d) + 4 + 2;
+}
+
+static bool validate_midi_out_jack(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct usb_midi_out_jack_descriptor *d = p;
+
+ return d->bLength >= sizeof(*d) &&
+ d->bLength >= sizeof(*d) + d->bNrInputPins * 2;
+}
+
+#define FIXED(p, t, s) { .protocol = (p), .type = (t), .size = sizeof(s) }
+#define FUNC(p, t, f) { .protocol = (p), .type = (t), .func = (f) }
+
+static struct usb_desc_validator audio_validators[] = {
+ /* UAC1 */
+ FUNC(UAC_VERSION_1, UAC_HEADER, validate_uac1_header),
+ FIXED(UAC_VERSION_1, UAC_INPUT_TERMINAL,
+ struct uac_input_terminal_descriptor),
+ FIXED(UAC_VERSION_1, UAC_OUTPUT_TERMINAL,
+ struct uac1_output_terminal_descriptor),
+ FUNC(UAC_VERSION_1, UAC_MIXER_UNIT, validate_mixer_unit),
+ FUNC(UAC_VERSION_1, UAC_SELECTOR_UNIT, validate_selector_unit),
+ FUNC(UAC_VERSION_1, UAC_FEATURE_UNIT, validate_uac1_feature_unit),
+ FUNC(UAC_VERSION_1, UAC1_PROCESSING_UNIT, validate_processing_unit),
+ FUNC(UAC_VERSION_1, UAC1_EXTENSION_UNIT, validate_processing_unit),
+
+ /* UAC2 */
+ FIXED(UAC_VERSION_2, UAC_HEADER, struct uac2_ac_header_descriptor),
+ FIXED(UAC_VERSION_2, UAC_INPUT_TERMINAL,
+ struct uac2_input_terminal_descriptor),
+ FIXED(UAC_VERSION_2, UAC_OUTPUT_TERMINAL,
+ struct uac2_output_terminal_descriptor),
+ FUNC(UAC_VERSION_2, UAC_MIXER_UNIT, validate_mixer_unit),
+ FUNC(UAC_VERSION_2, UAC_SELECTOR_UNIT, validate_selector_unit),
+ FUNC(UAC_VERSION_2, UAC_FEATURE_UNIT, validate_uac2_feature_unit),
+ /* UAC_VERSION_2, UAC2_EFFECT_UNIT: not implemented yet */
+ FUNC(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2, validate_processing_unit),
+ FUNC(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2, validate_processing_unit),
+ FIXED(UAC_VERSION_2, UAC2_CLOCK_SOURCE,
+ struct uac_clock_source_descriptor),
+ FUNC(UAC_VERSION_2, UAC2_CLOCK_SELECTOR, validate_selector_unit),
+ FIXED(UAC_VERSION_2, UAC2_CLOCK_MULTIPLIER,
+ struct uac_clock_multiplier_descriptor),
+ /* UAC_VERSION_2, UAC2_SAMPLE_RATE_CONVERTER: not implemented yet */
+
+ /* UAC3 */
+ FIXED(UAC_VERSION_2, UAC_HEADER, struct uac3_ac_header_descriptor),
+ FIXED(UAC_VERSION_3, UAC_INPUT_TERMINAL,
+ struct uac3_input_terminal_descriptor),
+ FIXED(UAC_VERSION_3, UAC_OUTPUT_TERMINAL,
+ struct uac3_output_terminal_descriptor),
+ /* UAC_VERSION_3, UAC3_EXTENDED_TERMINAL: not implemented yet */
+ FUNC(UAC_VERSION_3, UAC3_MIXER_UNIT, validate_mixer_unit),
+ FUNC(UAC_VERSION_3, UAC3_SELECTOR_UNIT, validate_selector_unit),
+ FUNC(UAC_VERSION_3, UAC_FEATURE_UNIT, validate_uac3_feature_unit),
+ /* UAC_VERSION_3, UAC3_EFFECT_UNIT: not implemented yet */
+ FUNC(UAC_VERSION_3, UAC3_PROCESSING_UNIT, validate_processing_unit),
+ FUNC(UAC_VERSION_3, UAC3_EXTENSION_UNIT, validate_processing_unit),
+ FIXED(UAC_VERSION_3, UAC3_CLOCK_SOURCE,
+ struct uac3_clock_source_descriptor),
+ FUNC(UAC_VERSION_3, UAC3_CLOCK_SELECTOR, validate_selector_unit),
+ FIXED(UAC_VERSION_3, UAC3_CLOCK_MULTIPLIER,
+ struct uac3_clock_multiplier_descriptor),
+ /* UAC_VERSION_3, UAC3_SAMPLE_RATE_CONVERTER: not implemented yet */
+ /* UAC_VERSION_3, UAC3_CONNECTORS: not implemented yet */
+ { } /* terminator */
+};
+
+static struct usb_desc_validator midi_validators[] = {
+ FIXED(UAC_VERSION_ALL, USB_MS_HEADER,
+ struct usb_ms_header_descriptor),
+ FIXED(UAC_VERSION_ALL, USB_MS_MIDI_IN_JACK,
+ struct usb_midi_in_jack_descriptor),
+ FUNC(UAC_VERSION_ALL, USB_MS_MIDI_OUT_JACK,
+ validate_midi_out_jack),
+ { } /* terminator */
+};
+
+
+/* Validate the given unit descriptor, return true if it's OK */
+static bool validate_desc(unsigned char *hdr, int protocol,
+ const struct usb_desc_validator *v)
+{
+ if (hdr[1] != USB_DT_CS_INTERFACE)
+ return true; /* don't care */
+
+ for (; v->type; v++) {
+ if (v->type == hdr[2] &&
+ (v->protocol == UAC_VERSION_ALL ||
+ v->protocol == protocol)) {
+ if (v->func)
+ return v->func(hdr, v);
+ /* check for the fixed size */
+ return hdr[0] >= v->size;
+ }
+ }
+
+ return true; /* not matching, skip validation */
+}
+
+bool snd_usb_validate_audio_desc(void *p, int protocol)
+{
+ return validate_desc(p, protocol, audio_validators);
+}
+
+bool snd_usb_validate_midi_desc(void *p)
+{
+ return validate_desc(p, UAC_VERSION_1, midi_validators);
+}