summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/aoa/Kconfig2
-rw-r--r--sound/aoa/codecs/Kconfig6
-rw-r--r--sound/aoa/fabrics/Kconfig2
-rw-r--r--sound/aoa/soundbus/Kconfig4
-rw-r--r--sound/core/compress_offload.c4
-rw-r--r--sound/core/hwdep.c4
-rw-r--r--sound/core/info.c4
-rw-r--r--sound/core/memalloc.c2
-rw-r--r--sound/core/oss/pcm_oss.c2
-rw-r--r--sound/core/oss/pcm_plugin.c10
-rw-r--r--sound/core/oss/pcm_plugin.h2
-rw-r--r--sound/core/pcm_lib.c1
-rw-r--r--sound/core/pcm_memory.c2
-rw-r--r--sound/core/pcm_native.c32
-rw-r--r--sound/core/rawmidi.c31
-rw-r--r--sound/core/seq/oss/seq_oss_timer.h10
-rw-r--r--sound/core/sgbuf.c1
-rw-r--r--sound/drivers/Kconfig12
-rw-r--r--sound/drivers/Makefile2
-rw-r--r--sound/drivers/ml403-ac97cr.c1298
-rw-r--r--sound/drivers/opl3/opl3_synth.c2
-rw-r--r--sound/drivers/pcm-indirect2.c560
-rw-r--r--sound/drivers/pcm-indirect2.h127
-rw-r--r--sound/drivers/portman2x4.c2
-rw-r--r--sound/firewire/Kconfig8
-rw-r--r--sound/firewire/amdtp-am824.c3
-rw-r--r--sound/firewire/amdtp-stream-trace.h3
-rw-r--r--sound/firewire/amdtp-stream.c326
-rw-r--r--sound/firewire/amdtp-stream.h20
-rw-r--r--sound/firewire/fireface/ff-protocol-latter.c58
-rw-r--r--sound/firewire/fireface/ff-stream.c10
-rw-r--r--sound/firewire/fireface/ff.c61
-rw-r--r--sound/firewire/fireface/ff.h11
-rw-r--r--sound/firewire/fireworks/fireworks.h2
-rw-r--r--sound/firewire/motu/amdtp-motu.c19
-rw-r--r--sound/firewire/motu/motu-pcm.c14
-rw-r--r--sound/firewire/motu/motu-proc.c20
-rw-r--r--sound/firewire/motu/motu-protocol-v2.c314
-rw-r--r--sound/firewire/motu/motu-protocol-v3.c312
-rw-r--r--sound/firewire/motu/motu-stream.c16
-rw-r--r--sound/firewire/motu/motu.c114
-rw-r--r--sound/firewire/motu/motu.h125
-rw-r--r--sound/hda/Kconfig7
-rw-r--r--sound/hda/ext/hdac_ext_bus.c2
-rw-r--r--sound/hda/hdac_bus.c6
-rw-r--r--sound/hda/hdac_controller.c13
-rw-r--r--sound/hda/hdac_device.c2
-rw-r--r--sound/hda/hdac_stream.c2
-rw-r--r--sound/hda/intel-dsp-config.c117
-rw-r--r--sound/hda/intel-nhlt.c49
-rw-r--r--sound/hda/local.h3
-rw-r--r--sound/isa/ad1816a/ad1816a.c2
-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/ac97/ac97_patch.c2
-rw-r--r--sound/pci/ctxfi/cthw20k1.c14
-rw-r--r--sound/pci/emu10k1/emu10k1x.c2
-rw-r--r--sound/pci/hda/Kconfig4
-rw-r--r--sound/pci/hda/hda_auto_parser.c6
-rw-r--r--sound/pci/hda/hda_codec.c28
-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_tegra.c50
-rw-r--r--sound/pci/hda/patch_hdmi.c73
-rw-r--r--sound/pci/hda/patch_realtek.c257
-rw-r--r--sound/pci/ice1712/ice1712.c3
-rw-r--r--sound/pci/oxygen/xonar_pcm179x.c4
-rw-r--r--sound/ppc/pmac.c4
-rw-r--r--sound/ppc/snd_ps3.c2
-rw-r--r--sound/soc/Kconfig2
-rw-r--r--sound/soc/codecs/hdac_hda.h4
-rw-r--r--sound/soc/codecs/max9867.c6
-rw-r--r--sound/soc/intel/common/sst-firmware.c2
-rw-r--r--sound/soc/intel/haswell/sst-haswell-pcm.c2
-rw-r--r--sound/soc/samsung/Kconfig2
-rw-r--r--sound/soc/soc-dapm.c12
-rw-r--r--sound/soc/sof/probe.h8
-rw-r--r--sound/soc/txx9/txx9aclc-ac97.c1
-rw-r--r--sound/usb/card.c54
-rw-r--r--sound/usb/card.h5
-rw-r--r--sound/usb/endpoint.c244
-rw-r--r--sound/usb/endpoint.h1
-rw-r--r--sound/usb/format.c57
-rw-r--r--sound/usb/line6/capture.c2
-rw-r--r--sound/usb/line6/driver.c22
-rw-r--r--sound/usb/line6/driver.h1
-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.c89
-rw-r--r--sound/usb/mixer.h19
-rw-r--r--sound/usb/mixer_maps.c65
-rw-r--r--sound/usb/mixer_quirks.c433
-rw-r--r--sound/usb/pcm.c14
-rw-r--r--sound/usb/proc.c53
-rw-r--r--sound/usb/quirks-table.h308
-rw-r--r--sound/usb/quirks.c48
-rw-r--r--sound/usb/usbaudio.h6
-rw-r--r--sound/usb/usx2y/usbusx2y.h2
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c2
-rw-r--r--sound/x86/Kconfig2
103 files changed, 2745 insertions, 3122 deletions
diff --git a/sound/aoa/Kconfig b/sound/aoa/Kconfig
index d70ca0f33c67..c58308ae4dc1 100644
--- a/sound/aoa/Kconfig
+++ b/sound/aoa/Kconfig
@@ -3,7 +3,7 @@ menuconfig SND_AOA
tristate "Apple Onboard Audio driver"
depends on PPC_PMAC
select SND_PCM
- ---help---
+ help
This option enables the new driver for the various
Apple Onboard Audio components.
diff --git a/sound/aoa/codecs/Kconfig b/sound/aoa/codecs/Kconfig
index 8ac13fdbcfe0..03f89dbcca75 100644
--- a/sound/aoa/codecs/Kconfig
+++ b/sound/aoa/codecs/Kconfig
@@ -3,7 +3,7 @@ config SND_AOA_ONYX
tristate "support Onyx chip"
select I2C
select I2C_POWERMAC
- ---help---
+ help
This option enables support for the Onyx (pcm3052)
codec chip found in the latest Apple machines
(most of those with digital audio output).
@@ -12,14 +12,14 @@ config SND_AOA_TAS
tristate "support TAS chips"
select I2C
select I2C_POWERMAC
- ---help---
+ help
This option enables support for the tas chips
found in a lot of Apple Machines, especially
iBooks and PowerBooks without digital.
config SND_AOA_TOONIE
tristate "support Toonie chip"
- ---help---
+ help
This option enables support for the toonie codec
found in the Mac Mini. If you have a Mac Mini and
want to hear sound, select this option.
diff --git a/sound/aoa/fabrics/Kconfig b/sound/aoa/fabrics/Kconfig
index 1d8a718e9c56..0fa2da247ad2 100644
--- a/sound/aoa/fabrics/Kconfig
+++ b/sound/aoa/fabrics/Kconfig
@@ -3,7 +3,7 @@ config SND_AOA_FABRIC_LAYOUT
tristate "layout-id fabric"
select SND_AOA_SOUNDBUS
select SND_AOA_SOUNDBUS_I2S
- ---help---
+ help
This enables the layout-id fabric for the Apple Onboard
Audio driver, the module holding it all together
based on the device-tree's layout-id property.
diff --git a/sound/aoa/soundbus/Kconfig b/sound/aoa/soundbus/Kconfig
index ac084df0f97b..00189aac8014 100644
--- a/sound/aoa/soundbus/Kconfig
+++ b/sound/aoa/soundbus/Kconfig
@@ -2,7 +2,7 @@
config SND_AOA_SOUNDBUS
tristate "Apple Soundbus support"
select SND_PCM
- ---help---
+ help
This option enables the generic driver for the soundbus
support on Apple machines.
@@ -11,5 +11,5 @@ config SND_AOA_SOUNDBUS
config SND_AOA_SOUNDBUS_I2S
tristate "I2S bus support"
depends on SND_AOA_SOUNDBUS && PCI
- ---help---
+ help
This option enables support for Apple I2S busses.
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index 509290f2efa8..0e53f6f31916 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -764,6 +764,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;
@@ -921,6 +924,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/hwdep.c b/sound/core/hwdep.c
index b412d3b3d5ff..21edb8ac95eb 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 8c6bc5241df5..9fec3070f8ba 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -606,7 +606,9 @@ int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
{
int c;
- 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/memalloc.c b/sound/core/memalloc.c
index a83553fbedf0..bea46ed157a6 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -143,7 +143,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
break;
case SNDRV_DMA_TYPE_VMALLOC:
gfp = snd_mem_get_gfp_flags(device, GFP_KERNEL | __GFP_HIGHMEM);
- dmab->area = __vmalloc(size, gfp, PAGE_KERNEL);
+ dmab->area = __vmalloc(size, gfp);
dmab->addr = 0;
break;
#ifdef CONFIG_HAS_DMA
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index 930def8201f4..68630244b00f 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -2876,7 +2876,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
if (runtime->oss.params) {
/* use mutex_trylock() for params_lock for avoiding a deadlock
- * between mmap_sem and params_lock taken by
+ * between mmap_lock and params_lock taken by
* copy_from/to_user() in snd_pcm_oss_write/read()
*/
err = snd_pcm_oss_change_params(substream, true);
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
index 59d62f05658f..1545f8fdb4db 100644
--- a/sound/core/oss/pcm_plugin.c
+++ b/sound/core/oss/pcm_plugin.c
@@ -205,13 +205,14 @@ static snd_pcm_sframes_t calc_dst_frames(struct snd_pcm_substream *plug,
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)
return frames;
}
- if (check_size && frames > plugin->buf_frames)
- frames = plugin->buf_frames;
plugin = plugin_next;
}
return frames;
@@ -225,14 +226,15 @@ static snd_pcm_sframes_t calc_src_frames(struct snd_pcm_substream *plug,
plugin = snd_pcm_plug_last(plug);
while (plugin && frames > 0) {
- if (check_size && frames > plugin->buf_frames)
- frames = plugin->buf_frames;
plugin_prev = plugin->prev;
if (plugin->src_frames) {
frames = plugin->src_frames(plugin, frames);
if (frames < 0)
return frames;
}
+ if (check_size && plugin->buf_frames &&
+ frames > plugin->buf_frames)
+ frames = plugin->buf_frames;
plugin = plugin_prev;
}
return frames;
diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h
index 8d2f7a4e3ab6..46e273bd4a78 100644
--- a/sound/core/oss/pcm_plugin.h
+++ b/sound/core/oss/pcm_plugin.h
@@ -64,7 +64,7 @@ struct snd_pcm_plugin {
char *buf;
snd_pcm_uframes_t buf_frames;
struct snd_pcm_plugin_channel *buf_channels;
- char extra_data[0];
+ char extra_data[];
};
int snd_pcm_plugin_build(struct snd_pcm_substream *handle,
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 872a852de75c..d531e1bc2b81 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -433,6 +433,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;
}
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index fcab37ea6641..860935e3aea4 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -460,7 +460,7 @@ int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream,
return 0; /* already large enough */
vfree(runtime->dma_area);
}
- runtime->dma_area = __vmalloc(size, gfp_flags, PAGE_KERNEL);
+ runtime->dma_area = __vmalloc(size, gfp_flags);
if (!runtime->dma_area)
return -ENOMEM;
runtime->dma_bytes = size;
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index aef860256278..9630d2523948 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -138,6 +138,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
@@ -2166,6 +2176,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;
@@ -2194,7 +2210,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);
@@ -2210,7 +2226,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);
}
@@ -3093,7 +3109,8 @@ static int snd_pcm_xferi_frames_ioctl(struct snd_pcm_substream *substream,
result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
else
result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
- __put_user(result, &_xferi->result);
+ if (put_user(result, &_xferi->result))
+ return -EFAULT;
return result < 0 ? result : 0;
}
@@ -3122,7 +3139,8 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream,
else
result = snd_pcm_lib_readv(substream, bufs, xfern.frames);
kfree(bufs);
- __put_user(result, &_xfern->result);
+ if (put_user(result, &_xfern->result))
+ return -EFAULT;
return result < 0 ? result : 0;
}
@@ -3137,7 +3155,8 @@ static int snd_pcm_rewind_ioctl(struct snd_pcm_substream *substream,
if (put_user(0, _frames))
return -EFAULT;
result = snd_pcm_rewind(substream, frames);
- __put_user(result, _frames);
+ if (put_user(result, _frames))
+ return -EFAULT;
return result < 0 ? result : 0;
}
@@ -3152,7 +3171,8 @@ static int snd_pcm_forward_ioctl(struct snd_pcm_substream *substream,
if (put_user(0, _frames))
return -EFAULT;
result = snd_pcm_forward(substream, frames);
- __put_user(result, _frames);
+ if (put_user(result, _frames))
+ return -EFAULT;
return result < 0 ? result : 0;
}
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 20dd08e1f675..2a688b711a9a 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -120,6 +120,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;
@@ -669,6 +680,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;
@@ -1019,8 +1035,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)
@@ -1039,16 +1057,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,
@@ -1342,6 +1363,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)
@@ -1373,6 +1395,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_timer.h b/sound/core/seq/oss/seq_oss_timer.h
index 2d86125b5d0f..dee190b4ec6b 100644
--- a/sound/core/seq/oss/seq_oss_timer.h
+++ b/sound/core/seq/oss/seq_oss_timer.h
@@ -44,14 +44,4 @@ snd_seq_oss_timer_cur_tick(struct seq_oss_timer *timer)
return timer->cur_tick;
}
-
-/*
- * is realtime event?
- */
-static inline int
-snd_seq_oss_timer_is_realtime(struct seq_oss_timer *timer)
-{
- return timer->realtime;
-}
-
#endif
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
index feefdfc3bcca..c42217e2dd19 100644
--- a/sound/core/sgbuf.c
+++ b/sound/core/sgbuf.c
@@ -9,7 +9,6 @@
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/export.h>
-#include <asm/pgtable.h>
#include <sound/memalloc.h>
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 577c8e03ec4d..7141f73cddd3 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -186,18 +186,6 @@ config SND_PORTMAN2X4
To compile this driver as a module, choose M here: the module
will be called snd-portman2x4.
-config SND_ML403_AC97CR
- tristate "Xilinx ML403 AC97 Controller Reference"
- depends on XILINX_VIRTEX
- select SND_AC97_CODEC
- help
- Say Y here to include support for the
- opb_ac97_controller_ref_v1_00_a ip core found in Xilinx's ML403
- reference design.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-ml403_ac97cr.
-
config SND_AC97_POWER_SAVE
bool "AC97 Power-Saving Mode"
depends on SND_AC97_CODEC
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index 615558a281c8..c0fe4eccdaef 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -11,7 +11,6 @@ snd-mts64-objs := mts64.o
snd-portman2x4-objs := portman2x4.o
snd-serial-u16550-objs := serial-u16550.o
snd-virmidi-objs := virmidi.o
-snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
@@ -21,6 +20,5 @@ obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
obj-$(CONFIG_SND_MTS64) += snd-mts64.o
obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
-obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o
obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/
diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c
deleted file mode 100644
index 0710707da8c1..000000000000
--- a/sound/drivers/ml403-ac97cr.c
+++ /dev/null
@@ -1,1298 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * ALSA driver for Xilinx ML403 AC97 Controller Reference
- * IP: opb_ac97_controller_ref_v1_00_a (EDK 8.1i)
- * IP: opb_ac97_controller_ref_v1_00_a (EDK 9.1i)
- *
- * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de>
- */
-
-/* Some notes / status of this driver:
- *
- * - Don't wonder about some strange implementations of things - especially the
- * (heavy) shadowing of codec registers, with which I tried to reduce read
- * accesses to a minimum, because after a variable amount of accesses, the AC97
- * controller doesn't raise the register access finished bit anymore ...
- *
- * - Playback support seems to be pretty stable - no issues here.
- * - Capture support "works" now, too. Overruns don't happen any longer so often.
- * But there might still be some ...
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-
-#include <linux/platform_device.h>
-
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-
-/* HZ */
-#include <linux/param.h>
-/* jiffies, time_*() */
-#include <linux/jiffies.h>
-/* schedule_timeout*() */
-#include <linux/sched.h>
-/* spin_lock*() */
-#include <linux/spinlock.h>
-/* struct mutex, mutex_init(), mutex_*lock() */
-#include <linux/mutex.h>
-
-/* snd_printk(), snd_printd() */
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/ac97_codec.h>
-
-#include "pcm-indirect2.h"
-
-
-#define SND_ML403_AC97CR_DRIVER "ml403-ac97cr"
-
-MODULE_AUTHOR("Joachim Foerster <JOFT@gmx.de>");
-MODULE_DESCRIPTION("Xilinx ML403 AC97 Controller Reference");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Xilinx,ML403 AC97 Controller Reference}}");
-
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for ML403 AC97 Controller Reference.");
-module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string for ML403 AC97 Controller Reference.");
-module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "Enable this ML403 AC97 Controller Reference.");
-
-/* Special feature options */
-/*#define CODEC_WRITE_CHECK_RAF*/ /* don't return after a write to a codec
- * register, while RAF bit is not set
- */
-/* Debug options for code which may be removed completely in a final version */
-#ifdef CONFIG_SND_DEBUG
-/*#define CODEC_STAT*/ /* turn on some minimal "statistics"
- * about codec register usage
- */
-#define SND_PCM_INDIRECT2_STAT /* turn on some "statistics" about the
- * process of copying bytes from the
- * intermediate buffer to the hardware
- * fifo and the other way round
- */
-#endif
-
-/* Definition of a "level/facility dependent" printk(); may be removed
- * completely in a final version
- */
-#undef PDEBUG
-#ifdef CONFIG_SND_DEBUG
-/* "facilities" for PDEBUG */
-#define UNKNOWN (1<<0)
-#define CODEC_SUCCESS (1<<1)
-#define CODEC_FAKE (1<<2)
-#define INIT_INFO (1<<3)
-#define INIT_FAILURE (1<<4)
-#define WORK_INFO (1<<5)
-#define WORK_FAILURE (1<<6)
-
-#define PDEBUG_FACILITIES (UNKNOWN | INIT_FAILURE | WORK_FAILURE)
-
-#define PDEBUG(fac, fmt, args...) do { \
- if (fac & PDEBUG_FACILITIES) \
- snd_printd(KERN_DEBUG SND_ML403_AC97CR_DRIVER ": " \
- fmt, ##args); \
- } while (0)
-#else
-#define PDEBUG(fac, fmt, args...) /* nothing */
-#endif
-
-
-
-/* Defines for "waits"/timeouts (portions of HZ=250 on arch/ppc by default) */
-#define CODEC_TIMEOUT_ON_INIT 5 /* timeout for checking for codec
- * readiness (after insmod)
- */
-#ifndef CODEC_WRITE_CHECK_RAF
-#define CODEC_WAIT_AFTER_WRITE 100 /* general, static wait after a write
- * access to a codec register, may be
- * 0 to completely remove wait
- */
-#else
-#define CODEC_TIMEOUT_AFTER_WRITE 5 /* timeout after a write access to a
- * codec register, if RAF bit is used
- */
-#endif
-#define CODEC_TIMEOUT_AFTER_READ 5 /* timeout after a read access to a
- * codec register (checking RAF bit)
- */
-
-/* Infrastructure for codec register shadowing */
-#define LM4550_REG_OK (1<<0) /* register exists */
-#define LM4550_REG_DONEREAD (1<<1) /* read register once, value should be
- * the same currently in the register
- */
-#define LM4550_REG_NOSAVE (1<<2) /* values written to this register will
- * not be saved in the register
- */
-#define LM4550_REG_NOSHADOW (1<<3) /* don't do register shadowing, use plain
- * hardware access
- */
-#define LM4550_REG_READONLY (1<<4) /* register is read only */
-#define LM4550_REG_FAKEPROBE (1<<5) /* fake write _and_ read actions during
- * probe() correctly
- */
-#define LM4550_REG_FAKEREAD (1<<6) /* fake read access, always return
- * default value
- */
-#define LM4550_REG_ALLFAKE (LM4550_REG_FAKEREAD | LM4550_REG_FAKEPROBE)
-
-struct lm4550_reg {
- u16 value;
- u16 flag;
- u16 wmask;
- u16 def;
-};
-
-struct lm4550_reg lm4550_regfile[64] = {
- [AC97_RESET / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_NOSAVE \
- | LM4550_REG_FAKEREAD,
- .def = 0x0D50},
- [AC97_MASTER / 2] = {.flag = LM4550_REG_OK
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8000},
- [AC97_HEADPHONE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8000},
- [AC97_MASTER_MONO / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x801F,
- .def = 0x8000},
- [AC97_PC_BEEP / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x801E,
- .def = 0x0},
- [AC97_PHONE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x801F,
- .def = 0x8008},
- [AC97_MIC / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x805F,
- .def = 0x8008},
- [AC97_LINE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8808},
- [AC97_CD / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8808},
- [AC97_VIDEO / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8808},
- [AC97_AUX / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8808},
- [AC97_PCM / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8008},
- [AC97_REC_SEL / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x707,
- .def = 0x0},
- [AC97_REC_GAIN / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x8F0F,
- .def = 0x8000},
- [AC97_GENERAL_PURPOSE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .def = 0x0,
- .wmask = 0xA380},
- [AC97_3D_CONTROL / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEREAD \
- | LM4550_REG_READONLY,
- .def = 0x0101},
- [AC97_POWERDOWN / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_NOSHADOW \
- | LM4550_REG_NOSAVE,
- .wmask = 0xFF00},
- /* may not write ones to
- * REF/ANL/DAC/ADC bits
- * FIXME: Is this ok?
- */
- [AC97_EXTENDED_ID / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEREAD \
- | LM4550_REG_READONLY,
- .def = 0x0201}, /* primary codec */
- [AC97_EXTENDED_STATUS / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_NOSHADOW \
- | LM4550_REG_NOSAVE,
- .wmask = 0x1},
- [AC97_PCM_FRONT_DAC_RATE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .def = 0xBB80,
- .wmask = 0xFFFF},
- [AC97_PCM_LR_ADC_RATE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .def = 0xBB80,
- .wmask = 0xFFFF},
- [AC97_VENDOR_ID1 / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_READONLY \
- | LM4550_REG_FAKEREAD,
- .def = 0x4E53},
- [AC97_VENDOR_ID2 / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_READONLY \
- | LM4550_REG_FAKEREAD,
- .def = 0x4350}
-};
-
-#define LM4550_RF_OK(reg) (lm4550_regfile[reg / 2].flag & LM4550_REG_OK)
-
-static void lm4550_regfile_init(void)
-{
- int i;
- for (i = 0; i < 64; i++)
- if (lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE)
- lm4550_regfile[i].value = lm4550_regfile[i].def;
-}
-
-static void lm4550_regfile_write_values_after_init(struct snd_ac97 *ac97)
-{
- int i;
- for (i = 0; i < 64; i++)
- if ((lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE) &&
- (lm4550_regfile[i].value != lm4550_regfile[i].def)) {
- PDEBUG(CODEC_FAKE, "lm4550_regfile_write_values_after_"
- "init(): reg=0x%x value=0x%x / %d is different "
- "from def=0x%x / %d\n",
- i, lm4550_regfile[i].value,
- lm4550_regfile[i].value, lm4550_regfile[i].def,
- lm4550_regfile[i].def);
- snd_ac97_write(ac97, i * 2, lm4550_regfile[i].value);
- lm4550_regfile[i].flag |= LM4550_REG_DONEREAD;
- }
-}
-
-
-/* direct registers */
-#define CR_REG(ml403_ac97cr, x) ((ml403_ac97cr)->port + CR_REG_##x)
-
-#define CR_REG_PLAYFIFO 0x00
-#define CR_PLAYDATA(a) ((a) & 0xFFFF)
-
-#define CR_REG_RECFIFO 0x04
-#define CR_RECDATA(a) ((a) & 0xFFFF)
-
-#define CR_REG_STATUS 0x08
-#define CR_RECOVER (1<<7)
-#define CR_PLAYUNDER (1<<6)
-#define CR_CODECREADY (1<<5)
-#define CR_RAF (1<<4)
-#define CR_RECEMPTY (1<<3)
-#define CR_RECFULL (1<<2)
-#define CR_PLAYHALF (1<<1)
-#define CR_PLAYFULL (1<<0)
-
-#define CR_REG_RESETFIFO 0x0C
-#define CR_RECRESET (1<<1)
-#define CR_PLAYRESET (1<<0)
-
-#define CR_REG_CODEC_ADDR 0x10
-/* UG082 says:
- * #define CR_CODEC_ADDR(a) ((a) << 1)
- * #define CR_CODEC_READ (1<<0)
- * #define CR_CODEC_WRITE (0<<0)
- */
-/* RefDesign example says: */
-#define CR_CODEC_ADDR(a) ((a) << 0)
-#define CR_CODEC_READ (1<<7)
-#define CR_CODEC_WRITE (0<<7)
-
-#define CR_REG_CODEC_DATAREAD 0x14
-#define CR_CODEC_DATAREAD(v) ((v) & 0xFFFF)
-
-#define CR_REG_CODEC_DATAWRITE 0x18
-#define CR_CODEC_DATAWRITE(v) ((v) & 0xFFFF)
-
-#define CR_FIFO_SIZE 32
-
-struct snd_ml403_ac97cr {
- /* lock for access to (controller) registers */
- spinlock_t reg_lock;
- /* mutex for the whole sequence of accesses to (controller) registers
- * which affect codec registers
- */
- struct mutex cdc_mutex;
-
- int irq; /* for playback */
- int enable_irq; /* for playback */
-
- int capture_irq;
- int enable_capture_irq;
-
- struct resource *res_port;
- void *port;
-
- struct snd_ac97 *ac97;
- int ac97_fake;
-#ifdef CODEC_STAT
- int ac97_read;
- int ac97_write;
-#endif
-
- struct platform_device *pfdev;
- struct snd_card *card;
- struct snd_pcm *pcm;
- struct snd_pcm_substream *playback_substream;
- struct snd_pcm_substream *capture_substream;
-
- struct snd_pcm_indirect2 ind_rec; /* for playback */
- struct snd_pcm_indirect2 capture_ind2_rec;
-};
-
-static const struct snd_pcm_hardware snd_ml403_ac97cr_playback = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID),
- .formats = SNDRV_PCM_FMTBIT_S16_BE,
- .rates = (SNDRV_PCM_RATE_CONTINUOUS |
- SNDRV_PCM_RATE_8000_48000),
- .rate_min = 4000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = CR_FIFO_SIZE/2,
- .period_bytes_max = (64*1024),
- .periods_min = 2,
- .periods_max = (128*1024)/(CR_FIFO_SIZE/2),
- .fifo_size = 0,
-};
-
-static const struct snd_pcm_hardware snd_ml403_ac97cr_capture = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID),
- .formats = SNDRV_PCM_FMTBIT_S16_BE,
- .rates = (SNDRV_PCM_RATE_CONTINUOUS |
- SNDRV_PCM_RATE_8000_48000),
- .rate_min = 4000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = CR_FIFO_SIZE/2,
- .period_bytes_max = (64*1024),
- .periods_min = 2,
- .periods_max = (128*1024)/(CR_FIFO_SIZE/2),
- .fifo_size = 0,
-};
-
-static size_t
-snd_ml403_ac97cr_playback_ind2_zero(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int copied_words = 0;
- u32 full = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- spin_lock(&ml403_ac97cr->reg_lock);
- while ((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_PLAYFULL)) != CR_PLAYFULL) {
- out_be32(CR_REG(ml403_ac97cr, PLAYFIFO), 0);
- copied_words++;
- }
- rec->hw_ready = 0;
- spin_unlock(&ml403_ac97cr->reg_lock);
-
- return (size_t) (copied_words * 2);
-}
-
-static size_t
-snd_ml403_ac97cr_playback_ind2_copy(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- size_t bytes)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- u16 *src;
- int copied_words = 0;
- u32 full = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- src = (u16 *)(substream->runtime->dma_area + rec->sw_data);
-
- spin_lock(&ml403_ac97cr->reg_lock);
- while (((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_PLAYFULL)) != CR_PLAYFULL) && (bytes > 1)) {
- out_be32(CR_REG(ml403_ac97cr, PLAYFIFO),
- CR_PLAYDATA(src[copied_words]));
- copied_words++;
- bytes = bytes - 2;
- }
- if (full != CR_PLAYFULL)
- rec->hw_ready = 1;
- else
- rec->hw_ready = 0;
- spin_unlock(&ml403_ac97cr->reg_lock);
-
- return (size_t) (copied_words * 2);
-}
-
-static size_t
-snd_ml403_ac97cr_capture_ind2_null(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int copied_words = 0;
- u32 empty = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- spin_lock(&ml403_ac97cr->reg_lock);
- while ((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_RECEMPTY)) != CR_RECEMPTY) {
- volatile u32 trash;
-
- trash = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr, RECFIFO)));
- /* Hmmmm, really necessary? Don't want call to in_be32()
- * to be optimised away!
- */
- trash++;
- copied_words++;
- }
- rec->hw_ready = 0;
- spin_unlock(&ml403_ac97cr->reg_lock);
-
- return (size_t) (copied_words * 2);
-}
-
-static size_t
-snd_ml403_ac97cr_capture_ind2_copy(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec, size_t bytes)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- u16 *dst;
- int copied_words = 0;
- u32 empty = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- dst = (u16 *)(substream->runtime->dma_area + rec->sw_data);
-
- spin_lock(&ml403_ac97cr->reg_lock);
- while (((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_RECEMPTY)) != CR_RECEMPTY) && (bytes > 1)) {
- dst[copied_words] = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr,
- RECFIFO)));
- copied_words++;
- bytes = bytes - 2;
- }
- if (empty != CR_RECEMPTY)
- rec->hw_ready = 1;
- else
- rec->hw_ready = 0;
- spin_unlock(&ml403_ac97cr->reg_lock);
-
- return (size_t) (copied_words * 2);
-}
-
-static snd_pcm_uframes_t
-snd_ml403_ac97cr_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_indirect2 *ind2_rec = NULL;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- if (substream == ml403_ac97cr->playback_substream)
- ind2_rec = &ml403_ac97cr->ind_rec;
- if (substream == ml403_ac97cr->capture_substream)
- ind2_rec = &ml403_ac97cr->capture_ind2_rec;
-
- if (ind2_rec != NULL)
- return snd_pcm_indirect2_pointer(substream, ind2_rec);
- return (snd_pcm_uframes_t) 0;
-}
-
-static int
-snd_ml403_ac97cr_pcm_playback_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int err = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- PDEBUG(WORK_INFO, "trigger(playback): START\n");
- ml403_ac97cr->ind_rec.hw_ready = 1;
-
- /* clear play FIFO */
- out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_PLAYRESET);
-
- /* enable play irq */
- ml403_ac97cr->enable_irq = 1;
- enable_irq(ml403_ac97cr->irq);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- PDEBUG(WORK_INFO, "trigger(playback): STOP\n");
- ml403_ac97cr->ind_rec.hw_ready = 0;
-#ifdef SND_PCM_INDIRECT2_STAT
- snd_pcm_indirect2_stat(substream, &ml403_ac97cr->ind_rec);
-#endif
- /* disable play irq */
- disable_irq_nosync(ml403_ac97cr->irq);
- ml403_ac97cr->enable_irq = 0;
- break;
- default:
- err = -EINVAL;
- break;
- }
- PDEBUG(WORK_INFO, "trigger(playback): (done)\n");
- return err;
-}
-
-static int
-snd_ml403_ac97cr_pcm_capture_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int err = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- PDEBUG(WORK_INFO, "trigger(capture): START\n");
- ml403_ac97cr->capture_ind2_rec.hw_ready = 0;
-
- /* clear record FIFO */
- out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_RECRESET);
-
- /* enable record irq */
- ml403_ac97cr->enable_capture_irq = 1;
- enable_irq(ml403_ac97cr->capture_irq);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- PDEBUG(WORK_INFO, "trigger(capture): STOP\n");
- ml403_ac97cr->capture_ind2_rec.hw_ready = 0;
-#ifdef SND_PCM_INDIRECT2_STAT
- snd_pcm_indirect2_stat(substream,
- &ml403_ac97cr->capture_ind2_rec);
-#endif
- /* disable capture irq */
- disable_irq_nosync(ml403_ac97cr->capture_irq);
- ml403_ac97cr->enable_capture_irq = 0;
- break;
- default:
- err = -EINVAL;
- break;
- }
- PDEBUG(WORK_INFO, "trigger(capture): (done)\n");
- return err;
-}
-
-static int
-snd_ml403_ac97cr_pcm_playback_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_runtime *runtime;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- runtime = substream->runtime;
-
- PDEBUG(WORK_INFO,
- "prepare(): period_bytes=%d, minperiod_bytes=%d\n",
- snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2);
-
- /* set sampling rate */
- snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_FRONT_DAC_RATE,
- runtime->rate);
- PDEBUG(WORK_INFO, "prepare(): rate=%d\n", runtime->rate);
-
- /* init struct for intermediate buffer */
- memset(&ml403_ac97cr->ind_rec, 0,
- sizeof(struct snd_pcm_indirect2));
- ml403_ac97cr->ind_rec.hw_buffer_size = CR_FIFO_SIZE;
- ml403_ac97cr->ind_rec.sw_buffer_size =
- snd_pcm_lib_buffer_bytes(substream);
- ml403_ac97cr->ind_rec.min_periods = -1;
- ml403_ac97cr->ind_rec.min_multiple =
- snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2);
- PDEBUG(WORK_INFO, "prepare(): hw_buffer_size=%d, "
- "sw_buffer_size=%d, min_multiple=%d\n",
- CR_FIFO_SIZE, ml403_ac97cr->ind_rec.sw_buffer_size,
- ml403_ac97cr->ind_rec.min_multiple);
- return 0;
-}
-
-static int
-snd_ml403_ac97cr_pcm_capture_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_runtime *runtime;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- runtime = substream->runtime;
-
- PDEBUG(WORK_INFO,
- "prepare(capture): period_bytes=%d, minperiod_bytes=%d\n",
- snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2);
-
- /* set sampling rate */
- snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_LR_ADC_RATE,
- runtime->rate);
- PDEBUG(WORK_INFO, "prepare(capture): rate=%d\n", runtime->rate);
-
- /* init struct for intermediate buffer */
- memset(&ml403_ac97cr->capture_ind2_rec, 0,
- sizeof(struct snd_pcm_indirect2));
- ml403_ac97cr->capture_ind2_rec.hw_buffer_size = CR_FIFO_SIZE;
- ml403_ac97cr->capture_ind2_rec.sw_buffer_size =
- snd_pcm_lib_buffer_bytes(substream);
- ml403_ac97cr->capture_ind2_rec.min_multiple =
- snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2);
- PDEBUG(WORK_INFO, "prepare(capture): hw_buffer_size=%d, "
- "sw_buffer_size=%d, min_multiple=%d\n", CR_FIFO_SIZE,
- ml403_ac97cr->capture_ind2_rec.sw_buffer_size,
- ml403_ac97cr->capture_ind2_rec.min_multiple);
- return 0;
-}
-
-static int snd_ml403_ac97cr_playback_open(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_runtime *runtime;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- runtime = substream->runtime;
-
- PDEBUG(WORK_INFO, "open(playback)\n");
- ml403_ac97cr->playback_substream = substream;
- runtime->hw = snd_ml403_ac97cr_playback;
-
- snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- CR_FIFO_SIZE / 2);
- return 0;
-}
-
-static int snd_ml403_ac97cr_capture_open(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_runtime *runtime;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- runtime = substream->runtime;
-
- PDEBUG(WORK_INFO, "open(capture)\n");
- ml403_ac97cr->capture_substream = substream;
- runtime->hw = snd_ml403_ac97cr_capture;
-
- snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- CR_FIFO_SIZE / 2);
- return 0;
-}
-
-static int snd_ml403_ac97cr_playback_close(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- PDEBUG(WORK_INFO, "close(playback)\n");
- ml403_ac97cr->playback_substream = NULL;
- return 0;
-}
-
-static int snd_ml403_ac97cr_capture_close(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- PDEBUG(WORK_INFO, "close(capture)\n");
- ml403_ac97cr->capture_substream = NULL;
- return 0;
-}
-
-static const struct snd_pcm_ops snd_ml403_ac97cr_playback_ops = {
- .open = snd_ml403_ac97cr_playback_open,
- .close = snd_ml403_ac97cr_playback_close,
- .prepare = snd_ml403_ac97cr_pcm_playback_prepare,
- .trigger = snd_ml403_ac97cr_pcm_playback_trigger,
- .pointer = snd_ml403_ac97cr_pcm_pointer,
-};
-
-static const struct snd_pcm_ops snd_ml403_ac97cr_capture_ops = {
- .open = snd_ml403_ac97cr_capture_open,
- .close = snd_ml403_ac97cr_capture_close,
- .prepare = snd_ml403_ac97cr_pcm_capture_prepare,
- .trigger = snd_ml403_ac97cr_pcm_capture_trigger,
- .pointer = snd_ml403_ac97cr_pcm_pointer,
-};
-
-static irqreturn_t snd_ml403_ac97cr_irq(int irq, void *dev_id)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct platform_device *pfdev;
- int cmp_irq;
-
- ml403_ac97cr = (struct snd_ml403_ac97cr *)dev_id;
- if (ml403_ac97cr == NULL)
- return IRQ_NONE;
-
- pfdev = ml403_ac97cr->pfdev;
-
- /* playback interrupt */
- cmp_irq = platform_get_irq(pfdev, 0);
- if (irq == cmp_irq) {
- if (ml403_ac97cr->enable_irq)
- snd_pcm_indirect2_playback_interrupt(
- ml403_ac97cr->playback_substream,
- &ml403_ac97cr->ind_rec,
- snd_ml403_ac97cr_playback_ind2_copy,
- snd_ml403_ac97cr_playback_ind2_zero);
- else
- goto __disable_irq;
- } else {
- /* record interrupt */
- cmp_irq = platform_get_irq(pfdev, 1);
- if (irq == cmp_irq) {
- if (ml403_ac97cr->enable_capture_irq)
- snd_pcm_indirect2_capture_interrupt(
- ml403_ac97cr->capture_substream,
- &ml403_ac97cr->capture_ind2_rec,
- snd_ml403_ac97cr_capture_ind2_copy,
- snd_ml403_ac97cr_capture_ind2_null);
- else
- goto __disable_irq;
- } else
- return IRQ_NONE;
- }
- return IRQ_HANDLED;
-
-__disable_irq:
- PDEBUG(INIT_INFO, "irq(): irq %d is meant to be disabled! So, now try "
- "to disable it _really_!\n", irq);
- disable_irq_nosync(irq);
- return IRQ_HANDLED;
-}
-
-static unsigned short
-snd_ml403_ac97cr_codec_read(struct snd_ac97 *ac97, unsigned short reg)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
-#ifdef CODEC_STAT
- u32 stat;
- u32 rafaccess = 0;
-#endif
- unsigned long end_time;
- u16 value = 0;
-
- if (!LM4550_RF_OK(reg)) {
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "access to unknown/unused codec register 0x%x "
- "ignored!\n", reg);
- return 0;
- }
- /* check if we can fake/answer this access from our shadow register */
- if ((lm4550_regfile[reg / 2].flag &
- (LM4550_REG_DONEREAD | LM4550_REG_ALLFAKE)) &&
- !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) {
- if (lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEREAD) {
- PDEBUG(CODEC_FAKE, "codec_read(): faking read from "
- "reg=0x%x, val=0x%x / %d\n",
- reg, lm4550_regfile[reg / 2].def,
- lm4550_regfile[reg / 2].def);
- return lm4550_regfile[reg / 2].def;
- } else if ((lm4550_regfile[reg / 2].flag &
- LM4550_REG_FAKEPROBE) &&
- ml403_ac97cr->ac97_fake) {
- PDEBUG(CODEC_FAKE, "codec_read(): faking read from "
- "reg=0x%x, val=0x%x / %d (probe)\n",
- reg, lm4550_regfile[reg / 2].value,
- lm4550_regfile[reg / 2].value);
- return lm4550_regfile[reg / 2].value;
- } else {
-#ifdef CODEC_STAT
- PDEBUG(CODEC_FAKE, "codec_read(): read access "
- "answered by shadow register 0x%x (value=0x%x "
- "/ %d) (cw=%d cr=%d)\n",
- reg, lm4550_regfile[reg / 2].value,
- lm4550_regfile[reg / 2].value,
- ml403_ac97cr->ac97_write,
- ml403_ac97cr->ac97_read);
-#else
- PDEBUG(CODEC_FAKE, "codec_read(): read access "
- "answered by shadow register 0x%x (value=0x%x "
- "/ %d)\n",
- reg, lm4550_regfile[reg / 2].value,
- lm4550_regfile[reg / 2].value);
-#endif
- return lm4550_regfile[reg / 2].value;
- }
- }
- /* if we are here, we _have_ to access the codec really, no faking */
- if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0)
- return 0;
-#ifdef CODEC_STAT
- ml403_ac97cr->ac97_read++;
-#endif
- spin_lock(&ml403_ac97cr->reg_lock);
- out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR),
- CR_CODEC_ADDR(reg) | CR_CODEC_READ);
- spin_unlock(&ml403_ac97cr->reg_lock);
- end_time = jiffies + (HZ / CODEC_TIMEOUT_AFTER_READ);
- do {
- spin_lock(&ml403_ac97cr->reg_lock);
-#ifdef CODEC_STAT
- rafaccess++;
- stat = in_be32(CR_REG(ml403_ac97cr, STATUS));
- if ((stat & CR_RAF) == CR_RAF) {
- value = CR_CODEC_DATAREAD(
- in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
- PDEBUG(CODEC_SUCCESS, "codec_read(): (done) reg=0x%x, "
- "value=0x%x / %d (STATUS=0x%x)\n",
- reg, value, value, stat);
-#else
- if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_RAF) == CR_RAF) {
- value = CR_CODEC_DATAREAD(
- in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
- PDEBUG(CODEC_SUCCESS, "codec_read(): (done) "
- "reg=0x%x, value=0x%x / %d\n",
- reg, value, value);
-#endif
- lm4550_regfile[reg / 2].value = value;
- lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
- spin_unlock(&ml403_ac97cr->reg_lock);
- mutex_unlock(&ml403_ac97cr->cdc_mutex);
- return value;
- }
- spin_unlock(&ml403_ac97cr->reg_lock);
- schedule_timeout_uninterruptible(1);
- } while (time_after(end_time, jiffies));
- /* read the DATAREAD register anyway, see comment below */
- spin_lock(&ml403_ac97cr->reg_lock);
- value =
- CR_CODEC_DATAREAD(in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
- spin_unlock(&ml403_ac97cr->reg_lock);
-#ifdef CODEC_STAT
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "timeout while codec read! "
- "(reg=0x%x, last STATUS=0x%x, DATAREAD=0x%x / %d, %d) "
- "(cw=%d, cr=%d)\n",
- reg, stat, value, value, rafaccess,
- ml403_ac97cr->ac97_write, ml403_ac97cr->ac97_read);
-#else
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "timeout while codec read! "
- "(reg=0x%x, DATAREAD=0x%x / %d)\n",
- reg, value, value);
-#endif
- /* BUG: This is PURE speculation! But after _most_ read timeouts the
- * value in the register is ok!
- */
- lm4550_regfile[reg / 2].value = value;
- lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
- mutex_unlock(&ml403_ac97cr->cdc_mutex);
- return value;
-}
-
-static void
-snd_ml403_ac97cr_codec_write(struct snd_ac97 *ac97, unsigned short reg,
- unsigned short val)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
-
-#ifdef CODEC_STAT
- u32 stat;
- u32 rafaccess = 0;
-#endif
-#ifdef CODEC_WRITE_CHECK_RAF
- unsigned long end_time;
-#endif
-
- if (!LM4550_RF_OK(reg)) {
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "access to unknown/unused codec register 0x%x "
- "ignored!\n", reg);
- return;
- }
- if (lm4550_regfile[reg / 2].flag & LM4550_REG_READONLY) {
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "write access to read only codec register 0x%x "
- "ignored!\n", reg);
- return;
- }
- if ((val & lm4550_regfile[reg / 2].wmask) != val) {
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "write access to codec register 0x%x "
- "with bad value 0x%x / %d!\n",
- reg, val, val);
- val = val & lm4550_regfile[reg / 2].wmask;
- }
- if (((lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEPROBE) &&
- ml403_ac97cr->ac97_fake) &&
- !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) {
- PDEBUG(CODEC_FAKE, "codec_write(): faking write to reg=0x%x, "
- "val=0x%x / %d\n", reg, val, val);
- lm4550_regfile[reg / 2].value = (val &
- lm4550_regfile[reg / 2].wmask);
- return;
- }
- if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0)
- return;
-#ifdef CODEC_STAT
- ml403_ac97cr->ac97_write++;
-#endif
- spin_lock(&ml403_ac97cr->reg_lock);
- out_be32(CR_REG(ml403_ac97cr, CODEC_DATAWRITE),
- CR_CODEC_DATAWRITE(val));
- out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR),
- CR_CODEC_ADDR(reg) | CR_CODEC_WRITE);
- spin_unlock(&ml403_ac97cr->reg_lock);
-#ifdef CODEC_WRITE_CHECK_RAF
- /* check CR_CODEC_RAF bit to see if write access to register is done;
- * loop until bit is set or timeout happens
- */
- end_time = jiffies + HZ / CODEC_TIMEOUT_AFTER_WRITE;
- do {
- spin_lock(&ml403_ac97cr->reg_lock);
-#ifdef CODEC_STAT
- rafaccess++;
- stat = in_be32(CR_REG(ml403_ac97cr, STATUS))
- if ((stat & CR_RAF) == CR_RAF) {
-#else
- if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_RAF) == CR_RAF) {
-#endif
- PDEBUG(CODEC_SUCCESS, "codec_write(): (done) "
- "reg=0x%x, value=%d / 0x%x\n",
- reg, val, val);
- if (!(lm4550_regfile[reg / 2].flag &
- LM4550_REG_NOSHADOW) &&
- !(lm4550_regfile[reg / 2].flag &
- LM4550_REG_NOSAVE))
- lm4550_regfile[reg / 2].value = val;
- lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
- spin_unlock(&ml403_ac97cr->reg_lock);
- mutex_unlock(&ml403_ac97cr->cdc_mutex);
- return;
- }
- spin_unlock(&ml403_ac97cr->reg_lock);
- schedule_timeout_uninterruptible(1);
- } while (time_after(end_time, jiffies));
-#ifdef CODEC_STAT
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "timeout while codec write "
- "(reg=0x%x, val=0x%x / %d, last STATUS=0x%x, %d) "
- "(cw=%d, cr=%d)\n",
- reg, val, val, stat, rafaccess, ml403_ac97cr->ac97_write,
- ml403_ac97cr->ac97_read);
-#else
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "timeout while codec write (reg=0x%x, val=0x%x / %d)\n",
- reg, val, val);
-#endif
-#else /* CODEC_WRITE_CHECK_RAF */
-#if CODEC_WAIT_AFTER_WRITE > 0
- /* officially, in AC97 spec there is no possibility for a AC97
- * controller to determine, if write access is done or not - so: How
- * is Xilinx able to provide a RAF bit for write access?
- * => very strange, thus just don't check RAF bit (compare with
- * Xilinx's example app in EDK 8.1i) and wait
- */
- schedule_timeout_uninterruptible(HZ / CODEC_WAIT_AFTER_WRITE);
-#endif
- PDEBUG(CODEC_SUCCESS, "codec_write(): (done) "
- "reg=0x%x, value=%d / 0x%x (no RAF check)\n",
- reg, val, val);
-#endif
- mutex_unlock(&ml403_ac97cr->cdc_mutex);
- return;
-}
-
-static int
-snd_ml403_ac97cr_chip_init(struct snd_ml403_ac97cr *ml403_ac97cr)
-{
- unsigned long end_time;
- PDEBUG(INIT_INFO, "chip_init():\n");
- end_time = jiffies + HZ / CODEC_TIMEOUT_ON_INIT;
- do {
- if (in_be32(CR_REG(ml403_ac97cr, STATUS)) & CR_CODECREADY) {
- /* clear both hardware FIFOs */
- out_be32(CR_REG(ml403_ac97cr, RESETFIFO),
- CR_RECRESET | CR_PLAYRESET);
- PDEBUG(INIT_INFO, "chip_init(): (done)\n");
- return 0;
- }
- schedule_timeout_uninterruptible(1);
- } while (time_after(end_time, jiffies));
- snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
- "timeout while waiting for codec, "
- "not ready!\n");
- return -EBUSY;
-}
-
-static int snd_ml403_ac97cr_free(struct snd_ml403_ac97cr *ml403_ac97cr)
-{
- PDEBUG(INIT_INFO, "free():\n");
- /* irq release */
- if (ml403_ac97cr->irq >= 0)
- free_irq(ml403_ac97cr->irq, ml403_ac97cr);
- if (ml403_ac97cr->capture_irq >= 0)
- free_irq(ml403_ac97cr->capture_irq, ml403_ac97cr);
- /* give back "port" */
- iounmap(ml403_ac97cr->port);
- kfree(ml403_ac97cr);
- PDEBUG(INIT_INFO, "free(): (done)\n");
- return 0;
-}
-
-static int snd_ml403_ac97cr_dev_free(struct snd_device *snddev)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr = snddev->device_data;
- PDEBUG(INIT_INFO, "dev_free():\n");
- return snd_ml403_ac97cr_free(ml403_ac97cr);
-}
-
-static int
-snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev,
- struct snd_ml403_ac97cr **rml403_ac97cr)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int err;
- static const struct snd_device_ops ops = {
- .dev_free = snd_ml403_ac97cr_dev_free,
- };
- struct resource *resource;
- int irq;
-
- *rml403_ac97cr = NULL;
- ml403_ac97cr = kzalloc(sizeof(*ml403_ac97cr), GFP_KERNEL);
- if (ml403_ac97cr == NULL)
- return -ENOMEM;
- spin_lock_init(&ml403_ac97cr->reg_lock);
- mutex_init(&ml403_ac97cr->cdc_mutex);
- ml403_ac97cr->card = card;
- ml403_ac97cr->pfdev = pfdev;
- ml403_ac97cr->irq = -1;
- ml403_ac97cr->enable_irq = 0;
- ml403_ac97cr->capture_irq = -1;
- ml403_ac97cr->enable_capture_irq = 0;
- ml403_ac97cr->port = NULL;
- ml403_ac97cr->res_port = NULL;
-
- PDEBUG(INIT_INFO, "Trying to reserve resources now ...\n");
- resource = platform_get_resource(pfdev, IORESOURCE_MEM, 0);
- /* get "port" */
- ml403_ac97cr->port = ioremap(resource->start,
- (resource->end) -
- (resource->start) + 1);
- if (ml403_ac97cr->port == NULL) {
- snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
- "unable to remap memory region (%pR)\n",
- resource);
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return -EBUSY;
- }
- snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
- "remap controller memory region to "
- "0x%x done\n", (unsigned int)ml403_ac97cr->port);
- /* get irq */
- irq = platform_get_irq(pfdev, 0);
- if (request_irq(irq, snd_ml403_ac97cr_irq, 0,
- dev_name(&pfdev->dev), (void *)ml403_ac97cr)) {
- snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
- "unable to grab IRQ %d\n",
- irq);
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return -EBUSY;
- }
- ml403_ac97cr->irq = irq;
- snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
- "request (playback) irq %d done\n",
- ml403_ac97cr->irq);
- irq = platform_get_irq(pfdev, 1);
- if (request_irq(irq, snd_ml403_ac97cr_irq, 0,
- dev_name(&pfdev->dev), (void *)ml403_ac97cr)) {
- snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
- "unable to grab IRQ %d\n",
- irq);
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return -EBUSY;
- }
- ml403_ac97cr->capture_irq = irq;
- snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
- "request (capture) irq %d done\n",
- ml403_ac97cr->capture_irq);
-
- err = snd_ml403_ac97cr_chip_init(ml403_ac97cr);
- if (err < 0) {
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return err;
- }
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ml403_ac97cr, &ops);
- if (err < 0) {
- PDEBUG(INIT_FAILURE, "probe(): snd_device_new() failed!\n");
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return err;
- }
-
- *rml403_ac97cr = ml403_ac97cr;
- return 0;
-}
-
-static void snd_ml403_ac97cr_mixer_free(struct snd_ac97 *ac97)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
- PDEBUG(INIT_INFO, "mixer_free():\n");
- ml403_ac97cr->ac97 = NULL;
- PDEBUG(INIT_INFO, "mixer_free(): (done)\n");
-}
-
-static int
-snd_ml403_ac97cr_mixer(struct snd_ml403_ac97cr *ml403_ac97cr)
-{
- struct snd_ac97_bus *bus;
- struct snd_ac97_template ac97;
- int err;
- static const struct snd_ac97_bus_ops ops = {
- .write = snd_ml403_ac97cr_codec_write,
- .read = snd_ml403_ac97cr_codec_read,
- };
- PDEBUG(INIT_INFO, "mixer():\n");
- err = snd_ac97_bus(ml403_ac97cr->card, 0, &ops, NULL, &bus);
- if (err < 0)
- return err;
-
- memset(&ac97, 0, sizeof(ac97));
- ml403_ac97cr->ac97_fake = 1;
- lm4550_regfile_init();
-#ifdef CODEC_STAT
- ml403_ac97cr->ac97_read = 0;
- ml403_ac97cr->ac97_write = 0;
-#endif
- ac97.private_data = ml403_ac97cr;
- ac97.private_free = snd_ml403_ac97cr_mixer_free;
- ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM |
- AC97_SCAP_NO_SPDIF;
- err = snd_ac97_mixer(bus, &ac97, &ml403_ac97cr->ac97);
- ml403_ac97cr->ac97_fake = 0;
- lm4550_regfile_write_values_after_init(ml403_ac97cr->ac97);
- PDEBUG(INIT_INFO, "mixer(): (done) snd_ac97_mixer()=%d\n", err);
- return err;
-}
-
-static int
-snd_ml403_ac97cr_pcm(struct snd_ml403_ac97cr *ml403_ac97cr, int device)
-{
- struct snd_pcm *pcm;
- int err;
-
- err = snd_pcm_new(ml403_ac97cr->card, "ML403AC97CR/1", device, 1, 1,
- &pcm);
- if (err < 0)
- return err;
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &snd_ml403_ac97cr_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_ml403_ac97cr_capture_ops);
- pcm->private_data = ml403_ac97cr;
- pcm->info_flags = 0;
- strcpy(pcm->name, "ML403AC97CR DAC/ADC");
- ml403_ac97cr->pcm = pcm;
-
- snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- NULL,
- 64 * 1024,
- 128 * 1024);
- return 0;
-}
-
-static int snd_ml403_ac97cr_probe(struct platform_device *pfdev)
-{
- struct snd_card *card;
- struct snd_ml403_ac97cr *ml403_ac97cr = NULL;
- int err;
- int dev = pfdev->id;
-
- if (dev >= SNDRV_CARDS)
- return -ENODEV;
- if (!enable[dev])
- return -ENOENT;
-
- err = snd_card_new(&pfdev->dev, index[dev], id[dev], THIS_MODULE,
- 0, &card);
- if (err < 0)
- return err;
- err = snd_ml403_ac97cr_create(card, pfdev, &ml403_ac97cr);
- if (err < 0) {
- PDEBUG(INIT_FAILURE, "probe(): create failed!\n");
- snd_card_free(card);
- return err;
- }
- PDEBUG(INIT_INFO, "probe(): create done\n");
- card->private_data = ml403_ac97cr;
- err = snd_ml403_ac97cr_mixer(ml403_ac97cr);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
- PDEBUG(INIT_INFO, "probe(): mixer done\n");
- err = snd_ml403_ac97cr_pcm(ml403_ac97cr, 0);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
- PDEBUG(INIT_INFO, "probe(): PCM done\n");
- strcpy(card->driver, SND_ML403_AC97CR_DRIVER);
- strcpy(card->shortname, "ML403 AC97 Controller Reference");
- sprintf(card->longname, "%s %s at 0x%lx, irq %i & %i, device %i",
- card->shortname, card->driver,
- (unsigned long)ml403_ac97cr->port, ml403_ac97cr->irq,
- ml403_ac97cr->capture_irq, dev + 1);
-
- err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
- platform_set_drvdata(pfdev, card);
- PDEBUG(INIT_INFO, "probe(): (done)\n");
- return 0;
-}
-
-static int snd_ml403_ac97cr_remove(struct platform_device *pfdev)
-{
- snd_card_free(platform_get_drvdata(pfdev));
- return 0;
-}
-
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:" SND_ML403_AC97CR_DRIVER);
-
-static struct platform_driver snd_ml403_ac97cr_driver = {
- .probe = snd_ml403_ac97cr_probe,
- .remove = snd_ml403_ac97cr_remove,
- .driver = {
- .name = SND_ML403_AC97CR_DRIVER,
- },
-};
-
-module_platform_driver(snd_ml403_ac97cr_driver);
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/drivers/pcm-indirect2.c b/sound/drivers/pcm-indirect2.c
deleted file mode 100644
index 4c491d0ff071..000000000000
--- a/sound/drivers/pcm-indirect2.c
+++ /dev/null
@@ -1,560 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Helper functions for indirect PCM data transfer to a simple FIFO in
- * hardware (small, no possibility to read "hardware io position",
- * updating position done by interrupt, ...)
- *
- * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de>
- *
- * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by
- *
- * Copyright (c) by Takashi Iwai <tiwai@suse.de>
- * Jaroslav Kysela <perex@suse.cz>
- */
-
-/* snd_printk/d() */
-#include <sound/core.h>
-/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t
- * snd_pcm_period_elapsed() */
-#include <sound/pcm.h>
-
-#include "pcm-indirect2.h"
-
-#ifdef SND_PCM_INDIRECT2_STAT
-/* jiffies */
-#include <linux/jiffies.h>
-
-void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int i;
- int j;
- int k;
- int seconds = (rec->lastbytetime - rec->firstbytetime) / HZ;
-
- snd_printk(KERN_DEBUG "STAT: mul_elapsed: %u, mul_elapsed_real: %d, "
- "irq_occurred: %d\n",
- rec->mul_elapsed, rec->mul_elapsed_real, rec->irq_occured);
- snd_printk(KERN_DEBUG "STAT: min_multiple: %d (irqs/period)\n",
- rec->min_multiple);
- snd_printk(KERN_DEBUG "STAT: firstbytetime: %lu, lastbytetime: %lu, "
- "firstzerotime: %lu\n",
- rec->firstbytetime, rec->lastbytetime, rec->firstzerotime);
- snd_printk(KERN_DEBUG "STAT: bytes2hw: %u Bytes => (by runtime->rate) "
- "length: %d s\n",
- rec->bytes2hw, rec->bytes2hw / 2 / 2 / runtime->rate);
- snd_printk(KERN_DEBUG "STAT: (by measurement) length: %d => "
- "rate: %d Bytes/s = %d Frames/s|Hz\n",
- seconds, rec->bytes2hw / seconds,
- rec->bytes2hw / 2 / 2 / seconds);
- snd_printk(KERN_DEBUG
- "STAT: zeros2hw: %u = %d ms ~ %d * %d zero copies\n",
- rec->zeros2hw, ((rec->zeros2hw / 2 / 2) * 1000) /
- runtime->rate,
- rec->zeros2hw / (rec->hw_buffer_size / 2),
- (rec->hw_buffer_size / 2));
- snd_printk(KERN_DEBUG "STAT: pointer_calls: %u, lastdifftime: %u\n",
- rec->pointer_calls, rec->lastdifftime);
- snd_printk(KERN_DEBUG "STAT: sw_io: %d, sw_data: %d\n", rec->sw_io,
- rec->sw_data);
- snd_printk(KERN_DEBUG "STAT: byte_sizes[]:\n");
- k = 0;
- for (j = 0; j < 8; j++) {
- for (i = j * 8; i < (j + 1) * 8; i++)
- if (rec->byte_sizes[i] != 0) {
- snd_printk(KERN_DEBUG "%u: %u",
- i, rec->byte_sizes[i]);
- k++;
- }
- if (((k % 8) == 0) && (k != 0)) {
- snd_printk(KERN_DEBUG "\n");
- k = 0;
- }
- }
- snd_printk(KERN_DEBUG "\n");
- snd_printk(KERN_DEBUG "STAT: zero_sizes[]:\n");
- for (j = 0; j < 8; j++) {
- k = 0;
- for (i = j * 8; i < (j + 1) * 8; i++)
- if (rec->zero_sizes[i] != 0)
- snd_printk(KERN_DEBUG "%u: %u",
- i, rec->zero_sizes[i]);
- else
- k++;
- if (!k)
- snd_printk(KERN_DEBUG "\n");
- }
- snd_printk(KERN_DEBUG "\n");
- snd_printk(KERN_DEBUG "STAT: min_adds[]:\n");
- for (j = 0; j < 8; j++) {
- if (rec->min_adds[j] != 0)
- snd_printk(KERN_DEBUG "%u: %u", j, rec->min_adds[j]);
- }
- snd_printk(KERN_DEBUG "\n");
- snd_printk(KERN_DEBUG "STAT: mul_adds[]:\n");
- for (j = 0; j < 8; j++) {
- if (rec->mul_adds[j] != 0)
- snd_printk(KERN_DEBUG "%u: %u", j, rec->mul_adds[j]);
- }
- snd_printk(KERN_DEBUG "\n");
- snd_printk(KERN_DEBUG
- "STAT: zero_times_saved: %d, zero_times_notsaved: %d\n",
- rec->zero_times_saved, rec->zero_times_notsaved);
- /* snd_printk(KERN_DEBUG "STAT: zero_times[]\n");
- i = 0;
- for (j = 0; j < 3750; j++) {
- if (rec->zero_times[j] != 0) {
- snd_printk(KERN_DEBUG "%u: %u", j, rec->zero_times[j]);
- i++;
- }
- if (((i % 8) == 0) && (i != 0))
- snd_printk(KERN_DEBUG "\n");
- }
- snd_printk(KERN_DEBUG "\n"); */
- return;
-}
-#endif
-
-/*
- * _internal_ helper function for playback/capture transfer function
- */
-static void
-snd_pcm_indirect2_increase_min_periods(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- int isplay, int iscopy,
- unsigned int bytes)
-{
- if (rec->min_periods >= 0) {
- if (iscopy) {
- rec->sw_io += bytes;
- if (rec->sw_io >= rec->sw_buffer_size)
- rec->sw_io -= rec->sw_buffer_size;
- } else if (isplay) {
- /* If application does not write data in multiples of
- * a period, move sw_data to the next correctly aligned
- * position, so that sw_io can converge to it (in the
- * next step).
- */
- if (!rec->check_alignment) {
- if (rec->bytes2hw %
- snd_pcm_lib_period_bytes(substream)) {
- unsigned bytes2hw_aligned =
- (1 +
- (rec->bytes2hw /
- snd_pcm_lib_period_bytes
- (substream))) *
- snd_pcm_lib_period_bytes
- (substream);
- rec->sw_data =
- bytes2hw_aligned %
- rec->sw_buffer_size;
-#ifdef SND_PCM_INDIRECT2_STAT
- snd_printk(KERN_DEBUG
- "STAT: @re-align: aligned "
- "bytes2hw to next period "
- "size boundary: %d "
- "(instead of %d)\n",
- bytes2hw_aligned,
- rec->bytes2hw);
- snd_printk(KERN_DEBUG
- "STAT: @re-align: sw_data "
- "moves to: %d\n",
- rec->sw_data);
-#endif
- }
- rec->check_alignment = 1;
- }
- /* We are at the end and are copying zeros into the
- * fifo.
- * Now, we have to make sure that sw_io is increased
- * until the position of sw_data: Filling the fifo with
- * the first zeros means, the last bytes were played.
- */
- if (rec->sw_io != rec->sw_data) {
- unsigned int diff;
- if (rec->sw_data > rec->sw_io)
- diff = rec->sw_data - rec->sw_io;
- else
- diff = (rec->sw_buffer_size -
- rec->sw_io) +
- rec->sw_data;
- if (bytes >= diff)
- rec->sw_io = rec->sw_data;
- else {
- rec->sw_io += bytes;
- if (rec->sw_io >= rec->sw_buffer_size)
- rec->sw_io -=
- rec->sw_buffer_size;
- }
- }
- }
- rec->min_period_count += bytes;
- if (rec->min_period_count >= (rec->hw_buffer_size / 2)) {
- rec->min_periods += (rec->min_period_count /
- (rec->hw_buffer_size / 2));
-#ifdef SND_PCM_INDIRECT2_STAT
- if ((rec->min_period_count /
- (rec->hw_buffer_size / 2)) > 7)
- snd_printk(KERN_DEBUG
- "STAT: more than 7 (%d) min_adds "
- "at once - too big to save!\n",
- (rec->min_period_count /
- (rec->hw_buffer_size / 2)));
- else
- rec->min_adds[(rec->min_period_count /
- (rec->hw_buffer_size / 2))]++;
-#endif
- rec->min_period_count = (rec->min_period_count %
- (rec->hw_buffer_size / 2));
- }
- } else if (isplay && iscopy)
- rec->min_periods = 0;
-}
-
-/*
- * helper function for playback/capture pointer callback
- */
-snd_pcm_uframes_t
-snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec)
-{
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->pointer_calls++;
-#endif
- return bytes_to_frames(substream->runtime, rec->sw_io);
-}
-
-/*
- * _internal_ helper function for playback interrupt callback
- */
-static void
-snd_pcm_indirect2_playback_transfer(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t zero)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
-
- /* runtime->control->appl_ptr: position where ALSA will write next time
- * rec->appl_ptr: position where ALSA was last time
- * diff: obviously ALSA wrote that much bytes into the intermediate
- * buffer since we checked last time
- */
- snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
-
- if (diff) {
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->lastdifftime = jiffies;
-#endif
- if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
- diff += runtime->boundary;
- /* number of bytes "added" by ALSA increases the number of
- * bytes which are ready to "be transferred to HW"/"played"
- * Then, set rec->appl_ptr to not count bytes twice next time.
- */
- rec->sw_ready += (int)frames_to_bytes(runtime, diff);
- rec->appl_ptr = appl_ptr;
- }
- if (rec->hw_ready && (rec->sw_ready <= 0)) {
- unsigned int bytes;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (rec->firstzerotime == 0) {
- rec->firstzerotime = jiffies;
- snd_printk(KERN_DEBUG
- "STAT: @firstzerotime: mul_elapsed: %d, "
- "min_period_count: %d\n",
- rec->mul_elapsed, rec->min_period_count);
- snd_printk(KERN_DEBUG
- "STAT: @firstzerotime: sw_io: %d, "
- "sw_data: %d, appl_ptr: %u\n",
- rec->sw_io, rec->sw_data,
- (unsigned int)appl_ptr);
- }
- if ((jiffies - rec->firstzerotime) < 3750) {
- rec->zero_times[(jiffies - rec->firstzerotime)]++;
- rec->zero_times_saved++;
- } else
- rec->zero_times_notsaved++;
-#endif
- bytes = zero(substream, rec);
-
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->zeros2hw += bytes;
- if (bytes < 64)
- rec->zero_sizes[bytes]++;
- else
- snd_printk(KERN_DEBUG
- "STAT: %d zero Bytes copied to hardware at "
- "once - too big to save!\n",
- bytes);
-#endif
- snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 0,
- bytes);
- return;
- }
- while (rec->hw_ready && (rec->sw_ready > 0)) {
- /* sw_to_end: max. number of bytes that can be read/take from
- * the current position (sw_data) in _one_ step
- */
- unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
-
- /* bytes: number of bytes we have available (for reading) */
- unsigned int bytes = rec->sw_ready;
-
- if (sw_to_end < bytes)
- bytes = sw_to_end;
- if (!bytes)
- break;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (rec->firstbytetime == 0)
- rec->firstbytetime = jiffies;
- rec->lastbytetime = jiffies;
-#endif
- /* copy bytes from intermediate buffer position sw_data to the
- * HW and return number of bytes actually written
- * Furthermore, set hw_ready to 0, if the fifo isn't empty
- * now => more could be transferred to fifo
- */
- bytes = copy(substream, rec, bytes);
- rec->bytes2hw += bytes;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (bytes < 64)
- rec->byte_sizes[bytes]++;
- else
- snd_printk(KERN_DEBUG
- "STAT: %d Bytes copied to hardware at once "
- "- too big to save!\n",
- bytes);
-#endif
- /* increase sw_data by the number of actually written bytes
- * (= number of taken bytes from intermediate buffer)
- */
- rec->sw_data += bytes;
- if (rec->sw_data == rec->sw_buffer_size)
- rec->sw_data = 0;
- /* now sw_data is the position where ALSA is going to write
- * in the intermediate buffer next time = position we are going
- * to read from next time
- */
-
- snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 1,
- bytes);
-
- /* we read bytes from intermediate buffer, so we need to say
- * that the number of bytes ready for transfer are decreased
- * now
- */
- rec->sw_ready -= bytes;
- }
- return;
-}
-
-/*
- * helper function for playback interrupt routine
- */
-void
-snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t zero)
-{
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->irq_occured++;
-#endif
- /* hardware played some bytes, so there is room again (in fifo) */
- rec->hw_ready = 1;
-
- /* don't call ack() now, instead call transfer() function directly
- * (normally called by ack() )
- */
- snd_pcm_indirect2_playback_transfer(substream, rec, copy, zero);
-
- if (rec->min_periods >= rec->min_multiple) {
-#ifdef SND_PCM_INDIRECT2_STAT
- if ((rec->min_periods / rec->min_multiple) > 7)
- snd_printk(KERN_DEBUG
- "STAT: more than 7 (%d) mul_adds - too big "
- "to save!\n",
- (rec->min_periods / rec->min_multiple));
- else
- rec->mul_adds[(rec->min_periods /
- rec->min_multiple)]++;
- rec->mul_elapsed_real += (rec->min_periods /
- rec->min_multiple);
- rec->mul_elapsed++;
-#endif
- rec->min_periods = (rec->min_periods % rec->min_multiple);
- snd_pcm_period_elapsed(substream);
- }
-}
-
-/*
- * _internal_ helper function for capture interrupt callback
- */
-static void
-snd_pcm_indirect2_capture_transfer(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t null)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
- snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
-
- if (diff) {
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->lastdifftime = jiffies;
-#endif
- if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
- diff += runtime->boundary;
- rec->sw_ready -= frames_to_bytes(runtime, diff);
- rec->appl_ptr = appl_ptr;
- }
- /* if hardware has something, but the intermediate buffer is full
- * => skip contents of buffer
- */
- if (rec->hw_ready && (rec->sw_ready >= (int)rec->sw_buffer_size)) {
- unsigned int bytes;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (rec->firstzerotime == 0) {
- rec->firstzerotime = jiffies;
- snd_printk(KERN_DEBUG "STAT: (capture) "
- "@firstzerotime: mul_elapsed: %d, "
- "min_period_count: %d\n",
- rec->mul_elapsed, rec->min_period_count);
- snd_printk(KERN_DEBUG "STAT: (capture) "
- "@firstzerotime: sw_io: %d, sw_data: %d, "
- "appl_ptr: %u\n",
- rec->sw_io, rec->sw_data,
- (unsigned int)appl_ptr);
- }
- if ((jiffies - rec->firstzerotime) < 3750) {
- rec->zero_times[(jiffies - rec->firstzerotime)]++;
- rec->zero_times_saved++;
- } else
- rec->zero_times_notsaved++;
-#endif
- bytes = null(substream, rec);
-
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->zeros2hw += bytes;
- if (bytes < 64)
- rec->zero_sizes[bytes]++;
- else
- snd_printk(KERN_DEBUG
- "STAT: (capture) %d zero Bytes copied to "
- "hardware at once - too big to save!\n",
- bytes);
-#endif
- snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 0,
- bytes);
- /* report an overrun */
- rec->sw_io = SNDRV_PCM_POS_XRUN;
- return;
- }
- while (rec->hw_ready && (rec->sw_ready < (int)rec->sw_buffer_size)) {
- /* sw_to_end: max. number of bytes that we can write to the
- * intermediate buffer (until it's end)
- */
- size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
-
- /* bytes: max. number of bytes, which may be copied to the
- * intermediate buffer without overflow (in _one_ step)
- */
- size_t bytes = rec->sw_buffer_size - rec->sw_ready;
-
- /* limit number of bytes (for transfer) by available room in
- * the intermediate buffer
- */
- if (sw_to_end < bytes)
- bytes = sw_to_end;
- if (!bytes)
- break;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (rec->firstbytetime == 0)
- rec->firstbytetime = jiffies;
- rec->lastbytetime = jiffies;
-#endif
- /* copy bytes from the intermediate buffer (position sw_data)
- * to the HW at most and return number of bytes actually copied
- * from HW
- * Furthermore, set hw_ready to 0, if the fifo is empty now.
- */
- bytes = copy(substream, rec, bytes);
- rec->bytes2hw += bytes;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (bytes < 64)
- rec->byte_sizes[bytes]++;
- else
- snd_printk(KERN_DEBUG
- "STAT: (capture) %d Bytes copied to "
- "hardware at once - too big to save!\n",
- bytes);
-#endif
- /* increase sw_data by the number of actually copied bytes from
- * HW
- */
- rec->sw_data += bytes;
- if (rec->sw_data == rec->sw_buffer_size)
- rec->sw_data = 0;
-
- snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 1,
- bytes);
-
- /* number of bytes in the intermediate buffer, which haven't
- * been fetched by ALSA yet.
- */
- rec->sw_ready += bytes;
- }
- return;
-}
-
-/*
- * helper function for capture interrupt routine
- */
-void
-snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t null)
-{
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->irq_occured++;
-#endif
- /* hardware recorded some bytes, so there is something to read from the
- * record fifo:
- */
- rec->hw_ready = 1;
-
- /* don't call ack() now, instead call transfer() function directly
- * (normally called by ack() )
- */
- snd_pcm_indirect2_capture_transfer(substream, rec, copy, null);
-
- if (rec->min_periods >= rec->min_multiple) {
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if ((rec->min_periods / rec->min_multiple) > 7)
- snd_printk(KERN_DEBUG
- "STAT: more than 7 (%d) mul_adds - "
- "too big to save!\n",
- (rec->min_periods / rec->min_multiple));
- else
- rec->mul_adds[(rec->min_periods /
- rec->min_multiple)]++;
- rec->mul_elapsed_real += (rec->min_periods /
- rec->min_multiple);
- rec->mul_elapsed++;
-#endif
- rec->min_periods = (rec->min_periods % rec->min_multiple);
- snd_pcm_period_elapsed(substream);
- }
-}
diff --git a/sound/drivers/pcm-indirect2.h b/sound/drivers/pcm-indirect2.h
deleted file mode 100644
index 355ce76d2403..000000000000
--- a/sound/drivers/pcm-indirect2.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Helper functions for indirect PCM data transfer to a simple FIFO in
- * hardware (small, no possibility to read "hardware io position",
- * updating position done by interrupt, ...)
- *
- * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de>
- *
- * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by
- *
- * Copyright (c) by Takashi Iwai <tiwai@suse.de>
- * Jaroslav Kysela <perex@suse.cz>
- */
-
-#ifndef __SOUND_PCM_INDIRECT2_H
-#define __SOUND_PCM_INDIRECT2_H
-
-/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t */
-#include <sound/pcm.h>
-
-/* Debug options for code which may be removed completely in a final version */
-#ifdef CONFIG_SND_DEBUG
-#define SND_PCM_INDIRECT2_STAT /* turn on some "statistics" about the
- * process of copying bytes from the
- * intermediate buffer to the hardware
- * fifo and the other way round
- */
-#endif
-
-struct snd_pcm_indirect2 {
- unsigned int hw_buffer_size; /* Byte size of hardware buffer */
- int hw_ready; /* playback: 1 = hw fifo has room left,
- * 0 = hw fifo is full
- */
- unsigned int min_multiple;
- int min_periods; /* counts number of min. periods until
- * min_multiple is reached
- */
- int min_period_count; /* counts bytes to count number of
- * min. periods
- */
-
- unsigned int sw_buffer_size; /* Byte size of software buffer */
-
- /* sw_data: position in intermediate buffer, where we will read (or
- * write) from/to next time (to transfer data to/from HW)
- */
- unsigned int sw_data; /* Offset to next dst (or src) in sw
- * ring buffer
- */
- /* easiest case (playback):
- * sw_data is nearly the same as ~ runtime->control->appl_ptr, with the
- * exception that sw_data is "behind" by the number if bytes ALSA wrote
- * to the intermediate buffer last time.
- * A call to ack() callback synchronizes both indirectly.
- */
-
- /* We have no real sw_io pointer here. Usually sw_io is pointing to the
- * current playback/capture position _inside_ the hardware. Devices
- * with plain FIFOs often have no possibility to publish this position.
- * So we say: if sw_data is updated, that means bytes were copied to
- * the hardware, we increase sw_io by that amount, because there have
- * to be as much bytes which were played. So sw_io will stay behind
- * sw_data all the time and has to converge to sw_data at the end of
- * playback.
- */
- unsigned int sw_io; /* Current software pointer in bytes */
-
- /* sw_ready: number of bytes ALSA copied to the intermediate buffer, so
- * it represents the number of bytes which wait for transfer to the HW
- */
- int sw_ready; /* Bytes ready to be transferred to/from hw */
-
- /* appl_ptr: last known position of ALSA (where ALSA is going to write
- * next time into the intermediate buffer
- */
- snd_pcm_uframes_t appl_ptr; /* Last seen appl_ptr */
-
- unsigned int bytes2hw;
- int check_alignment;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- unsigned int zeros2hw;
- unsigned int mul_elapsed;
- unsigned int mul_elapsed_real;
- unsigned long firstbytetime;
- unsigned long lastbytetime;
- unsigned long firstzerotime;
- unsigned int byte_sizes[64];
- unsigned int zero_sizes[64];
- unsigned int min_adds[8];
- unsigned int mul_adds[8];
- unsigned int zero_times[3750]; /* = 15s */
- unsigned int zero_times_saved;
- unsigned int zero_times_notsaved;
- unsigned int irq_occured;
- unsigned int pointer_calls;
- unsigned int lastdifftime;
-#endif
-};
-
-typedef size_t (*snd_pcm_indirect2_copy_t) (struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- size_t bytes);
-typedef size_t (*snd_pcm_indirect2_zero_t) (struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec);
-
-#ifdef SND_PCM_INDIRECT2_STAT
-void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec);
-#endif
-
-snd_pcm_uframes_t
-snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec);
-void
-snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t zero);
-void
-snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t null);
-
-#endif /* __SOUND_PCM_INDIRECT2_H */
diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c
index ecefa7c83134..38603cb2bd5b 100644
--- a/sound/drivers/portman2x4.c
+++ b/sound/drivers/portman2x4.c
@@ -457,7 +457,7 @@ static int portman_probe(struct parport *p)
/* Set for RXDATA0 where no damage will be done. */
/* 5 */
- parport_write_control(p, RXDATA0 + STROBE); /* Write Strobe=1 to command reg. */
+ parport_write_control(p, RXDATA0 | STROBE); /* Write Strobe=1 to command reg. */
/* 6 */
if ((parport_read_status(p) & ESTB) != ESTB)
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 995c2cefc222..25778765cbfe 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -150,8 +150,12 @@ config SND_FIREWIRE_MOTU
Say Y here to enable support for FireWire devices which MOTU produced:
* 828mk2
* Traveler
- * 828mk3
+ * Ultralite
+ * 8pre
+ * 828mk3 (FireWire only)
+ * 828mk3 (Hybrid)
* Audio Express
+ * 4pre
To compile this driver as a module, choose M here: the module
will be called snd-firewire-motu.
@@ -164,6 +168,8 @@ config SND_FIREFACE
Say Y here to include support for RME fireface series.
* Fireface 400
* Fireface 800
+ * Fireface UFX
* Fireface UCX
+ * Fireface 802
endif # SND_FIREWIRE
diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c
index 67d735e9a6a4..fea92e148790 100644
--- a/sound/firewire/amdtp-am824.c
+++ b/sound/firewire/amdtp-am824.c
@@ -82,7 +82,8 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
if (err < 0)
return err;
- s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
+ if (s->direction == AMDTP_OUT_STREAM)
+ s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
p->pcm_channels = pcm_channels;
p->midi_ports = midi_ports;
diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h
index 16c7f6605511..26e7cb555d3c 100644
--- a/sound/firewire/amdtp-stream-trace.h
+++ b/sound/firewire/amdtp-stream-trace.h
@@ -66,8 +66,7 @@ TRACE_EVENT(amdtp_packet,
__entry->irq,
__entry->index,
__print_array(__get_dynamic_array(cip_header),
- __get_dynamic_array_len(cip_header),
- sizeof(u8)))
+ __get_dynamic_array_len(cip_header), 1))
);
#endif
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index 37d38efb4c87..f8586f75441d 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -20,6 +20,8 @@
#define CYCLES_PER_SECOND 8000
#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
+#define OHCI_MAX_SECOND 8
+
/* Always support Linux tracing subsystem. */
#define CREATE_TRACE_POINTS
#include "amdtp-stream-trace.h"
@@ -337,25 +339,26 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
}
EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
-static unsigned int calculate_data_blocks(struct amdtp_stream *s,
- unsigned int syt)
+static unsigned int calculate_data_blocks(unsigned int *data_block_state,
+ bool is_blocking, bool is_no_info,
+ unsigned int syt_interval, enum cip_sfc sfc)
{
- unsigned int phase, data_blocks;
+ unsigned int data_blocks;
/* Blocking mode. */
- if (s->flags & CIP_BLOCKING) {
+ if (is_blocking) {
/* This module generate empty packet for 'no data'. */
- if (syt == CIP_SYT_NO_INFO)
+ if (is_no_info)
data_blocks = 0;
else
- data_blocks = s->syt_interval;
+ data_blocks = syt_interval;
/* Non-blocking mode. */
} else {
- if (!cip_sfc_is_base_44100(s->sfc)) {
+ if (!cip_sfc_is_base_44100(sfc)) {
// Sample_rate / 8000 is an integer, and precomputed.
- data_blocks = s->ctx_data.rx.data_block_state;
+ data_blocks = *data_block_state;
} else {
- phase = s->ctx_data.rx.data_block_state;
+ unsigned int phase = *data_block_state;
/*
* This calculates the number of data blocks per packet so that
@@ -365,31 +368,30 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s,
* as possible in the sequence (to prevent underruns of the
* device's buffer).
*/
- if (s->sfc == CIP_SFC_44100)
+ if (sfc == CIP_SFC_44100)
/* 6 6 5 6 5 6 5 ... */
data_blocks = 5 + ((phase & 1) ^
(phase == 0 || phase >= 40));
else
/* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
- data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
- if (++phase >= (80 >> (s->sfc >> 1)))
+ data_blocks = 11 * (sfc >> 1) + (phase == 0);
+ if (++phase >= (80 >> (sfc >> 1)))
phase = 0;
- s->ctx_data.rx.data_block_state = phase;
+ *data_block_state = phase;
}
}
return data_blocks;
}
-static unsigned int calculate_syt(struct amdtp_stream *s,
- unsigned int cycle)
+static unsigned int calculate_syt_offset(unsigned int *last_syt_offset,
+ unsigned int *syt_offset_state, enum cip_sfc sfc)
{
- unsigned int syt_offset, phase, index, syt;
+ unsigned int syt_offset;
- if (s->ctx_data.rx.last_syt_offset < TICKS_PER_CYCLE) {
- if (!cip_sfc_is_base_44100(s->sfc))
- syt_offset = s->ctx_data.rx.last_syt_offset +
- s->ctx_data.rx.syt_offset_state;
+ if (*last_syt_offset < TICKS_PER_CYCLE) {
+ if (!cip_sfc_is_base_44100(sfc))
+ syt_offset = *last_syt_offset + *syt_offset_state;
else {
/*
* The time, in ticks, of the n'th SYT_INTERVAL sample is:
@@ -401,28 +403,24 @@ static unsigned int calculate_syt(struct amdtp_stream *s,
* 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ...
* This code generates _exactly_ the same sequence.
*/
- phase = s->ctx_data.rx.syt_offset_state;
- index = phase % 13;
- syt_offset = s->ctx_data.rx.last_syt_offset;
+ unsigned int phase = *syt_offset_state;
+ unsigned int index = phase % 13;
+
+ syt_offset = *last_syt_offset;
syt_offset += 1386 + ((index && !(index & 3)) ||
phase == 146);
if (++phase >= 147)
phase = 0;
- s->ctx_data.rx.syt_offset_state = phase;
+ *syt_offset_state = phase;
}
} else
- syt_offset = s->ctx_data.rx.last_syt_offset - TICKS_PER_CYCLE;
- s->ctx_data.rx.last_syt_offset = syt_offset;
+ syt_offset = *last_syt_offset - TICKS_PER_CYCLE;
+ *last_syt_offset = syt_offset;
- if (syt_offset < TICKS_PER_CYCLE) {
- syt_offset += s->ctx_data.rx.transfer_delay;
- syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
- syt += syt_offset % TICKS_PER_CYCLE;
+ if (syt_offset >= TICKS_PER_CYCLE)
+ syt_offset = CIP_SYT_NO_INFO;
- return syt & CIP_SYT_MASK;
- } else {
- return CIP_SYT_NO_INFO;
- }
+ return syt_offset;
}
static void update_pcm_pointers(struct amdtp_stream *s,
@@ -680,8 +678,8 @@ static inline u32 compute_cycle_count(__be32 ctx_header_tstamp)
static inline u32 increment_cycle_count(u32 cycle, unsigned int addend)
{
cycle += addend;
- if (cycle >= 8 * CYCLES_PER_SECOND)
- cycle -= 8 * CYCLES_PER_SECOND;
+ if (cycle >= OHCI_MAX_SECOND * CYCLES_PER_SECOND)
+ cycle -= OHCI_MAX_SECOND * CYCLES_PER_SECOND;
return cycle;
}
@@ -738,21 +736,41 @@ static int generate_device_pkt_descs(struct amdtp_stream *s,
return 0;
}
-static void generate_ideal_pkt_descs(struct amdtp_stream *s,
- struct pkt_desc *descs,
- const __be32 *ctx_header,
- unsigned int packets)
+static unsigned int compute_syt(unsigned int syt_offset, unsigned int cycle,
+ unsigned int transfer_delay)
+{
+ unsigned int syt;
+
+ syt_offset += transfer_delay;
+ syt = ((cycle + syt_offset / TICKS_PER_CYCLE) << 12) |
+ (syt_offset % TICKS_PER_CYCLE);
+ return syt & CIP_SYT_MASK;
+}
+
+static void generate_pkt_descs(struct amdtp_stream *s, struct pkt_desc *descs,
+ const __be32 *ctx_header, unsigned int packets,
+ const struct seq_desc *seq_descs,
+ unsigned int seq_size)
{
unsigned int dbc = s->data_block_counter;
+ unsigned int seq_index = s->ctx_data.rx.seq_index;
int i;
for (i = 0; i < packets; ++i) {
struct pkt_desc *desc = descs + i;
unsigned int index = (s->packet_index + i) % s->queue_size;
+ const struct seq_desc *seq = seq_descs + seq_index;
+ unsigned int syt;
desc->cycle = compute_it_cycle(*ctx_header, s->queue_size);
- desc->syt = calculate_syt(s, desc->cycle);
- desc->data_blocks = calculate_data_blocks(s, desc->syt);
+
+ syt = seq->syt_offset;
+ if (syt != CIP_SYT_NO_INFO) {
+ syt = compute_syt(syt, desc->cycle,
+ s->ctx_data.rx.transfer_delay);
+ }
+ desc->syt = syt;
+ desc->data_blocks = seq->data_blocks;
if (s->flags & CIP_DBC_IS_END_EVENT)
dbc = (dbc + desc->data_blocks) & 0xff;
@@ -764,10 +782,13 @@ static void generate_ideal_pkt_descs(struct amdtp_stream *s,
desc->ctx_payload = s->buffer.packets[index].buffer;
+ seq_index = (seq_index + 1) % seq_size;
+
++ctx_header;
}
s->data_block_counter = dbc;
+ s->ctx_data.rx.seq_index = seq_index;
}
static inline void cancel_stream(struct amdtp_stream *s)
@@ -791,24 +812,16 @@ static void process_ctx_payloads(struct amdtp_stream *s,
update_pcm_pointers(s, pcm, pcm_frames);
}
-static void amdtp_stream_master_callback(struct fw_iso_context *context,
- u32 tstamp, size_t header_length,
- void *header, void *private_data);
-
-static void amdtp_stream_master_first_callback(struct fw_iso_context *context,
- u32 tstamp, size_t header_length,
- void *header, void *private_data);
-
static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
size_t header_length, void *header,
void *private_data)
{
struct amdtp_stream *s = private_data;
+ const struct amdtp_domain *d = s->domain;
const __be32 *ctx_header = header;
unsigned int events_per_period = s->ctx_data.rx.events_per_period;
unsigned int event_count = s->ctx_data.rx.event_count;
unsigned int packets;
- bool is_irq_target;
int i;
if (s->packet_index < 0)
@@ -817,14 +830,11 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
// Calculate the number of packets in buffer and check XRUN.
packets = header_length / sizeof(*ctx_header);
- generate_ideal_pkt_descs(s, s->pkt_descs, ctx_header, packets);
+ generate_pkt_descs(s, s->pkt_descs, ctx_header, packets, d->seq_descs,
+ d->seq_size);
process_ctx_payloads(s, s->pkt_descs, packets);
- is_irq_target =
- !!(context->callback.sc == amdtp_stream_master_callback ||
- context->callback.sc == amdtp_stream_master_first_callback);
-
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = s->pkt_descs + i;
unsigned int syt;
@@ -843,7 +853,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
desc->data_blocks, desc->data_block_counter,
syt, i);
- if (is_irq_target) {
+ if (s == s->domain->irq_target) {
event_count += desc->data_blocks;
if (event_count >= events_per_period) {
event_count -= events_per_period;
@@ -896,14 +906,63 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
}
}
-static void amdtp_stream_master_callback(struct fw_iso_context *context,
- u32 tstamp, size_t header_length,
- void *header, void *private_data)
+static void pool_ideal_seq_descs(struct amdtp_domain *d, unsigned int packets)
{
- struct amdtp_domain *d = private_data;
struct amdtp_stream *irq_target = d->irq_target;
+ unsigned int seq_tail = d->seq_tail;
+ unsigned int seq_size = d->seq_size;
+ unsigned int min_avail;
struct amdtp_stream *s;
+ min_avail = d->seq_size;
+ list_for_each_entry(s, &d->streams, list) {
+ unsigned int seq_index;
+ unsigned int avail;
+
+ if (s->direction == AMDTP_IN_STREAM)
+ continue;
+
+ seq_index = s->ctx_data.rx.seq_index;
+ avail = d->seq_tail;
+ if (seq_index > avail)
+ avail += d->seq_size;
+ avail -= seq_index;
+
+ if (avail < min_avail)
+ min_avail = avail;
+ }
+
+ while (min_avail < packets) {
+ struct seq_desc *desc = d->seq_descs + seq_tail;
+
+ desc->syt_offset = calculate_syt_offset(&d->last_syt_offset,
+ &d->syt_offset_state, irq_target->sfc);
+ desc->data_blocks = calculate_data_blocks(&d->data_block_state,
+ !!(irq_target->flags & CIP_BLOCKING),
+ desc->syt_offset == CIP_SYT_NO_INFO,
+ irq_target->syt_interval, irq_target->sfc);
+
+ ++seq_tail;
+ seq_tail %= seq_size;
+
+ ++min_avail;
+ }
+
+ d->seq_tail = seq_tail;
+}
+
+static void irq_target_callback(struct fw_iso_context *context, u32 tstamp,
+ size_t header_length, void *header,
+ void *private_data)
+{
+ struct amdtp_stream *irq_target = private_data;
+ struct amdtp_domain *d = irq_target->domain;
+ unsigned int packets = header_length / sizeof(__be32);
+ struct amdtp_stream *s;
+
+ // Record enough entries with extra 3 cycles at least.
+ pool_ideal_seq_descs(d, packets + 3);
+
out_stream_callback(context, tstamp, header_length, header, irq_target);
if (amdtp_streaming_error(irq_target))
goto error;
@@ -950,7 +1009,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
} else {
cycle = compute_it_cycle(*ctx_header, s->queue_size);
- context->callback.sc = out_stream_callback;
+ if (s == s->domain->irq_target)
+ context->callback.sc = irq_target_callback;
+ else
+ context->callback.sc = out_stream_callback;
}
s->start_cycle = cycle;
@@ -958,64 +1020,29 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
context->callback.sc(context, tstamp, header_length, header, s);
}
-static void amdtp_stream_master_first_callback(struct fw_iso_context *context,
- u32 tstamp, size_t header_length,
- void *header, void *private_data)
-{
- struct amdtp_domain *d = private_data;
- struct amdtp_stream *s = d->irq_target;
- const __be32 *ctx_header = header;
-
- s->callbacked = true;
- wake_up(&s->callback_wait);
-
- s->start_cycle = compute_it_cycle(*ctx_header, s->queue_size);
-
- context->callback.sc = amdtp_stream_master_callback;
-
- context->callback.sc(context, tstamp, header_length, header, d);
-}
-
/**
* amdtp_stream_start - start transferring packets
* @s: the AMDTP stream to start
* @channel: the isochronous channel on the bus
* @speed: firewire speed code
- * @d: the AMDTP domain to which the AMDTP stream belongs
- * @is_irq_target: whether isoc context for the AMDTP stream is used to generate
- * hardware IRQ.
* @start_cycle: the isochronous cycle to start the context. Start immediately
* if negative value is given.
+ * @queue_size: The number of packets in the queue.
+ * @idle_irq_interval: the interval to queue packet during initial state.
*
* The stream cannot be started until it has been configured with
* amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
* device can be started.
*/
static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
- struct amdtp_domain *d, bool is_irq_target,
- int start_cycle)
+ int start_cycle, unsigned int queue_size,
+ unsigned int idle_irq_interval)
{
- static const struct {
- unsigned int data_block;
- unsigned int syt_offset;
- } *entry, initial_state[] = {
- [CIP_SFC_32000] = { 4, 3072 },
- [CIP_SFC_48000] = { 6, 1024 },
- [CIP_SFC_96000] = { 12, 1024 },
- [CIP_SFC_192000] = { 24, 1024 },
- [CIP_SFC_44100] = { 0, 67 },
- [CIP_SFC_88200] = { 0, 67 },
- [CIP_SFC_176400] = { 0, 67 },
- };
- unsigned int events_per_buffer = d->events_per_buffer;
- unsigned int events_per_period = d->events_per_period;
- unsigned int idle_irq_interval;
+ bool is_irq_target = (s == s->domain->irq_target);
unsigned int ctx_header_size;
unsigned int max_ctx_payload_size;
enum dma_data_direction dir;
int type, tag, err;
- fw_iso_callback_t ctx_cb;
- void *ctx_data;
mutex_lock(&s->mutex);
@@ -1034,12 +1061,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
s->data_block_counter = UINT_MAX;
} else {
- entry = &initial_state[s->sfc];
-
s->data_block_counter = 0;
- s->ctx_data.rx.data_block_state = entry->data_block;
- s->ctx_data.rx.syt_offset_state = entry->syt_offset;
- s->ctx_data.rx.last_syt_offset = TICKS_PER_CYCLE;
}
/* initialize packet buffer */
@@ -1063,37 +1085,15 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP;
}
- // This is a case that AMDTP streams in domain run just for MIDI
- // substream. Use the number of events equivalent to 10 msec as
- // interval of hardware IRQ.
- if (events_per_period == 0)
- events_per_period = amdtp_rate_table[s->sfc] / 100;
- if (events_per_buffer == 0)
- events_per_buffer = events_per_period * 3;
-
- idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period,
- amdtp_rate_table[s->sfc]);
- s->queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer,
- amdtp_rate_table[s->sfc]);
-
- err = iso_packets_buffer_init(&s->buffer, s->unit, s->queue_size,
+ err = iso_packets_buffer_init(&s->buffer, s->unit, queue_size,
max_ctx_payload_size, dir);
if (err < 0)
goto err_unlock;
-
- if (is_irq_target) {
- s->ctx_data.rx.events_per_period = events_per_period;
- s->ctx_data.rx.event_count = 0;
- ctx_cb = amdtp_stream_master_first_callback;
- ctx_data = d;
- } else {
- ctx_cb = amdtp_stream_first_callback;
- ctx_data = s;
- }
+ s->queue_size = queue_size;
s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
type, channel, speed, ctx_header_size,
- ctx_cb, ctx_data);
+ amdtp_stream_first_callback, s);
if (IS_ERR(s->context)) {
err = PTR_ERR(s->context);
if (err == -EBUSY)
@@ -1302,6 +1302,8 @@ int amdtp_domain_init(struct amdtp_domain *d)
d->events_per_period = 0;
+ d->seq_descs = NULL;
+
return 0;
}
EXPORT_SYMBOL_GPL(amdtp_domain_init);
@@ -1338,6 +1340,7 @@ int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
s->channel = channel;
s->speed = speed;
+ s->domain = d;
return 0;
}
@@ -1374,6 +1377,22 @@ static int get_current_cycle_time(struct fw_card *fw_card, int *cur_cycle)
*/
int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle)
{
+ static const struct {
+ unsigned int data_block;
+ unsigned int syt_offset;
+ } *entry, initial_state[] = {
+ [CIP_SFC_32000] = { 4, 3072 },
+ [CIP_SFC_48000] = { 6, 1024 },
+ [CIP_SFC_96000] = { 12, 1024 },
+ [CIP_SFC_192000] = { 24, 1024 },
+ [CIP_SFC_44100] = { 0, 67 },
+ [CIP_SFC_88200] = { 0, 67 },
+ [CIP_SFC_176400] = { 0, 67 },
+ };
+ unsigned int events_per_buffer = d->events_per_buffer;
+ unsigned int events_per_period = d->events_per_period;
+ unsigned int idle_irq_interval;
+ unsigned int queue_size;
struct amdtp_stream *s;
int cycle;
int err;
@@ -1387,12 +1406,34 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle)
return -ENXIO;
d->irq_target = s;
+ // This is a case that AMDTP streams in domain run just for MIDI
+ // substream. Use the number of events equivalent to 10 msec as
+ // interval of hardware IRQ.
+ if (events_per_period == 0)
+ events_per_period = amdtp_rate_table[d->irq_target->sfc] / 100;
+ if (events_per_buffer == 0)
+ events_per_buffer = events_per_period * 3;
+
+ queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer,
+ amdtp_rate_table[d->irq_target->sfc]);
+
+ d->seq_descs = kcalloc(queue_size, sizeof(*d->seq_descs), GFP_KERNEL);
+ if (!d->seq_descs)
+ return -ENOMEM;
+ d->seq_size = queue_size;
+ d->seq_tail = 0;
+
+ entry = &initial_state[s->sfc];
+ d->data_block_state = entry->data_block;
+ d->syt_offset_state = entry->syt_offset;
+ d->last_syt_offset = TICKS_PER_CYCLE;
+
if (ir_delay_cycle > 0) {
struct fw_card *fw_card = fw_parent_device(s->unit)->card;
err = get_current_cycle_time(fw_card, &cycle);
if (err < 0)
- return err;
+ goto error;
// No need to care overflow in cycle field because of enough
// width.
@@ -1423,18 +1464,26 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle)
} else {
// IT context starts immediately.
cycle_match = -1;
+ s->ctx_data.rx.seq_index = 0;
}
if (s != d->irq_target) {
- err = amdtp_stream_start(s, s->channel, s->speed, d,
- false, cycle_match);
+ err = amdtp_stream_start(s, s->channel, s->speed,
+ cycle_match, queue_size, 0);
if (err < 0)
goto error;
}
}
s = d->irq_target;
- err = amdtp_stream_start(s, s->channel, s->speed, d, true, -1);
+ s->ctx_data.rx.events_per_period = events_per_period;
+ s->ctx_data.rx.event_count = 0;
+ s->ctx_data.rx.seq_index = 0;
+
+ idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period,
+ amdtp_rate_table[d->irq_target->sfc]);
+ err = amdtp_stream_start(s, s->channel, s->speed, -1, queue_size,
+ idle_irq_interval);
if (err < 0)
goto error;
@@ -1442,6 +1491,8 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle)
error:
list_for_each_entry(s, &d->streams, list)
amdtp_stream_stop(s);
+ kfree(d->seq_descs);
+ d->seq_descs = NULL;
return err;
}
EXPORT_SYMBOL_GPL(amdtp_domain_start);
@@ -1466,5 +1517,8 @@ void amdtp_domain_stop(struct amdtp_domain *d)
d->events_per_period = 0;
d->irq_target = NULL;
+
+ kfree(d->seq_descs);
+ d->seq_descs = NULL;
}
EXPORT_SYMBOL_GPL(amdtp_domain_stop);
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
index f2d44e2dc3c8..703b710aaf7f 100644
--- a/sound/firewire/amdtp-stream.h
+++ b/sound/firewire/amdtp-stream.h
@@ -108,6 +108,8 @@ typedef unsigned int (*amdtp_stream_process_ctx_payloads_t)(
const struct pkt_desc *desc,
unsigned int packets,
struct snd_pcm_substream *pcm);
+
+struct amdtp_domain;
struct amdtp_stream {
struct fw_unit *unit;
enum cip_flags flags;
@@ -136,9 +138,7 @@ struct amdtp_stream {
struct {
// To calculate CIP data blocks and tstamp.
unsigned int transfer_delay;
- unsigned int data_block_state;
- unsigned int last_syt_offset;
- unsigned int syt_offset_state;
+ unsigned int seq_index;
// To generate CIP header.
unsigned int fdf;
@@ -180,6 +180,7 @@ struct amdtp_stream {
int channel;
int speed;
struct list_head list;
+ struct amdtp_domain *domain;
};
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
@@ -273,6 +274,11 @@ static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
msecs_to_jiffies(timeout)) > 0;
}
+struct seq_desc {
+ unsigned int syt_offset;
+ unsigned int data_blocks;
+};
+
struct amdtp_domain {
struct list_head streams;
@@ -280,6 +286,14 @@ struct amdtp_domain {
unsigned int events_per_buffer;
struct amdtp_stream *irq_target;
+
+ struct seq_desc *seq_descs;
+ unsigned int seq_size;
+ unsigned int seq_tail;
+
+ unsigned int data_block_state;
+ unsigned int syt_offset_state;
+ unsigned int last_syt_offset;
};
int amdtp_domain_init(struct amdtp_domain *d);
diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c
index 0e4c3a9ed5e4..8d3b23778eb2 100644
--- a/sound/firewire/fireface/ff-protocol-latter.c
+++ b/sound/firewire/fireface/ff-protocol-latter.c
@@ -16,7 +16,8 @@
#define LATTER_SYNC_STATUS 0x0000801c0000ULL
static int parse_clock_bits(u32 data, unsigned int *rate,
- enum snd_ff_clock_src *src)
+ enum snd_ff_clock_src *src,
+ enum snd_ff_unit_version unit_version)
{
static const struct {
unsigned int rate;
@@ -43,6 +44,11 @@ static int parse_clock_bits(u32 data, unsigned int *rate,
};
int i;
+ if (unit_version != SND_FF_UNIT_VERSION_UCX) {
+ // e.g. 0x00fe0f20 but expected 0x00eff002.
+ data = ((data & 0xf0f0f0f0) >> 4) | ((data & 0x0f0f0f0f) << 4);
+ }
+
for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
rate_entry = rate_entries + i;
if ((data & 0x0f000000) == rate_entry->flag) {
@@ -79,7 +85,7 @@ static int latter_get_clock(struct snd_ff *ff, unsigned int *rate,
return err;
data = le32_to_cpu(reg);
- return parse_clock_bits(data, rate, src);
+ return parse_clock_bits(data, rate, src, ff->unit_version);
}
static int latter_switch_fetching_mode(struct snd_ff *ff, bool enable)
@@ -107,18 +113,18 @@ static int latter_allocate_resources(struct snd_ff *ff, unsigned int rate)
int err;
// Set the number of data blocks transferred in a second.
- if (rate % 32000 == 0)
- code = 0x00;
+ if (rate % 48000 == 0)
+ code = 0x04;
else if (rate % 44100 == 0)
code = 0x02;
- else if (rate % 48000 == 0)
- code = 0x04;
+ else if (rate % 32000 == 0)
+ code = 0x00;
else
return -EINVAL;
if (rate >= 64000 && rate < 128000)
code |= 0x08;
- else if (rate >= 128000 && rate < 192000)
+ else if (rate >= 128000)
code |= 0x10;
reg = cpu_to_le32(code);
@@ -140,7 +146,7 @@ static int latter_allocate_resources(struct snd_ff *ff, unsigned int rate)
if (curr_rate == rate)
break;
}
- if (count == 10)
+ if (count > 10)
return -ETIMEDOUT;
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); ++i) {
@@ -181,14 +187,30 @@ static int latter_begin_session(struct snd_ff *ff, unsigned int rate)
__le32 reg;
int err;
- if (rate >= 32000 && rate <= 48000)
- flag = 0x92;
- else if (rate >= 64000 && rate <= 96000)
- flag = 0x8e;
- else if (rate >= 128000 && rate <= 192000)
- flag = 0x8c;
- else
- return -EINVAL;
+ if (ff->unit_version == SND_FF_UNIT_VERSION_UCX) {
+ // For Fireface UCX. Always use the maximum number of data
+ // channels in data block of packet.
+ if (rate >= 32000 && rate <= 48000)
+ flag = 0x92;
+ else if (rate >= 64000 && rate <= 96000)
+ flag = 0x8e;
+ else if (rate >= 128000 && rate <= 192000)
+ flag = 0x8c;
+ else
+ return -EINVAL;
+ } else {
+ // For Fireface UFX and 802. Due to bandwidth limitation on
+ // IEEE 1394a (400 Mbps), Analog 1-12 and AES are available
+ // without any ADAT at quadruple speed.
+ if (rate >= 32000 && rate <= 48000)
+ flag = 0x9e;
+ else if (rate >= 64000 && rate <= 96000)
+ flag = 0x96;
+ else if (rate >= 128000 && rate <= 192000)
+ flag = 0x8e;
+ else
+ return -EINVAL;
+ }
if (generation != fw_parent_device(ff->unit)->card->generation) {
err = fw_iso_resources_update(&ff->tx_resources);
@@ -207,8 +229,6 @@ static int latter_begin_session(struct snd_ff *ff, unsigned int rate)
if (err < 0)
return err;
- // Always use the maximum number of data channels in data block of
- // packet.
reg = cpu_to_le32(flag);
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
LATTER_ISOC_START, &reg, sizeof(reg), 0);
@@ -263,7 +283,7 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer
}
}
- err = parse_clock_bits(data, &rate, &src);
+ err = parse_clock_bits(data, &rate, &src, ff->unit_version);
if (err < 0)
return;
label = snd_ff_proc_get_clk_label(src);
diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c
index 63b79c4a5405..5452115c0ef9 100644
--- a/sound/firewire/fireface/ff-stream.c
+++ b/sound/firewire/fireface/ff-stream.c
@@ -184,7 +184,6 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
*/
if (!amdtp_stream_running(&ff->rx_stream)) {
int spd = fw_parent_device(ff->unit)->max_speed;
- unsigned int ir_delay_cycle;
err = ff->spec->protocol->begin_session(ff, rate);
if (err < 0)
@@ -200,14 +199,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
if (err < 0)
goto error;
- // The device postpones start of transmission mostly for several
- // cycles after receiving packets firstly.
- if (ff->spec->protocol == &snd_ff_protocol_ff800)
- ir_delay_cycle = 800; // = 100 msec
- else
- ir_delay_cycle = 16; // = 2 msec
-
- err = amdtp_domain_start(&ff->domain, ir_delay_cycle);
+ err = amdtp_domain_start(&ff->domain, 0);
if (err < 0)
goto error;
diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c
index b62a4fd22407..bc39269415d2 100644
--- a/sound/firewire/fireface/ff.c
+++ b/sound/firewire/fireface/ff.c
@@ -16,12 +16,22 @@ MODULE_LICENSE("GPL v2");
static void name_card(struct snd_ff *ff)
{
struct fw_device *fw_dev = fw_parent_device(ff->unit);
+ const char *const names[] = {
+ [SND_FF_UNIT_VERSION_FF800] = "Fireface800",
+ [SND_FF_UNIT_VERSION_FF400] = "Fireface400",
+ [SND_FF_UNIT_VERSION_UFX] = "FirefaceUFX",
+ [SND_FF_UNIT_VERSION_UCX] = "FirefaceUCX",
+ [SND_FF_UNIT_VERSION_802] = "Fireface802",
+ };
+ const char *name;
+
+ name = names[ff->unit_version];
strcpy(ff->card->driver, "Fireface");
- strcpy(ff->card->shortname, ff->spec->name);
- strcpy(ff->card->mixername, ff->spec->name);
+ strcpy(ff->card->shortname, name);
+ strcpy(ff->card->mixername, name);
snprintf(ff->card->longname, sizeof(ff->card->longname),
- "RME %s, GUID %08x%08x at %s, S%d", ff->spec->name,
+ "RME %s, GUID %08x%08x at %s, S%d", name,
fw_dev->config_rom[3], fw_dev->config_rom[4],
dev_name(&ff->unit->device), 100 << fw_dev->max_speed);
}
@@ -101,6 +111,7 @@ static int snd_ff_probe(struct fw_unit *unit,
spin_lock_init(&ff->lock);
init_waitqueue_head(&ff->hwdep_wait);
+ ff->unit_version = entry->version;
ff->spec = (const struct snd_ff_spec *)entry->driver_data;
/* Register this sound card later. */
@@ -145,7 +156,6 @@ static void snd_ff_remove(struct fw_unit *unit)
}
static const struct snd_ff_spec spec_ff800 = {
- .name = "Fireface800",
.pcm_capture_channels = {28, 20, 12},
.pcm_playback_channels = {28, 20, 12},
.midi_in_ports = 1,
@@ -157,7 +167,6 @@ static const struct snd_ff_spec spec_ff800 = {
};
static const struct snd_ff_spec spec_ff400 = {
- .name = "Fireface400",
.pcm_capture_channels = {18, 14, 10},
.pcm_playback_channels = {18, 14, 10},
.midi_in_ports = 2,
@@ -169,7 +178,6 @@ static const struct snd_ff_spec spec_ff400 = {
};
static const struct snd_ff_spec spec_ucx = {
- .name = "FirefaceUCX",
.pcm_capture_channels = {18, 14, 12},
.pcm_playback_channels = {18, 14, 12},
.midi_in_ports = 2,
@@ -180,6 +188,17 @@ static const struct snd_ff_spec spec_ucx = {
.midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull},
};
+static const struct snd_ff_spec spec_ufx_802 = {
+ .pcm_capture_channels = {30, 22, 14},
+ .pcm_playback_channels = {30, 22, 14},
+ .midi_in_ports = 1,
+ .midi_out_ports = 1,
+ .protocol = &snd_ff_protocol_latter,
+ .midi_high_addr = 0xffff00000034ull,
+ .midi_addr_range = 0x80,
+ .midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull},
+};
+
static const struct ieee1394_device_id snd_ff_id_table[] = {
/* Fireface 800 */
{
@@ -189,7 +208,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = {
IEEE1394_MATCH_MODEL_ID,
.vendor_id = OUI_RME,
.specifier_id = OUI_RME,
- .version = 0x000001,
+ .version = SND_FF_UNIT_VERSION_FF800,
.model_id = 0x101800,
.driver_data = (kernel_ulong_t)&spec_ff800,
},
@@ -201,10 +220,22 @@ static const struct ieee1394_device_id snd_ff_id_table[] = {
IEEE1394_MATCH_MODEL_ID,
.vendor_id = OUI_RME,
.specifier_id = OUI_RME,
- .version = 0x000002,
+ .version = SND_FF_UNIT_VERSION_FF400,
.model_id = 0x101800,
.driver_data = (kernel_ulong_t)&spec_ff400,
},
+ // Fireface UFX.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_RME,
+ .specifier_id = OUI_RME,
+ .version = SND_FF_UNIT_VERSION_UFX,
+ .model_id = 0x101800,
+ .driver_data = (kernel_ulong_t)&spec_ufx_802,
+ },
// Fireface UCX.
{
.match_flags = IEEE1394_MATCH_VENDOR_ID |
@@ -213,10 +244,22 @@ static const struct ieee1394_device_id snd_ff_id_table[] = {
IEEE1394_MATCH_MODEL_ID,
.vendor_id = OUI_RME,
.specifier_id = OUI_RME,
- .version = 0x000004,
+ .version = SND_FF_UNIT_VERSION_UCX,
.model_id = 0x101800,
.driver_data = (kernel_ulong_t)&spec_ucx,
},
+ // Fireface 802.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_RME,
+ .specifier_id = OUI_RME,
+ .version = SND_FF_UNIT_VERSION_802,
+ .model_id = 0x101800,
+ .driver_data = (kernel_ulong_t)&spec_ufx_802,
+ },
{}
};
MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table);
diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h
index dc7a20f75983..705e7df4f929 100644
--- a/sound/firewire/fireface/ff.h
+++ b/sound/firewire/fireface/ff.h
@@ -34,6 +34,14 @@
#define SND_FF_IN_MIDI_PORTS 2
#define SND_FF_OUT_MIDI_PORTS 2
+enum snd_ff_unit_version {
+ SND_FF_UNIT_VERSION_FF800 = 0x000001,
+ SND_FF_UNIT_VERSION_FF400 = 0x000002,
+ SND_FF_UNIT_VERSION_UFX = 0x000003,
+ SND_FF_UNIT_VERSION_UCX = 0x000004,
+ SND_FF_UNIT_VERSION_802 = 0x000005,
+};
+
enum snd_ff_stream_mode {
SND_FF_STREAM_MODE_LOW = 0,
SND_FF_STREAM_MODE_MID,
@@ -43,8 +51,6 @@ enum snd_ff_stream_mode {
struct snd_ff_protocol;
struct snd_ff_spec {
- const char *const name;
-
const unsigned int pcm_capture_channels[SND_FF_STREAM_MODE_COUNT];
const unsigned int pcm_playback_channels[SND_FF_STREAM_MODE_COUNT];
@@ -66,6 +72,7 @@ struct snd_ff {
bool registered;
struct delayed_work dwork;
+ enum snd_ff_unit_version unit_version;
const struct snd_ff_spec *spec;
/* To handle MIDI tx. */
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index dda797209a27..654e28a6669f 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -177,7 +177,7 @@ struct snd_efw_phys_meters {
u32 in_meters;
u32 reserved4;
u32 reserved5;
- u32 values[0];
+ u32 values[];
} __packed;
enum snd_efw_clock_source {
SND_EFW_CLOCK_SOURCE_INTERNAL = 0,
diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c
index 0fd36e469ad0..edb31ac26868 100644
--- a/sound/firewire/motu/amdtp-motu.c
+++ b/sound/firewire/motu/amdtp-motu.c
@@ -76,15 +76,11 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
if (i == ARRAY_SIZE(snd_motu_clock_rates))
return -EINVAL;
- pcm_chunks = formats->fixed_part_pcm_chunks[mode] +
- formats->differed_part_pcm_chunks[mode];
+ // Each data block includes SPH in its head. Data chunks follow with
+ // 3 byte alignment. Padding follows with zero to conform to quadlet
+ // alignment.
+ pcm_chunks = formats->pcm_chunks[mode];
data_chunks = formats->msg_chunks + pcm_chunks;
-
- /*
- * Each data block includes SPH in its head. Data chunks follow with
- * 3 byte alignment. Padding follows with zero to conform to quadlet
- * alignment.
- */
data_block_quadlets = 1 + DIV_ROUND_UP(data_chunks * 3, 4);
err = amdtp_stream_set_parameters(s, rate, data_block_quadlets);
@@ -440,7 +436,7 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir,
- const struct snd_motu_protocol *const protocol)
+ const struct snd_motu_spec *spec)
{
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
int fmt = CIP_FMT_MOTU;
@@ -454,14 +450,15 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
* Units of version 3 transmits packets with invalid CIP header
* against IEC 61883-1.
*/
- if (protocol == &snd_motu_protocol_v3) {
+ if (spec->protocol_version == SND_MOTU_PROTOCOL_V3) {
flags |= CIP_WRONG_DBS |
CIP_SKIP_DBC_ZERO_CHECK |
CIP_HEADER_WITHOUT_EOH;
fmt = CIP_FMT_MOTU_TX_V3;
}
- if (protocol == &snd_motu_protocol_v2) {
+ if (spec == &snd_motu_spec_8pre ||
+ spec == &snd_motu_spec_ultralite) {
// 8pre has some quirks.
flags |= CIP_WRONG_DBS |
CIP_SKIP_DBC_ZERO_CHECK;
diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c
index 2d41a1a4052c..8e1437371263 100644
--- a/sound/firewire/motu/motu-pcm.c
+++ b/sound/firewire/motu/motu-pcm.c
@@ -26,8 +26,7 @@ static int motu_rate_constraint(struct snd_pcm_hw_params *params,
rate = snd_motu_clock_rates[i];
mode = i / 2;
- pcm_channels = formats->fixed_part_pcm_chunks[mode] +
- formats->differed_part_pcm_chunks[mode];
+ pcm_channels = formats->pcm_chunks[mode];
if (!snd_interval_test(c, pcm_channels))
continue;
@@ -59,8 +58,7 @@ static int motu_channels_constraint(struct snd_pcm_hw_params *params,
if (!snd_interval_test(r, rate))
continue;
- pcm_channels = formats->fixed_part_pcm_chunks[mode] +
- formats->differed_part_pcm_chunks[mode];
+ pcm_channels = formats->pcm_chunks[mode];
channels.min = min(channels.min, pcm_channels);
channels.max = max(channels.max, pcm_channels);
}
@@ -82,8 +80,7 @@ static void limit_channels_and_rates(struct snd_motu *motu,
rate = snd_motu_clock_rates[i];
mode = i / 2;
- pcm_channels = formats->fixed_part_pcm_chunks[mode] +
- formats->differed_part_pcm_chunks[mode];
+ pcm_channels = formats->pcm_chunks[mode];
if (pcm_channels == 0)
continue;
@@ -133,7 +130,6 @@ static int init_hw_info(struct snd_motu *motu,
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
- const struct snd_motu_protocol *const protocol = motu->spec->protocol;
struct amdtp_domain *d = &motu->domain;
enum snd_motu_clock_source src;
int err;
@@ -152,7 +148,7 @@ static int pcm_open(struct snd_pcm_substream *substream)
if (err < 0)
goto err_locked;
- err = protocol->get_clock_source(motu, &src);
+ err = snd_motu_protocol_get_clock_source(motu, &src);
if (err < 0)
goto err_locked;
@@ -166,7 +162,7 @@ static int pcm_open(struct snd_pcm_substream *substream)
unsigned int frames_per_buffer = d->events_per_buffer;
unsigned int rate;
- err = protocol->get_clock_rate(motu, &rate);
+ err = snd_motu_protocol_get_clock_rate(motu, &rate);
if (err < 0)
goto err_locked;
diff --git a/sound/firewire/motu/motu-proc.c b/sound/firewire/motu/motu-proc.c
index 187f6abd878c..f009cf7aa074 100644
--- a/sound/firewire/motu/motu-proc.c
+++ b/sound/firewire/motu/motu-proc.c
@@ -28,13 +28,12 @@ static void proc_read_clock(struct snd_info_entry *entry,
{
struct snd_motu *motu = entry->private_data;
- const struct snd_motu_protocol *const protocol = motu->spec->protocol;
unsigned int rate;
enum snd_motu_clock_source source;
- if (protocol->get_clock_rate(motu, &rate) < 0)
+ if (snd_motu_protocol_get_clock_rate(motu, &rate) < 0)
return;
- if (protocol->get_clock_source(motu, &source) < 0)
+ if (snd_motu_protocol_get_clock_source(motu, &source) < 0)
return;
snd_iprintf(buffer, "Rate:\t%d\n", rate);
@@ -45,15 +44,14 @@ static void proc_read_format(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_motu *motu = entry->private_data;
- const struct snd_motu_protocol *const protocol = motu->spec->protocol;
unsigned int mode;
struct snd_motu_packet_format *formats;
int i;
- if (protocol->cache_packet_formats(motu) < 0)
+ if (snd_motu_protocol_cache_packet_formats(motu) < 0)
return;
- snd_iprintf(buffer, "tx:\tmsg\tfixed\tdiffered\n");
+ snd_iprintf(buffer, "tx:\tmsg\tfixed\ttotal\n");
for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
mode = i >> 1;
@@ -62,11 +60,11 @@ static void proc_read_format(struct snd_info_entry *entry,
"%u:\t%u\t%u\t%u\n",
snd_motu_clock_rates[i],
formats->msg_chunks,
- formats->fixed_part_pcm_chunks[mode],
- formats->differed_part_pcm_chunks[mode]);
+ motu->spec->tx_fixed_pcm_chunks[mode],
+ formats->pcm_chunks[mode]);
}
- snd_iprintf(buffer, "rx:\tmsg\tfixed\tdiffered\n");
+ snd_iprintf(buffer, "rx:\tmsg\tfixed\ttotal\n");
for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
mode = i >> 1;
@@ -75,8 +73,8 @@ static void proc_read_format(struct snd_info_entry *entry,
"%u:\t%u\t%u\t%u\n",
snd_motu_clock_rates[i],
formats->msg_chunks,
- formats->fixed_part_pcm_chunks[mode],
- formats->differed_part_pcm_chunks[mode]);
+ motu->spec->rx_fixed_pcm_chunks[mode],
+ formats->pcm_chunks[mode]);
}
}
diff --git a/sound/firewire/motu/motu-protocol-v2.c b/sound/firewire/motu/motu-protocol-v2.c
index 619b6ae73f62..e59e69ab1538 100644
--- a/sound/firewire/motu/motu-protocol-v2.c
+++ b/sound/firewire/motu/motu-protocol-v2.c
@@ -35,7 +35,8 @@ static int get_clock_rate(u32 data, unsigned int *rate)
return 0;
}
-static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
+int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate)
{
__be32 reg;
int err;
@@ -48,7 +49,8 @@ static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
return get_clock_rate(be32_to_cpu(reg), rate);
}
-static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate)
+int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate)
{
__be32 reg;
u32 data;
@@ -76,14 +78,10 @@ static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate)
sizeof(reg));
}
-static int get_clock_source(struct snd_motu *motu, u32 data,
- enum snd_motu_clock_source *src)
+static int detect_clock_source_optical_model(struct snd_motu *motu, u32 data,
+ enum snd_motu_clock_source *src)
{
- unsigned int index = data & V2_CLOCK_SRC_MASK;
- if (index > 5)
- return -EIO;
-
- switch (index) {
+ switch (data) {
case 0:
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
break;
@@ -116,14 +114,50 @@ static int get_clock_source(struct snd_motu *motu, u32 data,
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
break;
default:
- return -EIO;
+ *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
+ break;
}
return 0;
}
-static int v2_get_clock_source(struct snd_motu *motu,
- enum snd_motu_clock_source *src)
+static int v2_detect_clock_source(struct snd_motu *motu, u32 data,
+ enum snd_motu_clock_source *src)
+{
+ switch (data) {
+ case 0:
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ break;
+ case 2:
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ break;
+ case 3:
+ *src = SND_MOTU_CLOCK_SOURCE_SPH;
+ break;
+ case 4:
+ *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
+ break;
+ default:
+ *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+static int get_clock_source(struct snd_motu *motu, u32 data,
+ enum snd_motu_clock_source *src)
+{
+ data &= V2_CLOCK_SRC_MASK;
+ if (motu->spec == &snd_motu_spec_828mk2 ||
+ motu->spec == &snd_motu_spec_traveler)
+ return detect_clock_source_optical_model(motu, data, src);
+ else
+ return v2_detect_clock_source(motu, data, src);
+}
+
+int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src)
{
__be32 reg;
int err;
@@ -136,167 +170,189 @@ static int v2_get_clock_source(struct snd_motu *motu,
return get_clock_source(motu, be32_to_cpu(reg), src);
}
-static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable)
+// Expected for Traveler and 896HD, which implements Altera Cyclone EP1C3.
+static int switch_fetching_mode_cyclone(struct snd_motu *motu, u32 *data,
+ bool enable)
{
- enum snd_motu_clock_source src;
- __be32 reg;
- u32 data;
- int err = 0;
+ *data |= V2_CLOCK_MODEL_SPECIFIC;
- // 828mkII implements Altera ACEX 1K EP1K30. Nothing to do.
- if (motu->spec == &snd_motu_spec_828mk2)
- return 0;
+ return 0;
+}
- err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
- sizeof(reg));
+// For UltraLite and 8pre, which implements Xilinx Spartan XC3S200.
+static int switch_fetching_mode_spartan(struct snd_motu *motu, u32 *data,
+ bool enable)
+{
+ unsigned int rate;
+ enum snd_motu_clock_source src;
+ int err;
+
+ err = get_clock_source(motu, *data, &src);
if (err < 0)
return err;
- data = be32_to_cpu(reg);
- err = get_clock_source(motu, data, &src);
+ err = get_clock_rate(*data, &rate);
if (err < 0)
return err;
- data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC);
- if (enable)
- data |= V2_CLOCK_FETCH_ENABLE;
+ if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000)
+ *data |= V2_CLOCK_MODEL_SPECIFIC;
+
+ return 0;
+}
- if (motu->spec->flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) {
- // Expected for Traveler and 896HD, which implements Altera
- // Cyclone EP1C3.
- data |= V2_CLOCK_MODEL_SPECIFIC;
+int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
+ bool enable)
+{
+ if (motu->spec == &snd_motu_spec_828mk2) {
+ // 828mkII implements Altera ACEX 1K EP1K30. Nothing to do.
+ return 0;
} else {
- // For UltraLite and 8pre, which implements Xilinx Spartan
- // XC3S200.
- unsigned int rate;
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
- err = get_clock_rate(data, &rate);
+ data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC);
+ if (enable)
+ data |= V2_CLOCK_FETCH_ENABLE;
+
+ if (motu->spec == &snd_motu_spec_traveler)
+ err = switch_fetching_mode_cyclone(motu, &data, enable);
+ else
+ err = switch_fetching_mode_spartan(motu, &data, enable);
if (err < 0)
return err;
- if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000)
- data |= V2_CLOCK_MODEL_SPECIFIC;
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET,
+ &reg, sizeof(reg));
}
+}
- reg = cpu_to_be32(data);
- return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, &reg,
- sizeof(reg));
+static int detect_packet_formats_828mk2(struct snd_motu *motu, u32 data)
+{
+ if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
+ V2_OPT_IFACE_MODE_ADAT) {
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ }
+
+ if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) ==
+ V2_OPT_IFACE_MODE_ADAT) {
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
+ }
+
+ return 0;
}
-static void calculate_fixed_part(struct snd_motu_packet_format *formats,
- enum amdtp_stream_direction dir,
- enum snd_motu_spec_flags flags,
- unsigned char analog_ports)
+static int detect_packet_formats_traveler(struct snd_motu *motu, u32 data)
{
- unsigned char pcm_chunks[3] = {0, 0, 0};
-
- formats->msg_chunks = 2;
-
- pcm_chunks[0] = analog_ports;
- pcm_chunks[1] = analog_ports;
- if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
- pcm_chunks[2] = analog_ports;
-
- if (dir == AMDTP_IN_STREAM) {
- if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
- }
- if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
- }
- } else {
- if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) {
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
- }
-
- // Packets to v2 units include 2 chunks for phone 1/2, except
- // for 176.4/192.0 kHz.
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
+ if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
+ V2_OPT_IFACE_MODE_ADAT) {
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
}
- if (flags & SND_MOTU_SPEC_HAS_AESEBU_IFACE) {
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
+ if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) ==
+ V2_OPT_IFACE_MODE_ADAT) {
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
}
- /*
- * All of v2 models have a pair of coaxial interfaces for digital in/out
- * port. At 44.1/48.0/88.2/96.0 kHz, packets includes PCM from these
- * ports.
- */
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
-
- formats->fixed_part_pcm_chunks[0] = pcm_chunks[0];
- formats->fixed_part_pcm_chunks[1] = pcm_chunks[1];
- formats->fixed_part_pcm_chunks[2] = pcm_chunks[2];
+ return 0;
}
-static void calculate_differed_part(struct snd_motu_packet_format *formats,
- enum snd_motu_spec_flags flags,
- u32 data, u32 mask, u32 shift)
+static int detect_packet_formats_8pre(struct snd_motu *motu, u32 data)
{
- unsigned char pcm_chunks[2] = {0, 0};
-
- /*
- * When optical interfaces are configured for S/PDIF (TOSLINK),
- * the above PCM frames come from them, instead of coaxial
- * interfaces.
- */
- data = (data & mask) >> shift;
- if (data == V2_OPT_IFACE_MODE_ADAT) {
- if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) {
- pcm_chunks[0] += 8;
- pcm_chunks[1] += 4;
- }
- // 8pre has two sets of optical interface and doesn't reduce
- // chunks for ADAT signals.
- if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) {
- pcm_chunks[1] += 4;
- }
+ if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
+ V2_OPT_IFACE_MODE_ADAT) {
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+ motu->tx_packet_formats.pcm_chunks[1] += 8;
}
- /* At mode x4, no data chunks are supported in this part. */
- formats->differed_part_pcm_chunks[0] = pcm_chunks[0];
- formats->differed_part_pcm_chunks[1] = pcm_chunks[1];
+ if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) ==
+ V2_OPT_IFACE_MODE_ADAT) {
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+ motu->rx_packet_formats.pcm_chunks[1] += 8;
+ }
+
+ return 0;
}
-static int v2_cache_packet_formats(struct snd_motu *motu)
+int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu)
{
__be32 reg;
u32 data;
int err;
+ motu->tx_packet_formats.pcm_byte_offset = 10;
+ motu->rx_packet_formats.pcm_byte_offset = 10;
+
+ motu->tx_packet_formats.msg_chunks = 2;
+ motu->rx_packet_formats.msg_chunks = 2;
+
err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
data = be32_to_cpu(reg);
- calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
- motu->spec->flags, motu->spec->analog_in_ports);
- calculate_differed_part(&motu->tx_packet_formats, motu->spec->flags,
- data, V2_OPT_IN_IFACE_MASK, V2_OPT_IN_IFACE_SHIFT);
+ memcpy(motu->tx_packet_formats.pcm_chunks,
+ motu->spec->tx_fixed_pcm_chunks,
+ sizeof(motu->tx_packet_formats.pcm_chunks));
+ memcpy(motu->rx_packet_formats.pcm_chunks,
+ motu->spec->rx_fixed_pcm_chunks,
+ sizeof(motu->rx_packet_formats.pcm_chunks));
- calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
- motu->spec->flags, motu->spec->analog_out_ports);
- calculate_differed_part(&motu->rx_packet_formats, motu->spec->flags,
- data, V2_OPT_OUT_IFACE_MASK, V2_OPT_OUT_IFACE_SHIFT);
+ if (motu->spec == &snd_motu_spec_828mk2)
+ return detect_packet_formats_828mk2(motu, data);
+ else if (motu->spec == &snd_motu_spec_traveler)
+ return detect_packet_formats_traveler(motu, data);
+ else if (motu->spec == &snd_motu_spec_8pre)
+ return detect_packet_formats_8pre(motu, data);
+ else
+ return 0;
+}
- motu->tx_packet_formats.pcm_byte_offset = 10;
- motu->rx_packet_formats.pcm_byte_offset = 10;
+const struct snd_motu_spec snd_motu_spec_828mk2 = {
+ .name = "828mk2",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+ .tx_fixed_pcm_chunks = {14, 14, 0},
+ .rx_fixed_pcm_chunks = {14, 14, 0},
+};
- return 0;
-}
+const struct snd_motu_spec snd_motu_spec_traveler = {
+ .name = "Traveler",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+ .tx_fixed_pcm_chunks = {14, 14, 8},
+ .rx_fixed_pcm_chunks = {14, 14, 8},
+};
+
+const struct snd_motu_spec snd_motu_spec_ultralite = {
+ .name = "UltraLite",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+ .tx_fixed_pcm_chunks = {14, 14, 0},
+ .rx_fixed_pcm_chunks = {14, 14, 0},
+};
-const struct snd_motu_protocol snd_motu_protocol_v2 = {
- .get_clock_rate = v2_get_clock_rate,
- .set_clock_rate = v2_set_clock_rate,
- .get_clock_source = v2_get_clock_source,
- .switch_fetching_mode = v2_switch_fetching_mode,
- .cache_packet_formats = v2_cache_packet_formats,
+const struct snd_motu_spec snd_motu_spec_8pre = {
+ .name = "8pre",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+ .tx_fixed_pcm_chunks = {10, 6, 0},
+ .rx_fixed_pcm_chunks = {10, 6, 0},
};
diff --git a/sound/firewire/motu/motu-protocol-v3.c b/sound/firewire/motu/motu-protocol-v3.c
index d1545e2b5caa..01a47ac7bb2d 100644
--- a/sound/firewire/motu/motu-protocol-v3.c
+++ b/sound/firewire/motu/motu-protocol-v3.c
@@ -24,7 +24,8 @@
#define V3_NO_ADAT_OPT_OUT_IFACE_A 0x00040000
#define V3_NO_ADAT_OPT_OUT_IFACE_B 0x00400000
-static int v3_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
+int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate)
{
__be32 reg;
u32 data;
@@ -45,7 +46,8 @@ static int v3_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
return 0;
}
-static int v3_set_clock_rate(struct snd_motu *motu, unsigned int rate)
+int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate)
{
__be32 reg;
u32 data;
@@ -85,55 +87,102 @@ static int v3_set_clock_rate(struct snd_motu *motu, unsigned int rate)
return 0;
}
-static int v3_get_clock_source(struct snd_motu *motu,
- enum snd_motu_clock_source *src)
+static int detect_clock_source_828mk3(struct snd_motu *motu, u32 data,
+ enum snd_motu_clock_source *src)
{
- __be32 reg;
- u32 data;
- unsigned int val;
- int err;
-
- err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
- sizeof(reg));
- if (err < 0)
- return err;
- data = be32_to_cpu(reg);
-
- val = data & V3_CLOCK_SOURCE_MASK;
- if (val == 0x00) {
+ switch (data) {
+ case 0x00:
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
- } else if (val == 0x01) {
+ break;
+ case 0x01:
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
- } else if (val == 0x02) {
+ break;
+ case 0x02:
*src = SND_MOTU_CLOCK_SOURCE_SPH;
- } else if (val == 0x10) {
+ break;
+ case 0x10:
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
- } else if (val == 0x18 || val == 0x19) {
- err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET,
- &reg, sizeof(reg));
+ break;
+ case 0x18:
+ case 0x19:
+ {
+ __be32 reg;
+ u32 options;
+ int err;
+
+ err = snd_motu_transaction_read(motu,
+ V3_OPT_IFACE_MODE_OFFSET, &reg, sizeof(reg));
if (err < 0)
return err;
- data = be32_to_cpu(reg);
+ options = be32_to_cpu(reg);
- if (val == 0x18) {
- if (data & V3_NO_ADAT_OPT_IN_IFACE_A)
+ if (data == 0x18) {
+ if (options & V3_NO_ADAT_OPT_IN_IFACE_A)
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A;
else
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A;
} else {
- if (data & V3_NO_ADAT_OPT_IN_IFACE_B)
+ if (options & V3_NO_ADAT_OPT_IN_IFACE_B)
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B;
else
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B;
}
- } else {
+
+ break;
+ }
+ default:
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
+ break;
}
return 0;
}
-static int v3_switch_fetching_mode(struct snd_motu *motu, bool enable)
+static int v3_detect_clock_source(struct snd_motu *motu, u32 data,
+ enum snd_motu_clock_source *src)
+{
+ switch (data) {
+ case 0x00:
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ break;
+ case 0x01:
+ *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
+ break;
+ case 0x02:
+ *src = SND_MOTU_CLOCK_SOURCE_SPH;
+ break;
+ case 0x10:
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ break;
+ default:
+ *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & V3_CLOCK_SOURCE_MASK;
+
+ if (motu->spec == &snd_motu_spec_828mk3)
+ return detect_clock_source_828mk3(motu, data, src);
+ else
+ return v3_detect_clock_source(motu, data, src);
+}
+
+int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
+ bool enable)
{
__be32 reg;
u32 data;
@@ -155,162 +204,113 @@ static int v3_switch_fetching_mode(struct snd_motu *motu, bool enable)
sizeof(reg));
}
-static void calculate_fixed_part(struct snd_motu_packet_format *formats,
- enum amdtp_stream_direction dir,
- enum snd_motu_spec_flags flags,
- unsigned char analog_ports)
+static int detect_packet_formats_828mk3(struct snd_motu *motu, u32 data)
{
- unsigned char pcm_chunks[3] = {0, 0, 0};
-
- formats->msg_chunks = 2;
-
- pcm_chunks[0] = analog_ports;
- pcm_chunks[1] = analog_ports;
- if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
- pcm_chunks[2] = analog_ports;
-
- if (dir == AMDTP_IN_STREAM) {
- if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
- if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
- pcm_chunks[2] += 2;
- }
-
- if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
- if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
- pcm_chunks[2] += 2;
- }
-
- if (flags & SND_MOTU_SPEC_TX_REVERB_CHUNK) {
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
- }
- } else {
- if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) {
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
+ if (data & V3_ENABLE_OPT_IN_IFACE_A) {
+ if (data & V3_NO_ADAT_OPT_IN_IFACE_A) {
+ motu->tx_packet_formats.pcm_chunks[0] += 4;
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ } else {
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
}
-
- // Packets to v3 units include 2 chunks for phone 1/2, except
- // for 176.4/192.0 kHz.
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
}
- if (flags & SND_MOTU_SPEC_HAS_AESEBU_IFACE) {
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
+ if (data & V3_ENABLE_OPT_IN_IFACE_B) {
+ if (data & V3_NO_ADAT_OPT_IN_IFACE_B) {
+ motu->tx_packet_formats.pcm_chunks[0] += 4;
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ } else {
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ }
}
- /*
- * At least, packets have two data chunks for S/PDIF on coaxial
- * interface.
- */
- pcm_chunks[0] += 2;
- pcm_chunks[1] += 2;
-
- /*
- * Fixed part consists of PCM chunks multiple of 4, with msg chunks. As
- * a result, this part can includes empty data chunks.
- */
- formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2;
- formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2;
- if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
- formats->fixed_part_pcm_chunks[2] =
- round_up(2 + pcm_chunks[2], 4) - 2;
-}
-
-static void calculate_differed_part(struct snd_motu_packet_format *formats,
- enum snd_motu_spec_flags flags, u32 data,
- u32 a_enable_mask, u32 a_no_adat_mask,
- u32 b_enable_mask, u32 b_no_adat_mask)
-{
- unsigned char pcm_chunks[3] = {0, 0, 0};
- int i;
-
- if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) && (data & a_enable_mask)) {
- if (data & a_no_adat_mask) {
- /*
- * Additional two data chunks for S/PDIF on optical
- * interface A. This includes empty data chunks.
- */
- pcm_chunks[0] += 4;
- pcm_chunks[1] += 4;
+ if (data & V3_ENABLE_OPT_OUT_IFACE_A) {
+ if (data & V3_NO_ADAT_OPT_OUT_IFACE_A) {
+ motu->rx_packet_formats.pcm_chunks[0] += 4;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
} else {
- /*
- * Additional data chunks for ADAT on optical interface
- * A.
- */
- pcm_chunks[0] += 8;
- pcm_chunks[1] += 4;
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
}
}
- if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) && (data & b_enable_mask)) {
- if (data & b_no_adat_mask) {
- /*
- * Additional two data chunks for S/PDIF on optical
- * interface B. This includes empty data chunks.
- */
- pcm_chunks[0] += 4;
- pcm_chunks[1] += 4;
+ if (data & V3_ENABLE_OPT_OUT_IFACE_B) {
+ if (data & V3_NO_ADAT_OPT_OUT_IFACE_B) {
+ motu->rx_packet_formats.pcm_chunks[0] += 4;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
} else {
- /*
- * Additional data chunks for ADAT on optical interface
- * B.
- */
- pcm_chunks[0] += 8;
- pcm_chunks[1] += 4;
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
}
}
- for (i = 0; i < 3; ++i) {
- if (pcm_chunks[i] > 0)
- pcm_chunks[i] = round_up(pcm_chunks[i], 4);
-
- formats->differed_part_pcm_chunks[i] = pcm_chunks[i];
- }
+ return 0;
}
-static int v3_cache_packet_formats(struct snd_motu *motu)
+int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu)
{
__be32 reg;
u32 data;
int err;
+ motu->tx_packet_formats.pcm_byte_offset = 10;
+ motu->rx_packet_formats.pcm_byte_offset = 10;
+
+ motu->tx_packet_formats.msg_chunks = 2;
+ motu->rx_packet_formats.msg_chunks = 2;
+
err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
data = be32_to_cpu(reg);
- calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
- motu->spec->flags, motu->spec->analog_in_ports);
- calculate_differed_part(&motu->tx_packet_formats,
- motu->spec->flags, data,
- V3_ENABLE_OPT_IN_IFACE_A, V3_NO_ADAT_OPT_IN_IFACE_A,
- V3_ENABLE_OPT_IN_IFACE_B, V3_NO_ADAT_OPT_IN_IFACE_B);
+ memcpy(motu->tx_packet_formats.pcm_chunks,
+ motu->spec->tx_fixed_pcm_chunks,
+ sizeof(motu->tx_packet_formats.pcm_chunks));
+ memcpy(motu->rx_packet_formats.pcm_chunks,
+ motu->spec->rx_fixed_pcm_chunks,
+ sizeof(motu->rx_packet_formats.pcm_chunks));
- calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
- motu->spec->flags, motu->spec->analog_out_ports);
- calculate_differed_part(&motu->rx_packet_formats,
- motu->spec->flags, data,
- V3_ENABLE_OPT_OUT_IFACE_A, V3_NO_ADAT_OPT_OUT_IFACE_A,
- V3_ENABLE_OPT_OUT_IFACE_B, V3_NO_ADAT_OPT_OUT_IFACE_B);
+ if (motu->spec == &snd_motu_spec_828mk3)
+ return detect_packet_formats_828mk3(motu, data);
+ else
+ return 0;
+}
- motu->tx_packet_formats.pcm_byte_offset = 10;
- motu->rx_packet_formats.pcm_byte_offset = 10;
- return 0;
-}
+const struct snd_motu_spec snd_motu_spec_828mk3 = {
+ .name = "828mk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q,
+ .tx_fixed_pcm_chunks = {18, 18, 14},
+ .rx_fixed_pcm_chunks = {14, 14, 10},
+};
+
+const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = {
+ .name = "UltraLiteMk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q,
+ .tx_fixed_pcm_chunks = {18, 14, 10},
+ .rx_fixed_pcm_chunks = {14, 14, 14},
+};
+
+const struct snd_motu_spec snd_motu_spec_audio_express = {
+ .name = "AudioExpress",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q,
+ .tx_fixed_pcm_chunks = {10, 10, 0},
+ .rx_fixed_pcm_chunks = {10, 10, 0},
+};
-const struct snd_motu_protocol snd_motu_protocol_v3 = {
- .get_clock_rate = v3_get_clock_rate,
- .set_clock_rate = v3_set_clock_rate,
- .get_clock_source = v3_get_clock_source,
- .switch_fetching_mode = v3_switch_fetching_mode,
- .cache_packet_formats = v3_cache_packet_formats,
+const struct snd_motu_spec snd_motu_spec_4pre = {
+ .name = "4pre",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .tx_fixed_pcm_chunks = {10, 10, 0},
+ .rx_fixed_pcm_chunks = {10, 10, 0},
};
diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c
index a17ddceb1bec..2028c5419f6f 100644
--- a/sound/firewire/motu/motu-stream.c
+++ b/sound/firewire/motu/motu-stream.c
@@ -88,7 +88,7 @@ static void finish_session(struct snd_motu *motu)
u32 data;
int err;
- err = motu->spec->protocol->switch_fetching_mode(motu, false);
+ err = snd_motu_protocol_switch_fetching_mode(motu, false);
if (err < 0)
return;
@@ -110,7 +110,7 @@ int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
{
int err;
- err = motu->spec->protocol->cache_packet_formats(motu);
+ err = snd_motu_protocol_cache_packet_formats(motu);
if (err < 0)
return err;
@@ -140,7 +140,7 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
unsigned int curr_rate;
int err;
- err = motu->spec->protocol->get_clock_rate(motu, &curr_rate);
+ err = snd_motu_protocol_get_clock_rate(motu, &curr_rate);
if (err < 0)
return err;
if (rate == 0)
@@ -153,7 +153,7 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
fw_iso_resources_free(&motu->tx_resources);
fw_iso_resources_free(&motu->rx_resources);
- err = motu->spec->protocol->set_clock_rate(motu, rate);
+ err = snd_motu_protocol_set_clock_rate(motu, rate);
if (err < 0) {
dev_err(&motu->unit->device,
"fail to set sampling rate: %d\n", err);
@@ -201,9 +201,9 @@ static int ensure_packet_formats(struct snd_motu *motu)
data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS |
RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS|
TX_PACKET_TRANSMISSION_SPEED_MASK);
- if (motu->tx_packet_formats.differed_part_pcm_chunks[0] == 0)
+ if (motu->spec->tx_fixed_pcm_chunks[0] == motu->tx_packet_formats.pcm_chunks[0])
data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
- if (motu->rx_packet_formats.differed_part_pcm_chunks[0] == 0)
+ if (motu->spec->rx_fixed_pcm_chunks[0] == motu->rx_packet_formats.pcm_chunks[0])
data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
data |= fw_parent_device(motu->unit)->max_speed;
@@ -272,7 +272,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
goto stop_streams;
}
- err = motu->spec->protocol->switch_fetching_mode(motu, true);
+ err = snd_motu_protocol_switch_fetching_mode(motu, true);
if (err < 0) {
dev_err(&motu->unit->device,
"fail to enable frame fetching: %d\n", err);
@@ -317,7 +317,7 @@ static int init_stream(struct snd_motu *motu, struct amdtp_stream *s)
if (err < 0)
return err;
- err = amdtp_motu_init(s, motu->unit, dir, motu->spec->protocol);
+ err = amdtp_motu_init(s, motu->unit, dir, motu->spec);
if (err < 0)
fw_iso_resources_destroy(resources);
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index f2080d720aa9..a4929c1302dc 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -172,105 +172,6 @@ static void motu_bus_update(struct fw_unit *unit)
snd_motu_transaction_reregister(motu);
}
-const struct snd_motu_spec snd_motu_spec_828mk2 = {
- .name = "828mk2",
- .protocol = &snd_motu_protocol_v2,
- .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
- SND_MOTU_SPEC_TX_MICINST_CHUNK |
- SND_MOTU_SPEC_TX_RETURN_CHUNK |
- SND_MOTU_SPEC_RX_SEPARATED_MAIN |
- SND_MOTU_SPEC_HAS_OPT_IFACE_A |
- SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_2ND_Q,
-
- .analog_in_ports = 8,
- .analog_out_ports = 8,
-};
-
-static const struct snd_motu_spec motu_traveler = {
- .name = "Traveler",
- .protocol = &snd_motu_protocol_v2,
- .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
- SND_MOTU_SPEC_SUPPORT_CLOCK_X4 |
- SND_MOTU_SPEC_TX_RETURN_CHUNK |
- SND_MOTU_SPEC_HAS_AESEBU_IFACE |
- SND_MOTU_SPEC_HAS_OPT_IFACE_A |
- SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_2ND_Q,
-
- .analog_in_ports = 8,
- .analog_out_ports = 8,
-};
-
-static const struct snd_motu_spec motu_ultralite = {
- .name = "UltraLite",
- .protocol = &snd_motu_protocol_v2,
- .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
- SND_MOTU_SPEC_TX_MICINST_CHUNK | // padding.
- SND_MOTU_SPEC_TX_RETURN_CHUNK |
- SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_2ND_Q |
- SND_MOTU_SPEC_RX_SEPARATED_MAIN,
- .analog_in_ports = 8,
- .analog_out_ports = 8,
-};
-
-static const struct snd_motu_spec motu_8pre = {
- .name = "8pre",
- .protocol = &snd_motu_protocol_v2,
- // In tx, use coax chunks for mix-return 1/2. In rx, use coax chunks for
- // dummy 1/2.
- .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
- SND_MOTU_SPEC_HAS_OPT_IFACE_A |
- SND_MOTU_SPEC_HAS_OPT_IFACE_B |
- SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_2ND_Q,
- .analog_in_ports = 8,
- .analog_out_ports = 2,
-};
-
-static const struct snd_motu_spec motu_828mk3 = {
- .name = "828mk3",
- .protocol = &snd_motu_protocol_v3,
- .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
- SND_MOTU_SPEC_SUPPORT_CLOCK_X4 |
- SND_MOTU_SPEC_TX_MICINST_CHUNK |
- SND_MOTU_SPEC_TX_RETURN_CHUNK |
- SND_MOTU_SPEC_TX_REVERB_CHUNK |
- SND_MOTU_SPEC_RX_SEPARATED_MAIN |
- SND_MOTU_SPEC_HAS_OPT_IFACE_A |
- SND_MOTU_SPEC_HAS_OPT_IFACE_B |
- SND_MOTU_SPEC_RX_MIDI_3RD_Q |
- SND_MOTU_SPEC_TX_MIDI_3RD_Q,
-
- .analog_in_ports = 8,
- .analog_out_ports = 8,
-};
-
-static const struct snd_motu_spec motu_audio_express = {
- .name = "AudioExpress",
- .protocol = &snd_motu_protocol_v3,
- .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
- SND_MOTU_SPEC_TX_MICINST_CHUNK |
- SND_MOTU_SPEC_TX_RETURN_CHUNK |
- SND_MOTU_SPEC_RX_SEPARATED_MAIN |
- SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_3RD_Q,
- .analog_in_ports = 2,
- .analog_out_ports = 4,
-};
-
-static const struct snd_motu_spec motu_4pre = {
- .name = "4pre",
- .protocol = &snd_motu_protocol_v3,
- .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
- SND_MOTU_SPEC_TX_MICINST_CHUNK |
- SND_MOTU_SPEC_TX_RETURN_CHUNK |
- SND_MOTU_SPEC_RX_SEPARATED_MAIN,
- .analog_in_ports = 2,
- .analog_out_ports = 2,
-};
-
#define SND_MOTU_DEV_ENTRY(model, data) \
{ \
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
@@ -284,13 +185,14 @@ static const struct snd_motu_spec motu_4pre = {
static const struct ieee1394_device_id motu_id_table[] = {
SND_MOTU_DEV_ENTRY(0x000003, &snd_motu_spec_828mk2),
- SND_MOTU_DEV_ENTRY(0x000009, &motu_traveler),
- SND_MOTU_DEV_ENTRY(0x00000d, &motu_ultralite),
- SND_MOTU_DEV_ENTRY(0x00000f, &motu_8pre),
- SND_MOTU_DEV_ENTRY(0x000015, &motu_828mk3), /* FireWire only. */
- SND_MOTU_DEV_ENTRY(0x000035, &motu_828mk3), /* Hybrid. */
- SND_MOTU_DEV_ENTRY(0x000033, &motu_audio_express),
- SND_MOTU_DEV_ENTRY(0x000045, &motu_4pre),
+ SND_MOTU_DEV_ENTRY(0x000009, &snd_motu_spec_traveler),
+ SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite),
+ SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre),
+ SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3), // FireWire only.
+ SND_MOTU_DEV_ENTRY(0x000019, &snd_motu_spec_ultralite_mk3), // FireWire only.
+ SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3), // Hybrid.
+ SND_MOTU_DEV_ENTRY(0x000033, &snd_motu_spec_audio_express),
+ SND_MOTU_DEV_ENTRY(0x000045, &snd_motu_spec_4pre),
{ }
};
MODULE_DEVICE_TABLE(ieee1394, motu_id_table);
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index 6efbde405a0d..3d0236ee6716 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -36,8 +36,7 @@ struct snd_motu_packet_format {
unsigned char pcm_byte_offset;
unsigned char msg_chunks;
- unsigned char fixed_part_pcm_chunks[3];
- unsigned char differed_part_pcm_chunks[3];
+ unsigned char pcm_chunks[3];
};
struct snd_motu {
@@ -74,19 +73,10 @@ struct snd_motu {
};
enum snd_motu_spec_flags {
- SND_MOTU_SPEC_SUPPORT_CLOCK_X2 = 0x0001,
- SND_MOTU_SPEC_SUPPORT_CLOCK_X4 = 0x0002,
- SND_MOTU_SPEC_TX_MICINST_CHUNK = 0x0004,
- SND_MOTU_SPEC_TX_RETURN_CHUNK = 0x0008,
- SND_MOTU_SPEC_TX_REVERB_CHUNK = 0x0010,
- SND_MOTU_SPEC_HAS_AESEBU_IFACE = 0x0020,
- SND_MOTU_SPEC_HAS_OPT_IFACE_A = 0x0040,
- SND_MOTU_SPEC_HAS_OPT_IFACE_B = 0x0080,
- SND_MOTU_SPEC_RX_MIDI_2ND_Q = 0x0100,
- SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0200,
- SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0400,
- SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0800,
- SND_MOTU_SPEC_RX_SEPARATED_MAIN = 0x1000,
+ SND_MOTU_SPEC_RX_MIDI_2ND_Q = 0x0001,
+ SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0002,
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0004,
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0008,
};
#define SND_MOTU_CLOCK_RATE_COUNT 6
@@ -108,33 +98,33 @@ enum snd_motu_clock_source {
SND_MOTU_CLOCK_SOURCE_UNKNOWN,
};
-struct snd_motu_protocol {
- int (*get_clock_rate)(struct snd_motu *motu, unsigned int *rate);
- int (*set_clock_rate)(struct snd_motu *motu, unsigned int rate);
- int (*get_clock_source)(struct snd_motu *motu,
- enum snd_motu_clock_source *source);
- int (*switch_fetching_mode)(struct snd_motu *motu, bool enable);
- int (*cache_packet_formats)(struct snd_motu *motu);
+enum snd_motu_protocol_version {
+ SND_MOTU_PROTOCOL_V2,
+ SND_MOTU_PROTOCOL_V3,
};
struct snd_motu_spec {
const char *const name;
+ enum snd_motu_protocol_version protocol_version;
enum snd_motu_spec_flags flags;
- unsigned char analog_in_ports;
- unsigned char analog_out_ports;
-
- const struct snd_motu_protocol *const protocol;
+ unsigned char tx_fixed_pcm_chunks[3];
+ unsigned char rx_fixed_pcm_chunks[3];
};
-extern const struct snd_motu_protocol snd_motu_protocol_v2;
-extern const struct snd_motu_protocol snd_motu_protocol_v3;
-
extern const struct snd_motu_spec snd_motu_spec_828mk2;
+extern const struct snd_motu_spec snd_motu_spec_traveler;
+extern const struct snd_motu_spec snd_motu_spec_ultralite;
+extern const struct snd_motu_spec snd_motu_spec_8pre;
+
+extern const struct snd_motu_spec snd_motu_spec_828mk3;
+extern const struct snd_motu_spec snd_motu_spec_ultralite_mk3;
+extern const struct snd_motu_spec snd_motu_spec_audio_express;
+extern const struct snd_motu_spec snd_motu_spec_4pre;
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir,
- const struct snd_motu_protocol *const protocol);
+ const struct snd_motu_spec *spec);
int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
unsigned int midi_ports,
struct snd_motu_packet_format *formats);
@@ -169,4 +159,79 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu);
int snd_motu_create_midi_devices(struct snd_motu *motu);
int snd_motu_create_hwdep_device(struct snd_motu *motu);
+
+int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate);
+int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate);
+int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src);
+int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
+ bool enable);
+int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu);
+
+int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate);
+int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate);
+int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src);
+int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
+ bool enable);
+int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu);
+
+static inline int snd_motu_protocol_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_get_clock_rate(motu, rate);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_get_clock_rate(motu, rate);
+ else
+ return -ENXIO;
+}
+
+static inline int snd_motu_protocol_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_set_clock_rate(motu, rate);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_set_clock_rate(motu, rate);
+ else
+ return -ENXIO;
+}
+
+static inline int snd_motu_protocol_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *source)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_get_clock_source(motu, source);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_get_clock_source(motu, source);
+ else
+ return -ENXIO;
+}
+
+static inline int snd_motu_protocol_switch_fetching_mode(struct snd_motu *motu,
+ bool enable)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_switch_fetching_mode(motu, enable);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_switch_fetching_mode(motu, enable);
+ else
+ return -ENXIO;
+}
+
+static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_cache_packet_formats(motu);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_cache_packet_formats(motu);
+ else
+ return -ENXIO;
+}
+
#endif
diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig
index 4ca6b09056f3..3bc9224d5e4f 100644
--- a/sound/hda/Kconfig
+++ b/sound/hda/Kconfig
@@ -21,16 +21,17 @@ config SND_HDA_EXT_CORE
select SND_HDA_CORE
config SND_HDA_PREALLOC_SIZE
- int "Pre-allocated buffer size for HD-audio driver" if !SND_DMA_SGBUF
+ int "Pre-allocated buffer size for HD-audio driver"
range 0 32768
- default 0 if SND_DMA_SGBUF
+ default 2048 if SND_DMA_SGBUF
default 64 if !SND_DMA_SGBUF
help
Specifies the default pre-allocated buffer-size in kB for the
HD-audio driver. A larger buffer (e.g. 2048) is preferred
for systems using PulseAudio. The default 64 is chosen just
for compatibility reasons.
- On x86 systems, the default is zero as we need no preallocation.
+ On x86 systems, the default is 2048 as a reasonable value for
+ most of modern systems.
Note that the pre-allocation size can be changed dynamically
via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c
index 73bfa71845f6..d0a604c939df 100644
--- a/sound/hda/ext/hdac_ext_bus.c
+++ b/sound/hda/ext/hdac_ext_bus.c
@@ -62,7 +62,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit);
static void default_release(struct device *dev)
{
- snd_hdac_ext_bus_device_exit(container_of(dev, struct hdac_device, dev));
+ snd_hdac_ext_bus_device_exit(dev_to_hdac_dev(dev));
}
/**
diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c
index 3fe62be1cbcc..09ddab5f5cae 100644
--- a/sound/hda/hdac_bus.c
+++ b/sound/hda/hdac_bus.c
@@ -81,7 +81,6 @@ int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
mutex_unlock(&bus->cmd_mutex);
return err;
}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb);
/**
* snd_hdac_bus_exec_verb_unlocked - unlocked version
@@ -150,7 +149,6 @@ void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex)
schedule_work(&bus->unsol_work);
}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event);
/*
* process queued unsolicited events
@@ -162,6 +160,7 @@ static void snd_hdac_bus_process_unsol_events(struct work_struct *work)
struct hdac_driver *drv;
unsigned int rp, caddr, res;
+ spin_lock_irq(&bus->reg_lock);
while (bus->unsol_rp != bus->unsol_wp) {
rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE;
bus->unsol_rp = rp;
@@ -173,10 +172,13 @@ static void snd_hdac_bus_process_unsol_events(struct work_struct *work)
codec = bus->caddr_tbl[caddr & 0x0f];
if (!codec || !codec->dev.driver)
continue;
+ spin_unlock_irq(&bus->reg_lock);
drv = drv_to_hdac_driver(codec->dev.driver);
if (drv->unsol_event)
drv->unsol_event(codec, res);
+ spin_lock_irq(&bus->reg_lock);
}
+ spin_unlock_irq(&bus->reg_lock);
}
/**
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
index bc4a8b606020..011b17cc1efa 100644
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/hdac_controller.c
@@ -9,6 +9,7 @@
#include <sound/core.h>
#include <sound/hdaudio.h>
#include <sound/hda_register.h>
+#include "local.h"
/* clear CORB read pointer properly */
static void azx_clear_corbrp(struct hdac_bus *bus)
@@ -527,6 +528,18 @@ bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
}
bus->chip_init = true;
+
+ /*
+ * Default value of '8' is as per the HD audio specification (Rev 1.0a).
+ * Following relation is used to derive STRIPE control value.
+ * For sample rate <= 48K:
+ * { ((num_channels * bits_per_sample) / number of SDOs) >= 8 }
+ * For sample rate > 48K:
+ * { ((num_channels * bits_per_sample * rate/48000) /
+ * number of SDOs) >= 8 }
+ */
+ bus->sdo_limit = 8;
+
return true;
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_init_chip);
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
index e3119f5cb0d5..333220f0f8af 100644
--- a/sound/hda/hdac_device.c
+++ b/sound/hda/hdac_device.c
@@ -20,7 +20,7 @@ static int get_codec_vendor_name(struct hdac_device *codec);
static void default_release(struct device *dev)
{
- snd_hdac_device_exit(container_of(dev, struct hdac_device, dev));
+ snd_hdac_device_exit(dev_to_hdac_dev(dev));
}
/**
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
index a314b03b4a4c..a38a2af1654f 100644
--- a/sound/hda/hdac_stream.c
+++ b/sound/hda/hdac_stream.c
@@ -38,7 +38,7 @@ int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
else
value = (channels * bits_per_sample) / sdo_line;
- if (value >= 8)
+ if (value >= bus->sdo_limit)
break;
}
diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c
index 5df0d2253844..99aec7349167 100644
--- a/sound/hda/intel-dsp-config.c
+++ b/sound/hda/intel-dsp-config.c
@@ -1,10 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz>
+#include <linux/acpi.h>
#include <linux/bits.h>
#include <linux/dmi.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_intel.h>
#include <sound/core.h>
#include <sound/intel-dsp-config.h>
#include <sound/intel-nhlt.h>
@@ -14,9 +17,14 @@ static int dsp_driver;
module_param(dsp_driver, int, 0444);
MODULE_PARM_DESC(dsp_driver, "Force the DSP driver for Intel DSP (0=auto, 1=legacy, 2=SST, 3=SOF)");
-#define FLAG_SST BIT(0)
-#define FLAG_SOF BIT(1)
-#define FLAG_SOF_ONLY_IF_DMIC BIT(16)
+#define FLAG_SST BIT(0)
+#define FLAG_SOF BIT(1)
+#define FLAG_SST_ONLY_IF_DMIC BIT(15)
+#define FLAG_SOF_ONLY_IF_DMIC BIT(16)
+#define FLAG_SOF_ONLY_IF_SOUNDWIRE BIT(17)
+
+#define FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE (FLAG_SOF_ONLY_IF_DMIC | \
+ FLAG_SOF_ONLY_IF_SOUNDWIRE)
struct config_entry {
u32 flags;
@@ -100,6 +108,10 @@ static const struct config_entry config_table[] = {
{}
}
},
+ {
+ .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
+ .device = 0x9d70,
+ },
#endif
/* Kabylake-LP */
#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL)
@@ -116,6 +128,10 @@ static const struct config_entry config_table[] = {
{}
}
},
+ {
+ .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
+ .device = 0x9d71,
+ },
#endif
/*
@@ -166,7 +182,7 @@ static const struct config_entry config_table[] = {
}
},
{
- .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = 0x9dc8,
},
#endif
@@ -187,7 +203,7 @@ static const struct config_entry config_table[] = {
}
},
{
- .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = 0xa348,
},
#endif
@@ -204,16 +220,48 @@ static const struct config_entry config_table[] = {
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
}
},
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
+ },
+ },
+ {
+ /* early version of SKU 09C6 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
+ },
+ },
{}
}
},
{
- .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = 0x02c8,
},
/* Cometlake-H */
{
- .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .flags = FLAG_SOF,
+ .device = 0x06c8,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
+ },
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = 0x06c8,
},
#endif
@@ -234,7 +282,7 @@ static const struct config_entry config_table[] = {
}
},
{
- .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = 0x34c8,
},
#endif
@@ -254,9 +302,8 @@ static const struct config_entry config_table[] = {
{}
}
},
-
{
- .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = 0xa0c8,
},
#endif
@@ -301,6 +348,28 @@ static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
return ret;
}
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
+static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
+{
+ struct sdw_intel_acpi_info info;
+ acpi_handle handle;
+ int ret;
+
+ handle = ACPI_HANDLE(&pci->dev);
+
+ ret = sdw_intel_acpi_scan(handle, &info);
+ if (ret < 0)
+ return ret;
+
+ return info.link_mask;
+}
+#else
+static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
+{
+ return 0;
+}
+#endif
+
int snd_intel_dsp_driver_probe(struct pci_dev *pci)
{
const struct config_entry *cfg;
@@ -334,22 +403,36 @@ int snd_intel_dsp_driver_probe(struct pci_dev *pci)
return SND_INTEL_DSP_DRIVER_ANY;
if (cfg->flags & FLAG_SOF) {
- if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC) {
+ if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE &&
+ snd_intel_dsp_check_soundwire(pci) > 0) {
+ dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n");
+ return SND_INTEL_DSP_DRIVER_SOF;
+ }
+ if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC &&
+ snd_intel_dsp_check_dmic(pci)) {
+ dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
+ return SND_INTEL_DSP_DRIVER_SOF;
+ }
+ if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE))
+ return SND_INTEL_DSP_DRIVER_SOF;
+ }
+
+
+ if (cfg->flags & FLAG_SST) {
+ if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) {
if (snd_intel_dsp_check_dmic(pci)) {
- dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
- return SND_INTEL_DSP_DRIVER_SOF;
+ dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n");
+ return SND_INTEL_DSP_DRIVER_SST;
}
} else {
- return SND_INTEL_DSP_DRIVER_SOF;
+ return SND_INTEL_DSP_DRIVER_SST;
}
}
- if (cfg->flags & FLAG_SST)
- return SND_INTEL_DSP_DRIVER_SST;
-
return SND_INTEL_DSP_DRIVER_LEGACY;
}
EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel DSP config driver");
+MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT);
diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c
index 73d4f8c4f4c7..059aaf04f536 100644
--- a/sound/hda/intel-nhlt.c
+++ b/sound/hda/intel-nhlt.c
@@ -4,58 +4,25 @@
#include <linux/acpi.h>
#include <sound/intel-nhlt.h>
-#define NHLT_ACPI_HEADER_SIG "NHLT"
-
-/* Unique identification for getting NHLT blobs */
-static const guid_t osc_guid =
- GUID_INIT(0xA69F886E, 0x6CEB, 0x4594,
- 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53);
-
struct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
{
- acpi_handle handle;
- union acpi_object *obj;
- struct nhlt_resource_desc *nhlt_ptr;
- struct nhlt_acpi_table *nhlt_table = NULL;
-
- handle = ACPI_HANDLE(dev);
- if (!handle) {
- dev_err(dev, "Didn't find ACPI_HANDLE\n");
- return NULL;
- }
+ struct nhlt_acpi_table *nhlt;
+ acpi_status status;
- obj = acpi_evaluate_dsm(handle, &osc_guid, 1, 1, NULL);
-
- if (!obj)
- return NULL;
-
- if (obj->type != ACPI_TYPE_BUFFER) {
- dev_dbg(dev, "No NHLT table found\n");
- ACPI_FREE(obj);
+ status = acpi_get_table(ACPI_SIG_NHLT, 0,
+ (struct acpi_table_header **)&nhlt);
+ if (ACPI_FAILURE(status)) {
+ dev_warn(dev, "NHLT table not found\n");
return NULL;
}
- nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer;
- if (nhlt_ptr->length)
- nhlt_table = (struct nhlt_acpi_table *)
- memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
- MEMREMAP_WB);
- ACPI_FREE(obj);
- if (nhlt_table &&
- (strncmp(nhlt_table->header.signature,
- NHLT_ACPI_HEADER_SIG,
- strlen(NHLT_ACPI_HEADER_SIG)) != 0)) {
- memunmap(nhlt_table);
- dev_err(dev, "NHLT ACPI header signature incorrect\n");
- return NULL;
- }
- return nhlt_table;
+ return nhlt;
}
EXPORT_SYMBOL_GPL(intel_nhlt_init);
void intel_nhlt_free(struct nhlt_acpi_table *nhlt)
{
- memunmap((void *)nhlt);
+ acpi_put_table((struct acpi_table_header *)nhlt);
}
EXPORT_SYMBOL_GPL(intel_nhlt_free);
diff --git a/sound/hda/local.h b/sound/hda/local.h
index 5b935219352f..896ba142e8bc 100644
--- a/sound/hda/local.h
+++ b/sound/hda/local.h
@@ -36,6 +36,9 @@ void hda_widget_sysfs_exit(struct hdac_device *codec);
int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec);
void snd_hdac_bus_remove_device(struct hdac_bus *bus,
struct hdac_device *codec);
+void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex);
+int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
+ unsigned int cmd, unsigned int *res);
int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
unsigned int flags, unsigned int *res);
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c
index ce4c8ba2fa98..ca18fe3ff8a5 100644
--- a/sound/isa/ad1816a/ad1816a.c
+++ b/sound/isa/ad1816a/ad1816a.c
@@ -54,7 +54,7 @@ MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0).")
static const struct pnp_card_device_id snd_ad1816a_pnpids[] = {
/* Analog Devices AD1815 */
{ .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } },
- /* Analog Device AD1816? */
+ /* Analog Devices AD1816? */
{ .id = "ADS7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
/* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */
{ .id = "ADS7181", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c
index ff3a05ad99c0..64610571a5e1 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 e764816a8f7a..b039429e6871 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 d06b29693c85..0e6d20e49158 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 c5b1d5900eed..d6420d224d09 100644
--- a/sound/isa/wavefront/wavefront_synth.c
+++ b/sound/isa/wavefront/wavefront_synth.c
@@ -1171,7 +1171,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),
@@ -1202,6 +1205,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/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index ebf926728c5f..45ef0f52ec55 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -1356,7 +1356,7 @@ static int patch_cx20551(struct snd_ac97 *ac97)
}
/*
- * Analog Device AD18xx, AD19xx codecs
+ * Analog Devices AD18xx, AD19xx codecs
*/
#ifdef CONFIG_PM
static void ad18xx_resume(struct snd_ac97 *ac97)
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
index 6e3177bcc709..015c0d676897 100644
--- a/sound/pci/ctxfi/cthw20k1.c
+++ b/sound/pci/ctxfi/cthw20k1.c
@@ -168,7 +168,7 @@ static int src_get_rsc_ctrl_blk(void **rblk)
static int src_put_rsc_ctrl_blk(void *blk)
{
- kfree((struct src_rsc_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -494,7 +494,7 @@ static int src_mgr_get_ctrl_blk(void **rblk)
static int src_mgr_put_ctrl_blk(void *blk)
{
- kfree((struct src_mgr_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -515,7 +515,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk)
static int srcimp_mgr_put_ctrl_blk(void *blk)
{
- kfree((struct srcimp_mgr_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -702,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk)
static int amixer_rsc_put_ctrl_blk(void *blk)
{
- kfree((struct amixer_rsc_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -909,7 +909,7 @@ static int dai_get_ctrl_blk(void **rblk)
static int dai_put_ctrl_blk(void *blk)
{
- kfree((struct dai_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -958,7 +958,7 @@ static int dao_get_ctrl_blk(void **rblk)
static int dao_put_ctrl_blk(void *blk)
{
- kfree((struct dao_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -1156,7 +1156,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
static int daio_mgr_put_ctrl_blk(void *blk)
{
- kfree((struct daio_mgr_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index ddb7c2ce3f7c..def8161cde4c 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -1040,7 +1040,7 @@ static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry,
if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
continue;
- if (reg < 0x49 && val <= 0xffffffff && channel_id <= 2)
+ if (reg < 0x49 && channel_id <= 2)
snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
}
}
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index e1d3082a4fe9..7ba542e45a3d 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -99,10 +99,10 @@ comment "Set to Y if you want auto-loading the codec driver"
depends on SND_HDA=y && SND_HDA_CODEC_REALTEK=m
config SND_HDA_CODEC_ANALOG
- tristate "Build Analog Device HD-audio codec support"
+ tristate "Build Analog Devices HD-audio codec support"
select SND_HDA_GENERIC
help
- Say Y or M here to include Analog Device HD-audio codec support in
+ Say Y or M here to include Analog Devices HD-audio codec support in
snd-hda-intel driver, such as AD1986A.
comment "Set to Y if you want auto-loading the codec driver"
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
index 2c6d2becfe1a..824f4ac1a8ce 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_codec.c b/sound/pci/hda/hda_codec.c
index a34a2c9f4bcf..7e3ae4534df9 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -641,8 +641,18 @@ static void hda_jackpoll_work(struct work_struct *work)
struct hda_codec *codec =
container_of(work, struct hda_codec, jackpoll_work.work);
- snd_hda_jack_set_dirty_all(codec);
- snd_hda_jack_poll_all(codec);
+ /* for non-polling trigger: we need nothing if already powered on */
+ if (!codec->jackpoll_interval && snd_hdac_is_power_on(&codec->core))
+ return;
+
+ /* the power-up/down sequence triggers the runtime resume */
+ snd_hda_power_up_pm(codec);
+ /* update jacks manually if polling is required, too */
+ if (codec->jackpoll_interval) {
+ snd_hda_jack_set_dirty_all(codec);
+ snd_hda_jack_poll_all(codec);
+ }
+ snd_hda_power_down_pm(codec);
if (!codec->jackpoll_interval)
return;
@@ -2951,18 +2961,14 @@ static int hda_codec_runtime_resume(struct device *dev)
static int hda_codec_force_resume(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
- bool forced_resume = !codec->relaxed_resume && codec->jacktbl.used;
int ret;
- /* The get/put pair below enforces the runtime resume even if the
- * device hasn't been used at suspend time. This trick is needed to
- * update the jack state change during the sleep.
- */
- if (forced_resume)
- pm_runtime_get_noresume(dev);
ret = pm_runtime_force_resume(dev);
- if (forced_resume)
- pm_runtime_put(dev);
+ /* schedule jackpoll work for jack detection update */
+ if (codec->jackpoll_interval ||
+ (pm_runtime_suspended(dev) && hda_codec_need_resume(codec)))
+ schedule_delayed_work(&codec->jackpoll_work,
+ codec->jackpoll_interval);
return ret;
}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index bd093593f8fb..3565e2ab0965 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -36,10 +36,10 @@
#include <linux/time.h>
#include <linux/completion.h>
#include <linux/acpi.h>
+#include <linux/pgtable.h>
#ifdef CONFIG_X86
/* for snoop control */
-#include <asm/pgtable.h>
#include <asm/set_memory.h>
#include <asm/cpufeature.h>
#endif
@@ -1004,7 +1004,8 @@ static void __azx_runtime_resume(struct azx *chip, bool from_rt)
if (status && from_rt) {
list_for_each_codec(codec, &chip->bus)
- if (status & (1 << codec->addr))
+ if (!codec->relaxed_resume &&
+ (status & (1 << codec->addr)))
schedule_delayed_work(&codec->jackpoll_work,
codec->jackpoll_interval);
}
@@ -1027,7 +1028,7 @@ static int azx_suspend(struct device *dev)
chip = card->private_data;
bus = azx_bus(chip);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- __azx_runtime_suspend(chip);
+ pm_runtime_force_suspend(dev);
if (bus->irq >= 0) {
free_irq(bus->irq, chip);
bus->irq = -1;
@@ -1055,7 +1056,8 @@ static int azx_resume(struct device *dev)
chip->msi = 0;
if (azx_acquire_irq(chip, 1) < 0)
return -EIO;
- __azx_runtime_resume(chip, false);
+
+ pm_runtime_force_resume(dev);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
trace_azx_resume(chip);
@@ -1071,6 +1073,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);
@@ -1083,6 +1087,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);
@@ -1098,12 +1104,12 @@ static int azx_runtime_suspend(struct device *dev)
if (!azx_is_pm_ready(card))
return 0;
chip = card->private_data;
- if (!azx_has_pm_runtime(chip))
- return 0;
/* enable controller wake up event */
- azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) |
- STATESTS_INT_MASK);
+ if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) {
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) |
+ STATESTS_INT_MASK);
+ }
__azx_runtime_suspend(chip);
trace_azx_runtime_suspend(chip);
@@ -1114,17 +1120,18 @@ static int azx_runtime_resume(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
+ bool from_rt = snd_power_get_state(card) == SNDRV_CTL_POWER_D0;
if (!azx_is_pm_ready(card))
return 0;
chip = card->private_data;
- if (!azx_has_pm_runtime(chip))
- return 0;
- __azx_runtime_resume(chip, true);
+ __azx_runtime_resume(chip, from_rt);
/* disable controller Wake Up event*/
- azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
- ~STATESTS_INT_MASK);
+ if (from_rt) {
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
+ ~STATESTS_INT_MASK);
+ }
trace_azx_runtime_resume(chip);
return 0;
@@ -1199,10 +1206,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",
@@ -1335,12 +1340,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;
@@ -1384,9 +1392,8 @@ 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)
@@ -1402,7 +1409,8 @@ static int azx_dev_disconnect(struct snd_device *device)
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
@@ -1769,7 +1777,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;
@@ -1810,7 +1818,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
err = azx_bus_init(chip, model[dev]);
if (err < 0) {
- kfree(hda);
pci_disable_device(pci);
return err;
}
@@ -2005,7 +2012,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)
@@ -2027,24 +2034,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
@@ -2080,10 +2078,10 @@ static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
* some HD-audio PCI entries are exposed without any codecs, and such devices
* should be ignored from the beginning.
*/
-static const struct snd_pci_quirk driver_blacklist[] = {
- SND_PCI_QUIRK(0x1043, 0x874f, "ASUS ROG Zenith II / Strix", 0),
- SND_PCI_QUIRK(0x1462, 0xcb59, "MSI TRX40 Creator", 0),
- SND_PCI_QUIRK(0x1462, 0xcb60, "MSI TRX40", 0),
+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 */
{}
};
@@ -2103,7 +2101,7 @@ static int azx_probe(struct pci_dev *pci,
bool schedule_probe;
int err;
- if (snd_pci_quirk_lookup(pci, driver_blacklist)) {
+ if (pci_match_id(driver_blacklist, pci)) {
dev_info(&pci->dev, "Skipping the blacklisted device\n");
return -ENODEV;
}
@@ -2308,9 +2306,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) {
@@ -2324,7 +2324,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;
@@ -2341,17 +2341,23 @@ 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_allow(&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)
@@ -2464,6 +2470,9 @@ static const struct pci_device_id azx_ids[] = {
/* 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},
@@ -2472,9 +2481,14 @@ static const struct pci_device_id azx_ids[] = {
/* Tigerlake */
{ PCI_DEVICE(0x8086, 0xa0c8),
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Tigerlake-H */
+ { PCI_DEVICE(0x8086, 0x43c8),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
/* Elkhart Lake */
{ PCI_DEVICE(0x8086, 0x4b55),
.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 },
@@ -2656,6 +2670,9 @@ static const struct pci_device_id azx_ids[] = {
{ PCI_DEVICE(0x1002, 0xab20),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
AZX_DCAPS_PM_RUNTIME },
+ { PCI_DEVICE(0x1002, 0xab28),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
{ PCI_DEVICE(0x1002, 0xab38),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
AZX_DCAPS_PM_RUNTIME },
diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h
index 2acfff3da1a0..3fb119f09040 100644
--- a/sound/pci/hda/hda_intel.h
+++ b/sound/pci/hda/hda_intel.h
@@ -27,6 +27,7 @@ struct hda_intel {
unsigned int use_vga_switcheroo: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_tegra.c b/sound/pci/hda/hda_tegra.c
index 773992a07efa..0cc5fad1af8a 100644
--- a/sound/pci/hda/hda_tegra.c
+++ b/sound/pci/hda/hda_tegra.c
@@ -52,10 +52,21 @@
#define HDA_IPFS_INTR_MASK 0x188
#define HDA_IPFS_EN_INTR (1 << 16)
+/* FPCI */
+#define FPCI_DBG_CFG_2 0x10F4
+#define FPCI_GCAP_NSDO_SHIFT 18
+#define FPCI_GCAP_NSDO_MASK (0x3 << FPCI_GCAP_NSDO_SHIFT)
+
/* max number of SDs */
#define NUM_CAPTURE_SD 1
#define NUM_PLAYBACK_SD 1
+/*
+ * Tegra194 does not reflect correct number of SDO lines. Below macro
+ * is used to update the GCAP register to workaround the issue.
+ */
+#define TEGRA194_NUM_SDO_LINES 4
+
struct hda_tegra {
struct azx chip;
struct device *dev;
@@ -275,6 +286,7 @@ static int hda_tegra_init_clk(struct hda_tegra *hda)
static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
{
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
struct hdac_bus *bus = azx_bus(chip);
struct snd_card *card = chip->card;
int err;
@@ -298,6 +310,26 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
bus->irq = irq_id;
card->sync_irq = bus->irq;
+ /*
+ * Tegra194 has 4 SDO lines and the STRIPE can be used to
+ * indicate how many of the SDO lines the stream should be
+ * striped. But GCAP register does not reflect the true
+ * capability of HW. Below workaround helps to fix this.
+ *
+ * GCAP_NSDO is bits 19:18 in T_AZA_DBG_CFG_2,
+ * 0 for 1 SDO, 1 for 2 SDO, 2 for 4 SDO lines.
+ */
+ if (of_device_is_compatible(np, "nvidia,tegra194-hda")) {
+ u32 val;
+
+ dev_info(card->dev, "Override SDO lines to %u\n",
+ TEGRA194_NUM_SDO_LINES);
+
+ val = readl(hda->regs + FPCI_DBG_CFG_2) & ~FPCI_GCAP_NSDO_MASK;
+ val |= (TEGRA194_NUM_SDO_LINES >> 1) << FPCI_GCAP_NSDO_SHIFT;
+ writel(val, hda->regs + FPCI_DBG_CFG_2);
+ }
+
gcap = azx_readw(chip, GCAP);
dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
@@ -332,6 +364,23 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
/* initialize chip */
azx_init_chip(chip, 1);
+ /*
+ * Playback (for 44.1K/48K, 2-channel, 16-bps) fails with
+ * 4 SDO lines due to legacy design limitation. Following
+ * is, from HD Audio Specification (Revision 1.0a), used to
+ * control striping of the stream across multiple SDO lines
+ * for sample rates <= 48K.
+ *
+ * { ((num_channels * bits_per_sample) / number of SDOs) >= 8 }
+ *
+ * Due to legacy design issue it is recommended that above
+ * ratio must be greater than 8. Since number of SDO lines is
+ * in powers of 2, next available ratio is 16 which can be
+ * used as a limiting factor here.
+ */
+ if (of_device_is_compatible(np, "nvidia,tegra194-hda"))
+ chip->bus.core.sdo_limit = 16;
+
/* codec detection */
if (!bus->codec_mask) {
dev_err(card->dev, "no codecs found!\n");
@@ -408,6 +457,7 @@ static int hda_tegra_create(struct snd_card *card,
static const struct of_device_id hda_tegra_match[] = {
{ .compatible = "nvidia,tegra30-hda" },
+ { .compatible = "nvidia,tegra194-hda" },
{},
};
MODULE_DEVICE_TABLE(of, hda_tegra_match);
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 403baca89452..41eaa89660c3 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -38,6 +38,10 @@ static bool static_hdmi_pcm;
module_param(static_hdmi_pcm, bool, 0644);
MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
+static bool enable_acomp = true;
+module_param(enable_acomp, bool, 0444);
+MODULE_PARM_DESC(enable_acomp, "Enable audio component binding (default=yes)");
+
struct hdmi_spec_per_cvt {
hda_nid_t cvt_nid;
int assigned;
@@ -255,7 +259,7 @@ static int hinfo_to_pcm_index(struct hda_codec *codec,
if (get_pcm_rec(spec, pcm_idx)->stream == hinfo)
return pcm_idx;
- codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo);
+ codec_warn(codec, "HDMI: hinfo %p not tied to a PCM\n", hinfo);
return -EINVAL;
}
@@ -273,7 +277,8 @@ static int hinfo_to_pin_index(struct hda_codec *codec,
return pin_idx;
}
- codec_dbg(codec, "HDMI: hinfo %p not registered\n", hinfo);
+ codec_dbg(codec, "HDMI: hinfo %p (pcm %d) not registered\n", hinfo,
+ hinfo_to_pcm_index(codec, hinfo));
return -EINVAL;
}
@@ -1800,33 +1805,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;
@@ -1844,8 +1859,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;
@@ -2018,7 +2035,7 @@ static const struct hda_pcm_ops generic_ops = {
static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
{
- struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
@@ -2031,7 +2048,7 @@ static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap)
{
- struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
@@ -2045,7 +2062,7 @@ static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap, int prepared)
{
- struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
@@ -2061,7 +2078,7 @@ static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
{
- struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
@@ -2194,7 +2211,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);
}
@@ -2505,6 +2524,11 @@ static void generic_acomp_init(struct hda_codec *codec,
{
struct hdmi_spec *spec = codec->spec;
+ if (!enable_acomp) {
+ codec_info(codec, "audio component disabled by module option\n");
+ return;
+ }
+
spec->port2pin = port2pin;
setup_drm_audio_ops(codec, ops);
if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops,
@@ -3774,7 +3798,7 @@ static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap,
static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac,
hda_nid_t pin_nid, int hdmi_slot, int stream_channel)
{
- struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
int verb;
int ati_channel_setup = 0;
@@ -3810,7 +3834,7 @@ static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac,
static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac,
hda_nid_t pin_nid, int asp_slot)
{
- struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
bool was_odd = false;
int ati_asp_slot = asp_slot;
int verb;
@@ -4132,6 +4156,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),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index de2826f90d34..1b2d8e56390a 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -17,6 +17,7 @@
#include <linux/dmi.h>
#include <linux/module.h>
#include <linux/input.h>
+#include <linux/leds.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/hda_codec.h>
@@ -81,6 +82,7 @@ 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;
@@ -377,12 +379,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:
@@ -797,9 +801,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;
+ }
}
}
@@ -2454,6 +2460,8 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
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),
@@ -2469,6 +2477,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),
@@ -4072,11 +4083,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 */
}
@@ -4087,7 +4096,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 */
@@ -4096,9 +4106,30 @@ 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);
}
+#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO)
+static int micmute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct alc_spec *spec = codec->spec;
+
+ alc_update_gpio_led(codec, spec->gpio_mic_led_mask,
+ spec->micmute_led_polarity, !!brightness);
+ return 0;
+}
+
+static struct led_classdev micmute_led_cdev = {
+ .name = "hda::micmute",
+ .max_brightness = 1,
+ .brightness_set_blocking = micmute_led_set,
+ .default_trigger = "audio-micmute",
+};
+#endif
+
/* setup mute and mic-mute GPIO bits, add hooks appropriately */
static void alc_fixup_hp_gpio_led(struct hda_codec *codec,
int action,
@@ -4106,6 +4137,9 @@ static void alc_fixup_hp_gpio_led(struct hda_codec *codec,
unsigned int micmute_mask)
{
struct alc_spec *spec = codec->spec;
+#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO)
+ int err;
+#endif
alc_fixup_gpio(codec, action, mute_mask | micmute_mask);
@@ -4118,6 +4152,13 @@ static void alc_fixup_hp_gpio_led(struct hda_codec *codec,
if (micmute_mask) {
spec->gpio_mic_led_mask = micmute_mask;
snd_hda_gen_add_micmute_led(codec, alc_gpio_micmute_update);
+
+#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO)
+ micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
+ err = devm_led_classdev_register(&codec->core.dev, &micmute_led_cdev);
+ if (err)
+ codec_warn(codec, "failed to register micmute LED\n");
+#endif
}
}
@@ -4130,7 +4171,11 @@ static void alc269_fixup_hp_gpio_led(struct hda_codec *codec,
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);
+ struct alc_spec *spec = codec->spec;
+
+ spec->micmute_led_polarity = 1;
+
+ alc_fixup_hp_gpio_led(codec, action, 0x04, 0x01);
}
static void alc286_fixup_hp_gpio_led(struct hda_codec *codec,
@@ -5477,18 +5522,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 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;
spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
snd_hda_apply_pincfgs(codec, pincfgs);
} else if (action == HDA_FIXUP_ACT_INIT) {
@@ -5501,6 +5537,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;
@@ -5792,7 +5845,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.
@@ -5853,6 +5907,15 @@ 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)
@@ -5957,6 +6020,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,
@@ -6042,9 +6106,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,
@@ -6077,9 +6143,17 @@ enum {
ALC294_FIXUP_ASUS_DUAL_SPK,
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_G401,
+ ALC256_FIXUP_ACER_MIC_NO_PRESENCE,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -6277,6 +6351,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,
@@ -6929,6 +7009,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] = {
@@ -6961,12 +7047,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[]) {
@@ -7027,7 +7119,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,
@@ -7036,7 +7128,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,
@@ -7044,6 +7136,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,
@@ -7201,6 +7295,17 @@ static const struct hda_fixup alc269_fixups[] = {
.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,
@@ -7213,6 +7318,67 @@ static const struct hda_fixup alc269_fixups[] = {
.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_G401] = {
+ .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
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -7228,16 +7394,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),
@@ -7357,6 +7527,8 @@ 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_LED),
SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED),
@@ -7378,13 +7550,18 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
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, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_G401),
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),
@@ -7404,9 +7581,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", ALC225_FIXUP_HEADSET_JACK),
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),
@@ -7416,12 +7597,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),
@@ -7449,8 +7631,7 @@ 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 Yoga 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK),
- SND_PCI_QUIRK(0x17aa, 0x2293, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK),
+ 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),
@@ -7560,6 +7741,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"},
@@ -7571,6 +7753,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"},
@@ -7988,6 +8171,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}),
@@ -8028,6 +8223,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}),
{}
};
@@ -8195,7 +8396,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;
@@ -8203,8 +8406,6 @@ static int patch_alc269(struct hda_codec *codec)
spec->gen.mixer_nid = 0;
break;
case 0x10ec0225:
- codec->power_save_node = 1;
- /* fall through */
case 0x10ec0295:
case 0x10ec0299:
spec->codec_variant = ALC269_TYPE_ALC225;
@@ -9456,6 +9657,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),
@@ -9475,6 +9677,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),
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 884d0cdec08c..73e1e5400506 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -2332,7 +2332,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/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c
index 6a0520c4fb5a..cf801a235df9 100644
--- a/sound/pci/oxygen/xonar_pcm179x.c
+++ b/sound/pci/oxygen/xonar_pcm179x.c
@@ -460,7 +460,7 @@ static void xonar_st_init(struct oxygen *chip)
data->generic.anti_pop_delay = 100;
data->h6 = chip->model.dac_channels_mixer > 2;
- data->has_cs2000 = 1;
+ data->has_cs2000 = true;
data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1;
data->broken_i2c = true;
@@ -502,7 +502,7 @@ static void xonar_xense_init(struct oxygen *chip)
xonar_init_ext_power(chip);
data->generic.anti_pop_delay = 100;
- data->has_cs2000 = 1;
+ data->has_cs2000 = true;
data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1;
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c
index 592532c09a82..2e750b317be1 100644
--- a/sound/ppc/pmac.c
+++ b/sound/ppc/pmac.c
@@ -226,7 +226,7 @@ static int snd_pmac_pcm_prepare(struct snd_pmac *chip, struct pmac_stream *rec,
offset += rec->period_size;
}
/* make loop */
- cp->command = cpu_to_le16(DBDMA_NOP + BR_ALWAYS);
+ cp->command = cpu_to_le16(DBDMA_NOP | BR_ALWAYS);
cp->cmd_dep = cpu_to_le32(rec->cmd.addr);
snd_pmac_dma_stop(rec);
@@ -726,7 +726,7 @@ void snd_pmac_beep_dma_start(struct snd_pmac *chip, int bytes, unsigned long add
chip->extra_dma.cmds->xfer_status = cpu_to_le16(0);
chip->extra_dma.cmds->cmd_dep = cpu_to_le32(chip->extra_dma.addr);
chip->extra_dma.cmds->phy_addr = cpu_to_le32(addr);
- chip->extra_dma.cmds->command = cpu_to_le16(OUTPUT_MORE + BR_ALWAYS);
+ chip->extra_dma.cmds->command = cpu_to_le16(OUTPUT_MORE | BR_ALWAYS);
out_le32(&chip->awacs->control,
(in_le32(&chip->awacs->control) & ~0x1f00)
| (speed << 8));
diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c
index 6d2a33b8faa0..b8161a08f2ca 100644
--- a/sound/ppc/snd_ps3.c
+++ b/sound/ppc/snd_ps3.c
@@ -926,7 +926,7 @@ static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
PAGE_SHIFT, /* use system page size */
0, /* dma type; not used */
NULL,
- _ALIGN_UP(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE));
+ ALIGN(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE));
dev->d_region->ioid = PS3_AUDIO_IOID;
ret = ps3_dma_region_create(dev->d_region);
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index bdc305cece6e..71a6fe87d1a1 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -10,7 +10,7 @@ menuconfig SND_SOC
select SND_JACK
select REGMAP_I2C if I2C
select REGMAP_SPI if SPI_MASTER
- ---help---
+ help
If you want ASoC support, you should say Y here and also to the
specific driver for your SoC platform below.
diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h
index 598b07d9b6fe..d0efc5e254ae 100644
--- a/sound/soc/codecs/hdac_hda.h
+++ b/sound/soc/codecs/hdac_hda.h
@@ -28,10 +28,6 @@ struct hdac_hda_priv {
bool need_display_power;
};
-#define hdac_to_hda_priv(_hdac) \
- container_of(_hdac, struct hdac_hda_priv, codec.core)
-#define hdac_to_hda_codec(_hdac) container_of(_hdac, struct hda_codec, core)
-
struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void);
#endif /* __HDAC_HDA_H__ */
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
index dc05c57db03a..c72cb2888c21 100644
--- a/sound/soc/codecs/max9867.c
+++ b/sound/soc/codecs/max9867.c
@@ -59,19 +59,19 @@ 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),
SOC_SINGLE_TLV("Digital Boost Playback Volume", MAX9867_DACLEVEL, 4, 3, 0,
max9867_dacboost_tlv),
- SOC_DOUBLE_TLV("Digital Capture Volume", MAX9867_ADCLEVEL, 0, 4, 15, 1,
+ SOC_DOUBLE_TLV("Digital Capture Volume", MAX9867_ADCLEVEL, 4, 0, 15, 1,
max9867_adc_tlv),
SOC_ENUM("Speaker Mode", max9867_spkmode),
SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0),
diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c
index d27947aeb079..0594f89ea7f2 100644
--- a/sound/soc/intel/common/sst-firmware.c
+++ b/sound/soc/intel/common/sst-firmware.c
@@ -16,12 +16,12 @@
#include <linux/dmaengine.h>
#include <linux/pci.h>
#include <linux/acpi.h>
+#include <linux/pgtable.h>
/* supported DMA engine drivers */
#include <linux/dma/dw.h>
#include <asm/page.h>
-#include <asm/pgtable.h>
#include "sst-dsp.h"
#include "sst-dsp-priv.h"
diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c
index c183f8e94ee4..16ac16f5a641 100644
--- a/sound/soc/intel/haswell/sst-haswell-pcm.c
+++ b/sound/soc/intel/haswell/sst-haswell-pcm.c
@@ -10,8 +10,8 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
+#include <linux/pgtable.h>
#include <asm/page.h>
-#include <asm/pgtable.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 112911dc271b..99a49248e966 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -4,7 +4,7 @@ menuconfig SND_SOC_SAMSUNG
depends on PLAT_SAMSUNG || ARCH_EXYNOS || COMPILE_TEST
depends on COMMON_CLK
select SND_SOC_GENERIC_DMAENGINE_PCM
- ---help---
+ help
Say Y or M if you want to add support for codecs attached to
the Samsung SoCs' Audio interfaces. You will also need to
select the audio interfaces to support below.
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 653a58c96e24..2491e1ce16d3 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -4338,16 +4338,16 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card,
codec = codec_dai->playback_widget;
if (playback_cpu && codec) {
- if (dai_link->params && !dai_link->playback_widget) {
+ if (dai_link->params && !rtd->playback_widget) {
substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
dai = snd_soc_dapm_new_dai(card, substream, "playback");
if (IS_ERR(dai))
goto capture;
- dai_link->playback_widget = dai;
+ rtd->playback_widget = dai;
}
dapm_connect_dai_routes(&card->dapm, cpu_dai, playback_cpu,
- dai_link->playback_widget,
+ rtd->playback_widget,
codec_dai, codec);
}
@@ -4356,16 +4356,16 @@ capture:
codec = codec_dai->capture_widget;
if (codec && capture_cpu) {
- if (dai_link->params && !dai_link->capture_widget) {
+ if (dai_link->params && !rtd->capture_widget) {
substream = streams[SNDRV_PCM_STREAM_CAPTURE].substream;
dai = snd_soc_dapm_new_dai(card, substream, "capture");
if (IS_ERR(dai))
return;
- dai_link->capture_widget = dai;
+ rtd->capture_widget = dai;
}
dapm_connect_dai_routes(&card->dapm, codec_dai, codec,
- dai_link->capture_widget,
+ rtd->capture_widget,
cpu_dai, capture_cpu);
}
}
diff --git a/sound/soc/sof/probe.h b/sound/soc/sof/probe.h
index b04b728c7224..5e159ab239fa 100644
--- a/sound/soc/sof/probe.h
+++ b/sound/soc/sof/probe.h
@@ -36,7 +36,7 @@ struct sof_probe_point_desc {
struct sof_ipc_probe_dma_add_params {
struct sof_ipc_cmd_hdr hdr;
unsigned int num_elems;
- struct sof_probe_dma dma[0];
+ struct sof_probe_dma dma[];
} __packed;
struct sof_ipc_probe_info_params {
@@ -51,19 +51,19 @@ struct sof_ipc_probe_info_params {
struct sof_ipc_probe_dma_remove_params {
struct sof_ipc_cmd_hdr hdr;
unsigned int num_elems;
- unsigned int stream_tag[0];
+ unsigned int stream_tag[];
} __packed;
struct sof_ipc_probe_point_add_params {
struct sof_ipc_cmd_hdr hdr;
unsigned int num_elems;
- struct sof_probe_point_desc desc[0];
+ struct sof_probe_point_desc desc[];
} __packed;
struct sof_ipc_probe_point_remove_params {
struct sof_ipc_cmd_hdr hdr;
unsigned int num_elems;
- unsigned int buffer_id[0];
+ unsigned int buffer_id[];
} __packed;
int sof_ipc_probe_init(struct snd_sof_dev *sdev,
diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c
index b1d9615f2375..d9e348444bd0 100644
--- a/sound/soc/txx9/txx9aclc-ac97.c
+++ b/sound/soc/txx9/txx9aclc-ac97.c
@@ -14,6 +14,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/gfp.h>
+#include <asm/mach-tx39xx/ioremap.h> /* for TXX9_DIRECTMAP_BASE */
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
diff --git a/sound/usb/card.c b/sound/usb/card.c
index fd6fd1726ea0..162bdd6eb4d4 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -634,7 +634,6 @@ static int usb_audio_probe(struct usb_interface *intf,
id, &chip);
if (err < 0)
goto __error;
- chip->pm_intf = intf;
break;
} else if (vid[i] != -1 || pid[i] != -1) {
dev_info(&dev->dev,
@@ -651,6 +650,13 @@ static int usb_audio_probe(struct usb_interface *intf,
goto __error;
}
}
+
+ if (chip->num_interfaces >= MAX_CARD_INTERFACES) {
+ dev_info(&dev->dev, "Too many interfaces assigned to the single USB-audio card\n");
+ err = -EINVAL;
+ goto __error;
+ }
+
dev_set_drvdata(&dev->dev, chip);
/*
@@ -703,6 +709,7 @@ static int usb_audio_probe(struct usb_interface *intf,
}
usb_chip[chip->index] = chip;
+ chip->intf[chip->num_interfaces] = intf;
chip->num_interfaces++;
usb_set_intfdata(intf, chip);
atomic_dec(&chip->active);
@@ -818,19 +825,37 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip)
int snd_usb_autoresume(struct snd_usb_audio *chip)
{
+ int i, err;
+
if (atomic_read(&chip->shutdown))
return -EIO;
- if (atomic_inc_return(&chip->active) == 1)
- return usb_autopm_get_interface(chip->pm_intf);
+ if (atomic_inc_return(&chip->active) != 1)
+ return 0;
+
+ for (i = 0; i < chip->num_interfaces; i++) {
+ err = usb_autopm_get_interface(chip->intf[i]);
+ if (err < 0) {
+ /* rollback */
+ while (--i >= 0)
+ usb_autopm_put_interface(chip->intf[i]);
+ atomic_dec(&chip->active);
+ return err;
+ }
+ }
return 0;
}
void snd_usb_autosuspend(struct snd_usb_audio *chip)
{
+ int i;
+
if (atomic_read(&chip->shutdown))
return;
- if (atomic_dec_and_test(&chip->active))
- usb_autopm_put_interface(chip->pm_intf);
+ if (!atomic_dec_and_test(&chip->active))
+ return;
+
+ for (i = 0; i < chip->num_interfaces; i++)
+ usb_autopm_put_interface(chip->intf[i]);
}
static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
@@ -843,9 +868,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);
@@ -858,6 +880,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;
}
@@ -871,10 +898,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);
@@ -896,9 +923,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 395403a2d33f..de43267b9c8a 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;
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 4a9a2f6ef5a4..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];
@@ -497,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);
@@ -597,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,
@@ -861,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 */
@@ -874,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;
@@ -936,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 50e1874c847c..1b28d01d1f4c 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -278,6 +278,52 @@ static bool s1810c_valid_sample_rate(struct audioformat *fp,
}
/*
+ * Many Focusrite devices supports a limited set of sampling rates per
+ * altsetting. Maximum rate is exposed in the last 4 bytes of Format Type
+ * descriptor which has a non-standard bLength = 10.
+ */
+static bool focusrite_valid_sample_rate(struct snd_usb_audio *chip,
+ struct audioformat *fp,
+ unsigned int rate)
+{
+ struct usb_interface *iface;
+ struct usb_host_interface *alts;
+ unsigned char *fmt;
+ unsigned int max_rate;
+
+ iface = usb_ifnum_to_if(chip->dev, fp->iface);
+ if (!iface)
+ return true;
+
+ alts = &iface->altsetting[fp->altset_idx];
+ fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen,
+ NULL, UAC_FORMAT_TYPE);
+ if (!fmt)
+ return true;
+
+ if (fmt[0] == 10) { /* bLength */
+ max_rate = combine_quad(&fmt[6]);
+
+ /* Validate max rate */
+ if (max_rate != 48000 &&
+ max_rate != 96000 &&
+ max_rate != 192000 &&
+ max_rate != 384000) {
+
+ usb_audio_info(chip,
+ "%u:%d : unexpected max rate: %u\n",
+ fp->iface, fp->altsetting, max_rate);
+
+ return true;
+ }
+
+ return rate <= max_rate;
+ }
+
+ return true;
+}
+
+/*
* Helper function to walk the array of sample rate triplets reported by
* the device. The problem is that we need to parse whole array first to
* get to know how many sample rates we have to expect.
@@ -319,6 +365,11 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
!s1810c_valid_sample_rate(fp, rate))
goto skip_rate;
+ /* Filter out invalid rates on Focusrite devices */
+ if (USB_ID_VENDOR(chip->usb_id) == 0x1235 &&
+ !focusrite_valid_sample_rate(chip, fp, rate))
+ goto skip_rate;
+
if (fp->rate_table)
fp->rate_table[nr_rates] = rate;
if (!fp->rate_min || rate < fp->rate_min)
@@ -343,8 +394,9 @@ skip_rate:
return nr_rates;
}
-/* Line6 Helix series don't support the UAC2_CS_RANGE usb function
- * call. Return a static table of known clock rates.
+/* Line6 Helix series and the Rode Rodecaster Pro 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)
@@ -357,6 +409,7 @@ static int line6_parse_audio_format_rates_quirk(struct snd_usb_audio *chip,
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 */
+ case USB_ID(0x19f7, 0x0011): /* Rode Rodecaster Pro */
return set_fixed_rate(fp, 48000, SNDRV_PCM_RATE_48000);
}
diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index 663d608c4287..970c9bdce0b2 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -286,6 +286,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 4f096685ed65..2746d9698180 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -550,6 +550,7 @@ static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file)
/* NOTE: hwdep layer provides atomicity here */
line6->messages.active = 1;
+ line6->messages.nonblock = file->f_flags & O_NONBLOCK ? 1 : 0;
return 0;
}
@@ -579,6 +580,9 @@ line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
while (kfifo_len(&line6->messages.fifo) == 0) {
mutex_unlock(&line6->messages.read_lock);
+ if (line6->messages.nonblock)
+ return -EAGAIN;
+
rv = wait_event_interruptible(
line6->messages.wait_queue,
kfifo_len(&line6->messages.fifo) != 0);
@@ -626,11 +630,27 @@ line6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
return rv;
}
+static __poll_t
+line6_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
+{
+ __poll_t rv;
+ struct usb_line6 *line6 = hwdep->private_data;
+
+ poll_wait(file, &line6->messages.wait_queue, wait);
+
+ mutex_lock(&line6->messages.read_lock);
+ rv = kfifo_len(&line6->messages.fifo) == 0 ? 0 : EPOLLIN | EPOLLRDNORM;
+ mutex_unlock(&line6->messages.read_lock);
+
+ return rv;
+}
+
static const struct snd_hwdep_ops hwdep_ops = {
.open = line6_hwdep_open,
.release = line6_hwdep_release,
.read = line6_hwdep_read,
.write = line6_hwdep_write,
+ .poll = line6_hwdep_poll,
};
/* Insert into circular buffer */
@@ -820,7 +840,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/driver.h b/sound/usb/line6/driver.h
index e5e572ed5f30..1a4e3700c80c 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -163,6 +163,7 @@ struct usb_line6 {
struct mutex read_lock;
wait_queue_head_t wait_queue;
unsigned int active:1;
+ unsigned int nonblock:1;
STRUCT_KFIFO_REC_2(LINE6_BUFSIZE_LISTEN * LINE6_RAW_MESSAGES_MAXCOUNT)
fifo;
} messages;
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 01930ce7bd75..8233c61e23f1 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -431,6 +431,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 d37db32ecd3b..e39dc85c355a 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -21,8 +21,7 @@
enum {
LINE6_PODHD300,
LINE6_PODHD400,
- LINE6_PODHD500_0,
- LINE6_PODHD500_1,
+ LINE6_PODHD500,
LINE6_PODX3,
LINE6_PODX3LIVE,
LINE6_PODHD500X,
@@ -318,8 +317,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 },
@@ -352,23 +350,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
+ .capabilities = LINE6_CAP_PCM | LINE6_CAP_CONTROL
| LINE6_CAP_HWMON,
.altsetting = 1,
- .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
- | LINE6_CAP_HWMON,
- .altsetting = 0,
+ .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 047b90595d65..354f57692938 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);
@@ -2301,16 +2302,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);
}
}
@@ -2326,7 +2333,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 721d12130d0c..eab0fd4fd7c3 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -581,8 +581,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;
@@ -596,6 +597,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;
@@ -1182,6 +1184,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;
}
}
@@ -1457,7 +1467,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;
@@ -1771,10 +1781,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)
@@ -1805,8 +1821,12 @@ static void build_connector_control(struct usb_mixer_interface *mixer,
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);
}
@@ -2109,8 +2129,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;
}
@@ -3071,13 +3092,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;
@@ -3106,7 +3127,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;
}
}
@@ -3149,10 +3171,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(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;
@@ -3174,10 +3197,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);
}
}
}
@@ -3185,13 +3209,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,
@@ -3258,6 +3307,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++;
@@ -3269,6 +3320,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 65d6d08c96f5..c29e27ac43a7 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;
@@ -56,6 +66,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;
};
@@ -93,8 +104,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 72b575c34860..9af7aa93f6fa 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;
};
@@ -360,13 +361,57 @@ static const struct usbmix_name_map corsair_virtuoso_map[] = {
};
/* Some mobos shipped with a dummy HD-audio show the invalid GET_MIN/GET_MAX
- * response for Input Gain Pad (id=19, control=12). Skip it.
+ * 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
*/
@@ -496,9 +541,14 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
.id = USB_ID(0x1b1c, 0x0a42),
.map = corsair_virtuoso_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 = asus_rog_map,
+ .map = trx40_mobo_map,
+ .connector_map = trx40_mobo_connector_map,
},
{ /* ASUS ROG Zenith II */
.id = USB_ID(0x0b05, 0x1916),
@@ -510,11 +560,18 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
},
{ /* MSI TRX40 Creator */
.id = USB_ID(0x0db0, 0x0d64),
- .map = asus_rog_map,
+ .map = trx40_mobo_map,
+ .connector_map = trx40_mobo_connector_map,
},
{ /* MSI TRX40 */
.id = USB_ID(0x0db0, 0x543d),
- .map = asus_rog_map,
+ .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 02b036b2aefb..cec1cfd7edb7 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -158,7 +158,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);
}
/*
@@ -1509,11 +1510,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,
@@ -2181,6 +2186,421 @@ static int snd_rme_controls_create(struct usb_mixer_interface *mixer)
return 0;
}
+/*
+ * RME Babyface Pro (FS)
+ *
+ * These devices exposes a couple of DSP functions via request to EP0.
+ * Switches are available via control registers, while routing is controlled
+ * by controlling the volume on each possible crossing point.
+ * Volume control is linear, from -inf (dec. 0) to +6dB (dec. 65536) with
+ * 0dB being at dec. 32768.
+ */
+enum {
+ SND_BBFPRO_CTL_REG1 = 0,
+ SND_BBFPRO_CTL_REG2
+};
+
+#define SND_BBFPRO_CTL_REG_MASK 1
+#define SND_BBFPRO_CTL_IDX_MASK 0xff
+#define SND_BBFPRO_CTL_IDX_SHIFT 1
+#define SND_BBFPRO_CTL_VAL_MASK 1
+#define SND_BBFPRO_CTL_VAL_SHIFT 9
+#define SND_BBFPRO_CTL_REG1_CLK_MASTER 0
+#define SND_BBFPRO_CTL_REG1_CLK_OPTICAL 1
+#define SND_BBFPRO_CTL_REG1_SPDIF_PRO 7
+#define SND_BBFPRO_CTL_REG1_SPDIF_EMPH 8
+#define SND_BBFPRO_CTL_REG1_SPDIF_OPTICAL 10
+#define SND_BBFPRO_CTL_REG2_48V_AN1 0
+#define SND_BBFPRO_CTL_REG2_48V_AN2 1
+#define SND_BBFPRO_CTL_REG2_SENS_IN3 2
+#define SND_BBFPRO_CTL_REG2_SENS_IN4 3
+#define SND_BBFPRO_CTL_REG2_PAD_AN1 4
+#define SND_BBFPRO_CTL_REG2_PAD_AN2 5
+
+#define SND_BBFPRO_MIXER_IDX_MASK 0x1ff
+#define SND_BBFPRO_MIXER_VAL_MASK 0x3ffff
+#define SND_BBFPRO_MIXER_VAL_SHIFT 9
+#define SND_BBFPRO_MIXER_VAL_MIN 0 // -inf
+#define SND_BBFPRO_MIXER_VAL_MAX 65536 // +6dB
+
+#define SND_BBFPRO_USBREQ_CTL_REG1 0x10
+#define SND_BBFPRO_USBREQ_CTL_REG2 0x17
+#define SND_BBFPRO_USBREQ_MIXER 0x12
+
+static int snd_bbfpro_ctl_update(struct usb_mixer_interface *mixer, u8 reg,
+ u8 index, u8 value)
+{
+ int err;
+ u16 usb_req, usb_idx, usb_val;
+ struct snd_usb_audio *chip = mixer->chip;
+
+ err = snd_usb_lock_shutdown(chip);
+ if (err < 0)
+ return err;
+
+ if (reg == SND_BBFPRO_CTL_REG1) {
+ usb_req = SND_BBFPRO_USBREQ_CTL_REG1;
+ if (index == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) {
+ usb_idx = 3;
+ usb_val = value ? 3 : 0;
+ } else {
+ usb_idx = 1 << index;
+ usb_val = value ? usb_idx : 0;
+ }
+ } else {
+ usb_req = SND_BBFPRO_USBREQ_CTL_REG2;
+ usb_idx = 1 << index;
+ usb_val = value ? usb_idx : 0;
+ }
+
+ err = snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0), usb_req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ usb_val, usb_idx, NULL, 0);
+
+ snd_usb_unlock_shutdown(chip);
+ return err;
+}
+
+static int snd_bbfpro_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 reg, idx, val;
+ int pv;
+
+ pv = kcontrol->private_value;
+ reg = pv & SND_BBFPRO_CTL_REG_MASK;
+ idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
+ val = kcontrol->private_value >> SND_BBFPRO_CTL_VAL_SHIFT;
+
+ if ((reg == SND_BBFPRO_CTL_REG1 &&
+ idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) ||
+ (reg == SND_BBFPRO_CTL_REG2 &&
+ (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 ||
+ idx == SND_BBFPRO_CTL_REG2_SENS_IN4))) {
+ ucontrol->value.enumerated.item[0] = val;
+ } else {
+ ucontrol->value.integer.value[0] = val;
+ }
+ return 0;
+}
+
+static int snd_bbfpro_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ u8 reg, idx;
+ int pv;
+
+ pv = kcontrol->private_value;
+ reg = pv & SND_BBFPRO_CTL_REG_MASK;
+ idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
+
+ if (reg == SND_BBFPRO_CTL_REG1 &&
+ idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) {
+ static const char * const texts[2] = {
+ "AutoSync",
+ "Internal"
+ };
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
+ } else if (reg == SND_BBFPRO_CTL_REG2 &&
+ (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 ||
+ idx == SND_BBFPRO_CTL_REG2_SENS_IN4)) {
+ static const char * const texts[2] = {
+ "-10dBV",
+ "+4dBu"
+ };
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
+ }
+
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ return 0;
+}
+
+static int snd_bbfpro_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int err;
+ u8 reg, idx;
+ int old_value, pv, val;
+
+ struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
+ struct usb_mixer_interface *mixer = list->mixer;
+
+ pv = kcontrol->private_value;
+ reg = pv & SND_BBFPRO_CTL_REG_MASK;
+ idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
+ old_value = (pv >> SND_BBFPRO_CTL_VAL_SHIFT) & SND_BBFPRO_CTL_VAL_MASK;
+
+ if ((reg == SND_BBFPRO_CTL_REG1 &&
+ idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) ||
+ (reg == SND_BBFPRO_CTL_REG2 &&
+ (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 ||
+ idx == SND_BBFPRO_CTL_REG2_SENS_IN4))) {
+ val = ucontrol->value.enumerated.item[0];
+ } else {
+ val = ucontrol->value.integer.value[0];
+ }
+
+ if (val > 1)
+ return -EINVAL;
+
+ if (val == old_value)
+ return 0;
+
+ kcontrol->private_value = reg
+ | ((idx & SND_BBFPRO_CTL_IDX_MASK) << SND_BBFPRO_CTL_IDX_SHIFT)
+ | ((val & SND_BBFPRO_CTL_VAL_MASK) << SND_BBFPRO_CTL_VAL_SHIFT);
+
+ err = snd_bbfpro_ctl_update(mixer, reg, idx, val);
+ return err < 0 ? err : 1;
+}
+
+static int snd_bbfpro_ctl_resume(struct usb_mixer_elem_list *list)
+{
+ u8 reg, idx;
+ int value, pv;
+
+ pv = list->kctl->private_value;
+ reg = pv & SND_BBFPRO_CTL_REG_MASK;
+ idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
+ value = (pv >> SND_BBFPRO_CTL_VAL_SHIFT) & SND_BBFPRO_CTL_VAL_MASK;
+
+ return snd_bbfpro_ctl_update(list->mixer, reg, idx, value);
+}
+
+static int snd_bbfpro_vol_update(struct usb_mixer_interface *mixer, u16 index,
+ u32 value)
+{
+ struct snd_usb_audio *chip = mixer->chip;
+ int err;
+ u16 idx;
+ u16 usb_idx, usb_val;
+ u32 v;
+
+ err = snd_usb_lock_shutdown(chip);
+ if (err < 0)
+ return err;
+
+ idx = index & SND_BBFPRO_MIXER_IDX_MASK;
+ // 18 bit linear volume, split so 2 bits end up in index.
+ v = value & SND_BBFPRO_MIXER_VAL_MASK;
+ usb_idx = idx | (v & 0x3) << 14;
+ usb_val = (v >> 2) & 0xffff;
+
+ err = snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0),
+ SND_BBFPRO_USBREQ_MIXER,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE,
+ usb_val, usb_idx, NULL, 0);
+
+ snd_usb_unlock_shutdown(chip);
+ return err;
+}
+
+static int snd_bbfpro_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ kcontrol->private_value >> SND_BBFPRO_MIXER_VAL_SHIFT;
+ return 0;
+}
+
+static int snd_bbfpro_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = SND_BBFPRO_MIXER_VAL_MIN;
+ uinfo->value.integer.max = SND_BBFPRO_MIXER_VAL_MAX;
+ return 0;
+}
+
+static int snd_bbfpro_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int err;
+ u16 idx;
+ u32 new_val, old_value, uvalue;
+ struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
+ struct usb_mixer_interface *mixer = list->mixer;
+
+ uvalue = ucontrol->value.integer.value[0];
+ idx = kcontrol->private_value & SND_BBFPRO_MIXER_IDX_MASK;
+ old_value = kcontrol->private_value >> SND_BBFPRO_MIXER_VAL_SHIFT;
+
+ if (uvalue > SND_BBFPRO_MIXER_VAL_MAX)
+ return -EINVAL;
+
+ if (uvalue == old_value)
+ return 0;
+
+ new_val = uvalue & SND_BBFPRO_MIXER_VAL_MASK;
+
+ kcontrol->private_value = idx
+ | (new_val << SND_BBFPRO_MIXER_VAL_SHIFT);
+
+ err = snd_bbfpro_vol_update(mixer, idx, new_val);
+ return err < 0 ? err : 1;
+}
+
+static int snd_bbfpro_vol_resume(struct usb_mixer_elem_list *list)
+{
+ int pv = list->kctl->private_value;
+ u16 idx = pv & SND_BBFPRO_MIXER_IDX_MASK;
+ u32 val = (pv >> SND_BBFPRO_MIXER_VAL_SHIFT)
+ & SND_BBFPRO_MIXER_VAL_MASK;
+ return snd_bbfpro_vol_update(list->mixer, idx, val);
+}
+
+// Predfine elements
+static const struct snd_kcontrol_new snd_bbfpro_ctl_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .index = 0,
+ .info = snd_bbfpro_ctl_info,
+ .get = snd_bbfpro_ctl_get,
+ .put = snd_bbfpro_ctl_put
+};
+
+static const struct snd_kcontrol_new snd_bbfpro_vol_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .index = 0,
+ .info = snd_bbfpro_vol_info,
+ .get = snd_bbfpro_vol_get,
+ .put = snd_bbfpro_vol_put
+};
+
+static int snd_bbfpro_ctl_add(struct usb_mixer_interface *mixer, u8 reg,
+ u8 index, char *name)
+{
+ struct snd_kcontrol_new knew = snd_bbfpro_ctl_control;
+
+ knew.name = name;
+ knew.private_value = (reg & SND_BBFPRO_CTL_REG_MASK)
+ | ((index & SND_BBFPRO_CTL_IDX_MASK)
+ << SND_BBFPRO_CTL_IDX_SHIFT);
+
+ return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_ctl_resume,
+ &knew, NULL);
+}
+
+static int snd_bbfpro_vol_add(struct usb_mixer_interface *mixer, u16 index,
+ char *name)
+{
+ struct snd_kcontrol_new knew = snd_bbfpro_vol_control;
+
+ knew.name = name;
+ knew.private_value = index & SND_BBFPRO_MIXER_IDX_MASK;
+
+ return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_vol_resume,
+ &knew, NULL);
+}
+
+static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer)
+{
+ int err, i, o;
+ char name[48];
+
+ static const char * const input[] = {
+ "AN1", "AN2", "IN3", "IN4", "AS1", "AS2", "ADAT3",
+ "ADAT4", "ADAT5", "ADAT6", "ADAT7", "ADAT8"};
+
+ static const char * const output[] = {
+ "AN1", "AN2", "PH3", "PH4", "AS1", "AS2", "ADAT3", "ADAT4",
+ "ADAT5", "ADAT6", "ADAT7", "ADAT8"};
+
+ for (o = 0 ; o < 12 ; ++o) {
+ for (i = 0 ; i < 12 ; ++i) {
+ // Line routing
+ snprintf(name, sizeof(name),
+ "%s-%s-%s Playback Volume",
+ (i < 2 ? "Mic" : "Line"),
+ input[i], output[o]);
+ err = snd_bbfpro_vol_add(mixer, (26 * o + i), name);
+ if (err < 0)
+ return err;
+
+ // PCM routing... yes, it is output remapping
+ snprintf(name, sizeof(name),
+ "PCM-%s-%s Playback Volume",
+ output[i], output[o]);
+ err = snd_bbfpro_vol_add(mixer, (26 * o + 12 + i),
+ name);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ // Control Reg 1
+ err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
+ SND_BBFPRO_CTL_REG1_CLK_OPTICAL,
+ "Sample Clock Source");
+ if (err < 0)
+ return err;
+
+ err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
+ SND_BBFPRO_CTL_REG1_SPDIF_PRO,
+ "IEC958 Pro Mask");
+ if (err < 0)
+ return err;
+
+ err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
+ SND_BBFPRO_CTL_REG1_SPDIF_EMPH,
+ "IEC958 Emphasis");
+ if (err < 0)
+ return err;
+
+ err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
+ SND_BBFPRO_CTL_REG1_SPDIF_OPTICAL,
+ "IEC958 Switch");
+ if (err < 0)
+ return err;
+
+ // Control Reg 2
+ err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
+ SND_BBFPRO_CTL_REG2_48V_AN1,
+ "Mic-AN1 48V");
+ if (err < 0)
+ return err;
+
+ err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
+ SND_BBFPRO_CTL_REG2_48V_AN2,
+ "Mic-AN2 48V");
+ if (err < 0)
+ return err;
+
+ err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
+ SND_BBFPRO_CTL_REG2_SENS_IN3,
+ "Line-IN3 Sens.");
+ if (err < 0)
+ return err;
+
+ err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
+ SND_BBFPRO_CTL_REG2_SENS_IN4,
+ "Line-IN4 Sens.");
+ if (err < 0)
+ return err;
+
+ err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
+ SND_BBFPRO_CTL_REG2_PAD_AN1,
+ "Mic-AN1 PAD");
+ if (err < 0)
+ return err;
+
+ err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
+ SND_BBFPRO_CTL_REG2_PAD_AN2,
+ "Mic-AN2 PAD");
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
{
int err = 0;
@@ -2282,6 +2702,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
case USB_ID(0x0194f, 0x010c): /* Presonus Studio 1810c */
err = snd_sc1810_init_mixer(mixer);
break;
+ case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */
+ err = snd_bbfpro_controls_create(mixer);
+ break;
}
return err;
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index a4e4064f9aee..40b7cd13fed9 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -367,9 +367,15 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
ifnum = 0;
goto add_sync_ep_from_ifnum;
case USB_ID(0x07fd, 0x0008): /* MOTU M Series */
+ 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(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */
+ ep = 0x82;
+ ifnum = 0;
+ goto add_sync_ep_from_ifnum;
case USB_ID(0x0582, 0x01d8): /* BOSS Katana */
/* BOSS Katana amplifiers do not need quirks */
return 0;
@@ -404,6 +410,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;
@@ -502,12 +510,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;
@@ -1579,6 +1590,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);
@@ -1775,6 +1788,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream
return 0;
case SNDRV_PCM_TRIGGER_STOP:
stop_endpoints(subs);
+ subs->data_endpoint->retire_data_urb = NULL;
subs->running = 0;
return 0;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
diff --git a/sound/usb/proc.c b/sound/usb/proc.c
index 4174ad11fca6..889c550c9f29 100644
--- a/sound/usb/proc.c
+++ b/sound/usb/proc.c
@@ -54,6 +54,38 @@ void snd_usb_audio_create_proc(struct snd_usb_audio *chip)
proc_audio_usbid_read);
}
+static const char * const channel_labels[] = {
+ [SNDRV_CHMAP_NA] = "N/A",
+ [SNDRV_CHMAP_MONO] = "MONO",
+ [SNDRV_CHMAP_FL] = "FL",
+ [SNDRV_CHMAP_FR] = "FR",
+ [SNDRV_CHMAP_FC] = "FC",
+ [SNDRV_CHMAP_LFE] = "LFE",
+ [SNDRV_CHMAP_RL] = "RL",
+ [SNDRV_CHMAP_RR] = "RR",
+ [SNDRV_CHMAP_FLC] = "FLC",
+ [SNDRV_CHMAP_FRC] = "FRC",
+ [SNDRV_CHMAP_RC] = "RC",
+ [SNDRV_CHMAP_SL] = "SL",
+ [SNDRV_CHMAP_SR] = "SR",
+ [SNDRV_CHMAP_TC] = "TC",
+ [SNDRV_CHMAP_TFL] = "TFL",
+ [SNDRV_CHMAP_TFC] = "TFC",
+ [SNDRV_CHMAP_TFR] = "TFR",
+ [SNDRV_CHMAP_TRL] = "TRL",
+ [SNDRV_CHMAP_TRC] = "TRC",
+ [SNDRV_CHMAP_TRR] = "TRR",
+ [SNDRV_CHMAP_TFLC] = "TFLC",
+ [SNDRV_CHMAP_TFRC] = "TFRC",
+ [SNDRV_CHMAP_LLFE] = "LLFE",
+ [SNDRV_CHMAP_RLFE] = "RLFE",
+ [SNDRV_CHMAP_TSL] = "TSL",
+ [SNDRV_CHMAP_TSR] = "TSR",
+ [SNDRV_CHMAP_BC] = "BC",
+ [SNDRV_CHMAP_RLC] = "RLC",
+ [SNDRV_CHMAP_RRC] = "RRC",
+};
+
/*
* proc interface for list the supported pcm formats
*/
@@ -97,6 +129,27 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
snd_iprintf(buffer, " Data packet interval: %d us\n",
125 * (1 << fp->datainterval));
snd_iprintf(buffer, " Bits: %d\n", fp->fmt_bits);
+
+ if (fp->dsd_raw)
+ snd_iprintf(buffer, " DSD raw: DOP=%d, bitrev=%d\n",
+ fp->dsd_dop, fp->dsd_bitrev);
+
+ if (fp->chmap) {
+ const struct snd_pcm_chmap_elem *map = fp->chmap;
+ int c;
+
+ snd_iprintf(buffer, " Channel map:");
+ for (c = 0; c < map->channels; c++) {
+ if (map->map[c] >= ARRAY_SIZE(channel_labels) ||
+ !channel_labels[map->map[c]])
+ snd_iprintf(buffer, " --");
+ else
+ snd_iprintf(buffer, " %s",
+ channel_labels[map->map[c]]);
+ }
+ snd_iprintf(buffer, "\n");
+ }
+
// snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize);
// snd_iprintf(buffer, " EP Attribute = %#x\n", fp->attributes);
}
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index e009d584e7d0..9092cc0aa807 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -25,6 +25,33 @@
.idProduct = prod, \
.bInterfaceClass = USB_CLASS_VENDOR_SPEC
+#define QUIRK_RENAME_DEVICE(_vendor, _device) \
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \
+ .vendor_name = _vendor, \
+ .product_name = _device, \
+ .ifnum = QUIRK_NO_INTERFACE \
+ }
+
+#define QUIRK_DEVICE_PROFILE(_vendor, _device, _profile) \
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \
+ .vendor_name = _vendor, \
+ .product_name = _device, \
+ .profile_name = _profile, \
+ .ifnum = QUIRK_NO_INTERFACE \
+ }
+
+/* HP Thunderbolt Dock Audio Headset */
+{
+ USB_DEVICE(0x03f0, 0x0269),
+ QUIRK_DEVICE_PROFILE("HP", "Thunderbolt Dock Audio Headset",
+ "HP-Thunderbolt-Dock-Audio-Headset"),
+},
+/* HP Thunderbolt Dock Audio Module */
+{
+ USB_DEVICE(0x03f0, 0x0567),
+ QUIRK_DEVICE_PROFILE("HP", "Thunderbolt Dock Audio Module",
+ "HP-Thunderbolt-Dock-Audio-Module"),
+},
/* FTDI devices */
{
USB_DEVICE(0x0403, 0xb8d8),
@@ -61,20 +88,12 @@
/* Creative/E-Mu devices */
{
USB_DEVICE(0x041e, 0x3010),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Creative Labs",
- .product_name = "Sound Blaster MP3+",
- .ifnum = QUIRK_NO_INTERFACE
- }
+ QUIRK_RENAME_DEVICE("Creative Labs", "Sound Blaster MP3+")
},
/* Creative/Toshiba Multimedia Center SB-0500 */
{
USB_DEVICE(0x041e, 0x3048),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Toshiba",
- .product_name = "SB-0500",
- .ifnum = QUIRK_NO_INTERFACE
- }
+ QUIRK_RENAME_DEVICE("Toshiba", "SB-0500")
},
{
/* E-Mu 0202 USB */
@@ -207,11 +226,7 @@
.idProduct = 0x0990,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Logitech, Inc.",
- .product_name = "QuickCam Pro 9000",
- .ifnum = QUIRK_NO_INTERFACE
- }
+ QUIRK_RENAME_DEVICE("Logitech, Inc.", "QuickCam Pro 9000")
},
/*
@@ -2596,11 +2611,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
USB_DEVICE(0x0ccd, 0x0028),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "TerraTec",
- .product_name = "Aureon5.1MkII",
- .ifnum = QUIRK_NO_INTERFACE
- }
+ QUIRK_RENAME_DEVICE("TerraTec", "Aureon5.1MkII")
},
{
USB_DEVICE(0x0ccd, 0x0035),
@@ -2615,19 +2626,11 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* Stanton/N2IT Final Scratch v1 device ('Scratchamp') */
{
USB_DEVICE(0x103d, 0x0100),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Stanton",
- .product_name = "ScratchAmp",
- .ifnum = QUIRK_NO_INTERFACE
- }
+ QUIRK_RENAME_DEVICE("Stanton", "ScratchAmp")
},
{
USB_DEVICE(0x103d, 0x0101),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Stanton",
- .product_name = "ScratchAmp",
- .ifnum = QUIRK_NO_INTERFACE
- }
+ QUIRK_RENAME_DEVICE("Stanton", "ScratchAmp")
},
/* Novation EMS devices */
@@ -2756,90 +2759,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 */
{
@@ -2872,11 +2791,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* aka. Serato Scratch Live DJ Box */
USB_DEVICE(0x13e5, 0x0001),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Rane",
- .product_name = "SL-1",
- .ifnum = QUIRK_NO_INTERFACE
- }
+ QUIRK_RENAME_DEVICE("Rane", "SL-1")
},
/* Native Instruments MK2 series */
@@ -3343,10 +3258,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
* is also used by the CM106 based cards, so make it unique.
*/
USB_DEVICE(0x0d8c, 0x0103),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .product_name = "Audio Advantage MicroII",
- .ifnum = QUIRK_NO_INTERFACE
- }
+ QUIRK_RENAME_DEVICE(NULL, "Audio Advantage MicroII")
},
/* disabled due to regression for other devices;
@@ -3452,12 +3364,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
/* Dell WD15 Dock */
{
USB_DEVICE(0x0bda, 0x4014),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Dell",
- .product_name = "WD15 Dock",
- .profile_name = "Dell-WD15-Dock",
- .ifnum = QUIRK_NO_INTERFACE
- }
+ QUIRK_DEVICE_PROFILE("Dell", "WD15 Dock", "Dell-WD15-Dock")
},
/* Dell WD19 Dock */
{
@@ -3635,4 +3542,147 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
}
},
+#define ALC1220_VB_DESKTOP(vend, prod) { \
+ USB_DEVICE(vend, prod), \
+ QUIRK_DEVICE_PROFILE("Realtek", "ALC1220-VB-DT", \
+ "Realtek-ALC1220-VB-Desktop") \
+}
+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),
+ QUIRK_DEVICE_PROFILE("Gigabyte", "Aorus Master Front Headphone",
+ "Gigabyte-Aorus-Master-Front-Headphone")
+},
+{
+ USB_DEVICE(0x0414, 0xa001),
+ QUIRK_DEVICE_PROFILE("Gigabyte", "Aorus Master Main Audio",
+ "Gigabyte-Aorus-Master-Main-Audio")
+},
+{
+ /*
+ * Pioneer DJ DJM-900NXS2
+ * 10 channels playback & 12 channels capture @ 44.1/48/96kHz S24LE
+ */
+ USB_DEVICE_VENDOR_SPEC(0x2b73, 0x000a),
+ .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 = 10,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x01,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC|
+ USB_ENDPOINT_SYNC_ASYNC,
+ .rates = SNDRV_PCM_RATE_44100|
+ SNDRV_PCM_RATE_48000|
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 3,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 96000
+ }
+ }
+ },
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 12,
+ .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|
+ SNDRV_PCM_RATE_48000|
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 3,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 96000
+ }
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+
+/*
+ * 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 swapped L-R channels, but that's for userspace to deal
+ * with.
+ */
+{
+ USB_DEVICE(0x534d, 0x2109),
+ .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 a8ece1701068..fca72730a802 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1458,6 +1458,30 @@ static void set_format_emu_quirk(struct snd_usb_substream *subs,
subs->pkt_offset_adj = (emu_samplerate_id >= EMU_QUIRK_SR_176400HZ) ? 4 : 0;
}
+
+/*
+ * Pioneer DJ DJM-900NXS2
+ * Device needs to know the sample rate each time substream is started
+ */
+static int pioneer_djm_set_format_quirk(struct snd_usb_substream *subs)
+{
+
+ /* Convert sample rate value to little endian */
+ u8 sr[3];
+
+ sr[0] = subs->cur_rate & 0xff;
+ sr[1] = (subs->cur_rate >> 8) & 0xff;
+ sr[2] = (subs->cur_rate >> 16) & 0xff;
+
+ /* Configure device */
+ usb_set_interface(subs->dev, 0, 1);
+ snd_usb_ctl_msg(subs->stream->chip->dev,
+ usb_rcvctrlpipe(subs->stream->chip->dev, 0),
+ 0x01, 0x22, 0x0100, 0x0082, &sr, 0x0003);
+
+ return 0;
+}
+
void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
struct audioformat *fmt)
{
@@ -1468,6 +1492,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(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */
+ pioneer_djm_set_format_quirk(subs);
+ break;
}
}
@@ -1505,6 +1532,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 */
@@ -1636,15 +1664,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);
}
/*
@@ -1687,7 +1724,7 @@ 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 */
@@ -1828,6 +1865,7 @@ struct registration_quirk {
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 */
};
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 1c892c7f14d7..b91c4c0807ec 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -19,14 +19,16 @@
struct media_device;
struct media_intf_devnode;
+#define MAX_CARD_INTERFACES 16
+
struct snd_usb_audio {
int index;
struct usb_device *dev;
struct snd_card *card;
- struct usb_interface *pm_intf;
+ struct usb_interface *intf[MAX_CARD_INTERFACES];
u32 usb_id;
struct mutex mutex;
- unsigned int autosuspended:1;
+ unsigned int system_suspend;
atomic_t active;
atomic_t shutdown;
atomic_t usage_count;
diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h
index e0f77172ce8f..144b85f57bd2 100644
--- a/sound/usb/usx2y/usbusx2y.h
+++ b/sound/usb/usx2y/usbusx2y.h
@@ -18,7 +18,7 @@ struct snd_usX2Y_AsyncSeq {
struct snd_usX2Y_urbSeq {
int submitted;
int len;
- struct urb *urb[0];
+ struct urb *urb[];
};
#include "usx2yhwdeppcm.h"
diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
index 37d290fe9d43..ecaf41265dcd 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/x86/Kconfig b/sound/x86/Kconfig
index 21f8919678fb..77777192f650 100644
--- a/sound/x86/Kconfig
+++ b/sound/x86/Kconfig
@@ -3,7 +3,7 @@ menuconfig SND_X86
bool "X86 sound devices"
depends on X86
default y
- ---help---
+ help
X86 sound devices that don't fall under SoC or PCI categories
if SND_X86