summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/core/Kconfig13
-rw-r--r--sound/core/Makefile3
-rw-r--r--sound/core/oss/mixer_oss.c3
-rw-r--r--sound/core/pcm.c3
-rw-r--r--sound/core/pcm_lib.c7
-rw-r--r--sound/core/pcm_native.c42
-rw-r--r--sound/core/seq/oss/seq_oss_readq.c6
-rw-r--r--sound/core/seq/oss/seq_oss_writeq.c4
-rw-r--r--sound/firewire/Kconfig27
-rw-r--r--sound/firewire/Makefile5
-rw-r--r--sound/firewire/amdtp-am824.c465
-rw-r--r--sound/firewire/amdtp-am824.h52
-rw-r--r--sound/firewire/amdtp-stream.c (renamed from sound/firewire/amdtp.c)379
-rw-r--r--sound/firewire/amdtp-stream.h (renamed from sound/firewire/amdtp.h)116
-rw-r--r--sound/firewire/bebob/Makefile2
-rw-r--r--sound/firewire/bebob/bebob.c9
-rw-r--r--sound/firewire/bebob/bebob.h34
-rw-r--r--sound/firewire/bebob/bebob_focusrite.c26
-rw-r--r--sound/firewire/bebob/bebob_maudio.c34
-rw-r--r--sound/firewire/bebob/bebob_midi.c16
-rw-r--r--sound/firewire/bebob/bebob_pcm.c16
-rw-r--r--sound/firewire/bebob/bebob_proc.c6
-rw-r--r--sound/firewire/bebob/bebob_stream.c40
-rw-r--r--sound/firewire/bebob/bebob_terratec.c10
-rw-r--r--sound/firewire/bebob/bebob_yamaha.c6
-rw-r--r--sound/firewire/dice/Makefile2
-rw-r--r--sound/firewire/dice/dice-midi.c12
-rw-r--r--sound/firewire/dice/dice-pcm.c12
-rw-r--r--sound/firewire/dice/dice-stream.c34
-rw-r--r--sound/firewire/dice/dice.c3
-rw-r--r--sound/firewire/dice/dice.h2
-rw-r--r--sound/firewire/digi00x/Makefile4
-rw-r--r--sound/firewire/digi00x/amdtp-dot.c442
-rw-r--r--sound/firewire/digi00x/digi00x-hwdep.c200
-rw-r--r--sound/firewire/digi00x/digi00x-midi.c160
-rw-r--r--sound/firewire/digi00x/digi00x-pcm.c373
-rw-r--r--sound/firewire/digi00x/digi00x-proc.c99
-rw-r--r--sound/firewire/digi00x/digi00x-stream.c422
-rw-r--r--sound/firewire/digi00x/digi00x-transaction.c137
-rw-r--r--sound/firewire/digi00x/digi00x.c170
-rw-r--r--sound/firewire/digi00x/digi00x.h157
-rw-r--r--sound/firewire/fcp.c2
-rw-r--r--sound/firewire/fireworks/Makefile2
-rw-r--r--sound/firewire/fireworks/fireworks.c12
-rw-r--r--sound/firewire/fireworks/fireworks.h2
-rw-r--r--sound/firewire/fireworks/fireworks_command.c2
-rw-r--r--sound/firewire/fireworks/fireworks_midi.c12
-rw-r--r--sound/firewire/fireworks/fireworks_pcm.c12
-rw-r--r--sound/firewire/fireworks/fireworks_stream.c8
-rw-r--r--sound/firewire/lib.c142
-rw-r--r--sound/firewire/lib.h56
-rw-r--r--sound/firewire/oxfw/Makefile2
-rw-r--r--sound/firewire/oxfw/oxfw-midi.c40
-rw-r--r--sound/firewire/oxfw/oxfw-pcm.c10
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c53
-rw-r--r--sound/firewire/oxfw/oxfw.c46
-rw-r--r--sound/firewire/oxfw/oxfw.h3
-rw-r--r--sound/firewire/tascam/Makefile4
-rw-r--r--sound/firewire/tascam/amdtp-tascam.c243
-rw-r--r--sound/firewire/tascam/tascam-hwdep.c201
-rw-r--r--sound/firewire/tascam/tascam-midi.c135
-rw-r--r--sound/firewire/tascam/tascam-pcm.c312
-rw-r--r--sound/firewire/tascam/tascam-proc.c88
-rw-r--r--sound/firewire/tascam/tascam-stream.c496
-rw-r--r--sound/firewire/tascam/tascam-transaction.c302
-rw-r--r--sound/firewire/tascam/tascam.c209
-rw-r--r--sound/firewire/tascam/tascam.h147
-rw-r--r--sound/hda/ext/hdac_ext_stream.c9
-rw-r--r--sound/hda/hda_bus_type.c13
-rw-r--r--sound/hda/hdac_device.c118
-rw-r--r--sound/hda/hdac_sysfs.c8
-rw-r--r--sound/pci/hda/hda_bind.c69
-rw-r--r--sound/pci/hda/hda_codec.c44
-rw-r--r--sound/pci/hda/hda_codec.h44
-rw-r--r--sound/pci/hda/hda_controller.c1
-rw-r--r--sound/pci/hda/hda_generic.c7
-rw-r--r--sound/pci/hda/hda_local.h7
-rw-r--r--sound/pci/hda/hda_sysfs.c3
-rw-r--r--sound/pci/hda/patch_analog.c37
-rw-r--r--sound/pci/hda/patch_ca0110.c15
-rw-r--r--sound/pci/hda/patch_ca0132.c9
-rw-r--r--sound/pci/hda/patch_cirrus.c22
-rw-r--r--sound/pci/hda/patch_cmedia.c15
-rw-r--r--sound/pci/hda/patch_conexant.c118
-rw-r--r--sound/pci/hda/patch_hdmi.c220
-rw-r--r--sound/pci/hda/patch_realtek.c134
-rw-r--r--sound/pci/hda/patch_si3054.c39
-rw-r--r--sound/pci/hda/patch_sigmatel.c214
-rw-r--r--sound/pci/hda/patch_via.c171
-rw-r--r--sound/pci/korg1212/korg1212.c8
-rw-r--r--sound/pci/lx6464es/lx6464es.c4
-rw-r--r--sound/pci/rme32.c4
-rw-r--r--sound/pci/rme96.c8
-rw-r--r--sound/pci/rme9652/hdsp.c1
-rw-r--r--sound/pci/rme9652/hdspm.c9
-rw-r--r--sound/soc/codecs/adav80x.c4
-rw-r--r--sound/soc/codecs/twl4030.c13
-rw-r--r--sound/soc/codecs/uda134x.c6
-rw-r--r--sound/soc/codecs/wl1273.c9
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c14
-rw-r--r--sound/soc/intel/boards/cht_bsw_max98090_ti.c14
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c14
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c14
-rw-r--r--sound/soc/omap/n810.c3
-rw-r--r--sound/soc/omap/rx51.c3
-rw-r--r--sound/soc/soc-pcm.c10
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c4
-rw-r--r--sound/usb/card.h1
-rw-r--r--sound/usb/endpoint.c92
-rw-r--r--sound/usb/midi.c11
-rw-r--r--sound/usb/mixer_quirks.c2
-rw-r--r--sound/usb/pcm.c74
-rw-r--r--sound/usb/quirks-table.h23
-rw-r--r--sound/usb/quirks.c3
-rw-r--r--sound/usb/stream.c1
-rw-r--r--sound/usb/usbaudio.h1
116 files changed, 6289 insertions, 1472 deletions
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 6c96feeaf01e..e3e949126a56 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -4,7 +4,7 @@ config SND_TIMER
config SND_PCM
tristate
- select SND_TIMER
+ select SND_TIMER if SND_PCM_TIMER
config SND_PCM_ELD
bool
@@ -93,6 +93,17 @@ config SND_PCM_OSS_PLUGINS
support conversion of channels, formats and rates. It will
behave like most of new OSS/Free drivers in 2.4/2.6 kernels.
+config SND_PCM_TIMER
+ bool "PCM timer interface" if EXPERT
+ default y
+ help
+ If you disable this option, pcm timer will be inavailable, so
+ those stubs used pcm timer (e.g. dmix, dsnoop & co) may work
+ incorrectlly.
+
+ For some embedded device, we may disable it to reduce memory
+ footprint, about 20KB on x86_64 platform.
+
config SND_SEQUENCER_OSS
bool "OSS Sequencer API"
depends on SND_SEQUENCER
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 3354f91e003a..48ab4b8f8279 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -13,8 +13,9 @@ snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o
snd-$(CONFIG_SND_VMASTER) += vmaster.o
snd-$(CONFIG_SND_JACK) += ctljack.o jack.o
-snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
+snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \
pcm_memory.o memalloc.o
+snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o
snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o
snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index a99f7200ff3f..7a8c79dd9734 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -1177,7 +1177,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
struct snd_mixer_oss *mixer = entry->private_data;
char line[128], str[32], idxstr[16];
const char *cptr;
- int ch, idx;
+ unsigned int idx;
+ int ch;
struct snd_mixer_oss_assign_table *tbl;
struct slot *slot;
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 02bd96954dc4..308c9ecf73db 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -1014,9 +1014,6 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
snd_free_pages((void*)runtime->control,
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));
kfree(runtime->hw_constraints.rules);
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- kfree(runtime->hwptr_log);
-#endif
kfree(runtime);
substream->runtime = NULL;
put_pid(substream->pid);
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 7d45645f10ba..05a3ca93c647 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1875,20 +1875,17 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
return;
runtime = substream->runtime;
- if (runtime->transfer_ack_begin)
- runtime->transfer_ack_begin(substream);
-
snd_pcm_stream_lock_irqsave(substream, flags);
if (!snd_pcm_running(substream) ||
snd_pcm_update_hw_ptr0(substream, 1) < 0)
goto _end;
+#ifdef CONFIG_SND_PCM_TIMER
if (substream->timer_running)
snd_timer_interrupt(substream->timer, 1);
+#endif
_end:
snd_pcm_stream_unlock_irqrestore(substream, flags);
- if (runtime->transfer_ack_end)
- runtime->transfer_ack_end(substream);
kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
}
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 75888dd38a7f..a8b27cdc2844 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -486,6 +486,16 @@ static void snd_pcm_set_state(struct snd_pcm_substream *substream, int state)
snd_pcm_stream_unlock_irq(substream);
}
+static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
+ int event)
+{
+#ifdef CONFIG_SND_PCM_TIMER
+ if (substream->timer)
+ snd_timer_notify(substream->timer, event,
+ &substream->runtime->trigger_tstamp);
+#endif
+}
+
static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -650,7 +660,8 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
}
snd_pcm_stream_unlock_irq(substream);
- if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
+ if (params->tstamp_mode < 0 ||
+ params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
return -EINVAL;
if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12) &&
params->tstamp_type > SNDRV_PCM_TSTAMP_TYPE_LAST)
@@ -1042,9 +1053,7 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
snd_pcm_playback_silence(substream, ULONG_MAX);
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
}
static struct action_ops snd_pcm_action_start = {
@@ -1092,9 +1101,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
if (runtime->status->state != state) {
snd_pcm_trigger_tstamp(substream);
runtime->status->state = state;
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTOP);
}
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
@@ -1208,18 +1215,12 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
snd_pcm_trigger_tstamp(substream);
if (push) {
runtime->status->state = SNDRV_PCM_STATE_PAUSED;
- if (substream->timer)
- snd_timer_notify(substream->timer,
- SNDRV_TIMER_EVENT_MPAUSE,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MPAUSE);
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
} else {
runtime->status->state = SNDRV_PCM_STATE_RUNNING;
- if (substream->timer)
- snd_timer_notify(substream->timer,
- SNDRV_TIMER_EVENT_MCONTINUE,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MCONTINUE);
}
}
@@ -1267,9 +1268,7 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
snd_pcm_trigger_tstamp(substream);
runtime->status->suspended_state = runtime->status->state;
runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSUSPEND);
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
}
@@ -1373,9 +1372,7 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
runtime->status->state = runtime->status->suspended_state;
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
}
static struct action_ops snd_pcm_action_resume = {
@@ -2226,7 +2223,8 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream)
snd_pcm_drop(substream);
if (substream->hw_opened) {
- if (substream->ops->hw_free != NULL)
+ if (substream->ops->hw_free &&
+ substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
substream->ops->hw_free(substream);
substream->ops->close(substream);
substream->hw_opened = 0;
diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c
index ccd893566f1d..046cb586fb2f 100644
--- a/sound/core/seq/oss/seq_oss_readq.c
+++ b/sound/core/seq/oss/seq_oss_readq.c
@@ -91,8 +91,7 @@ snd_seq_oss_readq_clear(struct seq_oss_readq *q)
q->head = q->tail = 0;
}
/* if someone sleeping, wake'em up */
- if (waitqueue_active(&q->midi_sleep))
- wake_up(&q->midi_sleep);
+ wake_up(&q->midi_sleep);
q->input_time = (unsigned long)-1;
}
@@ -138,8 +137,7 @@ snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev)
q->qlen++;
/* wake up sleeper */
- if (waitqueue_active(&q->midi_sleep))
- wake_up(&q->midi_sleep);
+ wake_up(&q->midi_sleep);
spin_unlock_irqrestore(&q->lock, flags);
diff --git a/sound/core/seq/oss/seq_oss_writeq.c b/sound/core/seq/oss/seq_oss_writeq.c
index d50338bbc21f..1f6788a18444 100644
--- a/sound/core/seq/oss/seq_oss_writeq.c
+++ b/sound/core/seq/oss/seq_oss_writeq.c
@@ -138,9 +138,7 @@ snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time)
spin_lock_irqsave(&q->sync_lock, flags);
q->sync_time = time;
q->sync_event_put = 0;
- if (waitqueue_active(&q->sync_sleep)) {
- wake_up(&q->sync_sleep);
- }
+ wake_up(&q->sync_sleep);
spin_unlock_irqrestore(&q->sync_lock, flags);
}
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 8850b7de1d38..bee0e5f1a116 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -120,4 +120,31 @@ config SND_BEBOB
To compile this driver as a module, choose M here: the module
will be called snd-bebob.
+config SND_FIREWIRE_DIGI00X
+ tristate "Digidesign Digi 002/003 family support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for Digidesign Digi 002/003 family.
+ * Digi 002 Console
+ * Digi 002 Rack
+ * Digi 003 Console
+ * Digi 003 Rack
+ * Digi 003 Rack+
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-firewire-digi00x.
+
+config SND_FIREWIRE_TASCAM
+ tristate "TASCAM FireWire series support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for TASCAM.
+ * FW-1884
+ * FW-1082
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-firewire-tascam.
+
endif # SND_FIREWIRE
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index 8b37f084b2ab..f5fb62551c60 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -1,6 +1,5 @@
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
- fcp.o cmp.o amdtp.o
-snd-oxfw-objs := oxfw.o
+ fcp.o cmp.o amdtp-stream.o amdtp-am824.o
snd-isight-objs := isight.o
snd-scs1x-objs := scs1x.o
@@ -11,3 +10,5 @@ obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
obj-$(CONFIG_SND_FIREWORKS) += fireworks/
obj-$(CONFIG_SND_BEBOB) += bebob/
+obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/
+obj-$(CONFIG_SND_FIREWIRE_TASCAM) += tascam/
diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c
new file mode 100644
index 000000000000..bebddc60fde8
--- /dev/null
+++ b/sound/firewire/amdtp-am824.c
@@ -0,0 +1,465 @@
+/*
+ * AM824 format in Audio and Music Data Transmission Protocol (IEC 61883-6)
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/slab.h>
+
+#include "amdtp-am824.h"
+
+#define CIP_FMT_AM 0x10
+
+/* "Clock-based rate control mode" is just supported. */
+#define AMDTP_FDF_AM824 0x00
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND 3093
+
+/*
+ * Several devices look only at the first eight data blocks.
+ * In any case, this is more than enough for the MIDI data rate.
+ */
+#define MAX_MIDI_RX_BLOCKS 8
+
+struct amdtp_am824 {
+ struct snd_rawmidi_substream *midi[AM824_MAX_CHANNELS_FOR_MIDI * 8];
+ int midi_fifo_limit;
+ int midi_fifo_used[AM824_MAX_CHANNELS_FOR_MIDI * 8];
+ unsigned int pcm_channels;
+ unsigned int midi_ports;
+
+ u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM];
+ u8 midi_position;
+
+ void (*transfer_samples)(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
+
+ unsigned int frame_multiplier;
+};
+
+/**
+ * amdtp_am824_set_parameters - set stream parameters
+ * @s: the AMDTP stream to configure
+ * @rate: the sample rate
+ * @pcm_channels: the number of PCM samples in each data block, to be encoded
+ * as AM824 multi-bit linear audio
+ * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
+ * @double_pcm_frames: one data block transfers two PCM frames
+ *
+ * The parameters must be set before the stream is started, and must not be
+ * changed while the stream is running.
+ */
+int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels,
+ unsigned int midi_ports,
+ bool double_pcm_frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int midi_channels;
+ unsigned int i;
+ int err;
+
+ if (amdtp_stream_running(s))
+ return -EINVAL;
+
+ if (pcm_channels > AM824_MAX_CHANNELS_FOR_PCM)
+ return -EINVAL;
+
+ midi_channels = DIV_ROUND_UP(midi_ports, 8);
+ if (midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)
+ return -EINVAL;
+
+ if (WARN_ON(amdtp_stream_running(s)) ||
+ WARN_ON(pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) ||
+ WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI))
+ return -EINVAL;
+
+ err = amdtp_stream_set_parameters(s, rate,
+ pcm_channels + midi_channels);
+ if (err < 0)
+ return err;
+
+ s->fdf = AMDTP_FDF_AM824 | s->sfc;
+
+ p->pcm_channels = pcm_channels;
+ p->midi_ports = midi_ports;
+
+ /*
+ * In IEC 61883-6, one data block represents one event. In ALSA, one
+ * event equals to one PCM frame. But Dice has a quirk at higher
+ * sampling rate to transfer two PCM frames in one data block.
+ */
+ if (double_pcm_frames)
+ p->frame_multiplier = 2;
+ else
+ p->frame_multiplier = 1;
+
+ /* init the position map for PCM and MIDI channels */
+ for (i = 0; i < pcm_channels; i++)
+ p->pcm_positions[i] = i;
+ p->midi_position = p->pcm_channels;
+
+ /*
+ * We do not know the actual MIDI FIFO size of most devices. Just
+ * assume two bytes, i.e., one byte can be received over the bus while
+ * the previous one is transmitted over MIDI.
+ * (The value here is adjusted for midi_ratelimit_per_packet().)
+ */
+ p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters);
+
+/**
+ * amdtp_am824_set_pcm_position - set an index of data channel for a channel
+ * of PCM frame
+ * @s: the AMDTP stream
+ * @index: the index of data channel in an data block
+ * @position: the channel of PCM frame
+ */
+void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
+ unsigned int position)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ if (index < p->pcm_channels)
+ p->pcm_positions[index] = position;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_position);
+
+/**
+ * amdtp_am824_set_midi_position - set a index of data channel for MIDI
+ * conformant data channel
+ * @s: the AMDTP stream
+ * @position: the index of data channel in an data block
+ */
+void amdtp_am824_set_midi_position(struct amdtp_stream *s,
+ unsigned int position)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ p->midi_position = position;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position);
+
+static void write_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u32 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[p->pcm_positions[c]] =
+ cpu_to_be32((*src >> 8) | 0x40000000);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u16 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[p->pcm_positions[c]] =
+ cpu_to_be32((*src << 8) | 0x42000000);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ u32 *dst;
+
+ channels = p->pcm_channels;
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[p->pcm_positions[c]]) << 8;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int i, c, channels = p->pcm_channels;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[p->pcm_positions[c]] = cpu_to_be32(0x40000000);
+ buffer += s->data_block_quadlets;
+ }
+}
+
+/**
+ * amdtp_am824_set_pcm_format - set the PCM format
+ * @s: the AMDTP stream to configure
+ * @format: the format of the ALSA PCM device
+ *
+ * The sample format must be set after the other parameters (rate/PCM channels/
+ * MIDI) and before the stream is started, and must not be changed while the
+ * stream is running.
+ */
+void amdtp_am824_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ if (WARN_ON(amdtp_stream_pcm_running(s)))
+ return;
+
+ switch (format) {
+ default:
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S16:
+ if (s->direction == AMDTP_OUT_STREAM) {
+ p->transfer_samples = write_pcm_s16;
+ break;
+ }
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S32:
+ if (s->direction == AMDTP_OUT_STREAM)
+ p->transfer_samples = write_pcm_s32;
+ else
+ p->transfer_samples = read_pcm_s32;
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_format);
+
+/**
+ * amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream
+ * @s: the AMDTP stream for AM824 data block, must be initialized.
+ * @runtime: the PCM substream runtime
+ *
+ */
+int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+ if (err < 0)
+ return err;
+
+ /* AM824 in IEC 61883-6 can deliver 24bit data. */
+ return snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_add_pcm_hw_constraints);
+
+/**
+ * amdtp_am824_midi_trigger - start/stop playback/capture with a MIDI device
+ * @s: the AMDTP stream
+ * @port: index of MIDI port
+ * @midi: the MIDI device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of MIDI data. This function should be called from the MIDI
+ * device's .trigger callback.
+ */
+void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ if (port < p->midi_ports)
+ ACCESS_ONCE(p->midi[port]) = midi;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_midi_trigger);
+
+/*
+ * To avoid sending MIDI bytes at too high a rate, assume that the receiving
+ * device has a FIFO, and track how much it is filled. This values increases
+ * by one whenever we send one byte in a packet, but the FIFO empties at
+ * a constant rate independent of our packet rate. One packet has syt_interval
+ * samples, so the number of bytes that empty out of the FIFO, per packet(!),
+ * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing
+ * fractional values, the values in midi_fifo_used[] are measured in bytes
+ * multiplied by the sample rate.
+ */
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_am824 *p = s->protocol;
+ int used;
+
+ used = p->midi_fifo_used[port];
+ if (used == 0) /* common shortcut */
+ return true;
+
+ used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
+ used = max(used, 0);
+ p->midi_fifo_used[port] = used;
+
+ return used < p->midi_fifo_limit;
+}
+
+static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ p->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int f, port;
+ u8 *b;
+
+ for (f = 0; f < frames; f++) {
+ b = (u8 *)&buffer[p->midi_position];
+
+ port = (s->data_block_counter + f) % 8;
+ if (f < MAX_MIDI_RX_BLOCKS &&
+ midi_ratelimit_per_packet(s, port) &&
+ p->midi[port] != NULL &&
+ snd_rawmidi_transmit(p->midi[port], &b[1], 1) == 1) {
+ midi_rate_use_one_byte(s, port);
+ b[0] = 0x81;
+ } else {
+ b[0] = 0x80;
+ b[1] = 0;
+ }
+ b[2] = 0;
+ b[3] = 0;
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void read_midi_messages(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int f, port;
+ int len;
+ u8 *b;
+
+ for (f = 0; f < frames; f++) {
+ port = (s->data_block_counter + f) % 8;
+ b = (u8 *)&buffer[p->midi_position];
+
+ len = b[0] - 0x80;
+ if ((1 <= len) && (len <= 3) && (p->midi[port]))
+ snd_rawmidi_receive(p->midi[port], b + 1, len);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks, unsigned int *syt)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+ unsigned int pcm_frames;
+
+ if (pcm) {
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ pcm_frames = data_blocks * p->frame_multiplier;
+ } else {
+ write_pcm_silence(s, buffer, data_blocks);
+ pcm_frames = 0;
+ }
+
+ if (p->midi_ports)
+ write_midi_messages(s, buffer, data_blocks);
+
+ return pcm_frames;
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks, unsigned int *syt)
+{
+ struct amdtp_am824 *p = s->protocol;
+ struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+ unsigned int pcm_frames;
+
+ if (pcm) {
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ pcm_frames = data_blocks * p->frame_multiplier;
+ } else {
+ pcm_frames = 0;
+ }
+
+ if (p->midi_ports)
+ read_midi_messages(s, buffer, data_blocks);
+
+ return pcm_frames;
+}
+
+/**
+ * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824
+ * data block
+ * @s: the AMDTP stream to initialize
+ * @unit: the target of the stream
+ * @dir: the direction of stream
+ * @flags: the packet transmission method to use
+ */
+int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, enum cip_flags flags)
+{
+ amdtp_stream_process_data_blocks_t process_data_blocks;
+
+ if (dir == AMDTP_IN_STREAM)
+ process_data_blocks = process_tx_data_blocks;
+ else
+ process_data_blocks = process_rx_data_blocks;
+
+ return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
+ process_data_blocks,
+ sizeof(struct amdtp_am824));
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_init);
diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h
new file mode 100644
index 000000000000..73b07b3109db
--- /dev/null
+++ b/sound/firewire/amdtp-am824.h
@@ -0,0 +1,52 @@
+#ifndef SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
+#define SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
+
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+
+#include "amdtp-stream.h"
+
+#define AM824_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
+
+#define AM824_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \
+ SNDRV_PCM_FMTBIT_S32)
+
+/*
+ * This module supports maximum 64 PCM channels for one PCM stream
+ * This is for our convenience.
+ */
+#define AM824_MAX_CHANNELS_FOR_PCM 64
+
+/*
+ * AMDTP packet can include channels for MIDI conformant data.
+ * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
+ * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
+ *
+ * This module supports maximum 1 MIDI conformant data channels.
+ * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
+ */
+#define AM824_MAX_CHANNELS_FOR_MIDI 1
+
+int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels,
+ unsigned int midi_ports,
+ bool double_pcm_frames);
+
+void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
+ unsigned int position);
+
+void amdtp_am824_set_midi_position(struct amdtp_stream *s,
+ unsigned int position);
+
+int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+
+void amdtp_am824_set_pcm_format(struct amdtp_stream *s,
+ snd_pcm_format_t format);
+
+void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi);
+
+int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, enum cip_flags flags);
+#endif
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp-stream.c
index 2a153d260836..ed2902609a4c 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp-stream.c
@@ -11,28 +11,14 @@
#include <linux/firewire.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/sched.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
-#include <sound/rawmidi.h>
-#include "amdtp.h"
+#include "amdtp-stream.h"
#define TICKS_PER_CYCLE 3072
#define CYCLES_PER_SECOND 8000
#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
-/*
- * Nominally 3125 bytes/second, but the MIDI port's clock might be
- * 1% too slow, and the bus clock 100 ppm too fast.
- */
-#define MIDI_BYTES_PER_SECOND 3093
-
-/*
- * Several devices look only at the first eight data blocks.
- * In any case, this is more than enough for the MIDI data rate.
- */
-#define MAX_MIDI_RX_BLOCKS 8
-
#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */
/* isochronous header parameters */
@@ -55,12 +41,8 @@
#define CIP_SYT_MASK 0x0000ffff
#define CIP_SYT_NO_INFO 0xffff
-/*
- * Audio and Music transfer protocol specific parameters
- * only "Clock-based rate control mode" is supported
- */
-#define CIP_FMT_AM (0x10 << CIP_FMT_SHIFT)
-#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SHIFT + 3))
+/* Audio and Music transfer protocol specific parameters */
+#define CIP_FMT_AM 0x10
#define AMDTP_FDF_NO_DATA 0xff
/* TODO: make these configurable */
@@ -78,10 +60,23 @@ static void pcm_period_tasklet(unsigned long data);
* @unit: the target of the stream
* @dir: the direction of stream
* @flags: the packet transmission method to use
+ * @fmt: the value of fmt field in CIP header
+ * @process_data_blocks: callback handler to process data blocks
+ * @protocol_size: the size to allocate newly for protocol
*/
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
- enum amdtp_stream_direction dir, enum cip_flags flags)
+ enum amdtp_stream_direction dir, enum cip_flags flags,
+ unsigned int fmt,
+ amdtp_stream_process_data_blocks_t process_data_blocks,
+ unsigned int protocol_size)
{
+ if (process_data_blocks == NULL)
+ return -EINVAL;
+
+ s->protocol = kzalloc(protocol_size, GFP_KERNEL);
+ if (!s->protocol)
+ return -ENOMEM;
+
s->unit = unit;
s->direction = dir;
s->flags = flags;
@@ -94,6 +89,9 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
s->callbacked = false;
s->sync_slave = NULL;
+ s->fmt = fmt;
+ s->process_data_blocks = process_data_blocks;
+
return 0;
}
EXPORT_SYMBOL(amdtp_stream_init);
@@ -105,6 +103,7 @@ EXPORT_SYMBOL(amdtp_stream_init);
void amdtp_stream_destroy(struct amdtp_stream *s)
{
WARN_ON(amdtp_stream_running(s));
+ kfree(s->protocol);
mutex_destroy(&s->mutex);
}
EXPORT_SYMBOL(amdtp_stream_destroy);
@@ -141,11 +140,6 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
{
int err;
- /* AM824 in IEC 61883-6 can deliver 24bit data */
- err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
- if (err < 0)
- goto end;
-
/*
* Currently firewire-lib processes 16 packets in one software
* interrupt callback. This equals to 2msec but actually the
@@ -190,39 +184,25 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
* amdtp_stream_set_parameters - set stream parameters
* @s: the AMDTP stream to configure
* @rate: the sample rate
- * @pcm_channels: the number of PCM samples in each data block, to be encoded
- * as AM824 multi-bit linear audio
- * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
+ * @data_block_quadlets: the size of a data block in quadlet unit
*
* The parameters must be set before the stream is started, and must not be
* changed while the stream is running.
*/
-void amdtp_stream_set_parameters(struct amdtp_stream *s,
- unsigned int rate,
- unsigned int pcm_channels,
- unsigned int midi_ports)
+int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int data_block_quadlets)
{
- unsigned int i, sfc, midi_channels;
+ unsigned int sfc;
- midi_channels = DIV_ROUND_UP(midi_ports, 8);
-
- if (WARN_ON(amdtp_stream_running(s)) |
- WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) |
- WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
- return;
-
- for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc)
+ for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) {
if (amdtp_rate_table[sfc] == rate)
- goto sfc_found;
- WARN_ON(1);
- return;
+ break;
+ }
+ if (sfc == ARRAY_SIZE(amdtp_rate_table))
+ return -EINVAL;
-sfc_found:
- s->pcm_channels = pcm_channels;
s->sfc = sfc;
- s->data_block_quadlets = s->pcm_channels + midi_channels;
- s->midi_ports = midi_ports;
-
+ s->data_block_quadlets = data_block_quadlets;
s->syt_interval = amdtp_syt_intervals[sfc];
/* default buffering in the device */
@@ -231,18 +211,7 @@ sfc_found:
/* additional buffering needed to adjust for no-data packets */
s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
- /* init the position map for PCM and MIDI channels */
- for (i = 0; i < pcm_channels; i++)
- s->pcm_positions[i] = i;
- s->midi_position = s->pcm_channels;
-
- /*
- * We do not know the actual MIDI FIFO size of most devices. Just
- * assume two bytes, i.e., one byte can be received over the bus while
- * the previous one is transmitted over MIDI.
- * (The value here is adjusted for midi_ratelimit_per_packet().)
- */
- s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+ return 0;
}
EXPORT_SYMBOL(amdtp_stream_set_parameters);
@@ -264,52 +233,6 @@ unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
}
EXPORT_SYMBOL(amdtp_stream_get_max_payload);
-static void write_pcm_s16(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames);
-static void write_pcm_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames);
-static void read_pcm_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames);
-
-/**
- * amdtp_stream_set_pcm_format - set the PCM format
- * @s: the AMDTP stream to configure
- * @format: the format of the ALSA PCM device
- *
- * The sample format must be set after the other parameters (rate/PCM channels/
- * MIDI) and before the stream is started, and must not be changed while the
- * stream is running.
- */
-void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
- snd_pcm_format_t format)
-{
- if (WARN_ON(amdtp_stream_pcm_running(s)))
- return;
-
- switch (format) {
- default:
- WARN_ON(1);
- /* fall through */
- case SNDRV_PCM_FORMAT_S16:
- if (s->direction == AMDTP_OUT_STREAM) {
- s->transfer_samples = write_pcm_s16;
- break;
- }
- WARN_ON(1);
- /* fall through */
- case SNDRV_PCM_FORMAT_S32:
- if (s->direction == AMDTP_OUT_STREAM)
- s->transfer_samples = write_pcm_s32;
- else
- s->transfer_samples = read_pcm_s32;
- break;
- }
-}
-EXPORT_SYMBOL(amdtp_stream_set_pcm_format);
-
/**
* amdtp_stream_pcm_prepare - prepare PCM device for running
* @s: the AMDTP stream
@@ -412,182 +335,12 @@ static unsigned int calculate_syt(struct amdtp_stream *s,
}
}
-static void write_pcm_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
-{
- struct snd_pcm_runtime *runtime = pcm->runtime;
- unsigned int channels, remaining_frames, i, c;
- const u32 *src;
-
- channels = s->pcm_channels;
- src = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
- for (i = 0; i < frames; ++i) {
- for (c = 0; c < channels; ++c) {
- buffer[s->pcm_positions[c]] =
- cpu_to_be32((*src >> 8) | 0x40000000);
- src++;
- }
- buffer += s->data_block_quadlets;
- if (--remaining_frames == 0)
- src = (void *)runtime->dma_area;
- }
-}
-
-static void write_pcm_s16(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
-{
- struct snd_pcm_runtime *runtime = pcm->runtime;
- unsigned int channels, remaining_frames, i, c;
- const u16 *src;
-
- channels = s->pcm_channels;
- src = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
- for (i = 0; i < frames; ++i) {
- for (c = 0; c < channels; ++c) {
- buffer[s->pcm_positions[c]] =
- cpu_to_be32((*src << 8) | 0x42000000);
- src++;
- }
- buffer += s->data_block_quadlets;
- if (--remaining_frames == 0)
- src = (void *)runtime->dma_area;
- }
-}
-
-static void read_pcm_s32(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
-{
- struct snd_pcm_runtime *runtime = pcm->runtime;
- unsigned int channels, remaining_frames, i, c;
- u32 *dst;
-
- channels = s->pcm_channels;
- dst = (void *)runtime->dma_area +
- frames_to_bytes(runtime, s->pcm_buffer_pointer);
- remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
- for (i = 0; i < frames; ++i) {
- for (c = 0; c < channels; ++c) {
- *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8;
- dst++;
- }
- buffer += s->data_block_quadlets;
- if (--remaining_frames == 0)
- dst = (void *)runtime->dma_area;
- }
-}
-
-static void write_pcm_silence(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
-{
- unsigned int i, c;
-
- for (i = 0; i < frames; ++i) {
- for (c = 0; c < s->pcm_channels; ++c)
- buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000);
- buffer += s->data_block_quadlets;
- }
-}
-
-/*
- * To avoid sending MIDI bytes at too high a rate, assume that the receiving
- * device has a FIFO, and track how much it is filled. This values increases
- * by one whenever we send one byte in a packet, but the FIFO empties at
- * a constant rate independent of our packet rate. One packet has syt_interval
- * samples, so the number of bytes that empty out of the FIFO, per packet(!),
- * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing
- * fractional values, the values in midi_fifo_used[] are measured in bytes
- * multiplied by the sample rate.
- */
-static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
-{
- int used;
-
- used = s->midi_fifo_used[port];
- if (used == 0) /* common shortcut */
- return true;
-
- used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
- used = max(used, 0);
- s->midi_fifo_used[port] = used;
-
- return used < s->midi_fifo_limit;
-}
-
-static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
-{
- s->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
-}
-
-static void write_midi_messages(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
-{
- unsigned int f, port;
- u8 *b;
-
- for (f = 0; f < frames; f++) {
- b = (u8 *)&buffer[s->midi_position];
-
- port = (s->data_block_counter + f) % 8;
- if (f < MAX_MIDI_RX_BLOCKS &&
- midi_ratelimit_per_packet(s, port) &&
- s->midi[port] != NULL &&
- snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) {
- midi_rate_use_one_byte(s, port);
- b[0] = 0x81;
- } else {
- b[0] = 0x80;
- b[1] = 0;
- }
- b[2] = 0;
- b[3] = 0;
-
- buffer += s->data_block_quadlets;
- }
-}
-
-static void read_midi_messages(struct amdtp_stream *s,
- __be32 *buffer, unsigned int frames)
-{
- unsigned int f, port;
- int len;
- u8 *b;
-
- for (f = 0; f < frames; f++) {
- port = (s->data_block_counter + f) % 8;
- b = (u8 *)&buffer[s->midi_position];
-
- len = b[0] - 0x80;
- if ((1 <= len) && (len <= 3) && (s->midi[port]))
- snd_rawmidi_receive(s->midi[port], b + 1, len);
-
- buffer += s->data_block_quadlets;
- }
-}
-
static void update_pcm_pointers(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
unsigned int frames)
{
unsigned int ptr;
- /*
- * In IEC 61883-6, one data block represents one event. In ALSA, one
- * event equals to one PCM frame. But Dice has a quirk to transfer
- * two PCM frames in one data block.
- */
- if (s->double_pcm_frames)
- frames *= 2;
-
ptr = s->pcm_buffer_pointer + frames;
if (ptr >= pcm->runtime->buffer_size)
ptr -= pcm->runtime->buffer_size;
@@ -656,23 +409,19 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
{
__be32 *buffer;
unsigned int payload_length;
+ unsigned int pcm_frames;
struct snd_pcm_substream *pcm;
buffer = s->buffer.packets[s->packet_index].buffer;
+ pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
+
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
(s->data_block_quadlets << CIP_DBS_SHIFT) |
s->data_block_counter);
- buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 |
- (s->sfc << CIP_FDF_SHIFT) | syt);
- buffer += 2;
-
- pcm = ACCESS_ONCE(s->pcm);
- if (pcm)
- s->transfer_samples(s, pcm, buffer, data_blocks);
- else
- write_pcm_silence(s, buffer, data_blocks);
- if (s->midi_ports)
- write_midi_messages(s, buffer, data_blocks);
+ buffer[1] = cpu_to_be32(CIP_EOH |
+ ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
+ ((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
+ (syt & CIP_SYT_MASK));
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
@@ -680,8 +429,9 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
if (queue_out_packet(s, payload_length, false) < 0)
return -EIO;
- if (pcm)
- update_pcm_pointers(s, pcm, data_blocks);
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm && pcm_frames > 0)
+ update_pcm_pointers(s, pcm, pcm_frames);
/* No need to return the number of handled data blocks. */
return 0;
@@ -689,11 +439,13 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
static int handle_in_packet(struct amdtp_stream *s,
unsigned int payload_quadlets, __be32 *buffer,
- unsigned int *data_blocks)
+ unsigned int *data_blocks, unsigned int syt)
{
u32 cip_header[2];
+ unsigned int fmt, fdf;
unsigned int data_block_quadlets, data_block_counter, dbc_interval;
- struct snd_pcm_substream *pcm = NULL;
+ struct snd_pcm_substream *pcm;
+ unsigned int pcm_frames;
bool lost;
cip_header[0] = be32_to_cpu(buffer[0]);
@@ -704,19 +456,30 @@ static int handle_in_packet(struct amdtp_stream *s,
* For convenience, also check FMT field is AM824 or not.
*/
if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
- ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) ||
- ((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) {
+ ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) {
dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
*data_blocks = 0;
+ pcm_frames = 0;
+ goto end;
+ }
+
+ /* Check valid protocol or not. */
+ fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT;
+ if (fmt != s->fmt) {
+ dev_info_ratelimited(&s->unit->device,
+ "Detect unexpected protocol: %08x %08x\n",
+ cip_header[0], cip_header[1]);
+ *data_blocks = 0;
+ pcm_frames = 0;
goto end;
}
/* Calculate data blocks */
+ fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
if (payload_quadlets < 3 ||
- ((cip_header[1] & CIP_FDF_MASK) ==
- (AMDTP_FDF_NO_DATA << CIP_FDF_SHIFT))) {
+ (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
*data_blocks = 0;
} else {
data_block_quadlets =
@@ -763,16 +526,7 @@ static int handle_in_packet(struct amdtp_stream *s,
return -EIO;
}
- if (*data_blocks > 0) {
- buffer += 2;
-
- pcm = ACCESS_ONCE(s->pcm);
- if (pcm)
- s->transfer_samples(s, pcm, buffer, *data_blocks);
-
- if (s->midi_ports)
- read_midi_messages(s, buffer, *data_blocks);
- }
+ pcm_frames = s->process_data_blocks(s, buffer + 2, *data_blocks, &syt);
if (s->flags & CIP_DBC_IS_END_EVENT)
s->data_block_counter = data_block_counter;
@@ -783,8 +537,9 @@ end:
if (queue_in_packet(s) < 0)
return -EIO;
- if (pcm)
- update_pcm_pointers(s, pcm, *data_blocks);
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm && pcm_frames > 0)
+ update_pcm_pointers(s, pcm, pcm_frames);
return 0;
}
@@ -854,15 +609,15 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
break;
}
+ syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
if (handle_in_packet(s, payload_quadlets, buffer,
- &data_blocks) < 0) {
+ &data_blocks, syt) < 0) {
s->packet_index = -1;
break;
}
/* Process sync slave stream */
if (s->sync_slave && s->sync_slave->callbacked) {
- syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
if (handle_out_packet(s->sync_slave,
data_blocks, syt) < 0) {
s->packet_index = -1;
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp-stream.h
index b2cf9e75693b..8775704a3665 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp-stream.h
@@ -4,6 +4,7 @@
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
+#include <linux/sched.h>
#include <sound/asound.h>
#include "packets-buffer.h"
@@ -80,100 +81,78 @@ enum cip_sfc {
CIP_SFC_COUNT
};
-#define AMDTP_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
-
-#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \
- SNDRV_PCM_FMTBIT_S32)
-
-
-/*
- * This module supports maximum 64 PCM channels for one PCM stream
- * This is for our convenience.
- */
-#define AMDTP_MAX_CHANNELS_FOR_PCM 64
-
-/*
- * AMDTP packet can include channels for MIDI conformant data.
- * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
- * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
- *
- * This module supports maximum 1 MIDI conformant data channels.
- * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
- */
-#define AMDTP_MAX_CHANNELS_FOR_MIDI 1
-
struct fw_unit;
struct fw_iso_context;
struct snd_pcm_substream;
struct snd_pcm_runtime;
-struct snd_rawmidi_substream;
enum amdtp_stream_direction {
AMDTP_OUT_STREAM = 0,
AMDTP_IN_STREAM
};
+struct amdtp_stream;
+typedef unsigned int (*amdtp_stream_process_data_blocks_t)(
+ struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt);
struct amdtp_stream {
struct fw_unit *unit;
enum cip_flags flags;
enum amdtp_stream_direction direction;
- struct fw_iso_context *context;
struct mutex mutex;
- enum cip_sfc sfc;
- unsigned int data_block_quadlets;
- unsigned int pcm_channels;
- unsigned int midi_ports;
- void (*transfer_samples)(struct amdtp_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames);
- u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM];
- u8 midi_position;
-
- unsigned int syt_interval;
- unsigned int transfer_delay;
- unsigned int source_node_id_field;
+ /* For packet processing. */
+ struct fw_iso_context *context;
struct iso_packets_buffer buffer;
-
- struct snd_pcm_substream *pcm;
- struct tasklet_struct period_tasklet;
-
int packet_index;
+
+ /* For CIP headers. */
+ unsigned int source_node_id_field;
+ unsigned int data_block_quadlets;
unsigned int data_block_counter;
+ unsigned int fmt;
+ unsigned int fdf;
+ /* quirk: fixed interval of dbc between previos/current packets. */
+ unsigned int tx_dbc_interval;
+ /* quirk: indicate the value of dbc field in a first packet. */
+ unsigned int tx_first_dbc;
+ /* Internal flags. */
+ enum cip_sfc sfc;
+ unsigned int syt_interval;
+ unsigned int transfer_delay;
unsigned int data_block_state;
-
unsigned int last_syt_offset;
unsigned int syt_offset_state;
+ /* For a PCM substream processing. */
+ struct snd_pcm_substream *pcm;
+ struct tasklet_struct period_tasklet;
unsigned int pcm_buffer_pointer;
unsigned int pcm_period_pointer;
bool pointer_flush;
- bool double_pcm_frames;
-
- struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
- int midi_fifo_limit;
- int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
-
- /* quirk: fixed interval of dbc between previos/current packets. */
- unsigned int tx_dbc_interval;
- /* quirk: indicate the value of dbc field in a first packet. */
- unsigned int tx_first_dbc;
+ /* To wait for first packet. */
bool callbacked;
wait_queue_head_t callback_wait;
struct amdtp_stream *sync_slave;
+
+ /* For backends to process data blocks. */
+ void *protocol;
+ amdtp_stream_process_data_blocks_t process_data_blocks;
};
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
- enum amdtp_stream_direction dir,
- enum cip_flags flags);
+ enum amdtp_stream_direction dir, enum cip_flags flags,
+ unsigned int fmt,
+ amdtp_stream_process_data_blocks_t process_data_blocks,
+ unsigned int protocol_size);
void amdtp_stream_destroy(struct amdtp_stream *s);
-void amdtp_stream_set_parameters(struct amdtp_stream *s,
- unsigned int rate,
- unsigned int pcm_channels,
- unsigned int midi_ports);
+int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int data_block_quadlets);
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
@@ -182,8 +161,7 @@ void amdtp_stream_stop(struct amdtp_stream *s);
int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime);
-void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
- snd_pcm_format_t format);
+
void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
void amdtp_stream_pcm_abort(struct amdtp_stream *s);
@@ -240,24 +218,6 @@ static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
ACCESS_ONCE(s->pcm) = pcm;
}
-/**
- * amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device
- * @s: the AMDTP stream
- * @port: index of MIDI port
- * @midi: the MIDI device to be started, or %NULL to stop the current device
- *
- * Call this function on a running isochronous stream to enable the actual
- * transmission of MIDI data. This function should be called from the MIDI
- * device's .trigger callback.
- */
-static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s,
- unsigned int port,
- struct snd_rawmidi_substream *midi)
-{
- if (port < s->midi_ports)
- ACCESS_ONCE(s->midi[port]) = midi;
-}
-
static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
{
return sfc & 1;
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
index 6cf470c80d1f..af7ed6643266 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,4 +1,4 @@
snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \
bebob_focusrite.o bebob_maudio.o bebob.o
-obj-m += snd-bebob.o
+obj-$(CONFIG_SND_BEBOB) += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 27a04ac8ffee..091290d1f3ea 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -41,7 +41,8 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
#define VEN_EDIROL 0x000040ab
#define VEN_PRESONUS 0x00000a92
#define VEN_BRIDGECO 0x000007f5
-#define VEN_MACKIE 0x0000000f
+#define VEN_MACKIE1 0x0000000f
+#define VEN_MACKIE2 0x00000ff2
#define VEN_STANTON 0x00001260
#define VEN_TASCAM 0x0000022e
#define VEN_BEHRINGER 0x00001564
@@ -334,7 +335,7 @@ static void bebob_remove(struct fw_unit *unit)
snd_card_free_when_closed(bebob->card);
}
-static struct snd_bebob_rate_spec normal_rate_spec = {
+static const struct snd_bebob_rate_spec normal_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate
};
@@ -360,9 +361,9 @@ static const struct ieee1394_device_id bebob_id_table[] = {
/* BridgeCo, Audio5 */
SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal),
/* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */
- SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal),
+ SND_BEBOB_DEV_ENTRY(VEN_MACKIE2, 0x00010065, &spec_normal),
/* Mackie, d.2 (Firewire Option) */
- SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal),
+ SND_BEBOB_DEV_ENTRY(VEN_MACKIE1, 0x00010067, &spec_normal),
/* Stanton, ScratchAmp */
SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal),
/* Tascam, IF-FW DM */
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index d23caca7f369..4d8fcc78e747 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -31,7 +31,7 @@
#include "../fcp.h"
#include "../packets-buffer.h"
#include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
#include "../cmp.h"
/* basic register addresses on DM1000/DM1100/DM1500 */
@@ -70,9 +70,9 @@ struct snd_bebob_meter_spec {
int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size);
};
struct snd_bebob_spec {
- struct snd_bebob_clock_spec *clock;
- struct snd_bebob_rate_spec *rate;
- struct snd_bebob_meter_spec *meter;
+ const struct snd_bebob_clock_spec *clock;
+ const struct snd_bebob_rate_spec *rate;
+ const struct snd_bebob_meter_spec *meter;
};
struct snd_bebob {
@@ -235,19 +235,19 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
/* model specific operations */
-extern struct snd_bebob_spec phase88_rack_spec;
-extern struct snd_bebob_spec phase24_series_spec;
-extern struct snd_bebob_spec yamaha_go_spec;
-extern struct snd_bebob_spec saffirepro_26_spec;
-extern struct snd_bebob_spec saffirepro_10_spec;
-extern struct snd_bebob_spec saffire_le_spec;
-extern struct snd_bebob_spec saffire_spec;
-extern struct snd_bebob_spec maudio_fw410_spec;
-extern struct snd_bebob_spec maudio_audiophile_spec;
-extern struct snd_bebob_spec maudio_solo_spec;
-extern struct snd_bebob_spec maudio_ozonic_spec;
-extern struct snd_bebob_spec maudio_nrv10_spec;
-extern struct snd_bebob_spec maudio_special_spec;
+extern const struct snd_bebob_spec phase88_rack_spec;
+extern const struct snd_bebob_spec phase24_series_spec;
+extern const struct snd_bebob_spec yamaha_go_spec;
+extern const struct snd_bebob_spec saffirepro_26_spec;
+extern const struct snd_bebob_spec saffirepro_10_spec;
+extern const struct snd_bebob_spec saffire_le_spec;
+extern const struct snd_bebob_spec saffire_spec;
+extern const struct snd_bebob_spec maudio_fw410_spec;
+extern const struct snd_bebob_spec maudio_audiophile_spec;
+extern const struct snd_bebob_spec maudio_solo_spec;
+extern const struct snd_bebob_spec maudio_ozonic_spec;
+extern const struct snd_bebob_spec maudio_nrv10_spec;
+extern const struct snd_bebob_spec maudio_special_spec;
int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814);
int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c
index a1a39494ea6c..f11090057949 100644
--- a/sound/firewire/bebob/bebob_focusrite.c
+++ b/sound/firewire/bebob/bebob_focusrite.c
@@ -200,7 +200,7 @@ end:
return err;
}
-struct snd_bebob_spec saffire_le_spec;
+const struct snd_bebob_spec saffire_le_spec;
static enum snd_bebob_clock_type saffire_both_clk_src_types[] = {
SND_BEBOB_CLOCK_TYPE_INTERNAL,
SND_BEBOB_CLOCK_TYPE_EXTERNAL,
@@ -229,7 +229,7 @@ static const char *const saffire_meter_labels[] = {
static int
saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
{
- struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
unsigned int channels;
u64 offset;
int err;
@@ -260,60 +260,60 @@ saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
return err;
}
-static struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
+static const struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
.get = &saffirepro_both_clk_freq_get,
.set = &saffirepro_both_clk_freq_set,
};
/* Saffire Pro 26 I/O */
-static struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
+static const struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
.num = ARRAY_SIZE(saffirepro_26_clk_src_types),
.types = saffirepro_26_clk_src_types,
.get = &saffirepro_both_clk_src_get,
};
-struct snd_bebob_spec saffirepro_26_spec = {
+const struct snd_bebob_spec saffirepro_26_spec = {
.clock = &saffirepro_26_clk_spec,
.rate = &saffirepro_both_rate_spec,
.meter = NULL
};
/* Saffire Pro 10 I/O */
-static struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
+static const struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
.num = ARRAY_SIZE(saffirepro_10_clk_src_types),
.types = saffirepro_10_clk_src_types,
.get = &saffirepro_both_clk_src_get,
};
-struct snd_bebob_spec saffirepro_10_spec = {
+const struct snd_bebob_spec saffirepro_10_spec = {
.clock = &saffirepro_10_clk_spec,
.rate = &saffirepro_both_rate_spec,
.meter = NULL
};
-static struct snd_bebob_rate_spec saffire_both_rate_spec = {
+static const struct snd_bebob_rate_spec saffire_both_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
-static struct snd_bebob_clock_spec saffire_both_clk_spec = {
+static const struct snd_bebob_clock_spec saffire_both_clk_spec = {
.num = ARRAY_SIZE(saffire_both_clk_src_types),
.types = saffire_both_clk_src_types,
.get = &saffire_both_clk_src_get,
};
/* Saffire LE */
-static struct snd_bebob_meter_spec saffire_le_meter_spec = {
+static const struct snd_bebob_meter_spec saffire_le_meter_spec = {
.num = ARRAY_SIZE(saffire_le_meter_labels),
.labels = saffire_le_meter_labels,
.get = &saffire_meter_get,
};
-struct snd_bebob_spec saffire_le_spec = {
+const struct snd_bebob_spec saffire_le_spec = {
.clock = &saffire_both_clk_spec,
.rate = &saffire_both_rate_spec,
.meter = &saffire_le_meter_spec
};
/* Saffire */
-static struct snd_bebob_meter_spec saffire_meter_spec = {
+static const struct snd_bebob_meter_spec saffire_meter_spec = {
.num = ARRAY_SIZE(saffire_meter_labels),
.labels = saffire_meter_labels,
.get = &saffire_meter_get,
};
-struct snd_bebob_spec saffire_spec = {
+const struct snd_bebob_spec saffire_spec = {
.clock = &saffire_both_clk_spec,
.rate = &saffire_both_rate_spec,
.meter = &saffire_meter_spec
diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c
index 057495d54ab0..07e5abdbceb5 100644
--- a/sound/firewire/bebob/bebob_maudio.c
+++ b/sound/firewire/bebob/bebob_maudio.c
@@ -628,7 +628,7 @@ static const char *const special_meter_labels[] = {
static int
special_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size)
{
- u16 *buf;
+ __be16 *buf;
unsigned int i, c, channels;
int err;
@@ -687,7 +687,7 @@ static const char *const nrv10_meter_labels[] = {
static int
normal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
{
- struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
unsigned int c, channels;
int err;
@@ -712,85 +712,85 @@ end:
}
/* for special customized devices */
-static struct snd_bebob_rate_spec special_rate_spec = {
+static const struct snd_bebob_rate_spec special_rate_spec = {
.get = &special_get_rate,
.set = &special_set_rate,
};
-static struct snd_bebob_clock_spec special_clk_spec = {
+static const struct snd_bebob_clock_spec special_clk_spec = {
.num = ARRAY_SIZE(special_clk_types),
.types = special_clk_types,
.get = &special_clk_get,
};
-static struct snd_bebob_meter_spec special_meter_spec = {
+static const struct snd_bebob_meter_spec special_meter_spec = {
.num = ARRAY_SIZE(special_meter_labels),
.labels = special_meter_labels,
.get = &special_meter_get
};
-struct snd_bebob_spec maudio_special_spec = {
+const struct snd_bebob_spec maudio_special_spec = {
.clock = &special_clk_spec,
.rate = &special_rate_spec,
.meter = &special_meter_spec
};
/* Firewire 410 specification */
-static struct snd_bebob_rate_spec usual_rate_spec = {
+static const struct snd_bebob_rate_spec usual_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
-static struct snd_bebob_meter_spec fw410_meter_spec = {
+static const struct snd_bebob_meter_spec fw410_meter_spec = {
.num = ARRAY_SIZE(fw410_meter_labels),
.labels = fw410_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_fw410_spec = {
+const struct snd_bebob_spec maudio_fw410_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &fw410_meter_spec
};
/* Firewire Audiophile specification */
-static struct snd_bebob_meter_spec audiophile_meter_spec = {
+static const struct snd_bebob_meter_spec audiophile_meter_spec = {
.num = ARRAY_SIZE(audiophile_meter_labels),
.labels = audiophile_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_audiophile_spec = {
+const struct snd_bebob_spec maudio_audiophile_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &audiophile_meter_spec
};
/* Firewire Solo specification */
-static struct snd_bebob_meter_spec solo_meter_spec = {
+static const struct snd_bebob_meter_spec solo_meter_spec = {
.num = ARRAY_SIZE(solo_meter_labels),
.labels = solo_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_solo_spec = {
+const struct snd_bebob_spec maudio_solo_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &solo_meter_spec
};
/* Ozonic specification */
-static struct snd_bebob_meter_spec ozonic_meter_spec = {
+static const struct snd_bebob_meter_spec ozonic_meter_spec = {
.num = ARRAY_SIZE(ozonic_meter_labels),
.labels = ozonic_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_ozonic_spec = {
+const struct snd_bebob_spec maudio_ozonic_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &ozonic_meter_spec
};
/* NRV10 specification */
-static struct snd_bebob_meter_spec nrv10_meter_spec = {
+static const struct snd_bebob_meter_spec nrv10_meter_spec = {
.num = ARRAY_SIZE(nrv10_meter_labels),
.labels = nrv10_meter_labels,
.get = &normal_meter_get
};
-struct snd_bebob_spec maudio_nrv10_spec = {
+const struct snd_bebob_spec maudio_nrv10_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &nrv10_meter_spec
diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c
index 5681143925cd..90d95be499b0 100644
--- a/sound/firewire/bebob/bebob_midi.c
+++ b/sound/firewire/bebob/bebob_midi.c
@@ -72,11 +72,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&bebob->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&bebob->tx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&bebob->tx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&bebob->tx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&bebob->tx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&bebob->lock, flags);
}
@@ -89,11 +89,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&bebob->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&bebob->rx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&bebob->rx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&bebob->rx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&bebob->rx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&bebob->lock, flags);
}
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c
index c0f018a61fdc..ef224d6f5c24 100644
--- a/sound/firewire/bebob/bebob_pcm.c
+++ b/sound/firewire/bebob/bebob_pcm.c
@@ -122,11 +122,11 @@ pcm_init_hw_params(struct snd_bebob *bebob,
SNDRV_PCM_INFO_MMAP_VALID;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
s = &bebob->tx_stream;
formations = bebob->tx_stream_formations;
} else {
- runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
s = &bebob->rx_stream;
formations = bebob->rx_stream_formations;
}
@@ -146,7 +146,7 @@ pcm_init_hw_params(struct snd_bebob *bebob,
if (err < 0)
goto end;
- err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+ err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
end:
return err;
}
@@ -155,7 +155,7 @@ static int
pcm_open(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
- struct snd_bebob_rate_spec *spec = bebob->spec->rate;
+ const struct snd_bebob_rate_spec *spec = bebob->spec->rate;
unsigned int sampling_rate;
enum snd_bebob_clock_type src;
int err;
@@ -220,8 +220,8 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&bebob->substreams_counter);
- amdtp_stream_set_pcm_format(&bebob->tx_stream,
- params_format(hw_params));
+
+ amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params));
return 0;
}
@@ -239,8 +239,8 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream,
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&bebob->substreams_counter);
- amdtp_stream_set_pcm_format(&bebob->rx_stream,
- params_format(hw_params));
+
+ amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params));
return 0;
}
diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c
index 301cc6a93945..ec24f96794f5 100644
--- a/sound/firewire/bebob/bebob_proc.c
+++ b/sound/firewire/bebob/bebob_proc.c
@@ -73,7 +73,7 @@ proc_read_meters(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_bebob *bebob = entry->private_data;
- struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
u32 *buf;
unsigned int i, c, channels, size;
@@ -138,8 +138,8 @@ proc_read_clock(struct snd_info_entry *entry,
"SYT-Match",
};
struct snd_bebob *bebob = entry->private_data;
- struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
- struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+ const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
enum snd_bebob_clock_type src;
unsigned int rate;
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 5be5242e1ed8..926e5dcbb66a 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -119,7 +119,7 @@ end:
int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
enum snd_bebob_clock_type *src)
{
- struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
unsigned int id;
enum avc_bridgeco_plug_type type;
@@ -338,7 +338,7 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
err = -ENOSYS;
goto end;
}
- s->midi_position = stm_pos;
+ amdtp_am824_set_midi_position(s, stm_pos);
midi = stm_pos;
break;
/* for PCM data channel */
@@ -354,11 +354,12 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
case 0x09: /* Digital */
default:
location = pcm + sec_loc;
- if (location >= AMDTP_MAX_CHANNELS_FOR_PCM) {
+ if (location >= AM824_MAX_CHANNELS_FOR_PCM) {
err = -ENOSYS;
goto end;
}
- s->pcm_positions[location] = stm_pos;
+ amdtp_am824_set_pcm_position(s, location,
+ stm_pos);
break;
}
}
@@ -427,12 +428,19 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate)
index = get_formation_index(rate);
pcm_channels = bebob->tx_stream_formations[index].pcm;
midi_channels = bebob->tx_stream_formations[index].midi;
- amdtp_stream_set_parameters(&bebob->tx_stream,
- rate, pcm_channels, midi_channels * 8);
+ err = amdtp_am824_set_parameters(&bebob->tx_stream, rate,
+ pcm_channels, midi_channels * 8,
+ false);
+ if (err < 0)
+ goto end;
+
pcm_channels = bebob->rx_stream_formations[index].pcm;
midi_channels = bebob->rx_stream_formations[index].midi;
- amdtp_stream_set_parameters(&bebob->rx_stream,
- rate, pcm_channels, midi_channels * 8);
+ err = amdtp_am824_set_parameters(&bebob->rx_stream, rate,
+ pcm_channels, midi_channels * 8,
+ false);
+ if (err < 0)
+ goto end;
/* establish connections for both streams */
err = cmp_connection_establish(&bebob->out_conn,
@@ -530,8 +538,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
if (err < 0)
goto end;
- err = amdtp_stream_init(&bebob->tx_stream, bebob->unit,
- AMDTP_IN_STREAM, CIP_BLOCKING);
+ err = amdtp_am824_init(&bebob->tx_stream, bebob->unit,
+ AMDTP_IN_STREAM, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(&bebob->tx_stream);
destroy_both_connections(bebob);
@@ -559,8 +567,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
if (bebob->maudio_special_quirk)
bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC;
- err = amdtp_stream_init(&bebob->rx_stream, bebob->unit,
- AMDTP_OUT_STREAM, CIP_BLOCKING);
+ err = amdtp_am824_init(&bebob->rx_stream, bebob->unit,
+ AMDTP_OUT_STREAM, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(&bebob->tx_stream);
amdtp_stream_destroy(&bebob->rx_stream);
@@ -572,7 +580,7 @@ end:
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
{
- struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+ const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
struct amdtp_stream *master, *slave;
enum cip_flags sync_mode;
unsigned int curr_rate;
@@ -864,8 +872,8 @@ parse_stream_formation(u8 *buf, unsigned int len,
}
}
- if (formation[i].pcm > AMDTP_MAX_CHANNELS_FOR_PCM ||
- formation[i].midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
+ if (formation[i].pcm > AM824_MAX_CHANNELS_FOR_PCM ||
+ formation[i].midi > AM824_MAX_CHANNELS_FOR_MIDI)
return -ENOSYS;
return 0;
@@ -959,7 +967,7 @@ end:
int snd_bebob_stream_discover(struct snd_bebob *bebob)
{
- struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
enum avc_bridgeco_plug_type type;
unsigned int i;
diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c
index 9242e33d2cf1..c38358b82ada 100644
--- a/sound/firewire/bebob/bebob_terratec.c
+++ b/sound/firewire/bebob/bebob_terratec.c
@@ -55,30 +55,30 @@ phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
return 0;
}
-static struct snd_bebob_rate_spec phase_series_rate_spec = {
+static const struct snd_bebob_rate_spec phase_series_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
/* PHASE 88 Rack FW */
-static struct snd_bebob_clock_spec phase88_rack_clk = {
+static const struct snd_bebob_clock_spec phase88_rack_clk = {
.num = ARRAY_SIZE(phase88_rack_clk_src_types),
.types = phase88_rack_clk_src_types,
.get = &phase88_rack_clk_src_get,
};
-struct snd_bebob_spec phase88_rack_spec = {
+const struct snd_bebob_spec phase88_rack_spec = {
.clock = &phase88_rack_clk,
.rate = &phase_series_rate_spec,
.meter = NULL
};
/* 'PHASE 24 FW' and 'PHASE X24 FW' */
-static struct snd_bebob_clock_spec phase24_series_clk = {
+static const struct snd_bebob_clock_spec phase24_series_clk = {
.num = ARRAY_SIZE(phase24_series_clk_src_types),
.types = phase24_series_clk_src_types,
.get = &phase24_series_clk_src_get,
};
-struct snd_bebob_spec phase24_series_spec = {
+const struct snd_bebob_spec phase24_series_spec = {
.clock = &phase24_series_clk,
.rate = &phase_series_rate_spec,
.meter = NULL
diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha.c
index 58101702410b..90d4404f77ce 100644
--- a/sound/firewire/bebob/bebob_yamaha.c
+++ b/sound/firewire/bebob/bebob_yamaha.c
@@ -46,16 +46,16 @@ clk_src_get(struct snd_bebob *bebob, unsigned int *id)
return 0;
}
-static struct snd_bebob_clock_spec clock_spec = {
+static const struct snd_bebob_clock_spec clock_spec = {
.num = ARRAY_SIZE(clk_src_types),
.types = clk_src_types,
.get = &clk_src_get,
};
-static struct snd_bebob_rate_spec rate_spec = {
+static const struct snd_bebob_rate_spec rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
-struct snd_bebob_spec yamaha_go_spec = {
+const struct snd_bebob_spec yamaha_go_spec = {
.clock = &clock_spec,
.rate = &rate_spec,
.meter = NULL
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile
index 9ef228ef7baf..55b4be9b0034 100644
--- a/sound/firewire/dice/Makefile
+++ b/sound/firewire/dice/Makefile
@@ -1,3 +1,3 @@
snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
dice-pcm.o dice-hwdep.o dice.o
-obj-m += snd-dice.o
+obj-$(CONFIG_SND_DICE) += snd-dice.o
diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c
index fe43ce791f84..151b09f240f2 100644
--- a/sound/firewire/dice/dice-midi.c
+++ b/sound/firewire/dice/dice-midi.c
@@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&dice->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&dice->tx_stream,
+ amdtp_am824_midi_trigger(&dice->tx_stream,
substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&dice->tx_stream,
+ amdtp_am824_midi_trigger(&dice->tx_stream,
substrm->number, NULL);
spin_unlock_irqrestore(&dice->lock, flags);
@@ -69,11 +69,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&dice->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&dice->rx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&dice->rx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&dice->rx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&dice->rx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&dice->lock, flags);
}
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
index 4e67b1da0fe6..9b3431999fc8 100644
--- a/sound/firewire/dice/dice-pcm.c
+++ b/sound/firewire/dice/dice-pcm.c
@@ -133,11 +133,11 @@ static int init_hw_info(struct snd_dice *dice,
SNDRV_PCM_INFO_BLOCK_TRANSFER;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- hw->formats = AMDTP_IN_PCM_FORMAT_BITS;
+ hw->formats = AM824_IN_PCM_FORMAT_BITS;
stream = &dice->tx_stream;
pcm_channels = dice->tx_channels;
} else {
- hw->formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ hw->formats = AM824_OUT_PCM_FORMAT_BITS;
stream = &dice->rx_stream;
pcm_channels = dice->rx_channels;
}
@@ -156,7 +156,7 @@ static int init_hw_info(struct snd_dice *dice,
if (err < 0)
goto end;
- err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
+ err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
end:
return err;
}
@@ -243,8 +243,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&dice->mutex);
}
- amdtp_stream_set_pcm_format(&dice->tx_stream,
- params_format(hw_params));
+ amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params));
return 0;
}
@@ -265,8 +264,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&dice->mutex);
}
- amdtp_stream_set_pcm_format(&dice->rx_stream,
- params_format(hw_params));
+ amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params));
return 0;
}
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
index 07dbd01d7a6b..a6a39f7ef58d 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -44,16 +44,16 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
static void release_resources(struct snd_dice *dice,
struct fw_iso_resources *resources)
{
- unsigned int channel;
+ __be32 channel;
/* Reset channel number */
channel = cpu_to_be32((u32)-1);
if (resources == &dice->tx_resources)
snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
- &channel, 4);
+ &channel, sizeof(channel));
else
snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
- &channel, 4);
+ &channel, sizeof(channel));
fw_iso_resources_free(resources);
}
@@ -62,7 +62,7 @@ static int keep_resources(struct snd_dice *dice,
struct fw_iso_resources *resources,
unsigned int max_payload_bytes)
{
- unsigned int channel;
+ __be32 channel;
int err;
err = fw_iso_resources_allocate(resources, max_payload_bytes,
@@ -74,10 +74,10 @@ static int keep_resources(struct snd_dice *dice,
channel = cpu_to_be32(resources->channel);
if (resources == &dice->tx_resources)
err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
- &channel, 4);
+ &channel, sizeof(channel));
else
err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
- &channel, 4);
+ &channel, sizeof(channel));
if (err < 0)
release_resources(dice, resources);
end:
@@ -100,6 +100,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
{
struct fw_iso_resources *resources;
unsigned int i, mode, pcm_chs, midi_ports;
+ bool double_pcm_frames;
int err;
err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
@@ -125,21 +126,24 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
* For this quirk, blocking mode is required and PCM buffer size should
* be aligned to SYT_INTERVAL.
*/
- if (mode > 1) {
+ double_pcm_frames = mode > 1;
+ if (double_pcm_frames) {
rate /= 2;
pcm_chs *= 2;
- stream->double_pcm_frames = true;
- } else {
- stream->double_pcm_frames = false;
}
- amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports);
- if (mode > 1) {
+ err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
+ double_pcm_frames);
+ if (err < 0)
+ goto end;
+
+ if (double_pcm_frames) {
pcm_chs /= 2;
for (i = 0; i < pcm_chs; i++) {
- stream->pcm_positions[i] = i * 2;
- stream->pcm_positions[i + pcm_chs] = i * 2 + 1;
+ amdtp_am824_set_pcm_position(stream, i, i * 2);
+ amdtp_am824_set_pcm_position(stream, i + pcm_chs,
+ i * 2 + 1);
}
}
@@ -302,7 +306,7 @@ static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream)
goto end;
resources->channels_mask = 0x00000000ffffffffuLL;
- err = amdtp_stream_init(stream, dice->unit, dir, CIP_BLOCKING);
+ err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(stream);
fw_iso_resources_destroy(resources);
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 70a111d7f428..5d99436dfcae 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -29,7 +29,8 @@ static int dice_interface_check(struct fw_unit *unit)
struct fw_csr_iterator it;
int key, val, vendor = -1, model = -1, err;
unsigned int category, i;
- __be32 *pointers, value;
+ __be32 *pointers;
+ u32 value;
__be32 version;
pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index ecf5dc862235..101550ac1a24 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -34,7 +34,7 @@
#include <sound/pcm_params.h>
#include <sound/rawmidi.h>
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
#include "../iso-resources.h"
#include "../lib.h"
#include "dice-interface.h"
diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile
new file mode 100644
index 000000000000..1123e68c8b28
--- /dev/null
+++ b/sound/firewire/digi00x/Makefile
@@ -0,0 +1,4 @@
+snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \
+ digi00x-pcm.o digi00x-hwdep.o \
+ digi00x-transaction.o digi00x-midi.o digi00x.o
+obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c
new file mode 100644
index 000000000000..b02a5e8cad44
--- /dev/null
+++ b/sound/firewire/digi00x/amdtp-dot.c
@@ -0,0 +1,442 @@
+/*
+ * amdtp-dot.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ * Copyright (C) 2012 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2012 Damien Zammit <damien@zamaudio.com>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/pcm.h>
+#include "digi00x.h"
+
+#define CIP_FMT_AM 0x10
+
+/* 'Clock-based rate control mode' is just supported. */
+#define AMDTP_FDF_AM824 0x00
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND 3093
+
+/*
+ * Several devices look only at the first eight data blocks.
+ * In any case, this is more than enough for the MIDI data rate.
+ */
+#define MAX_MIDI_RX_BLOCKS 8
+
+/*
+ * The double-oh-three algorithm was discovered by Robin Gareus and Damien
+ * Zammit in 2012, with reverse-engineering for Digi 003 Rack.
+ */
+struct dot_state {
+ u8 carry;
+ u8 idx;
+ unsigned int off;
+};
+
+struct amdtp_dot {
+ unsigned int pcm_channels;
+ struct dot_state state;
+
+ unsigned int midi_ports;
+ /* 2 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) */
+ struct snd_rawmidi_substream *midi[2];
+ int midi_fifo_used[2];
+ int midi_fifo_limit;
+
+ void (*transfer_samples)(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
+};
+
+/*
+ * double-oh-three look up table
+ *
+ * @param idx index byte (audio-sample data) 0x00..0xff
+ * @param off channel offset shift
+ * @return salt to XOR with given data
+ */
+#define BYTE_PER_SAMPLE (4)
+#define MAGIC_DOT_BYTE (2)
+#define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE)
+static const u8 dot_scrt(const u8 idx, const unsigned int off)
+{
+ /*
+ * the length of the added pattern only depends on the lower nibble
+ * of the last non-zero data
+ */
+ static const u8 len[16] = {0, 1, 3, 5, 7, 9, 11, 13, 14,
+ 12, 10, 8, 6, 4, 2, 0};
+
+ /*
+ * the lower nibble of the salt. Interleaved sequence.
+ * this is walked backwards according to len[]
+ */
+ static const u8 nib[15] = {0x8, 0x7, 0x9, 0x6, 0xa, 0x5, 0xb, 0x4,
+ 0xc, 0x3, 0xd, 0x2, 0xe, 0x1, 0xf};
+
+ /* circular list for the salt's hi nibble. */
+ static const u8 hir[15] = {0x0, 0x6, 0xf, 0x8, 0x7, 0x5, 0x3, 0x4,
+ 0xc, 0xd, 0xe, 0x1, 0x2, 0xb, 0xa};
+
+ /*
+ * start offset for upper nibble mapping.
+ * note: 9 is /special/. In the case where the high nibble == 0x9,
+ * hir[] is not used and - coincidentally - the salt's hi nibble is
+ * 0x09 regardless of the offset.
+ */
+ static const u8 hio[16] = {0, 11, 12, 6, 7, 5, 1, 4,
+ 3, 0x00, 14, 13, 8, 9, 10, 2};
+
+ const u8 ln = idx & 0xf;
+ const u8 hn = (idx >> 4) & 0xf;
+ const u8 hr = (hn == 0x9) ? 0x9 : hir[(hio[hn] + off) % 15];
+
+ if (len[ln] < off)
+ return 0x00;
+
+ return ((nib[14 + off - len[ln]]) | (hr << 4));
+}
+
+static void dot_encode_step(struct dot_state *state, __be32 *const buffer)
+{
+ u8 * const data = (u8 *) buffer;
+
+ if (data[MAGIC_DOT_BYTE] != 0x00) {
+ state->off = 0;
+ state->idx = data[MAGIC_DOT_BYTE] ^ state->carry;
+ }
+ data[MAGIC_DOT_BYTE] ^= state->carry;
+ state->carry = dot_scrt(state->idx, ++(state->off));
+}
+
+int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels)
+{
+ struct amdtp_dot *p = s->protocol;
+ int err;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ /*
+ * A first data channel is for MIDI conformant data channel, the rest is
+ * Multi Bit Linear Audio data channel.
+ */
+ err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1);
+ if (err < 0)
+ return err;
+
+ s->fdf = AMDTP_FDF_AM824 | s->sfc;
+
+ p->pcm_channels = pcm_channels;
+
+ if (s->direction == AMDTP_IN_STREAM)
+ p->midi_ports = DOT_MIDI_IN_PORTS;
+ else
+ p->midi_ports = DOT_MIDI_OUT_PORTS;
+
+ /*
+ * We do not know the actual MIDI FIFO size of most devices. Just
+ * assume two bytes, i.e., one byte can be received over the bus while
+ * the previous one is transmitted over MIDI.
+ * (The value here is adjusted for midi_ratelimit_per_packet().)
+ */
+ p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+
+ return 0;
+}
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_dot *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u32 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ buffer++;
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32((*src >> 8) | 0x40000000);
+ dot_encode_step(&p->state, &buffer[c]);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_dot *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u16 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ buffer++;
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32((*src << 8) | 0x40000000);
+ dot_encode_step(&p->state, &buffer[c]);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_dot *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ u32 *dst;
+
+ channels = p->pcm_channels;
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ buffer++;
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[c]) << 8;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int channels, i, c;
+
+ channels = p->pcm_channels;
+
+ buffer++;
+ for (i = 0; i < data_blocks; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[c] = cpu_to_be32(0x40000000);
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_dot *p = s->protocol;
+ int used;
+
+ used = p->midi_fifo_used[port];
+ if (used == 0)
+ return true;
+
+ used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
+ used = max(used, 0);
+ p->midi_fifo_used[port] = used;
+
+ return used < p->midi_fifo_limit;
+}
+
+static inline void midi_use_bytes(struct amdtp_stream *s,
+ unsigned int port, unsigned int count)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ p->midi_fifo_used[port] += amdtp_rate_table[s->sfc] * count;
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int f, port;
+ int len;
+ u8 *b;
+
+ for (f = 0; f < data_blocks; f++) {
+ port = (s->data_block_counter + f) % 8;
+ b = (u8 *)&buffer[0];
+
+ len = 0;
+ if (port < p->midi_ports &&
+ midi_ratelimit_per_packet(s, port) &&
+ p->midi[port] != NULL)
+ len = snd_rawmidi_transmit(p->midi[port], b + 1, 2);
+
+ if (len > 0) {
+ b[3] = (0x10 << port) | len;
+ midi_use_bytes(s, port, len);
+ } else {
+ b[1] = 0;
+ b[2] = 0;
+ b[3] = 0;
+ }
+ b[0] = 0x80;
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int f, port, len;
+ u8 *b;
+
+ for (f = 0; f < data_blocks; f++) {
+ b = (u8 *)&buffer[0];
+ port = b[3] >> 4;
+ len = b[3] & 0x0f;
+
+ if (port < p->midi_ports && p->midi[port] && len > 0)
+ snd_rawmidi_receive(p->midi[port], b + 1, len);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ /* This protocol delivers 24 bit data in 32bit data channel. */
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ if (WARN_ON(amdtp_stream_pcm_running(s)))
+ return;
+
+ switch (format) {
+ default:
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S16:
+ if (s->direction == AMDTP_OUT_STREAM) {
+ p->transfer_samples = write_pcm_s16;
+ break;
+ }
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S32:
+ if (s->direction == AMDTP_OUT_STREAM)
+ p->transfer_samples = write_pcm_s32;
+ else
+ p->transfer_samples = read_pcm_s32;
+ break;
+ }
+}
+
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ if (port < p->midi_ports)
+ ACCESS_ONCE(p->midi[port]) = midi;
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct amdtp_dot *p = (struct amdtp_dot *)s->protocol;
+ struct snd_pcm_substream *pcm;
+ unsigned int pcm_frames;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm) {
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ pcm_frames = data_blocks;
+ } else {
+ pcm_frames = 0;
+ }
+
+ read_midi_messages(s, buffer, data_blocks);
+
+ return pcm_frames;
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct amdtp_dot *p = (struct amdtp_dot *)s->protocol;
+ struct snd_pcm_substream *pcm;
+ unsigned int pcm_frames;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm) {
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ pcm_frames = data_blocks;
+ } else {
+ write_pcm_silence(s, buffer, data_blocks);
+ pcm_frames = 0;
+ }
+
+ write_midi_messages(s, buffer, data_blocks);
+
+ return pcm_frames;
+}
+
+int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir)
+{
+ amdtp_stream_process_data_blocks_t process_data_blocks;
+ enum cip_flags flags;
+
+ /* Use different mode between incoming/outgoing. */
+ if (dir == AMDTP_IN_STREAM) {
+ flags = CIP_NONBLOCKING | CIP_SKIP_INIT_DBC_CHECK;
+ process_data_blocks = process_tx_data_blocks;
+ } else {
+ flags = CIP_BLOCKING;
+ process_data_blocks = process_rx_data_blocks;
+ }
+
+ return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
+ process_data_blocks, sizeof(struct amdtp_dot));
+}
+
+void amdtp_dot_reset(struct amdtp_stream *s)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ p->state.carry = 0x00;
+ p->state.idx = 0x00;
+ p->state.off = 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-hwdep.c b/sound/firewire/digi00x/digi00x-hwdep.c
new file mode 100644
index 000000000000..f188e4758fd2
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-hwdep.c
@@ -0,0 +1,200 @@
+/*
+ * digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ * 4.get asynchronous messaging
+ */
+
+#include "digi00x.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&dg00x->lock);
+
+ while (!dg00x->dev_lock_changed && dg00x->msg == 0) {
+ prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&dg00x->lock);
+ schedule();
+ finish_wait(&dg00x->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&dg00x->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (dg00x->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (dg00x->dev_lock_count > 0);
+ dg00x->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+ } else {
+ event.digi00x_message.type =
+ SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE;
+ event.digi00x_message.message = dg00x->msg;
+ dg00x->msg = 0;
+
+ count = min_t(long, count, sizeof(event.digi00x_message));
+ }
+
+ spin_unlock_irq(&dg00x->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+ unsigned int events;
+
+ poll_wait(file, &dg00x->hwdep_wait, wait);
+
+ spin_lock_irq(&dg00x->lock);
+ if (dg00x->dev_lock_changed || dg00x->msg)
+ events = POLLIN | POLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&dg00x->lock);
+
+ return events;
+}
+
+static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(dg00x->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_DIGI00X;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strlcpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ spin_lock_irq(&dg00x->lock);
+
+ if (dg00x->dev_lock_count == 0) {
+ dg00x->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&dg00x->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ spin_lock_irq(&dg00x->lock);
+
+ if (dg00x->dev_lock_count == -1) {
+ dg00x->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&dg00x->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+
+ spin_lock_irq(&dg00x->lock);
+ if (dg00x->dev_lock_count == -1)
+ dg00x->dev_lock_count = 0;
+ spin_unlock_irq(&dg00x->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(dg00x, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(dg00x);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(dg00x);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+};
+
+int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x)
+{
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strcpy(hwdep->name, "Digi00x");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = dg00x;
+ hwdep->exclusive = true;
+
+ return err;
+}
diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c
new file mode 100644
index 000000000000..9aa8b4623108
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-midi.c
@@ -0,0 +1,160 @@
+/*
+ * digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int midi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ int err;
+
+ /* This port is for asynchronous transaction. */
+ if (substream->number == 0)
+ return 0;
+
+ err = snd_dg00x_stream_lock_try(dg00x);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&dg00x->mutex);
+ dg00x->substreams_counter++;
+ err = snd_dg00x_stream_start_duplex(dg00x, 0);
+ mutex_unlock(&dg00x->mutex);
+ if (err < 0)
+ snd_dg00x_stream_lock_release(dg00x);
+
+ return err;
+}
+
+static int midi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+
+ /* This port is for asynchronous transaction. */
+ if (substream->number == 0)
+ return 0;
+
+ mutex_lock(&dg00x->mutex);
+ dg00x->substreams_counter--;
+ snd_dg00x_stream_stop_duplex(dg00x);
+ mutex_unlock(&dg00x->mutex);
+
+ snd_dg00x_stream_lock_release(dg00x);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_dg00x *dg00x = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+
+ /* This port is for asynchronous transaction. */
+ if (substrm->number == 0) {
+ if (up)
+ dg00x->in_control = substrm;
+ else
+ dg00x->in_control = NULL;
+ } else {
+ if (up)
+ amdtp_dot_midi_trigger(&dg00x->tx_stream,
+ substrm->number - 1, substrm);
+ else
+ amdtp_dot_midi_trigger(&dg00x->tx_stream,
+ substrm->number - 1, NULL);
+ }
+
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_dg00x *dg00x = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+
+ /* This port is for asynchronous transaction. */
+ if (substrm->number == 0) {
+ if (up)
+ snd_fw_async_midi_port_run(&dg00x->out_control,
+ substrm);
+ } else {
+ if (up)
+ amdtp_dot_midi_trigger(&dg00x->rx_stream,
+ substrm->number - 1, substrm);
+ else
+ amdtp_dot_midi_trigger(&dg00x->rx_stream,
+ substrm->number - 1, NULL);
+ }
+
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_dg00x *dg00x,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ if (subs->number > 0)
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ dg00x->card->shortname, subs->number);
+ else
+ /* This port is for asynchronous transaction. */
+ snprintf(subs->name, sizeof(subs->name),
+ "%s control",
+ dg00x->card->shortname);
+ }
+}
+
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
+{
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ int err;
+
+ err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0,
+ DOT_MIDI_OUT_PORTS + 1, DOT_MIDI_IN_PORTS + 1, &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", dg00x->card->shortname);
+ rmidi->private_data = dg00x;
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_capture_ops);
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+ set_midi_substream_names(dg00x, str);
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_playback_ops);
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+ set_midi_substream_names(dg00x, str);
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c
new file mode 100644
index 000000000000..cac28f70aef7
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-pcm.c
@@ -0,0 +1,373 @@
+/*
+ * digi00x-pcm.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1,
+ };
+ unsigned int i;
+
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (!snd_interval_test(c,
+ snd_dg00x_stream_pcm_channels[i]))
+ continue;
+
+ t.min = min(t.min, snd_dg00x_stream_rates[i]);
+ t.max = max(t.max, snd_dg00x_stream_rates[i]);
+ }
+
+ return snd_interval_refine(r, &t);
+}
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1,
+ };
+ unsigned int i;
+
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (!snd_interval_test(r, snd_dg00x_stream_rates[i]))
+ continue;
+
+ t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]);
+ t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]);
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+static int pcm_init_hw_params(struct snd_dg00x *dg00x,
+ struct snd_pcm_substream *substream)
+{
+ static const struct snd_pcm_hardware hardware = {
+ .info = SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .channels_min = 10,
+ .channels_max = 18,
+ .period_bytes_min = 4 * 18,
+ .period_bytes_max = 4 * 18 * 2048,
+ .buffer_bytes_max = 4 * 18 * 2048 * 2,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+ };
+ struct amdtp_stream *s;
+ int err;
+
+ substream->runtime->hw = hardware;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ s = &dg00x->tx_stream;
+ } else {
+ substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16 |
+ SNDRV_PCM_FMTBIT_S32;
+ s = &dg00x->rx_stream;
+ }
+
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, NULL,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+
+ return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ enum snd_dg00x_clock clock;
+ bool detect;
+ unsigned int rate;
+ int err;
+
+ err = snd_dg00x_stream_lock_try(dg00x);
+ if (err < 0)
+ goto end;
+
+ err = pcm_init_hw_params(dg00x, substream);
+ if (err < 0)
+ goto err_locked;
+
+ /* Check current clock source. */
+ err = snd_dg00x_stream_get_clock(dg00x, &clock);
+ if (err < 0)
+ goto err_locked;
+ if (clock != SND_DG00X_CLOCK_INTERNAL) {
+ err = snd_dg00x_stream_check_external_clock(dg00x, &detect);
+ if (err < 0)
+ goto err_locked;
+ if (!detect) {
+ err = -EBUSY;
+ goto err_locked;
+ }
+ }
+
+ if ((clock != SND_DG00X_CLOCK_INTERNAL) ||
+ amdtp_stream_pcm_running(&dg00x->rx_stream) ||
+ amdtp_stream_pcm_running(&dg00x->tx_stream)) {
+ err = snd_dg00x_stream_get_external_rate(dg00x, &rate);
+ if (err < 0)
+ goto err_locked;
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+ }
+
+ snd_pcm_set_sync(substream);
+end:
+ return err;
+err_locked:
+ snd_dg00x_stream_lock_release(dg00x);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ snd_dg00x_stream_lock_release(dg00x);
+
+ return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&dg00x->mutex);
+ dg00x->substreams_counter++;
+ mutex_unlock(&dg00x->mutex);
+ }
+
+ amdtp_dot_set_pcm_format(&dg00x->tx_stream, params_format(hw_params));
+
+ return 0;
+}
+
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&dg00x->mutex);
+ dg00x->substreams_counter++;
+ mutex_unlock(&dg00x->mutex);
+ }
+
+ amdtp_dot_set_pcm_format(&dg00x->rx_stream, params_format(hw_params));
+
+ return 0;
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ mutex_lock(&dg00x->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ dg00x->substreams_counter--;
+
+ snd_dg00x_stream_stop_duplex(dg00x);
+
+ mutex_unlock(&dg00x->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ mutex_lock(&dg00x->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ dg00x->substreams_counter--;
+
+ snd_dg00x_stream_stop_duplex(dg00x);
+
+ mutex_unlock(&dg00x->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&dg00x->mutex);
+
+ err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&dg00x->tx_stream);
+
+ mutex_unlock(&dg00x->mutex);
+
+ return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&dg00x->mutex);
+
+ err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+ if (err >= 0) {
+ amdtp_stream_pcm_prepare(&dg00x->rx_stream);
+ amdtp_dot_reset(&dg00x->rx_stream);
+ }
+
+ mutex_unlock(&dg00x->mutex);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_dg00x *dg00x = sbstrm->private_data;
+
+ return amdtp_stream_pcm_pointer(&dg00x->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_dg00x *dg00x = sbstrm->private_data;
+
+ return amdtp_stream_pcm_pointer(&dg00x->rx_stream);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_capture_hw_params,
+ .hw_free = pcm_capture_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static struct snd_pcm_ops pcm_playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_playback_hw_params,
+ .hw_free = pcm_playback_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = dg00x;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", dg00x->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+
+ return 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-proc.c b/sound/firewire/digi00x/digi00x-proc.c
new file mode 100644
index 000000000000..a1d601f31165
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-proc.c
@@ -0,0 +1,99 @@
+/*
+ * digi00x-proc.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int get_optical_iface_mode(struct snd_dg00x *dg00x,
+ enum snd_dg00x_optical_mode *mode)
+{
+ __be32 data;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_OPT_IFACE_MODE,
+ &data, sizeof(data), 0);
+ if (err >= 0)
+ *mode = be32_to_cpu(data) & 0x01;
+
+ return err;
+}
+
+static void proc_read_clock(struct snd_info_entry *entry,
+ struct snd_info_buffer *buf)
+{
+ static const char *const source_name[] = {
+ [SND_DG00X_CLOCK_INTERNAL] = "internal",
+ [SND_DG00X_CLOCK_SPDIF] = "s/pdif",
+ [SND_DG00X_CLOCK_ADAT] = "adat",
+ [SND_DG00X_CLOCK_WORD] = "word clock",
+ };
+ static const char *const optical_name[] = {
+ [SND_DG00X_OPT_IFACE_MODE_ADAT] = "adat",
+ [SND_DG00X_OPT_IFACE_MODE_SPDIF] = "s/pdif",
+ };
+ struct snd_dg00x *dg00x = entry->private_data;
+ enum snd_dg00x_optical_mode mode;
+ unsigned int rate;
+ enum snd_dg00x_clock clock;
+ bool detect;
+
+ if (get_optical_iface_mode(dg00x, &mode) < 0)
+ return;
+ if (snd_dg00x_stream_get_local_rate(dg00x, &rate) < 0)
+ return;
+ if (snd_dg00x_stream_get_clock(dg00x, &clock) < 0)
+ return;
+
+ snd_iprintf(buf, "Optical mode: %s\n", optical_name[mode]);
+ snd_iprintf(buf, "Sampling Rate: %d\n", rate);
+ snd_iprintf(buf, "Clock Source: %s\n", source_name[clock]);
+
+ if (clock == SND_DG00X_CLOCK_INTERNAL)
+ return;
+
+ if (snd_dg00x_stream_check_external_clock(dg00x, &detect) < 0)
+ return;
+ snd_iprintf(buf, "External source: %s\n", detect ? "detected" : "not");
+ if (!detect)
+ return;
+
+ if (snd_dg00x_stream_get_external_rate(dg00x, &rate) >= 0)
+ snd_iprintf(buf, "External sampling rate: %d\n", rate);
+}
+
+void snd_dg00x_proc_init(struct snd_dg00x *dg00x)
+{
+ struct snd_info_entry *root, *entry;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(dg00x->card, "firewire",
+ dg00x->card->proc_root);
+ if (root == NULL)
+ return;
+
+ root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(root) < 0) {
+ snd_info_free_entry(root);
+ return;
+ }
+
+ entry = snd_info_create_card_entry(dg00x->card, "clock", root);
+ if (entry == NULL) {
+ snd_info_free_entry(root);
+ return;
+ }
+
+ snd_info_set_text_ops(entry, dg00x, proc_read_clock);
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ snd_info_free_entry(root);
+ }
+}
diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c
new file mode 100644
index 000000000000..4d3b4ebbdd49
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-stream.c
@@ -0,0 +1,422 @@
+/*
+ * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+#define CALLBACK_TIMEOUT 500
+
+const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
+ [SND_DG00X_RATE_44100] = 44100,
+ [SND_DG00X_RATE_48000] = 48000,
+ [SND_DG00X_RATE_88200] = 88200,
+ [SND_DG00X_RATE_96000] = 96000,
+};
+
+/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
+const unsigned int
+snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
+ /* Analog/ADAT/SPDIF */
+ [SND_DG00X_RATE_44100] = (8 + 8 + 2),
+ [SND_DG00X_RATE_48000] = (8 + 8 + 2),
+ /* Analog/SPDIF */
+ [SND_DG00X_RATE_88200] = (8 + 2),
+ [SND_DG00X_RATE_96000] = (8 + 2),
+};
+
+int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = be32_to_cpu(reg) & 0x0f;
+ if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
+ *rate = snd_dg00x_stream_rates[data];
+ else
+ err = -EIO;
+
+ return err;
+}
+
+int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)
+{
+ __be32 reg;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
+ if (rate == snd_dg00x_stream_rates[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
+ return -EINVAL;
+
+ reg = cpu_to_be32(i);
+ return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
+ &reg, sizeof(reg), 0);
+}
+
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+ enum snd_dg00x_clock *clock)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ *clock = be32_to_cpu(reg) & 0x0f;
+ if (*clock >= SND_DG00X_CLOCK_COUNT)
+ err = -EIO;
+
+ return err;
+}
+
+int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,
+ &reg, sizeof(reg), 0);
+ if (err >= 0)
+ *detect = be32_to_cpu(reg) > 0;
+
+ return err;
+}
+
+int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = be32_to_cpu(reg) & 0x0f;
+ if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
+ *rate = snd_dg00x_stream_rates[data];
+ /* This means desync. */
+ else
+ err = -EBUSY;
+
+ return err;
+}
+
+static void finish_session(struct snd_dg00x *dg00x)
+{
+ __be32 data = cpu_to_be32(0x00000003);
+
+ snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
+ &data, sizeof(data), 0);
+}
+
+static int begin_session(struct snd_dg00x *dg00x)
+{
+ __be32 data;
+ u32 curr;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ goto error;
+ curr = be32_to_cpu(data);
+
+ if (curr == 0)
+ curr = 2;
+
+ curr--;
+ while (curr > 0) {
+ data = cpu_to_be32(curr);
+ err = snd_fw_transaction(dg00x->unit,
+ TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE +
+ DG00X_OFFSET_STREAMING_SET,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ goto error;
+
+ msleep(20);
+ curr--;
+ }
+
+ return 0;
+error:
+ finish_session(dg00x);
+ return err;
+}
+
+static void release_resources(struct snd_dg00x *dg00x)
+{
+ __be32 data = 0;
+
+ /* Unregister isochronous channels for both direction. */
+ snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+ &data, sizeof(data), 0);
+
+ /* Release isochronous resources. */
+ fw_iso_resources_free(&dg00x->tx_resources);
+ fw_iso_resources_free(&dg00x->rx_resources);
+}
+
+static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
+{
+ unsigned int i;
+ __be32 data;
+ int err;
+
+ /* Check sampling rate. */
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (snd_dg00x_stream_rates[i] == rate)
+ break;
+ }
+ if (i == SND_DG00X_RATE_COUNT)
+ return -EINVAL;
+
+ /* Keep resources for out-stream. */
+ err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate,
+ snd_dg00x_stream_pcm_channels[i]);
+ if (err < 0)
+ return err;
+ err = fw_iso_resources_allocate(&dg00x->rx_resources,
+ amdtp_stream_get_max_payload(&dg00x->rx_stream),
+ fw_parent_device(dg00x->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ /* Keep resources for in-stream. */
+ err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate,
+ snd_dg00x_stream_pcm_channels[i]);
+ if (err < 0)
+ return err;
+ err = fw_iso_resources_allocate(&dg00x->tx_resources,
+ amdtp_stream_get_max_payload(&dg00x->tx_stream),
+ fw_parent_device(dg00x->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ /* Register isochronous channels for both direction. */
+ data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
+ dg00x->rx_resources.channel);
+ err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ release_resources(dg00x);
+ return err;
+}
+
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ /* For out-stream. */
+ err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
+ if (err < 0)
+ goto error;
+ err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
+ if (err < 0)
+ goto error;
+
+ /* For in-stream. */
+ err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
+ if (err < 0)
+ goto error;
+ err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_dg00x_stream_destroy_duplex(dg00x);
+ return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
+{
+ amdtp_stream_destroy(&dg00x->rx_stream);
+ fw_iso_resources_destroy(&dg00x->rx_resources);
+
+ amdtp_stream_destroy(&dg00x->tx_stream);
+ fw_iso_resources_destroy(&dg00x->tx_resources);
+}
+
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
+{
+ unsigned int curr_rate;
+ int err = 0;
+
+ if (dg00x->substreams_counter == 0)
+ goto end;
+
+ /* Check current sampling rate. */
+ err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
+ if (err < 0)
+ goto error;
+ if (rate == 0)
+ rate = curr_rate;
+ if (curr_rate != rate ||
+ amdtp_streaming_error(&dg00x->tx_stream) ||
+ amdtp_streaming_error(&dg00x->rx_stream)) {
+ finish_session(dg00x);
+
+ amdtp_stream_stop(&dg00x->tx_stream);
+ amdtp_stream_stop(&dg00x->rx_stream);
+ release_resources(dg00x);
+ }
+
+ /*
+ * No packets are transmitted without receiving packets, reagardless of
+ * which source of clock is used.
+ */
+ if (!amdtp_stream_running(&dg00x->rx_stream)) {
+ err = snd_dg00x_stream_set_local_rate(dg00x, rate);
+ if (err < 0)
+ goto error;
+
+ err = keep_resources(dg00x, rate);
+ if (err < 0)
+ goto error;
+
+ err = begin_session(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_stream_start(&dg00x->rx_stream,
+ dg00x->rx_resources.channel,
+ fw_parent_device(dg00x->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
+ CALLBACK_TIMEOUT)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ /*
+ * The value of SYT field in transmitted packets is always 0x0000. Thus,
+ * duplex streams with timestamp synchronization cannot be built.
+ */
+ if (!amdtp_stream_running(&dg00x->tx_stream)) {
+ err = amdtp_stream_start(&dg00x->tx_stream,
+ dg00x->tx_resources.channel,
+ fw_parent_device(dg00x->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
+ CALLBACK_TIMEOUT)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+end:
+ return err;
+error:
+ finish_session(dg00x);
+
+ amdtp_stream_stop(&dg00x->tx_stream);
+ amdtp_stream_stop(&dg00x->rx_stream);
+ release_resources(dg00x);
+
+ return err;
+}
+
+void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
+{
+ if (dg00x->substreams_counter > 0)
+ return;
+
+ amdtp_stream_stop(&dg00x->tx_stream);
+ amdtp_stream_stop(&dg00x->rx_stream);
+ finish_session(dg00x);
+ release_resources(dg00x);
+
+ /*
+ * Just after finishing the session, the device may lost transmitting
+ * functionality for a short time.
+ */
+ msleep(50);
+}
+
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
+{
+ fw_iso_resources_update(&dg00x->tx_resources);
+ fw_iso_resources_update(&dg00x->rx_resources);
+
+ amdtp_stream_update(&dg00x->tx_stream);
+ amdtp_stream_update(&dg00x->rx_stream);
+}
+
+void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
+{
+ dg00x->dev_lock_changed = true;
+ wake_up(&dg00x->hwdep_wait);
+}
+
+int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ spin_lock_irq(&dg00x->lock);
+
+ /* user land lock this */
+ if (dg00x->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (dg00x->dev_lock_count++ == 0)
+ snd_dg00x_stream_lock_changed(dg00x);
+ err = 0;
+end:
+ spin_unlock_irq(&dg00x->lock);
+ return err;
+}
+
+void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
+{
+ spin_lock_irq(&dg00x->lock);
+
+ if (WARN_ON(dg00x->dev_lock_count <= 0))
+ goto end;
+ if (--dg00x->dev_lock_count == 0)
+ snd_dg00x_stream_lock_changed(dg00x);
+end:
+ spin_unlock_irq(&dg00x->lock);
+}
diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c
new file mode 100644
index 000000000000..554324d8c602
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-transaction.c
@@ -0,0 +1,137 @@
+/*
+ * digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/asound.h>
+#include "digi00x.h"
+
+static int fill_midi_message(struct snd_rawmidi_substream *substream, u8 *buf)
+{
+ int bytes;
+
+ buf[0] = 0x80;
+ bytes = snd_rawmidi_transmit_peek(substream, buf + 1, 2);
+ if (bytes >= 0)
+ buf[3] = 0xc0 | bytes;
+
+ return bytes;
+}
+
+static void handle_midi_control(struct snd_dg00x *dg00x, __be32 *buf,
+ unsigned int length)
+{
+ struct snd_rawmidi_substream *substream;
+ unsigned int i;
+ unsigned int len;
+ u8 *b;
+
+ substream = ACCESS_ONCE(dg00x->in_control);
+ if (substream == NULL)
+ return;
+
+ length /= 4;
+
+ for (i = 0; i < length; i++) {
+ b = (u8 *)&buf[i];
+ len = b[3] & 0xf;
+ if (len > 0)
+ snd_rawmidi_receive(dg00x->in_control, b + 1, len);
+ }
+}
+
+static void handle_unknown_message(struct snd_dg00x *dg00x,
+ unsigned long long offset, __be32 *buf)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dg00x->lock, flags);
+ dg00x->msg = be32_to_cpu(*buf);
+ spin_unlock_irqrestore(&dg00x->lock, flags);
+
+ wake_up(&dg00x->hwdep_wait);
+}
+
+static void handle_message(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_dg00x *dg00x = callback_data;
+ __be32 *buf = (__be32 *)data;
+
+ if (offset == dg00x->async_handler.offset)
+ handle_unknown_message(dg00x, offset, buf);
+ else if (offset == dg00x->async_handler.offset + 4)
+ handle_midi_control(dg00x, buf, length);
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x)
+{
+ struct fw_device *device = fw_parent_device(dg00x->unit);
+ __be32 data[2];
+ int err;
+
+ /* Unknown. 4bytes. */
+ data[0] = cpu_to_be32((device->card->node_id << 16) |
+ (dg00x->async_handler.offset >> 32));
+ data[1] = cpu_to_be32(dg00x->async_handler.offset);
+ err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return err;
+
+ /* Asynchronous transactions for MIDI control message. */
+ data[0] = cpu_to_be32((device->card->node_id << 16) |
+ (dg00x->async_handler.offset >> 32));
+ data[1] = cpu_to_be32(dg00x->async_handler.offset + 4);
+ return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_MIDI_CTL_ADDR,
+ &data, sizeof(data), 0);
+}
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = 0xffffe0000000ull,
+ .end = 0xffffe000ffffull,
+ };
+ int err;
+
+ dg00x->async_handler.length = 12;
+ dg00x->async_handler.address_callback = handle_message;
+ dg00x->async_handler.callback_data = dg00x;
+
+ err = fw_core_add_address_handler(&dg00x->async_handler,
+ &resp_register_region);
+ if (err < 0)
+ return err;
+
+ err = snd_dg00x_transaction_reregister(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_fw_async_midi_port_init(&dg00x->out_control, dg00x->unit,
+ DG00X_ADDR_BASE + DG00X_OFFSET_MMC,
+ 4, fill_midi_message);
+ if (err < 0)
+ goto error;
+
+ return err;
+error:
+ fw_core_remove_address_handler(&dg00x->async_handler);
+ dg00x->async_handler.address_callback = NULL;
+ return err;
+}
+
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
+{
+ snd_fw_async_midi_port_destroy(&dg00x->out_control);
+ fw_core_remove_address_handler(&dg00x->async_handler);
+}
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
new file mode 100644
index 000000000000..1f33b7a1fca4
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x.c
@@ -0,0 +1,170 @@
+/*
+ * digi00x.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+MODULE_DESCRIPTION("Digidesign Digi 002/003 family Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+#define VENDOR_DIGIDESIGN 0x00a07e
+#define MODEL_DIGI00X 0x000002
+
+static int name_card(struct snd_dg00x *dg00x)
+{
+ struct fw_device *fw_dev = fw_parent_device(dg00x->unit);
+ char name[32] = {0};
+ char *model;
+ int err;
+
+ err = fw_csr_string(dg00x->unit->directory, CSR_MODEL, name,
+ sizeof(name));
+ if (err < 0)
+ return err;
+
+ model = skip_spaces(name);
+
+ strcpy(dg00x->card->driver, "Digi00x");
+ strcpy(dg00x->card->shortname, model);
+ strcpy(dg00x->card->mixername, model);
+ snprintf(dg00x->card->longname, sizeof(dg00x->card->longname),
+ "Digidesign %s, GUID %08x%08x at %s, S%d", model,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&dg00x->unit->device), 100 << fw_dev->max_speed);
+
+ return 0;
+}
+
+static void dg00x_card_free(struct snd_card *card)
+{
+ struct snd_dg00x *dg00x = card->private_data;
+
+ snd_dg00x_stream_destroy_duplex(dg00x);
+ snd_dg00x_transaction_unregister(dg00x);
+
+ fw_unit_put(dg00x->unit);
+
+ mutex_destroy(&dg00x->mutex);
+}
+
+static int snd_dg00x_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_dg00x *dg00x;
+ int err;
+
+ /* create card */
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+ sizeof(struct snd_dg00x), &card);
+ if (err < 0)
+ return err;
+ card->private_free = dg00x_card_free;
+
+ /* initialize myself */
+ dg00x = card->private_data;
+ dg00x->card = card;
+ dg00x->unit = fw_unit_get(unit);
+
+ mutex_init(&dg00x->mutex);
+ spin_lock_init(&dg00x->lock);
+ init_waitqueue_head(&dg00x->hwdep_wait);
+
+ err = name_card(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_stream_init_duplex(dg00x);
+ if (err < 0)
+ goto error;
+
+ snd_dg00x_proc_init(dg00x);
+
+ err = snd_dg00x_create_pcm_devices(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_create_midi_devices(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_create_hwdep_device(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_transaction_register(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dev_set_drvdata(&unit->device, dg00x);
+
+ return err;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void snd_dg00x_update(struct fw_unit *unit)
+{
+ struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+ snd_dg00x_transaction_reregister(dg00x);
+
+ mutex_lock(&dg00x->mutex);
+ snd_dg00x_stream_update_duplex(dg00x);
+ mutex_unlock(&dg00x->mutex);
+}
+
+static void snd_dg00x_remove(struct fw_unit *unit)
+{
+ struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(dg00x->card);
+}
+
+static const struct ieee1394_device_id snd_dg00x_id_table[] = {
+ /* Both of 002/003 use the same ID. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = VENDOR_DIGIDESIGN,
+ .model_id = MODEL_DIGI00X,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_dg00x_id_table);
+
+static struct fw_driver dg00x_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "snd-firewire-digi00x",
+ .bus = &fw_bus_type,
+ },
+ .probe = snd_dg00x_probe,
+ .update = snd_dg00x_update,
+ .remove = snd_dg00x_remove,
+ .id_table = snd_dg00x_id_table,
+};
+
+static int __init snd_dg00x_init(void)
+{
+ return driver_register(&dg00x_driver.driver);
+}
+
+static void __exit snd_dg00x_exit(void)
+{
+ driver_unregister(&dg00x_driver.driver);
+}
+
+module_init(snd_dg00x_init);
+module_exit(snd_dg00x_exit);
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
new file mode 100644
index 000000000000..907e73993677
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x.h
@@ -0,0 +1,157 @@
+/*
+ * digi00x.h - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_DIGI00X_H_INCLUDED
+#define SOUND_DIGI00X_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/rawmidi.h>
+
+#include "../lib.h"
+#include "../iso-resources.h"
+#include "../amdtp-stream.h"
+
+struct snd_dg00x {
+ struct snd_card *card;
+ struct fw_unit *unit;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ struct amdtp_stream tx_stream;
+ struct fw_iso_resources tx_resources;
+
+ struct amdtp_stream rx_stream;
+ struct fw_iso_resources rx_resources;
+
+ unsigned int substreams_counter;
+
+ /* for uapi */
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* For asynchronous messages. */
+ struct fw_address_handler async_handler;
+ u32 msg;
+
+ /* For asynchronous MIDI controls. */
+ struct snd_rawmidi_substream *in_control;
+ struct snd_fw_async_midi_port out_control;
+};
+
+#define DG00X_ADDR_BASE 0xffffe0000000ull
+
+#define DG00X_OFFSET_STREAMING_STATE 0x0000
+#define DG00X_OFFSET_STREAMING_SET 0x0004
+#define DG00X_OFFSET_MIDI_CTL_ADDR 0x0008
+/* For LSB of the address 0x000c */
+/* unknown 0x0010 */
+#define DG00X_OFFSET_MESSAGE_ADDR 0x0014
+/* For LSB of the address 0x0018 */
+/* unknown 0x001c */
+/* unknown 0x0020 */
+/* not used 0x0024--0x00ff */
+#define DG00X_OFFSET_ISOC_CHANNELS 0x0100
+/* unknown 0x0104 */
+/* unknown 0x0108 */
+/* unknown 0x010c */
+#define DG00X_OFFSET_LOCAL_RATE 0x0110
+#define DG00X_OFFSET_EXTERNAL_RATE 0x0114
+#define DG00X_OFFSET_CLOCK_SOURCE 0x0118
+#define DG00X_OFFSET_OPT_IFACE_MODE 0x011c
+/* unknown 0x0120 */
+/* Mixer control on/off 0x0124 */
+/* unknown 0x0128 */
+#define DG00X_OFFSET_DETECT_EXTERNAL 0x012c
+/* unknown 0x0138 */
+#define DG00X_OFFSET_MMC 0x0400
+
+enum snd_dg00x_rate {
+ SND_DG00X_RATE_44100 = 0,
+ SND_DG00X_RATE_48000,
+ SND_DG00X_RATE_88200,
+ SND_DG00X_RATE_96000,
+ SND_DG00X_RATE_COUNT,
+};
+
+enum snd_dg00x_clock {
+ SND_DG00X_CLOCK_INTERNAL = 0,
+ SND_DG00X_CLOCK_SPDIF,
+ SND_DG00X_CLOCK_ADAT,
+ SND_DG00X_CLOCK_WORD,
+ SND_DG00X_CLOCK_COUNT,
+};
+
+enum snd_dg00x_optical_mode {
+ SND_DG00X_OPT_IFACE_MODE_ADAT = 0,
+ SND_DG00X_OPT_IFACE_MODE_SPDIF,
+ SND_DG00X_OPT_IFACE_MODE_COUNT,
+};
+
+#define DOT_MIDI_IN_PORTS 1
+#define DOT_MIDI_OUT_PORTS 2
+
+int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir);
+int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels);
+void amdtp_dot_reset(struct amdtp_stream *s);
+int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi);
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x);
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x);
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x);
+
+extern const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT];
+extern const unsigned int snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT];
+int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate);
+int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate);
+int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate);
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+ enum snd_dg00x_clock *clock);
+int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x,
+ bool *detect);
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate);
+void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x);
+
+void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x);
+
+void snd_dg00x_proc_init(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x);
+#endif
diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c
index 0619597e3a3f..cce19768f43d 100644
--- a/sound/firewire/fcp.c
+++ b/sound/firewire/fcp.c
@@ -17,7 +17,7 @@
#include <linux/delay.h>
#include "fcp.h"
#include "lib.h"
-#include "amdtp.h"
+#include "amdtp-stream.h"
#define CTS_AVC 0x00
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
index 0c7440826db8..15ef7f75a8ef 100644
--- a/sound/firewire/fireworks/Makefile
+++ b/sound/firewire/fireworks/Makefile
@@ -1,4 +1,4 @@
snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
fireworks_stream.o fireworks_proc.o fireworks_midi.o \
fireworks_pcm.o fireworks_hwdep.o fireworks.o
-obj-m += snd-fireworks.o
+obj-$(CONFIG_SND_FIREWORKS) += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index c94a432f7cc6..d5b19bc11e59 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -138,12 +138,12 @@ get_hardware_info(struct snd_efw *efw)
efw->midi_out_ports = hwinfo->midi_out_ports;
efw->midi_in_ports = hwinfo->midi_in_ports;
- if (hwinfo->amdtp_tx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_tx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_tx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_rx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_rx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
- hwinfo->amdtp_rx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM) {
+ if (hwinfo->amdtp_tx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_tx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_tx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM) {
err = -ENOSYS;
goto end;
}
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 084d414b228c..c7cb7deafe48 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -29,7 +29,7 @@
#include "../packets-buffer.h"
#include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
#include "../cmp.h"
#include "../lib.h"
diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c
index 166f80584c2a..94bab0476a65 100644
--- a/sound/firewire/fireworks/fireworks_command.c
+++ b/sound/firewire/fireworks/fireworks_command.c
@@ -257,7 +257,7 @@ int snd_efw_command_get_phys_meters(struct snd_efw *efw,
struct snd_efw_phys_meters *meters,
unsigned int len)
{
- __be32 *buf = (__be32 *)meters;
+ u32 *buf = (u32 *)meters;
unsigned int i;
int err;
diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c
index cf9c65260439..fba01bbba456 100644
--- a/sound/firewire/fireworks/fireworks_midi.c
+++ b/sound/firewire/fireworks/fireworks_midi.c
@@ -73,10 +73,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&efw->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&efw->tx_stream,
+ amdtp_am824_midi_trigger(&efw->tx_stream,
substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&efw->tx_stream,
+ amdtp_am824_midi_trigger(&efw->tx_stream,
substrm->number, NULL);
spin_unlock_irqrestore(&efw->lock, flags);
@@ -90,11 +90,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&efw->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&efw->rx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&efw->rx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&efw->rx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&efw->rx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&efw->lock, flags);
}
diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c
index c30b2ffa8dfb..d27135bac513 100644
--- a/sound/firewire/fireworks/fireworks_pcm.c
+++ b/sound/firewire/fireworks/fireworks_pcm.c
@@ -159,11 +159,11 @@ pcm_init_hw_params(struct snd_efw *efw,
SNDRV_PCM_INFO_MMAP_VALID;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
s = &efw->tx_stream;
pcm_channels = efw->pcm_capture_channels;
} else {
- runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
s = &efw->rx_stream;
pcm_channels = efw->pcm_playback_channels;
}
@@ -187,7 +187,7 @@ pcm_init_hw_params(struct snd_efw *efw,
if (err < 0)
goto end;
- err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+ err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
end:
return err;
}
@@ -253,7 +253,8 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&efw->capture_substreams);
- amdtp_stream_set_pcm_format(&efw->tx_stream, params_format(hw_params));
+
+ amdtp_am824_set_pcm_format(&efw->tx_stream, params_format(hw_params));
return 0;
}
@@ -270,7 +271,8 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&efw->playback_substreams);
- amdtp_stream_set_pcm_format(&efw->rx_stream, params_format(hw_params));
+
+ amdtp_am824_set_pcm_format(&efw->rx_stream, params_format(hw_params));
return 0;
}
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
index 7e353f1f7bff..759f6e3ed44a 100644
--- a/sound/firewire/fireworks/fireworks_stream.c
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -31,7 +31,7 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
if (err < 0)
goto end;
- err = amdtp_stream_init(stream, efw->unit, s_dir, CIP_BLOCKING);
+ err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
@@ -73,8 +73,10 @@ start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
midi_ports = efw->midi_in_ports;
}
- amdtp_stream_set_parameters(stream, sampling_rate,
- pcm_channels, midi_ports);
+ err = amdtp_am824_set_parameters(stream, sampling_rate,
+ pcm_channels, midi_ports, false);
+ if (err < 0)
+ goto end;
/* establish connection via CMP */
err = cmp_connection_establish(conn,
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
index 7409edba9f06..f80aafa44c89 100644
--- a/sound/firewire/lib.c
+++ b/sound/firewire/lib.c
@@ -9,6 +9,7 @@
#include <linux/device.h>
#include <linux/firewire.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include "lib.h"
#define ERROR_RETRY_DELAY_MS 20
@@ -66,6 +67,147 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
}
EXPORT_SYMBOL(snd_fw_transaction);
+static void async_midi_port_callback(struct fw_card *card, int rcode,
+ void *data, size_t length,
+ void *callback_data)
+{
+ struct snd_fw_async_midi_port *port = callback_data;
+ struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
+
+ /* This port is closed. */
+ if (substream == NULL)
+ return;
+
+ if (rcode == RCODE_COMPLETE)
+ snd_rawmidi_transmit_ack(substream, port->consume_bytes);
+ else if (!rcode_is_permanent_error(rcode))
+ /* To start next transaction immediately for recovery. */
+ port->next_ktime = ktime_set(0, 0);
+ else
+ /* Don't continue processing. */
+ port->error = true;
+
+ port->idling = true;
+
+ if (!snd_rawmidi_transmit_empty(substream))
+ schedule_work(&port->work);
+}
+
+static void midi_port_work(struct work_struct *work)
+{
+ struct snd_fw_async_midi_port *port =
+ container_of(work, struct snd_fw_async_midi_port, work);
+ struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
+ int generation;
+ int type;
+
+ /* Under transacting or error state. */
+ if (!port->idling || port->error)
+ return;
+
+ /* Nothing to do. */
+ if (substream == NULL || snd_rawmidi_transmit_empty(substream))
+ return;
+
+ /* Do it in next chance. */
+ if (ktime_after(port->next_ktime, ktime_get())) {
+ schedule_work(&port->work);
+ return;
+ }
+
+ /*
+ * Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
+ * Later, snd_rawmidi_transmit_ack() is called.
+ */
+ memset(port->buf, 0, port->len);
+ port->consume_bytes = port->fill(substream, port->buf);
+ if (port->consume_bytes <= 0) {
+ /* Do it in next chance, immediately. */
+ if (port->consume_bytes == 0) {
+ port->next_ktime = ktime_set(0, 0);
+ schedule_work(&port->work);
+ } else {
+ /* Fatal error. */
+ port->error = true;
+ }
+ return;
+ }
+
+ /* Calculate type of transaction. */
+ if (port->len == 4)
+ type = TCODE_WRITE_QUADLET_REQUEST;
+ else
+ type = TCODE_WRITE_BLOCK_REQUEST;
+
+ /* Set interval to next transaction. */
+ port->next_ktime = ktime_add_ns(ktime_get(),
+ port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
+
+ /* Start this transaction. */
+ port->idling = false;
+
+ /*
+ * In Linux FireWire core, when generation is updated with memory
+ * barrier, node id has already been updated. In this module, After
+ * this smp_rmb(), load/store instructions to memory are completed.
+ * Thus, both of generation and node id are available with recent
+ * values. This is a light-serialization solution to handle bus reset
+ * events on IEEE 1394 bus.
+ */
+ generation = port->parent->generation;
+ smp_rmb();
+
+ fw_send_request(port->parent->card, &port->transaction, type,
+ port->parent->node_id, generation,
+ port->parent->max_speed, port->addr,
+ port->buf, port->len, async_midi_port_callback,
+ port);
+}
+
+/**
+ * snd_fw_async_midi_port_init - initialize asynchronous MIDI port structure
+ * @port: the asynchronous MIDI port to initialize
+ * @unit: the target of the asynchronous transaction
+ * @addr: the address to which transactions are transferred
+ * @len: the length of transaction
+ * @fill: the callback function to fill given buffer, and returns the
+ * number of consumed bytes for MIDI message.
+ *
+ */
+int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
+ struct fw_unit *unit, u64 addr, unsigned int len,
+ snd_fw_async_midi_port_fill fill)
+{
+ port->len = DIV_ROUND_UP(len, 4) * 4;
+ port->buf = kzalloc(port->len, GFP_KERNEL);
+ if (port->buf == NULL)
+ return -ENOMEM;
+
+ port->parent = fw_parent_device(unit);
+ port->addr = addr;
+ port->fill = fill;
+ port->idling = true;
+ port->next_ktime = ktime_set(0, 0);
+ port->error = false;
+
+ INIT_WORK(&port->work, midi_port_work);
+
+ return 0;
+}
+EXPORT_SYMBOL(snd_fw_async_midi_port_init);
+
+/**
+ * snd_fw_async_midi_port_destroy - free asynchronous MIDI port structure
+ * @port: the asynchronous MIDI port structure
+ */
+void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port)
+{
+ snd_fw_async_midi_port_finish(port);
+ cancel_work_sync(&port->work);
+ kfree(port->buf);
+}
+EXPORT_SYMBOL(snd_fw_async_midi_port_destroy);
+
MODULE_DESCRIPTION("FireWire audio helper functions");
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_LICENSE("GPL v2");
diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h
index 02cfabc9c3c4..f3f6f84c48d6 100644
--- a/sound/firewire/lib.h
+++ b/sound/firewire/lib.h
@@ -3,6 +3,8 @@
#include <linux/firewire-constants.h>
#include <linux/types.h>
+#include <linux/sched.h>
+#include <sound/rawmidi.h>
struct fw_unit;
@@ -20,4 +22,58 @@ static inline bool rcode_is_permanent_error(int rcode)
return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
}
+struct snd_fw_async_midi_port;
+typedef int (*snd_fw_async_midi_port_fill)(
+ struct snd_rawmidi_substream *substream,
+ u8 *buf);
+
+struct snd_fw_async_midi_port {
+ struct fw_device *parent;
+ struct work_struct work;
+ bool idling;
+ ktime_t next_ktime;
+ bool error;
+
+ u64 addr;
+ struct fw_transaction transaction;
+
+ u8 *buf;
+ unsigned int len;
+
+ struct snd_rawmidi_substream *substream;
+ snd_fw_async_midi_port_fill fill;
+ unsigned int consume_bytes;
+};
+
+int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
+ struct fw_unit *unit, u64 addr, unsigned int len,
+ snd_fw_async_midi_port_fill fill);
+void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port);
+
+/**
+ * snd_fw_async_midi_port_run - run transactions for the async MIDI port
+ * @port: the asynchronous MIDI port
+ * @substream: the MIDI substream
+ */
+static inline void
+snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port,
+ struct snd_rawmidi_substream *substream)
+{
+ if (!port->error) {
+ port->substream = substream;
+ schedule_work(&port->work);
+ }
+}
+
+/**
+ * snd_fw_async_midi_port_finish - finish the asynchronous MIDI port
+ * @port: the asynchronous MIDI port
+ */
+static inline void
+snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port)
+{
+ port->substream = NULL;
+ port->error = false;
+}
+
#endif
diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
index a926850864f6..06ff50f4e6c0 100644
--- a/sound/firewire/oxfw/Makefile
+++ b/sound/firewire/oxfw/Makefile
@@ -1,3 +1,3 @@
snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \
oxfw-proc.o oxfw-midi.o oxfw-hwdep.o oxfw.o
-obj-m += snd-oxfw.o
+obj-$(CONFIG_SND_OXFW) += snd-oxfw.o
diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c
index 540a30338516..8665e1043d41 100644
--- a/sound/firewire/oxfw/oxfw-midi.c
+++ b/sound/firewire/oxfw/oxfw-midi.c
@@ -90,11 +90,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&oxfw->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&oxfw->tx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&oxfw->tx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&oxfw->tx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&oxfw->tx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&oxfw->lock, flags);
}
@@ -107,11 +107,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&oxfw->lock, flags);
if (up)
- amdtp_stream_midi_trigger(&oxfw->rx_stream,
- substrm->number, substrm);
+ amdtp_am824_midi_trigger(&oxfw->rx_stream,
+ substrm->number, substrm);
else
- amdtp_stream_midi_trigger(&oxfw->rx_stream,
- substrm->number, NULL);
+ amdtp_am824_midi_trigger(&oxfw->rx_stream,
+ substrm->number, NULL);
spin_unlock_irqrestore(&oxfw->lock, flags);
}
@@ -142,29 +142,11 @@ static void set_midi_substream_names(struct snd_oxfw *oxfw,
int snd_oxfw_create_midi(struct snd_oxfw *oxfw)
{
- struct snd_oxfw_stream_formation formation;
struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *str;
- u8 *format;
- int i, err;
-
- /* If its stream has MIDI conformant data channel, add one MIDI port */
- for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
- format = oxfw->tx_stream_formats[i];
- if (format != NULL) {
- err = snd_oxfw_stream_parse_format(format, &formation);
- if (err >= 0 && formation.midi > 0)
- oxfw->midi_input_ports = 1;
- }
-
- format = oxfw->rx_stream_formats[i];
- if (format != NULL) {
- err = snd_oxfw_stream_parse_format(format, &formation);
- if (err >= 0 && formation.midi > 0)
- oxfw->midi_output_ports = 1;
- }
- }
- if ((oxfw->midi_input_ports == 0) && (oxfw->midi_output_ports == 0))
+ int err;
+
+ if (oxfw->midi_input_ports == 0 && oxfw->midi_output_ports == 0)
return 0;
/* create midi ports */
diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
index 9c73930d0278..8d233417695d 100644
--- a/sound/firewire/oxfw/oxfw-pcm.c
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -134,11 +134,11 @@ static int init_hw_params(struct snd_oxfw *oxfw,
SNDRV_PCM_INFO_MMAP_VALID;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
stream = &oxfw->tx_stream;
formats = oxfw->tx_stream_formats;
} else {
- runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
stream = &oxfw->rx_stream;
formats = oxfw->rx_stream_formats;
}
@@ -158,7 +158,7 @@ static int init_hw_params(struct snd_oxfw *oxfw,
if (err < 0)
goto end;
- err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
+ err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
end:
return err;
}
@@ -244,7 +244,7 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&oxfw->mutex);
}
- amdtp_stream_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
+ amdtp_am824_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
return 0;
}
@@ -265,7 +265,7 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&oxfw->mutex);
}
- amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
+ amdtp_am824_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
return 0;
}
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index 77ad5b98e806..7cb5743c073b 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -148,14 +148,17 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream,
}
pcm_channels = formation.pcm;
- midi_ports = DIV_ROUND_UP(formation.midi, 8);
+ midi_ports = formation.midi * 8;
/* The stream should have one pcm channels at least */
if (pcm_channels == 0) {
err = -EINVAL;
goto end;
}
- amdtp_stream_set_parameters(stream, rate, pcm_channels, midi_ports);
+ err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports,
+ false);
+ if (err < 0)
+ goto end;
err = cmp_connection_establish(conn,
amdtp_stream_get_max_payload(stream));
@@ -225,7 +228,7 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
if (err < 0)
goto end;
- err = amdtp_stream_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
+ err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
if (err < 0) {
amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
@@ -238,9 +241,12 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
* packets. As a result, next isochronous packet includes more data
* blocks than IEC 61883-6 defines.
*/
- if (stream == &oxfw->tx_stream)
+ if (stream == &oxfw->tx_stream) {
oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK |
CIP_JUMBO_PAYLOAD;
+ if (oxfw->wrong_dbs)
+ oxfw->tx_stream.flags |= CIP_WRONG_DBS;
+ }
end:
return err;
}
@@ -480,8 +486,8 @@ int snd_oxfw_stream_parse_format(u8 *format,
}
}
- if (formation->pcm > AMDTP_MAX_CHANNELS_FOR_PCM ||
- formation->midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
+ if (formation->pcm > AM824_MAX_CHANNELS_FOR_PCM ||
+ formation->midi > AM824_MAX_CHANNELS_FOR_MIDI)
return -ENOSYS;
return 0;
@@ -623,6 +629,9 @@ end:
int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
{
u8 plugs[AVC_PLUG_INFO_BUF_BYTES];
+ struct snd_oxfw_stream_formation formation;
+ u8 *format;
+ unsigned int i;
int err;
/* the number of plugs for isoc in/out, ext in/out */
@@ -642,12 +651,42 @@ int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0);
if (err < 0)
goto end;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->tx_stream_formats[i];
+ if (format == NULL)
+ continue;
+ err = snd_oxfw_stream_parse_format(format, &formation);
+ if (err < 0)
+ continue;
+
+ /* Add one MIDI port. */
+ if (formation.midi > 0)
+ oxfw->midi_input_ports = 1;
+ }
+
oxfw->has_output = true;
}
/* use iPCR[0] if exists */
- if (plugs[0] > 0)
+ if (plugs[0] > 0) {
err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0)
+ goto end;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->rx_stream_formats[i];
+ if (format == NULL)
+ continue;
+ err = snd_oxfw_stream_parse_format(format, &formation);
+ if (err < 0)
+ continue;
+
+ /* Add one MIDI port. */
+ if (formation.midi > 0)
+ oxfw->midi_output_ports = 1;
+ }
+ }
end:
return err;
}
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index 8c6ce019f437..588b93f20c2e 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -18,6 +18,9 @@
#define VENDOR_GRIFFIN 0x001292
#define VENDOR_BEHRINGER 0x001564
#define VENDOR_LACIE 0x00d04b
+#define VENDOR_TASCAM 0x00022e
+
+#define MODEL_SATELLITE 0x00200f
#define SPECIFIER_1394TA 0x00a02d
#define VERSION_AVC 0x010001
@@ -129,6 +132,40 @@ static void oxfw_card_free(struct snd_card *card)
mutex_destroy(&oxfw->mutex);
}
+static void detect_quirks(struct snd_oxfw *oxfw)
+{
+ struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
+ struct fw_csr_iterator it;
+ int key, val;
+ int vendor, model;
+
+ /* Seek from Root Directory of Config ROM. */
+ vendor = model = 0;
+ fw_csr_iterator_init(&it, fw_dev->config_rom + 5);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ if (key == CSR_VENDOR)
+ vendor = val;
+ else if (key == CSR_MODEL)
+ model = val;
+ }
+
+ /*
+ * Mackie Onyx Satellite with base station has a quirk to report a wrong
+ * value in 'dbs' field of CIP header against its format information.
+ */
+ if (vendor == VENDOR_LOUD && model == MODEL_SATELLITE)
+ oxfw->wrong_dbs = true;
+
+ /*
+ * TASCAM FireOne has physical control and requires a pair of additional
+ * MIDI ports.
+ */
+ if (vendor == VENDOR_TASCAM) {
+ oxfw->midi_input_ports++;
+ oxfw->midi_output_ports++;
+ }
+}
+
static int oxfw_probe(struct fw_unit *unit,
const struct ieee1394_device_id *id)
{
@@ -157,6 +194,8 @@ static int oxfw_probe(struct fw_unit *unit,
if (err < 0)
goto error;
+ detect_quirks(oxfw);
+
err = name_card(oxfw);
if (err < 0)
goto error;
@@ -294,6 +333,13 @@ static const struct ieee1394_device_id oxfw_id_table[] = {
.specifier_id = SPECIFIER_1394TA,
.version = VERSION_AVC,
},
+ /* TASCAM, FireOne */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = VENDOR_TASCAM,
+ .model_id = 0x800007,
+ },
{ }
};
MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index cace5ad4fe76..8392c424ad1d 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -28,7 +28,7 @@
#include "../fcp.h"
#include "../packets-buffer.h"
#include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
#include "../cmp.h"
struct device_info {
@@ -49,6 +49,7 @@ struct snd_oxfw {
struct mutex mutex;
spinlock_t lock;
+ bool wrong_dbs;
bool has_output;
u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
u8 *rx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile
new file mode 100644
index 000000000000..0fc955d5bd15
--- /dev/null
+++ b/sound/firewire/tascam/Makefile
@@ -0,0 +1,4 @@
+snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \
+ tascam-pcm.o tascam-hwdep.o tascam-transaction.o \
+ tascam-midi.o tascam.o
+obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o
diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c
new file mode 100644
index 000000000000..9dd0fccd5ccc
--- /dev/null
+++ b/sound/firewire/tascam/amdtp-tascam.c
@@ -0,0 +1,243 @@
+/*
+ * amdtp-tascam.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/pcm.h>
+#include "tascam.h"
+
+#define AMDTP_FMT_TSCM_TX 0x1e
+#define AMDTP_FMT_TSCM_RX 0x3e
+
+struct amdtp_tscm {
+ unsigned int pcm_channels;
+
+ void (*transfer_samples)(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
+};
+
+int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)
+{
+ struct amdtp_tscm *p = s->protocol;
+ unsigned int data_channels;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ data_channels = p->pcm_channels;
+
+ /* Packets in in-stream have extra 2 data channels. */
+ if (s->direction == AMDTP_IN_STREAM)
+ data_channels += 2;
+
+ return amdtp_stream_set_parameters(s, rate, data_channels);
+}
+
+static void write_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_tscm *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u32 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32(*src);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_tscm *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u16 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32(*src << 16);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_tscm *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ u32 *dst;
+
+ channels = p->pcm_channels;
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ /* The first data channel is for event counter. */
+ buffer += 1;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[c]);
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_tscm *p = s->protocol;
+ unsigned int channels, i, c;
+
+ channels = p->pcm_channels;
+
+ for (i = 0; i < data_blocks; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[c] = 0x00000000;
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ /*
+ * Our implementation allows this protocol to deliver 24 bit sample in
+ * 32bit data channel.
+ */
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+ struct amdtp_tscm *p = s->protocol;
+
+ if (WARN_ON(amdtp_stream_pcm_running(s)))
+ return;
+
+ switch (format) {
+ default:
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S16:
+ if (s->direction == AMDTP_OUT_STREAM) {
+ p->transfer_samples = write_pcm_s16;
+ break;
+ }
+ WARN_ON(1);
+ /* fall through */
+ case SNDRV_PCM_FORMAT_S32:
+ if (s->direction == AMDTP_OUT_STREAM)
+ p->transfer_samples = write_pcm_s32;
+ else
+ p->transfer_samples = read_pcm_s32;
+ break;
+ }
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol;
+ struct snd_pcm_substream *pcm;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (data_blocks > 0 && pcm)
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+
+ /* A place holder for control messages. */
+
+ return data_blocks;
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol;
+ struct snd_pcm_substream *pcm;
+
+ /* This field is not used. */
+ *syt = 0x0000;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm)
+ p->transfer_samples(s, pcm, buffer, data_blocks);
+ else
+ write_pcm_silence(s, buffer, data_blocks);
+
+ return data_blocks;
+}
+
+int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int pcm_channels)
+{
+ amdtp_stream_process_data_blocks_t process_data_blocks;
+ struct amdtp_tscm *p;
+ unsigned int fmt;
+ int err;
+
+ if (dir == AMDTP_IN_STREAM) {
+ fmt = AMDTP_FMT_TSCM_TX;
+ process_data_blocks = process_tx_data_blocks;
+ } else {
+ fmt = AMDTP_FMT_TSCM_RX;
+ process_data_blocks = process_rx_data_blocks;
+ }
+
+ err = amdtp_stream_init(s, unit, dir,
+ CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
+ process_data_blocks, sizeof(struct amdtp_tscm));
+ if (err < 0)
+ return 0;
+
+ /* Use fixed value for FDF field. */
+ s->fdf = 0x00;
+
+ /* This protocol uses fixed number of data channels for PCM samples. */
+ p = s->protocol;
+ p->pcm_channels = pcm_channels;
+
+ return 0;
+}
diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c
new file mode 100644
index 000000000000..131267c3a042
--- /dev/null
+++ b/sound/firewire/tascam/tascam-hwdep.c
@@ -0,0 +1,201 @@
+/*
+ * tascam-hwdep.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "tascam.h"
+
+static long hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
+ long count)
+{
+ union snd_firewire_event event;
+
+ memset(&event, 0, sizeof(event));
+
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (tscm->dev_lock_count > 0);
+ tscm->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&tscm->lock);
+
+ while (!tscm->dev_lock_changed) {
+ prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&tscm->lock);
+ schedule();
+ finish_wait(&tscm->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&tscm->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ count = hwdep_read_locked(tscm, buf, count);
+ spin_unlock_irq(&tscm->lock);
+
+ return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+ unsigned int events;
+
+ poll_wait(file, &tscm->hwdep_wait, wait);
+
+ spin_lock_irq(&tscm->lock);
+ if (tscm->dev_lock_changed)
+ events = POLLIN | POLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&tscm->lock);
+
+ return events;
+}
+
+static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(tscm->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_TASCAM;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strlcpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_tscm *tscm)
+{
+ int err;
+
+ spin_lock_irq(&tscm->lock);
+
+ if (tscm->dev_lock_count == 0) {
+ tscm->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&tscm->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_tscm *tscm)
+{
+ int err;
+
+ spin_lock_irq(&tscm->lock);
+
+ if (tscm->dev_lock_count == -1) {
+ tscm->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&tscm->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+
+ spin_lock_irq(&tscm->lock);
+ if (tscm->dev_lock_count == -1)
+ tscm->dev_lock_count = 0;
+ spin_unlock_irq(&tscm->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(tscm, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(tscm);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(tscm);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+};
+
+int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
+{
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strcpy(hwdep->name, "Tascam");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = tscm;
+ hwdep->exclusive = true;
+
+ return err;
+}
diff --git a/sound/firewire/tascam/tascam-midi.c b/sound/firewire/tascam/tascam-midi.c
new file mode 100644
index 000000000000..41f842079d9d
--- /dev/null
+++ b/sound/firewire/tascam/tascam-midi.c
@@ -0,0 +1,135 @@
+/*
+ * tascam-midi.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_tscm *tscm = substream->rmidi->private_data;
+
+ /* Initialize internal status. */
+ tscm->running_status[substream->number] = 0;
+ tscm->on_sysex[substream->number] = 0;
+ return 0;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_tscm *tscm = substream->rmidi->private_data;
+
+ snd_fw_async_midi_port_finish(&tscm->out_ports[substream->number]);
+
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_tscm *tscm = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tscm->lock, flags);
+
+ if (up)
+ tscm->tx_midi_substreams[substrm->number] = substrm;
+ else
+ tscm->tx_midi_substreams[substrm->number] = NULL;
+
+ spin_unlock_irqrestore(&tscm->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_tscm *tscm = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tscm->lock, flags);
+
+ if (up)
+ snd_fw_async_midi_port_run(&tscm->out_ports[substrm->number],
+ substrm);
+
+ spin_unlock_irqrestore(&tscm->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+};
+
+int snd_tscm_create_midi_devices(struct snd_tscm *tscm)
+{
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *stream;
+ struct snd_rawmidi_substream *subs;
+ int err;
+
+ err = snd_rawmidi_new(tscm->card, tscm->card->driver, 0,
+ tscm->spec->midi_playback_ports,
+ tscm->spec->midi_capture_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", tscm->card->shortname);
+ rmidi->private_data = tscm;
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_capture_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ /* Set port names for MIDI input. */
+ list_for_each_entry(subs, &stream->substreams, list) {
+ /* TODO: support virtual MIDI ports. */
+ if (subs->number < tscm->spec->midi_capture_ports) {
+ /* Hardware MIDI ports. */
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ tscm->card->shortname, subs->number + 1);
+ }
+ }
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_playback_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ /* Set port names for MIDI ourput. */
+ list_for_each_entry(subs, &stream->substreams, list) {
+ if (subs->number < tscm->spec->midi_playback_ports) {
+ /* Hardware MIDI ports only. */
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ tscm->card->shortname, subs->number + 1);
+ }
+ }
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c
new file mode 100644
index 000000000000..380d3db969a5
--- /dev/null
+++ b/sound/firewire/tascam/tascam-pcm.c
@@ -0,0 +1,312 @@
+/*
+ * tascam-pcm.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+static void set_buffer_params(struct snd_pcm_hardware *hw)
+{
+ hw->period_bytes_min = 4 * hw->channels_min;
+ hw->period_bytes_max = hw->period_bytes_min * 2048;
+ hw->buffer_bytes_max = hw->period_bytes_max * 2;
+
+ hw->periods_min = 2;
+ hw->periods_max = UINT_MAX;
+}
+
+static int pcm_init_hw_params(struct snd_tscm *tscm,
+ struct snd_pcm_substream *substream)
+{
+ static const struct snd_pcm_hardware hardware = {
+ .info = SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .channels_min = 10,
+ .channels_max = 18,
+ };
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct amdtp_stream *stream;
+ unsigned int pcm_channels;
+
+ runtime->hw = hardware;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ stream = &tscm->tx_stream;
+ pcm_channels = tscm->spec->pcm_capture_analog_channels;
+ } else {
+ runtime->hw.formats =
+ SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32;
+ stream = &tscm->rx_stream;
+ pcm_channels = tscm->spec->pcm_playback_analog_channels;
+ }
+
+ if (tscm->spec->has_adat)
+ pcm_channels += 8;
+ if (tscm->spec->has_spdif)
+ pcm_channels += 2;
+ runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
+
+ set_buffer_params(&runtime->hw);
+
+ return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ enum snd_tscm_clock clock;
+ unsigned int rate;
+ int err;
+
+ err = snd_tscm_stream_lock_try(tscm);
+ if (err < 0)
+ goto end;
+
+ err = pcm_init_hw_params(tscm, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_tscm_stream_get_clock(tscm, &clock);
+ if (clock != SND_TSCM_CLOCK_INTERNAL ||
+ amdtp_stream_pcm_running(&tscm->rx_stream) ||
+ amdtp_stream_pcm_running(&tscm->tx_stream)) {
+ err = snd_tscm_stream_get_rate(tscm, &rate);
+ if (err < 0)
+ goto err_locked;
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+ }
+
+ snd_pcm_set_sync(substream);
+end:
+ return err;
+err_locked:
+ snd_tscm_stream_lock_release(tscm);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ snd_tscm_stream_lock_release(tscm);
+
+ return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&tscm->mutex);
+ tscm->substreams_counter++;
+ mutex_unlock(&tscm->mutex);
+ }
+
+ amdtp_tscm_set_pcm_format(&tscm->tx_stream, params_format(hw_params));
+
+ return 0;
+}
+
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&tscm->mutex);
+ tscm->substreams_counter++;
+ mutex_unlock(&tscm->mutex);
+ }
+
+ amdtp_tscm_set_pcm_format(&tscm->rx_stream, params_format(hw_params));
+
+ return 0;
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ mutex_lock(&tscm->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ tscm->substreams_counter--;
+
+ snd_tscm_stream_stop_duplex(tscm);
+
+ mutex_unlock(&tscm->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ mutex_lock(&tscm->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ tscm->substreams_counter--;
+
+ snd_tscm_stream_stop_duplex(tscm);
+
+ mutex_unlock(&tscm->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&tscm->mutex);
+
+ err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&tscm->tx_stream);
+
+ mutex_unlock(&tscm->mutex);
+
+ return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&tscm->mutex);
+
+ err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&tscm->rx_stream);
+
+ mutex_unlock(&tscm->mutex);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_tscm *tscm = sbstrm->private_data;
+
+ return amdtp_stream_pcm_pointer(&tscm->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_tscm *tscm = sbstrm->private_data;
+
+ return amdtp_stream_pcm_pointer(&tscm->rx_stream);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_capture_hw_params,
+ .hw_free = pcm_capture_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static struct snd_pcm_ops pcm_playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_playback_hw_params,
+ .hw_free = pcm_playback_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = tscm;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", tscm->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+
+ return 0;
+}
diff --git a/sound/firewire/tascam/tascam-proc.c b/sound/firewire/tascam/tascam-proc.c
new file mode 100644
index 000000000000..bfd4a4c06914
--- /dev/null
+++ b/sound/firewire/tascam/tascam-proc.c
@@ -0,0 +1,88 @@
+/*
+ * tascam-proc.h - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./tascam.h"
+
+static void proc_read_firmware(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_tscm *tscm = entry->private_data;
+ __be32 data;
+ unsigned int reg, fpga, arm, hw;
+ int err;
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_REGISTER,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ reg = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_FPGA,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ fpga = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_ARM,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ arm = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_HW,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ hw = be32_to_cpu(data);
+
+ snd_iprintf(buffer, "Register: %d (0x%08x)\n", reg & 0xffff, reg);
+ snd_iprintf(buffer, "FPGA: %d (0x%08x)\n", fpga & 0xffff, fpga);
+ snd_iprintf(buffer, "ARM: %d (0x%08x)\n", arm & 0xffff, arm);
+ snd_iprintf(buffer, "Hardware: %d (0x%08x)\n", hw >> 16, hw);
+}
+
+static void add_node(struct snd_tscm *tscm, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *e,
+ struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(tscm->card, name, root);
+ if (entry == NULL)
+ return;
+
+ snd_info_set_text_ops(entry, tscm, op);
+ if (snd_info_register(entry) < 0)
+ snd_info_free_entry(entry);
+}
+
+void snd_tscm_proc_init(struct snd_tscm *tscm)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(tscm->card, "firewire",
+ tscm->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(root) < 0) {
+ snd_info_free_entry(root);
+ return;
+ }
+
+ add_node(tscm, root, "firmware", proc_read_firmware);
+}
diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c
new file mode 100644
index 000000000000..0e6dd5c61f53
--- /dev/null
+++ b/sound/firewire/tascam/tascam-stream.c
@@ -0,0 +1,496 @@
+/*
+ * tascam-stream.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/delay.h>
+#include "tascam.h"
+
+#define CALLBACK_TIMEOUT 500
+
+static int get_clock(struct snd_tscm *tscm, u32 *data)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
+ &reg, sizeof(reg), 0);
+ if (err >= 0)
+ *data = be32_to_cpu(reg);
+
+ return err;
+}
+
+static int set_clock(struct snd_tscm *tscm, unsigned int rate,
+ enum snd_tscm_clock clock)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+ data &= 0x0000ffff;
+
+ if (rate > 0) {
+ data &= 0x000000ff;
+ /* Base rate. */
+ if ((rate % 44100) == 0) {
+ data |= 0x00000100;
+ /* Multiplier. */
+ if (rate / 44100 == 2)
+ data |= 0x00008000;
+ } else if ((rate % 48000) == 0) {
+ data |= 0x00000200;
+ /* Multiplier. */
+ if (rate / 48000 == 2)
+ data |= 0x00008000;
+ } else {
+ return -EAGAIN;
+ }
+ }
+
+ if (clock != INT_MAX) {
+ data &= 0x0000ff00;
+ data |= clock + 1;
+ }
+
+ reg = cpu_to_be32(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ if (data & 0x00008000)
+ reg = cpu_to_be32(0x0000001a);
+ else
+ reg = cpu_to_be32(0x0000000d);
+
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE,
+ &reg, sizeof(reg), 0);
+}
+
+int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
+{
+ u32 data = 0x0;
+ unsigned int trials = 0;
+ int err;
+
+ while (data == 0x0 || trials++ < 5) {
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+
+ data = (data & 0xff000000) >> 24;
+ }
+
+ /* Check base rate. */
+ if ((data & 0x0f) == 0x01)
+ *rate = 44100;
+ else if ((data & 0x0f) == 0x02)
+ *rate = 48000;
+ else
+ return -EAGAIN;
+
+ /* Check multiplier. */
+ if ((data & 0xf0) == 0x80)
+ *rate *= 2;
+ else if ((data & 0xf0) != 0x00)
+ return -EAGAIN;
+
+ return err;
+}
+
+int snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock)
+{
+ u32 data;
+ int err;
+
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+
+ *clock = ((data & 0x00ff0000) >> 16) - 1;
+ if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT)
+ return -EIO;
+
+ return 0;
+}
+
+static int enable_data_channels(struct snd_tscm *tscm)
+{
+ __be32 reg;
+ u32 data;
+ unsigned int i;
+ int err;
+
+ data = 0;
+ for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i)
+ data |= BIT(i);
+ if (tscm->spec->has_adat)
+ data |= 0x0000ff00;
+ if (tscm->spec->has_spdif)
+ data |= 0x00030000;
+
+ reg = cpu_to_be32(data);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = 0;
+ for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i)
+ data |= BIT(i);
+ if (tscm->spec->has_adat)
+ data |= 0x0000ff00;
+ if (tscm->spec->has_spdif)
+ data |= 0x00030000;
+
+ reg = cpu_to_be32(data);
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS,
+ &reg, sizeof(reg), 0);
+}
+
+static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
+{
+ __be32 reg;
+ int err;
+
+ /* Set an option for unknown purpose. */
+ reg = cpu_to_be32(0x00200000);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ err = enable_data_channels(tscm);
+ if (err < 0)
+ return err;
+
+ return set_clock(tscm, rate, INT_MAX);
+}
+
+static void finish_session(struct snd_tscm *tscm)
+{
+ __be32 reg;
+
+ reg = 0;
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
+ &reg, sizeof(reg), 0);
+
+ reg = 0;
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
+ &reg, sizeof(reg), 0);
+
+}
+
+static int begin_session(struct snd_tscm *tscm)
+{
+ __be32 reg;
+ int err;
+
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Set an option for unknown purpose. */
+ reg = cpu_to_be32(0x00002000);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Start multiplexing PCM samples on packets. */
+ reg = cpu_to_be32(0x00000001);
+ return snd_fw_transaction(tscm->unit,
+ TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON,
+ &reg, sizeof(reg), 0);
+}
+
+static void release_resources(struct snd_tscm *tscm)
+{
+ __be32 reg;
+
+ /* Unregister channels. */
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+ &reg, sizeof(reg), 0);
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
+ &reg, sizeof(reg), 0);
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
+ &reg, sizeof(reg), 0);
+
+ /* Release isochronous resources. */
+ fw_iso_resources_free(&tscm->tx_resources);
+ fw_iso_resources_free(&tscm->rx_resources);
+}
+
+static int keep_resources(struct snd_tscm *tscm, unsigned int rate)
+{
+ __be32 reg;
+ int err;
+
+ /* Keep resources for in-stream. */
+ err = amdtp_tscm_set_parameters(&tscm->tx_stream, rate);
+ if (err < 0)
+ return err;
+ err = fw_iso_resources_allocate(&tscm->tx_resources,
+ amdtp_stream_get_max_payload(&tscm->tx_stream),
+ fw_parent_device(tscm->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ /* Keep resources for out-stream. */
+ err = amdtp_tscm_set_parameters(&tscm->rx_stream, rate);
+ if (err < 0)
+ return err;
+ err = fw_iso_resources_allocate(&tscm->rx_resources,
+ amdtp_stream_get_max_payload(&tscm->rx_stream),
+ fw_parent_device(tscm->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ /* Register the isochronous channel for transmitting stream. */
+ reg = cpu_to_be32(tscm->tx_resources.channel);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ goto error;
+
+ /* Unknown */
+ reg = cpu_to_be32(0x00000002);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ goto error;
+
+ /* Register the isochronous channel for receiving stream. */
+ reg = cpu_to_be32(tscm->rx_resources.channel);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ release_resources(tscm);
+ return err;
+}
+
+int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
+{
+ unsigned int pcm_channels;
+ int err;
+
+ /* For out-stream. */
+ err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit);
+ if (err < 0)
+ return err;
+ pcm_channels = tscm->spec->pcm_playback_analog_channels;
+ if (tscm->spec->has_adat)
+ pcm_channels += 8;
+ if (tscm->spec->has_spdif)
+ pcm_channels += 2;
+ err = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM,
+ pcm_channels);
+ if (err < 0)
+ return err;
+
+ /* For in-stream. */
+ err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit);
+ if (err < 0)
+ return err;
+ pcm_channels = tscm->spec->pcm_capture_analog_channels;
+ if (tscm->spec->has_adat)
+ pcm_channels += 8;
+ if (tscm->spec->has_spdif)
+ pcm_channels += 2;
+ err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM,
+ pcm_channels);
+ if (err < 0)
+ amdtp_stream_destroy(&tscm->rx_stream);
+
+ return 0;
+}
+
+/* At bus reset, streaming is stopped and some registers are clear. */
+void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
+{
+ amdtp_stream_pcm_abort(&tscm->tx_stream);
+ amdtp_stream_stop(&tscm->tx_stream);
+
+ amdtp_stream_pcm_abort(&tscm->rx_stream);
+ amdtp_stream_stop(&tscm->rx_stream);
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
+{
+ amdtp_stream_destroy(&tscm->rx_stream);
+ amdtp_stream_destroy(&tscm->tx_stream);
+
+ fw_iso_resources_destroy(&tscm->rx_resources);
+ fw_iso_resources_destroy(&tscm->tx_resources);
+}
+
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
+{
+ unsigned int curr_rate;
+ int err;
+
+ if (tscm->substreams_counter == 0)
+ return 0;
+
+ err = snd_tscm_stream_get_rate(tscm, &curr_rate);
+ if (err < 0)
+ return err;
+ if (curr_rate != rate ||
+ amdtp_streaming_error(&tscm->tx_stream) ||
+ amdtp_streaming_error(&tscm->rx_stream)) {
+ finish_session(tscm);
+
+ amdtp_stream_stop(&tscm->tx_stream);
+ amdtp_stream_stop(&tscm->rx_stream);
+
+ release_resources(tscm);
+ }
+
+ if (!amdtp_stream_running(&tscm->tx_stream)) {
+ amdtp_stream_set_sync(CIP_SYNC_TO_DEVICE,
+ &tscm->tx_stream, &tscm->rx_stream);
+ err = keep_resources(tscm, rate);
+ if (err < 0)
+ goto error;
+
+ err = set_stream_formats(tscm, rate);
+ if (err < 0)
+ goto error;
+
+ err = begin_session(tscm);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_stream_start(&tscm->tx_stream,
+ tscm->tx_resources.channel,
+ fw_parent_device(tscm->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_stream_wait_callback(&tscm->tx_stream,
+ CALLBACK_TIMEOUT)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ if (!amdtp_stream_running(&tscm->rx_stream)) {
+ err = amdtp_stream_start(&tscm->rx_stream,
+ tscm->rx_resources.channel,
+ fw_parent_device(tscm->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_stream_wait_callback(&tscm->rx_stream,
+ CALLBACK_TIMEOUT)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_stream_stop(&tscm->tx_stream);
+ amdtp_stream_stop(&tscm->rx_stream);
+
+ finish_session(tscm);
+ release_resources(tscm);
+
+ return err;
+}
+
+void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
+{
+ if (tscm->substreams_counter > 0)
+ return;
+
+ amdtp_stream_stop(&tscm->tx_stream);
+ amdtp_stream_stop(&tscm->rx_stream);
+
+ finish_session(tscm);
+ release_resources(tscm);
+}
+
+void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
+{
+ tscm->dev_lock_changed = true;
+ wake_up(&tscm->hwdep_wait);
+}
+
+int snd_tscm_stream_lock_try(struct snd_tscm *tscm)
+{
+ int err;
+
+ spin_lock_irq(&tscm->lock);
+
+ /* user land lock this */
+ if (tscm->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (tscm->dev_lock_count++ == 0)
+ snd_tscm_stream_lock_changed(tscm);
+ err = 0;
+end:
+ spin_unlock_irq(&tscm->lock);
+ return err;
+}
+
+void snd_tscm_stream_lock_release(struct snd_tscm *tscm)
+{
+ spin_lock_irq(&tscm->lock);
+
+ if (WARN_ON(tscm->dev_lock_count <= 0))
+ goto end;
+ if (--tscm->dev_lock_count == 0)
+ snd_tscm_stream_lock_changed(tscm);
+end:
+ spin_unlock_irq(&tscm->lock);
+}
diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c
new file mode 100644
index 000000000000..904ce0329fa1
--- /dev/null
+++ b/sound/firewire/tascam/tascam-transaction.c
@@ -0,0 +1,302 @@
+/*
+ * tascam-transaction.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+/*
+ * When return minus value, given argument is not MIDI status.
+ * When return 0, given argument is a beginning of system exclusive.
+ * When return the others, given argument is MIDI data.
+ */
+static inline int calculate_message_bytes(u8 status)
+{
+ switch (status) {
+ case 0xf6: /* Tune request. */
+ case 0xf8: /* Timing clock. */
+ case 0xfa: /* Start. */
+ case 0xfb: /* Continue. */
+ case 0xfc: /* Stop. */
+ case 0xfe: /* Active sensing. */
+ case 0xff: /* System reset. */
+ return 1;
+ case 0xf1: /* MIDI time code quarter frame. */
+ case 0xf3: /* Song select. */
+ return 2;
+ case 0xf2: /* Song position pointer. */
+ return 3;
+ case 0xf0: /* Exclusive. */
+ return 0;
+ case 0xf7: /* End of exclusive. */
+ break;
+ case 0xf4: /* Undefined. */
+ case 0xf5: /* Undefined. */
+ case 0xf9: /* Undefined. */
+ case 0xfd: /* Undefined. */
+ break;
+ default:
+ switch (status & 0xf0) {
+ case 0x80: /* Note on. */
+ case 0x90: /* Note off. */
+ case 0xa0: /* Polyphonic key pressure. */
+ case 0xb0: /* Control change and Mode change. */
+ case 0xe0: /* Pitch bend change. */
+ return 3;
+ case 0xc0: /* Program change. */
+ case 0xd0: /* Channel pressure. */
+ return 2;
+ default:
+ break;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
+{
+ struct snd_tscm *tscm = substream->rmidi->private_data;
+ unsigned int port = substream->number;
+ int i, len, consume;
+ u8 *label, *msg;
+ u8 status;
+
+ /* The first byte is used for label, the rest for MIDI bytes. */
+ label = buf;
+ msg = buf + 1;
+
+ consume = snd_rawmidi_transmit_peek(substream, msg, 3);
+ if (consume == 0)
+ return 0;
+
+ /* On exclusive message. */
+ if (tscm->on_sysex[port]) {
+ /* Seek the end of exclusives. */
+ for (i = 0; i < consume; ++i) {
+ if (msg[i] == 0xf7) {
+ tscm->on_sysex[port] = false;
+ break;
+ }
+ }
+
+ /* At the end of exclusive message, use label 0x07. */
+ if (!tscm->on_sysex[port]) {
+ consume = i + 1;
+ *label = (port << 4) | 0x07;
+ /* During exclusive message, use label 0x04. */
+ } else if (consume == 3) {
+ *label = (port << 4) | 0x04;
+ /* We need to fill whole 3 bytes. Go to next change. */
+ } else {
+ return 0;
+ }
+
+ len = consume;
+ } else {
+ /* The beginning of exclusives. */
+ if (msg[0] == 0xf0) {
+ /* Transfer it in next chance in another condition. */
+ tscm->on_sysex[port] = true;
+ return 0;
+ } else {
+ /* On running-status. */
+ if ((msg[0] & 0x80) != 0x80)
+ status = tscm->running_status[port];
+ else
+ status = msg[0];
+
+ /* Calculate consume bytes. */
+ len = calculate_message_bytes(status);
+ if (len <= 0)
+ return 0;
+
+ /* On running-status. */
+ if ((msg[0] & 0x80) != 0x80) {
+ /* Enough MIDI bytes were not retrieved. */
+ if (consume < len - 1)
+ return 0;
+ consume = len - 1;
+
+ msg[2] = msg[1];
+ msg[1] = msg[0];
+ msg[0] = tscm->running_status[port];
+ } else {
+ /* Enough MIDI bytes were not retrieved. */
+ if (consume < len)
+ return 0;
+ consume = len;
+
+ tscm->running_status[port] = msg[0];
+ }
+ }
+
+ *label = (port << 4) | (msg[0] >> 4);
+ }
+
+ if (len > 0 && len < 3)
+ memset(msg + len, 0, 3 - len);
+
+ return consume;
+}
+
+static void handle_midi_tx(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_tscm *tscm = callback_data;
+ u32 *buf = (u32 *)data;
+ unsigned int messages;
+ unsigned int i;
+ unsigned int port;
+ struct snd_rawmidi_substream *substream;
+ u8 *b;
+ int bytes;
+
+ if (offset != tscm->async_handler.offset)
+ goto end;
+
+ messages = length / 8;
+ for (i = 0; i < messages; i++) {
+ b = (u8 *)(buf + i * 2);
+
+ port = b[0] >> 4;
+ /* TODO: support virtual MIDI ports. */
+ if (port >= tscm->spec->midi_capture_ports)
+ goto end;
+
+ /* Assume the message length. */
+ bytes = calculate_message_bytes(b[1]);
+ /* On MIDI data or exclusives. */
+ if (bytes <= 0) {
+ /* Seek the end of exclusives. */
+ for (bytes = 1; bytes < 4; bytes++) {
+ if (b[bytes] == 0xf7)
+ break;
+ }
+ if (bytes == 4)
+ bytes = 3;
+ }
+
+ substream = ACCESS_ONCE(tscm->tx_midi_substreams[port]);
+ if (substream != NULL)
+ snd_rawmidi_receive(substream, b + 1, bytes);
+ }
+end:
+ fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+int snd_tscm_transaction_register(struct snd_tscm *tscm)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = 0xffffe0000000ull,
+ .end = 0xffffe000ffffull,
+ };
+ unsigned int i;
+ int err;
+
+ /*
+ * Usually, two quadlets are transferred by one transaction. The first
+ * quadlet has MIDI messages, the rest includes timestamp.
+ * Sometimes, 8 set of the data is transferred by a block transaction.
+ */
+ tscm->async_handler.length = 8 * 8;
+ tscm->async_handler.address_callback = handle_midi_tx;
+ tscm->async_handler.callback_data = tscm;
+
+ err = fw_core_add_address_handler(&tscm->async_handler,
+ &resp_register_region);
+ if (err < 0)
+ return err;
+
+ err = snd_tscm_transaction_reregister(tscm);
+ if (err < 0)
+ goto error;
+
+ for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) {
+ err = snd_fw_async_midi_port_init(
+ &tscm->out_ports[i], tscm->unit,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD,
+ 4, fill_message);
+ if (err < 0)
+ goto error;
+ }
+
+ return err;
+error:
+ fw_core_remove_address_handler(&tscm->async_handler);
+ return err;
+}
+
+/* At bus reset, these registers are cleared. */
+int snd_tscm_transaction_reregister(struct snd_tscm *tscm)
+{
+ struct fw_device *device = fw_parent_device(tscm->unit);
+ __be32 reg;
+ int err;
+
+ /* Register messaging address. Block transaction is not allowed. */
+ reg = cpu_to_be32((device->card->node_id << 16) |
+ (tscm->async_handler.offset >> 32));
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_be32(tscm->async_handler.offset);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Turn on messaging. */
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Turn on FireWire LED. */
+ reg = cpu_to_be32(0x0001008e);
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
+ &reg, sizeof(reg), 0);
+}
+
+void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
+{
+ __be32 reg;
+ unsigned int i;
+
+ /* Turn off FireWire LED. */
+ reg = cpu_to_be32(0x0000008e);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
+ &reg, sizeof(reg), 0);
+
+ /* Turn off messaging. */
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
+ &reg, sizeof(reg), 0);
+
+ /* Unregister the address. */
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
+ &reg, sizeof(reg), 0);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
+ &reg, sizeof(reg), 0);
+
+ fw_core_remove_address_handler(&tscm->async_handler);
+ for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++)
+ snd_fw_async_midi_port_destroy(&tscm->out_ports[i]);
+}
diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c
new file mode 100644
index 000000000000..ee0bc1839508
--- /dev/null
+++ b/sound/firewire/tascam/tascam.c
@@ -0,0 +1,209 @@
+/*
+ * tascam.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+MODULE_DESCRIPTION("TASCAM FireWire series Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static struct snd_tscm_spec model_specs[] = {
+ {
+ .name = "FW-1884",
+ .has_adat = true,
+ .has_spdif = true,
+ .pcm_capture_analog_channels = 8,
+ .pcm_playback_analog_channels = 8,
+ .midi_capture_ports = 4,
+ .midi_playback_ports = 4,
+ .is_controller = true,
+ },
+ {
+ .name = "FW-1082",
+ .has_adat = false,
+ .has_spdif = true,
+ .pcm_capture_analog_channels = 8,
+ .pcm_playback_analog_channels = 2,
+ .midi_capture_ports = 2,
+ .midi_playback_ports = 2,
+ .is_controller = true,
+ },
+ /* FW-1804 may be supported. */
+};
+
+static int identify_model(struct snd_tscm *tscm)
+{
+ struct fw_device *fw_dev = fw_parent_device(tscm->unit);
+ const u32 *config_rom = fw_dev->config_rom;
+ char model[9];
+ unsigned int i;
+ u8 c;
+
+ if (fw_dev->config_rom_length < 30) {
+ dev_err(&tscm->unit->device,
+ "Configuration ROM is too short.\n");
+ return -ENODEV;
+ }
+
+ /* Pick up model name from certain addresses. */
+ for (i = 0; i < 8; i++) {
+ c = config_rom[28 + i / 4] >> (24 - 8 * (i % 4));
+ if (c == '\0')
+ break;
+ model[i] = c;
+ }
+ model[i] = '\0';
+
+ for (i = 0; i < ARRAY_SIZE(model_specs); i++) {
+ if (strcmp(model, model_specs[i].name) == 0) {
+ tscm->spec = &model_specs[i];
+ break;
+ }
+ }
+ if (tscm->spec == NULL)
+ return -ENODEV;
+
+ strcpy(tscm->card->driver, "FW-TASCAM");
+ strcpy(tscm->card->shortname, model);
+ strcpy(tscm->card->mixername, model);
+ snprintf(tscm->card->longname, sizeof(tscm->card->longname),
+ "TASCAM %s, GUID %08x%08x at %s, S%d", model,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&tscm->unit->device), 100 << fw_dev->max_speed);
+
+ return 0;
+}
+
+static void tscm_card_free(struct snd_card *card)
+{
+ struct snd_tscm *tscm = card->private_data;
+
+ snd_tscm_transaction_unregister(tscm);
+ snd_tscm_stream_destroy_duplex(tscm);
+
+ fw_unit_put(tscm->unit);
+
+ mutex_destroy(&tscm->mutex);
+}
+
+static int snd_tscm_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_tscm *tscm;
+ int err;
+
+ /* create card */
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+ sizeof(struct snd_tscm), &card);
+ if (err < 0)
+ return err;
+ card->private_free = tscm_card_free;
+
+ /* initialize myself */
+ tscm = card->private_data;
+ tscm->card = card;
+ tscm->unit = fw_unit_get(unit);
+
+ mutex_init(&tscm->mutex);
+ spin_lock_init(&tscm->lock);
+ init_waitqueue_head(&tscm->hwdep_wait);
+
+ err = identify_model(tscm);
+ if (err < 0)
+ goto error;
+
+ snd_tscm_proc_init(tscm);
+
+ err = snd_tscm_stream_init_duplex(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_create_pcm_devices(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_transaction_register(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_create_midi_devices(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_create_hwdep_device(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dev_set_drvdata(&unit->device, tscm);
+
+ return err;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void snd_tscm_update(struct fw_unit *unit)
+{
+ struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+
+ snd_tscm_transaction_reregister(tscm);
+
+ mutex_lock(&tscm->mutex);
+ snd_tscm_stream_update_duplex(tscm);
+ mutex_unlock(&tscm->mutex);
+}
+
+static void snd_tscm_remove(struct fw_unit *unit)
+{
+ struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(tscm->card);
+}
+
+static const struct ieee1394_device_id snd_tscm_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID,
+ .vendor_id = 0x00022e,
+ .specifier_id = 0x00022e,
+ },
+ /* FE-08 requires reverse-engineering because it just has faders. */
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);
+
+static struct fw_driver tscm_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "snd-firewire-tascam",
+ .bus = &fw_bus_type,
+ },
+ .probe = snd_tscm_probe,
+ .update = snd_tscm_update,
+ .remove = snd_tscm_remove,
+ .id_table = snd_tscm_id_table,
+};
+
+static int __init snd_tscm_init(void)
+{
+ return driver_register(&tscm_driver.driver);
+}
+
+static void __exit snd_tscm_exit(void)
+{
+ driver_unregister(&tscm_driver.driver);
+}
+
+module_init(snd_tscm_init);
+module_exit(snd_tscm_exit);
diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h
new file mode 100644
index 000000000000..2d028d2bd3bd
--- /dev/null
+++ b/sound/firewire/tascam/tascam.h
@@ -0,0 +1,147 @@
+/*
+ * tascam.h - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_TASCAM_H_INCLUDED
+#define SOUND_TASCAM_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/rawmidi.h>
+
+#include "../lib.h"
+#include "../amdtp-stream.h"
+#include "../iso-resources.h"
+
+struct snd_tscm_spec {
+ const char *const name;
+ bool has_adat;
+ bool has_spdif;
+ unsigned int pcm_capture_analog_channels;
+ unsigned int pcm_playback_analog_channels;
+ unsigned int midi_capture_ports;
+ unsigned int midi_playback_ports;
+ bool is_controller;
+};
+
+#define TSCM_MIDI_IN_PORT_MAX 4
+#define TSCM_MIDI_OUT_PORT_MAX 4
+
+struct snd_tscm {
+ struct snd_card *card;
+ struct fw_unit *unit;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ const struct snd_tscm_spec *spec;
+
+ struct fw_iso_resources tx_resources;
+ struct fw_iso_resources rx_resources;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ unsigned int substreams_counter;
+
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* For MIDI message incoming transactions. */
+ struct fw_address_handler async_handler;
+ struct snd_rawmidi_substream *tx_midi_substreams[TSCM_MIDI_IN_PORT_MAX];
+
+ /* For MIDI message outgoing transactions. */
+ struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX];
+ u8 running_status[TSCM_MIDI_OUT_PORT_MAX];
+ bool on_sysex[TSCM_MIDI_OUT_PORT_MAX];
+
+ /* For control messages. */
+ struct snd_firewire_tascam_status *status;
+};
+
+#define TSCM_ADDR_BASE 0xffff00000000ull
+
+#define TSCM_OFFSET_FIRMWARE_REGISTER 0x0000
+#define TSCM_OFFSET_FIRMWARE_FPGA 0x0004
+#define TSCM_OFFSET_FIRMWARE_ARM 0x0008
+#define TSCM_OFFSET_FIRMWARE_HW 0x000c
+
+#define TSCM_OFFSET_ISOC_TX_CH 0x0200
+#define TSCM_OFFSET_UNKNOWN 0x0204
+#define TSCM_OFFSET_START_STREAMING 0x0208
+#define TSCM_OFFSET_ISOC_RX_CH 0x020c
+#define TSCM_OFFSET_ISOC_RX_ON 0x0210 /* Little conviction. */
+#define TSCM_OFFSET_TX_PCM_CHANNELS 0x0214
+#define TSCM_OFFSET_RX_PCM_CHANNELS 0x0218
+#define TSCM_OFFSET_MULTIPLEX_MODE 0x021c
+#define TSCM_OFFSET_ISOC_TX_ON 0x0220
+/* Unknown 0x0224 */
+#define TSCM_OFFSET_CLOCK_STATUS 0x0228
+#define TSCM_OFFSET_SET_OPTION 0x022c
+
+#define TSCM_OFFSET_MIDI_TX_ON 0x0300
+#define TSCM_OFFSET_MIDI_TX_ADDR_HI 0x0304
+#define TSCM_OFFSET_MIDI_TX_ADDR_LO 0x0308
+
+#define TSCM_OFFSET_LED_POWER 0x0404
+
+#define TSCM_OFFSET_MIDI_RX_QUAD 0x4000
+
+enum snd_tscm_clock {
+ SND_TSCM_CLOCK_INTERNAL = 0,
+ SND_TSCM_CLOCK_WORD = 1,
+ SND_TSCM_CLOCK_SPDIF = 2,
+ SND_TSCM_CLOCK_ADAT = 3,
+};
+
+int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int pcm_channels);
+int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate);
+int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
+
+int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate);
+int snd_tscm_stream_get_clock(struct snd_tscm *tscm,
+ enum snd_tscm_clock *clock);
+int snd_tscm_stream_init_duplex(struct snd_tscm *tscm);
+void snd_tscm_stream_update_duplex(struct snd_tscm *tscm);
+void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm);
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate);
+void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm);
+
+void snd_tscm_stream_lock_changed(struct snd_tscm *tscm);
+int snd_tscm_stream_lock_try(struct snd_tscm *tscm);
+void snd_tscm_stream_lock_release(struct snd_tscm *tscm);
+
+int snd_tscm_transaction_register(struct snd_tscm *tscm);
+int snd_tscm_transaction_reregister(struct snd_tscm *tscm);
+void snd_tscm_transaction_unregister(struct snd_tscm *tscm);
+
+void snd_tscm_proc_init(struct snd_tscm *tscm);
+
+int snd_tscm_create_pcm_devices(struct snd_tscm *tscm);
+
+int snd_tscm_create_midi_devices(struct snd_tscm *tscm);
+
+int snd_tscm_create_hwdep_device(struct snd_tscm *tscm);
+
+#endif
diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c
index 33ba77dd32f2..cb89ec7c8147 100644
--- a/sound/hda/ext/hdac_ext_stream.c
+++ b/sound/hda/ext/hdac_ext_stream.c
@@ -227,7 +227,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup);
void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
int stream)
{
- snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0);
+ snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_link_set_stream_id);
@@ -385,14 +385,13 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type)
break;
case HDAC_EXT_STREAM_TYPE_HOST:
- if (stream->decoupled) {
+ if (stream->decoupled && !stream->link_locked)
snd_hdac_ext_stream_decouple(ebus, stream, false);
- snd_hdac_stream_release(&stream->hstream);
- }
+ snd_hdac_stream_release(&stream->hstream);
break;
case HDAC_EXT_STREAM_TYPE_LINK:
- if (stream->decoupled)
+ if (stream->decoupled && !stream->hstream.opened)
snd_hdac_ext_stream_decouple(ebus, stream, false);
spin_lock_irq(&bus->reg_lock);
stream->link_locked = 0;
diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c
index 89c2711baaaf..3060e2aee36f 100644
--- a/sound/hda/hda_bus_type.c
+++ b/sound/hda/hda_bus_type.c
@@ -4,6 +4,7 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/export.h>
#include <sound/hdaudio.h>
@@ -63,9 +64,21 @@ static int hda_bus_match(struct device *dev, struct device_driver *drv)
return 1;
}
+static int hda_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ char modalias[32];
+
+ snd_hdac_codec_modalias(dev_to_hdac_dev(dev), modalias,
+ sizeof(modalias));
+ if (add_uevent_var(env, "MODALIAS=%s", modalias))
+ return -ENOMEM;
+ return 0;
+}
+
struct bus_type snd_hda_bus_type = {
.name = "hdaudio",
.match = hda_bus_match,
+ .uevent = hda_uevent,
};
EXPORT_SYMBOL_GPL(snd_hda_bus_type);
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
index db96042a497f..bbdb25f5bbb9 100644
--- a/sound/hda/hdac_device.c
+++ b/sound/hda/hdac_device.c
@@ -164,6 +164,43 @@ void snd_hdac_device_unregister(struct hdac_device *codec)
EXPORT_SYMBOL_GPL(snd_hdac_device_unregister);
/**
+ * snd_hdac_device_set_chip_name - set/update the codec name
+ * @codec: the HDAC device
+ * @name: name string to set
+ *
+ * Returns 0 if the name is set or updated, or a negative error code.
+ */
+int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name)
+{
+ char *newname;
+
+ if (!name)
+ return 0;
+ newname = kstrdup(name, GFP_KERNEL);
+ if (!newname)
+ return -ENOMEM;
+ kfree(codec->chip_name);
+ codec->chip_name = newname;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name);
+
+/**
+ * snd_hdac_codec_modalias - give the module alias name
+ * @codec: HDAC device
+ * @buf: string buffer to store
+ * @size: string buffer size
+ *
+ * Returns the size of string, like snprintf(), or a negative error code.
+ */
+int snd_hdac_codec_modalias(struct hdac_device *codec, char *buf, size_t size)
+{
+ return snprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n",
+ codec->vendor_id, codec->revision_id, codec->type);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
+
+/**
* snd_hdac_make_cmd - compose a 32bit command word to be sent to the
* HD-audio controller
* @codec: the codec object
@@ -952,3 +989,84 @@ bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
return true;
}
EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format);
+
+static unsigned int codec_read(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+ unsigned int res;
+
+ if (snd_hdac_exec_verb(hdac, cmd, flags, &res))
+ return -1;
+
+ return res;
+}
+
+static int codec_write(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+
+ return snd_hdac_exec_verb(hdac, cmd, flags, NULL);
+}
+
+/**
+ * snd_hdac_codec_read - send a command and get the response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command and read the corresponding response.
+ *
+ * Returns the obtained response value, or -1 for an error.
+ */
+int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ return codec_read(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_read);
+
+/**
+ * snd_hdac_codec_write - send a single command without waiting for response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command without waiting for response.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ return codec_write(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_write);
+
+/*
+ * snd_hdac_check_power_state: check whether the actual power state matches
+ * with the target state
+ *
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @target_state: target state to check for
+ *
+ * Return true if state matches, false if not
+ */
+bool snd_hdac_check_power_state(struct hdac_device *hdac,
+ hda_nid_t nid, unsigned int target_state)
+{
+ unsigned int state = codec_read(hdac, nid, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+
+ if (state & AC_PWRST_ERROR)
+ return true;
+ state = (state >> 4) & 0x0f;
+ return (state == target_state);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_check_power_state);
diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c
index c71142dea98a..42d61bf41969 100644
--- a/sound/hda/hdac_sysfs.c
+++ b/sound/hda/hdac_sysfs.c
@@ -45,6 +45,13 @@ CODEC_ATTR(mfg);
CODEC_ATTR_STR(vendor_name);
CODEC_ATTR_STR(chip_name);
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256);
+}
+static DEVICE_ATTR_RO(modalias);
+
static struct attribute *hdac_dev_attrs[] = {
&dev_attr_type.attr,
&dev_attr_vendor_id.attr,
@@ -54,6 +61,7 @@ static struct attribute *hdac_dev_attrs[] = {
&dev_attr_mfg.attr,
&dev_attr_vendor_name.attr,
&dev_attr_chip_name.attr,
+ &dev_attr_modalias.attr,
NULL
};
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c
index d5ac25cc7fee..70671ad65d24 100644
--- a/sound/pci/hda/hda_bind.c
+++ b/sound/pci/hda/hda_bind.c
@@ -15,21 +15,22 @@
#include "hda_local.h"
/*
- * find a matching codec preset
+ * find a matching codec id
*/
static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv)
{
struct hda_codec *codec = container_of(dev, struct hda_codec, core);
struct hda_codec_driver *driver =
container_of(drv, struct hda_codec_driver, core);
- const struct hda_codec_preset *preset;
+ const struct hda_device_id *list;
/* check probe_id instead of vendor_id if set */
u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id;
+ u32 rev_id = codec->core.revision_id;
- for (preset = driver->preset; preset->id; preset++) {
- if (preset->id == id &&
- (!preset->rev || preset->rev == codec->core.revision_id)) {
- codec->preset = preset;
+ for (list = driver->id; list->vendor_id; list++) {
+ if (list->vendor_id == id &&
+ (!list->rev_id || list->rev_id == rev_id)) {
+ codec->preset = list;
return 1;
}
}
@@ -45,26 +46,45 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
codec->patch_ops.unsol_event(codec, ev);
}
-/* reset the codec name from the preset */
-static int codec_refresh_name(struct hda_codec *codec, const char *name)
+/**
+ * snd_hda_codec_set_name - set the codec name
+ * @codec: the HDA codec
+ * @name: name string to set
+ */
+int snd_hda_codec_set_name(struct hda_codec *codec, const char *name)
{
- if (name) {
- kfree(codec->core.chip_name);
- codec->core.chip_name = kstrdup(name, GFP_KERNEL);
+ int err;
+
+ if (!name)
+ return 0;
+ err = snd_hdac_device_set_chip_name(&codec->core, name);
+ if (err < 0)
+ return err;
+
+ /* update the mixer name */
+ if (!*codec->card->mixername ||
+ codec->bus->mixer_assigned >= codec->core.addr) {
+ snprintf(codec->card->mixername,
+ sizeof(codec->card->mixername), "%s %s",
+ codec->core.vendor_name, codec->core.chip_name);
+ codec->bus->mixer_assigned = codec->core.addr;
}
- return codec->core.chip_name ? 0 : -ENOMEM;
+
+ return 0;
}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_name);
static int hda_codec_driver_probe(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
struct module *owner = dev->driver->owner;
+ hda_codec_patch_t patch;
int err;
if (WARN_ON(!codec->preset))
return -EINVAL;
- err = codec_refresh_name(codec, codec->preset->name);
+ err = snd_hda_codec_set_name(codec, codec->preset->name);
if (err < 0)
goto error;
err = snd_hdac_regmap_init(&codec->core);
@@ -76,9 +96,12 @@ static int hda_codec_driver_probe(struct device *dev)
goto error;
}
- err = codec->preset->patch(codec);
- if (err < 0)
- goto error_module;
+ patch = (hda_codec_patch_t)codec->preset->driver_data;
+ if (patch) {
+ err = patch(codec);
+ if (err < 0)
+ goto error_module;
+ }
err = snd_hda_codec_build_pcms(codec);
if (err < 0)
@@ -155,11 +178,10 @@ static inline bool codec_probed(struct hda_codec *codec)
static void codec_bind_module(struct hda_codec *codec)
{
#ifdef MODULE
- request_module("snd-hda-codec-id:%08x", codec->core.vendor_id);
- if (codec_probed(codec))
- return;
- request_module("snd-hda-codec-id:%04x*",
- (codec->core.vendor_id >> 16) & 0xffff);
+ char modalias[32];
+
+ snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias));
+ request_module(modalias);
if (codec_probed(codec))
return;
#endif
@@ -251,11 +273,6 @@ int snd_hda_codec_configure(struct hda_codec *codec)
}
}
- /* audio codec should override the mixer name */
- if (codec->core.afg || !*codec->card->mixername)
- snprintf(codec->card->mixername,
- sizeof(codec->card->mixername), "%s %s",
- codec->core.vendor_name, codec->core.chip_name);
return 0;
error:
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index a249d5486889..83741887faa1 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -91,50 +91,6 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
}
/**
- * snd_hda_codec_read - send a command and get the response
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @flags: optional bit flags
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command and read the corresponding response.
- *
- * Returns the obtained response value, or -1 for an error.
- */
-unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
- int flags,
- unsigned int verb, unsigned int parm)
-{
- unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm);
- unsigned int res;
- if (snd_hdac_exec_verb(&codec->core, cmd, flags, &res))
- return -1;
- return res;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_read);
-
-/**
- * snd_hda_codec_write - send a single command without waiting for response
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @flags: optional bit flags
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command without waiting for response.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
- unsigned int verb, unsigned int parm)
-{
- unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm);
- return snd_hdac_exec_verb(&codec->core, cmd, flags, NULL);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_write);
-
-/**
* snd_hda_sequence_write - sequence writes
* @codec: the HDA codec
* @seq: VERB array to send
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 2970413f18a0..373fcad840ea 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -22,6 +22,7 @@
#define __SOUND_HDA_CODEC_H
#include <linux/kref.h>
+#include <linux/mod_devicetable.h>
#include <sound/info.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -69,6 +70,7 @@ struct hda_bus {
unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
int primary_dig_out_type; /* primary digital out PCM type */
+ unsigned int mixer_assigned; /* codec addr for mixer name */
};
/* from hdac_bus to hda_bus */
@@ -80,19 +82,21 @@ struct hda_bus {
* Known codecs have the patch to build and set up the controls/PCMs
* better than the generic parser.
*/
-struct hda_codec_preset {
- unsigned int id;
- unsigned int rev;
- const char *name;
- int (*patch)(struct hda_codec *codec);
-};
+typedef int (*hda_codec_patch_t)(struct hda_codec *);
#define HDA_CODEC_ID_GENERIC_HDMI 0x00000101
#define HDA_CODEC_ID_GENERIC 0x00000201
+#define HDA_CODEC_REV_ENTRY(_vid, _rev, _name, _patch) \
+ { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \
+ .api_version = HDA_DEV_LEGACY, \
+ .driver_data = (unsigned long)(_patch) }
+#define HDA_CODEC_ENTRY(_vid, _name, _patch) \
+ HDA_CODEC_REV_ENTRY(_vid, 0, _name, _patch)
+
struct hda_codec_driver {
struct hdac_driver core;
- const struct hda_codec_preset *preset;
+ const struct hda_device_id *id;
};
int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
@@ -183,7 +187,7 @@ struct hda_codec {
u32 probe_id; /* overridden id for probing */
/* detected preset */
- const struct hda_codec_preset *preset;
+ const struct hda_device_id *preset;
const char *modelname; /* model name for preset */
/* set by patch */
@@ -297,10 +301,6 @@ struct hda_codec {
/*
* constructors
*/
-int snd_hda_bus_new(struct snd_card *card,
- const struct hdac_bus_ops *ops,
- const struct hdac_io_ops *io_ops,
- struct hda_bus **busp);
int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
unsigned int codec_addr, struct hda_codec **codecp);
int snd_hda_codec_configure(struct hda_codec *codec);
@@ -309,11 +309,21 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec);
/*
* low level functions
*/
-unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
+static inline unsigned int
+snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
int flags,
- unsigned int verb, unsigned int parm);
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
- unsigned int verb, unsigned int parm);
+ unsigned int verb, unsigned int parm)
+{
+ return snd_hdac_codec_read(&codec->core, nid, flags, verb, parm);
+}
+
+static inline int
+snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
+ unsigned int verb, unsigned int parm)
+{
+ return snd_hdac_codec_write(&codec->core, nid, flags, verb, parm);
+}
+
#define snd_hda_param_read(codec, nid, param) \
snd_hdac_read_parm(&(codec)->core, nid, param)
#define snd_hda_get_sub_nodes(codec, nid, start_nid) \
@@ -453,6 +463,8 @@ void snd_hda_unlock_devices(struct hda_bus *bus);
void snd_hda_bus_reset(struct hda_bus *bus);
void snd_hda_bus_reset_codecs(struct hda_bus *bus);
+int snd_hda_codec_set_name(struct hda_codec *codec, const char *name);
+
/*
* power management
*/
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index 944455997fdc..d6b93a20361b 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -1045,6 +1045,7 @@ int azx_bus_init(struct azx *chip, const char *model,
mutex_init(&bus->prepare_mutex);
bus->pci = chip->pci;
bus->modelname = model;
+ bus->mixer_assigned = -1;
bus->core.snoop = azx_snoop(chip);
if (chip->get_position[0] != azx_get_pos_lpib ||
chip->get_position[1] != azx_get_pos_lpib)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 24f91114a32c..c6e8a651cea1 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -5877,13 +5877,14 @@ error:
return err;
}
-static const struct hda_codec_preset snd_hda_preset_generic[] = {
- { .id = HDA_CODEC_ID_GENERIC, .patch = snd_hda_parse_generic_codec },
+static const struct hda_device_id snd_hda_id_generic[] = {
+ HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec),
{} /* terminator */
};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic);
static struct hda_codec_driver generic_driver = {
- .preset = snd_hda_preset_generic,
+ .id = snd_hda_id_generic,
};
module_hda_codec_driver(generic_driver);
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 4a21c2199e02..d0e066e4c985 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -681,12 +681,7 @@ static inline bool
snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
unsigned int target_state)
{
- unsigned int state = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_POWER_STATE, 0);
- if (state & AC_PWRST_ERROR)
- return true;
- state = (state >> 4) & 0x0f;
- return (state == target_state);
+ return snd_hdac_check_power_state(&codec->core, nid, target_state);
}
unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c
index a6e3d9b511ab..64e0d1d81ca5 100644
--- a/sound/pci/hda/hda_sysfs.c
+++ b/sound/pci/hda/hda_sysfs.c
@@ -595,8 +595,7 @@ static void parse_model_mode(char *buf, struct hda_bus *bus,
static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
struct hda_codec **codecp)
{
- kfree((*codecp)->core.chip_name);
- (*codecp)->core.chip_name = kstrdup(buf, GFP_KERNEL);
+ snd_hda_codec_set_name(*codecp, buf);
}
#define DEFINE_PARSE_ID_MODE(name) \
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index c033a4ee6547..e0fb8c6d1bc2 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -1165,32 +1165,31 @@ static int patch_ad1882(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_analog[] = {
- { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884 },
- { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
- { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884 },
- { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
- { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884 },
- { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884 },
- { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
- { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
- { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1884 },
- { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
- { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
- { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
- { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
- { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
- { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
+static const struct hda_device_id snd_hda_id_analog[] = {
+ HDA_CODEC_ENTRY(0x11d4184a, "AD1884A", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41882, "AD1882", patch_ad1882),
+ HDA_CODEC_ENTRY(0x11d41883, "AD1883", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41884, "AD1884", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d4194a, "AD1984A", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d4194b, "AD1984B", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41981, "AD1981", patch_ad1981),
+ HDA_CODEC_ENTRY(0x11d41983, "AD1983", patch_ad1983),
+ HDA_CODEC_ENTRY(0x11d41984, "AD1984", patch_ad1884),
+ HDA_CODEC_ENTRY(0x11d41986, "AD1986A", patch_ad1986a),
+ HDA_CODEC_ENTRY(0x11d41988, "AD1988", patch_ad1988),
+ HDA_CODEC_ENTRY(0x11d4198b, "AD1988B", patch_ad1988),
+ HDA_CODEC_ENTRY(0x11d4882a, "AD1882A", patch_ad1882),
+ HDA_CODEC_ENTRY(0x11d4989a, "AD1989A", patch_ad1988),
+ HDA_CODEC_ENTRY(0x11d4989b, "AD1989B", patch_ad1988),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:11d4*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Analog Devices HD-audio codec");
static struct hda_codec_driver analog_driver = {
- .preset = snd_hda_preset_analog,
+ .id = snd_hda_id_analog,
};
module_hda_codec_driver(analog_driver);
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index 484bbf4134cd..c2d9ee9cfdc0 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -83,22 +83,19 @@ static int patch_ca0110(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_ca0110[] = {
- { .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
- { .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
- { .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
+static const struct hda_device_id snd_hda_id_ca0110[] = {
+ HDA_CODEC_ENTRY(0x1102000a, "CA0110-IBG", patch_ca0110),
+ HDA_CODEC_ENTRY(0x1102000b, "CA0110-IBG", patch_ca0110),
+ HDA_CODEC_ENTRY(0x1102000d, "SB0880 X-Fi", patch_ca0110),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:1102000a");
-MODULE_ALIAS("snd-hda-codec-id:1102000b");
-MODULE_ALIAS("snd-hda-codec-id:1102000d");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0110);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
static struct hda_codec_driver ca0110_driver = {
- .preset = snd_hda_preset_ca0110,
+ .id = snd_hda_id_ca0110,
};
module_hda_codec_driver(ca0110_driver);
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 186792fe226e..3a02e5c14d09 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -4778,18 +4778,17 @@ static int patch_ca0132(struct hda_codec *codec)
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_ca0132[] = {
- { .id = 0x11020011, .name = "CA0132", .patch = patch_ca0132 },
+static struct hda_device_id snd_hda_id_ca0132[] = {
+ HDA_CODEC_ENTRY(0x11020011, "CA0132", patch_ca0132),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:11020011");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0132);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Creative Sound Core3D codec");
static struct hda_codec_driver ca0132_driver = {
- .preset = snd_hda_preset_ca0132,
+ .id = snd_hda_id_ca0132,
};
module_hda_codec_driver(ca0132_driver);
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 85813de26da8..a12ae8ac0914 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -570,6 +570,7 @@ static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
return NULL;
codec->spec = spec;
spec->vendor_nid = vendor_nid;
+ codec->power_save_node = 1;
snd_hda_gen_spec_init(&spec->gen);
return spec;
@@ -1200,26 +1201,21 @@ static int patch_cs4213(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_cirrus[] = {
- { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
- { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
- { .id = 0x10134208, .name = "CS4208", .patch = patch_cs4208 },
- { .id = 0x10134210, .name = "CS4210", .patch = patch_cs4210 },
- { .id = 0x10134213, .name = "CS4213", .patch = patch_cs4213 },
+static const struct hda_device_id snd_hda_id_cirrus[] = {
+ HDA_CODEC_ENTRY(0x10134206, "CS4206", patch_cs420x),
+ HDA_CODEC_ENTRY(0x10134207, "CS4207", patch_cs420x),
+ HDA_CODEC_ENTRY(0x10134208, "CS4208", patch_cs4208),
+ HDA_CODEC_ENTRY(0x10134210, "CS4210", patch_cs4210),
+ HDA_CODEC_ENTRY(0x10134213, "CS4213", patch_cs4213),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:10134206");
-MODULE_ALIAS("snd-hda-codec-id:10134207");
-MODULE_ALIAS("snd-hda-codec-id:10134208");
-MODULE_ALIAS("snd-hda-codec-id:10134210");
-MODULE_ALIAS("snd-hda-codec-id:10134213");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cirrus);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
static struct hda_codec_driver cirrus_driver = {
- .preset = snd_hda_preset_cirrus,
+ .id = snd_hda_id_cirrus,
};
module_hda_codec_driver(cirrus_driver);
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index f5ed078710f8..1b2195dd2b26 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -123,22 +123,19 @@ static int patch_cmi8888(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_cmedia[] = {
- { .id = 0x13f68888, .name = "CMI8888", .patch = patch_cmi8888 },
- { .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
- { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
+static const struct hda_device_id snd_hda_id_cmedia[] = {
+ HDA_CODEC_ENTRY(0x13f68888, "CMI8888", patch_cmi8888),
+ HDA_CODEC_ENTRY(0x13f69880, "CMI9880", patch_cmi9880),
+ HDA_CODEC_ENTRY(0x434d4980, "CMI9880", patch_cmi9880),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:13f68888");
-MODULE_ALIAS("snd-hda-codec-id:13f69880");
-MODULE_ALIAS("snd-hda-codec-id:434d4980");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("C-Media HD-audio codec");
static struct hda_codec_driver cmedia_driver = {
- .preset = snd_hda_preset_cmedia,
+ .id = snd_hda_id_cmedia,
};
module_hda_codec_driver(cmedia_driver);
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 2f0ec7c45fc7..c8b8ef5246a6 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -954,100 +954,44 @@ static int patch_conexant_auto(struct hda_codec *codec)
/*
*/
-static const struct hda_codec_preset snd_hda_preset_conexant[] = {
- { .id = 0x14f15045, .name = "CX20549 (Venice)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15047, .name = "CX20551 (Waikiki)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15051, .name = "CX20561 (Hermosa)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15066, .name = "CX20582 (Pebble)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
- .patch = patch_conexant_auto },
- { .id = 0x14f15068, .name = "CX20584",
- .patch = patch_conexant_auto },
- { .id = 0x14f15069, .name = "CX20585",
- .patch = patch_conexant_auto },
- { .id = 0x14f1506c, .name = "CX20588",
- .patch = patch_conexant_auto },
- { .id = 0x14f1506e, .name = "CX20590",
- .patch = patch_conexant_auto },
- { .id = 0x14f15097, .name = "CX20631",
- .patch = patch_conexant_auto },
- { .id = 0x14f15098, .name = "CX20632",
- .patch = patch_conexant_auto },
- { .id = 0x14f150a1, .name = "CX20641",
- .patch = patch_conexant_auto },
- { .id = 0x14f150a2, .name = "CX20642",
- .patch = patch_conexant_auto },
- { .id = 0x14f150ab, .name = "CX20651",
- .patch = patch_conexant_auto },
- { .id = 0x14f150ac, .name = "CX20652",
- .patch = patch_conexant_auto },
- { .id = 0x14f150b8, .name = "CX20664",
- .patch = patch_conexant_auto },
- { .id = 0x14f150b9, .name = "CX20665",
- .patch = patch_conexant_auto },
- { .id = 0x14f150f1, .name = "CX20721",
- .patch = patch_conexant_auto },
- { .id = 0x14f150f2, .name = "CX20722",
- .patch = patch_conexant_auto },
- { .id = 0x14f150f3, .name = "CX20723",
- .patch = patch_conexant_auto },
- { .id = 0x14f150f4, .name = "CX20724",
- .patch = patch_conexant_auto },
- { .id = 0x14f1510f, .name = "CX20751/2",
- .patch = patch_conexant_auto },
- { .id = 0x14f15110, .name = "CX20751/2",
- .patch = patch_conexant_auto },
- { .id = 0x14f15111, .name = "CX20753/4",
- .patch = patch_conexant_auto },
- { .id = 0x14f15113, .name = "CX20755",
- .patch = patch_conexant_auto },
- { .id = 0x14f15114, .name = "CX20756",
- .patch = patch_conexant_auto },
- { .id = 0x14f15115, .name = "CX20757",
- .patch = patch_conexant_auto },
- { .id = 0x14f151d7, .name = "CX20952",
- .patch = patch_conexant_auto },
+static const struct hda_device_id snd_hda_id_conexant[] = {
+ HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15066, "CX20582 (Pebble)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15067, "CX20583 (Pebble HSF)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15068, "CX20584", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15069, "CX20585", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f1506c, "CX20588", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f1506e, "CX20590", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15097, "CX20631", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15098, "CX20632", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150a1, "CX20641", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150a2, "CX20642", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150ab, "CX20651", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150ac, "CX20652", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150b8, "CX20664", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150b9, "CX20665", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f1, "CX20721", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f2, "CX20722", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f3, "CX20723", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f4, "CX20724", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f1510f, "CX20751/2", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15110, "CX20751/2", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15111, "CX20753/4", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15113, "CX20755", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15114, "CX20756", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15115, "CX20757", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f151d7, "CX20952", patch_conexant_auto),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:14f15045");
-MODULE_ALIAS("snd-hda-codec-id:14f15047");
-MODULE_ALIAS("snd-hda-codec-id:14f15051");
-MODULE_ALIAS("snd-hda-codec-id:14f15066");
-MODULE_ALIAS("snd-hda-codec-id:14f15067");
-MODULE_ALIAS("snd-hda-codec-id:14f15068");
-MODULE_ALIAS("snd-hda-codec-id:14f15069");
-MODULE_ALIAS("snd-hda-codec-id:14f1506c");
-MODULE_ALIAS("snd-hda-codec-id:14f1506e");
-MODULE_ALIAS("snd-hda-codec-id:14f15097");
-MODULE_ALIAS("snd-hda-codec-id:14f15098");
-MODULE_ALIAS("snd-hda-codec-id:14f150a1");
-MODULE_ALIAS("snd-hda-codec-id:14f150a2");
-MODULE_ALIAS("snd-hda-codec-id:14f150ab");
-MODULE_ALIAS("snd-hda-codec-id:14f150ac");
-MODULE_ALIAS("snd-hda-codec-id:14f150b8");
-MODULE_ALIAS("snd-hda-codec-id:14f150b9");
-MODULE_ALIAS("snd-hda-codec-id:14f150f1");
-MODULE_ALIAS("snd-hda-codec-id:14f150f2");
-MODULE_ALIAS("snd-hda-codec-id:14f150f3");
-MODULE_ALIAS("snd-hda-codec-id:14f150f4");
-MODULE_ALIAS("snd-hda-codec-id:14f1510f");
-MODULE_ALIAS("snd-hda-codec-id:14f15110");
-MODULE_ALIAS("snd-hda-codec-id:14f15111");
-MODULE_ALIAS("snd-hda-codec-id:14f15113");
-MODULE_ALIAS("snd-hda-codec-id:14f15114");
-MODULE_ALIAS("snd-hda-codec-id:14f15115");
-MODULE_ALIAS("snd-hda-codec-id:14f151d7");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_conexant);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec");
static struct hda_codec_driver conexant_driver = {
- .preset = snd_hda_preset_conexant,
+ .id = snd_hda_id_conexant,
};
module_hda_codec_driver(conexant_driver);
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index acbfbe087ee8..f503a883bef3 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1775,6 +1775,16 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
return non_pcm;
}
+/* There is a fixed mapping between audio pin node and display port
+ * on current Intel platforms:
+ * Pin Widget 5 - PORT B (port = 1 in i915 driver)
+ * Pin Widget 6 - PORT C (port = 2 in i915 driver)
+ * Pin Widget 7 - PORT D (port = 3 in i915 driver)
+ */
+static int intel_pin2port(hda_nid_t pin_nid)
+{
+ return pin_nid - 4;
+}
/*
* HDMI callbacks
@@ -1791,6 +1801,8 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
int pin_idx = hinfo_to_pin_index(codec, hinfo);
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct i915_audio_component *acomp = codec->bus->core.audio_component;
bool non_pcm;
int pinctl;
@@ -1807,6 +1819,13 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx);
}
+ /* Call sync_audio_rate to set the N/CTS/M manually if necessary */
+ /* Todo: add DP1.2 MST audio support later */
+ if (acomp && acomp->ops && acomp->ops->sync_audio_rate)
+ acomp->ops->sync_audio_rate(acomp->dev,
+ intel_pin2port(pin_nid),
+ runtime->rate);
+
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
mutex_lock(&per_pin->lock);
per_pin->channels = substream->runtime->channels;
@@ -2561,7 +2580,7 @@ static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hdmi_spec *spec = codec->spec;
struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL;
- switch (codec->preset->id) {
+ switch (codec->preset->vendor_id) {
case 0x10de0002:
case 0x10de0003:
case 0x10de0005:
@@ -2879,7 +2898,7 @@ static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)
snd_pcm_alt_chmaps, 8, 0, &chmap);
if (err < 0)
return err;
- switch (codec->preset->id) {
+ switch (codec->preset->vendor_id) {
case 0x10de0002:
case 0x10de0003:
case 0x10de0005:
@@ -3487,138 +3506,77 @@ static int patch_via_hdmi(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
-{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
-{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
-{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
-{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
-{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi },
-{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_nvhdmi },
+static const struct hda_device_id snd_hda_id_hdmi[] = {
+HDA_CODEC_ENTRY(0x1002793c, "RS600 HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x10027919, "RS600 HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x1002791a, "RS690/780 HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x1002aa01, "R6xx HDMI", patch_atihdmi),
+HDA_CODEC_ENTRY(0x10951390, "SiI1390 HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x10951392, "SiI1392 HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x17e80047, "Chrontel HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x10de0002, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0003, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI", patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi),
/* 17 is known to be absent */
-{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0020, .name = "Tegra30 HDMI", .patch = patch_tegra_hdmi },
-{ .id = 0x10de0022, .name = "Tegra114 HDMI", .patch = patch_tegra_hdmi },
-{ .id = 0x10de0028, .name = "Tegra124 HDMI", .patch = patch_tegra_hdmi },
-{ .id = 0x10de0029, .name = "Tegra210 HDMI/DP", .patch = patch_tegra_hdmi },
-{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
-{ .id = 0x10de0070, .name = "GPU 70 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0071, .name = "GPU 71 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de0072, .name = "GPU 72 HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de007d, .name = "GPU 7d HDMI/DP", .patch = patch_nvhdmi },
-{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
-{ .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
-{ .id = 0x11069f81, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
-{ .id = 0x11069f84, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x11069f85, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi },
-{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862807, .name = "Haswell HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862808, .name = "Broadwell HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862809, .name = "Skylake HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x8086280a, .name = "Broxton HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862883, .name = "Braswell HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
+HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP", patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0043, "GPU 43 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0044, "GPU 44 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0051, "GPU 51 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0060, "GPU 60 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0067, "MCP67 HDMI", patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi),
+HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi),
+HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi),
/* special ID for generic HDMI */
-{ .id = HDA_CODEC_ID_GENERIC_HDMI, .patch = patch_generic_hdmi },
+HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:1002793c");
-MODULE_ALIAS("snd-hda-codec-id:10027919");
-MODULE_ALIAS("snd-hda-codec-id:1002791a");
-MODULE_ALIAS("snd-hda-codec-id:1002aa01");
-MODULE_ALIAS("snd-hda-codec-id:10951390");
-MODULE_ALIAS("snd-hda-codec-id:10951392");
-MODULE_ALIAS("snd-hda-codec-id:10de0002");
-MODULE_ALIAS("snd-hda-codec-id:10de0003");
-MODULE_ALIAS("snd-hda-codec-id:10de0005");
-MODULE_ALIAS("snd-hda-codec-id:10de0006");
-MODULE_ALIAS("snd-hda-codec-id:10de0007");
-MODULE_ALIAS("snd-hda-codec-id:10de000a");
-MODULE_ALIAS("snd-hda-codec-id:10de000b");
-MODULE_ALIAS("snd-hda-codec-id:10de000c");
-MODULE_ALIAS("snd-hda-codec-id:10de000d");
-MODULE_ALIAS("snd-hda-codec-id:10de0010");
-MODULE_ALIAS("snd-hda-codec-id:10de0011");
-MODULE_ALIAS("snd-hda-codec-id:10de0012");
-MODULE_ALIAS("snd-hda-codec-id:10de0013");
-MODULE_ALIAS("snd-hda-codec-id:10de0014");
-MODULE_ALIAS("snd-hda-codec-id:10de0015");
-MODULE_ALIAS("snd-hda-codec-id:10de0016");
-MODULE_ALIAS("snd-hda-codec-id:10de0018");
-MODULE_ALIAS("snd-hda-codec-id:10de0019");
-MODULE_ALIAS("snd-hda-codec-id:10de001a");
-MODULE_ALIAS("snd-hda-codec-id:10de001b");
-MODULE_ALIAS("snd-hda-codec-id:10de001c");
-MODULE_ALIAS("snd-hda-codec-id:10de0028");
-MODULE_ALIAS("snd-hda-codec-id:10de0040");
-MODULE_ALIAS("snd-hda-codec-id:10de0041");
-MODULE_ALIAS("snd-hda-codec-id:10de0042");
-MODULE_ALIAS("snd-hda-codec-id:10de0043");
-MODULE_ALIAS("snd-hda-codec-id:10de0044");
-MODULE_ALIAS("snd-hda-codec-id:10de0051");
-MODULE_ALIAS("snd-hda-codec-id:10de0060");
-MODULE_ALIAS("snd-hda-codec-id:10de0067");
-MODULE_ALIAS("snd-hda-codec-id:10de0070");
-MODULE_ALIAS("snd-hda-codec-id:10de0071");
-MODULE_ALIAS("snd-hda-codec-id:10de0072");
-MODULE_ALIAS("snd-hda-codec-id:10de007d");
-MODULE_ALIAS("snd-hda-codec-id:10de8001");
-MODULE_ALIAS("snd-hda-codec-id:11069f80");
-MODULE_ALIAS("snd-hda-codec-id:11069f81");
-MODULE_ALIAS("snd-hda-codec-id:11069f84");
-MODULE_ALIAS("snd-hda-codec-id:11069f85");
-MODULE_ALIAS("snd-hda-codec-id:17e80047");
-MODULE_ALIAS("snd-hda-codec-id:80860054");
-MODULE_ALIAS("snd-hda-codec-id:80862801");
-MODULE_ALIAS("snd-hda-codec-id:80862802");
-MODULE_ALIAS("snd-hda-codec-id:80862803");
-MODULE_ALIAS("snd-hda-codec-id:80862804");
-MODULE_ALIAS("snd-hda-codec-id:80862805");
-MODULE_ALIAS("snd-hda-codec-id:80862806");
-MODULE_ALIAS("snd-hda-codec-id:80862807");
-MODULE_ALIAS("snd-hda-codec-id:80862808");
-MODULE_ALIAS("snd-hda-codec-id:80862809");
-MODULE_ALIAS("snd-hda-codec-id:8086280a");
-MODULE_ALIAS("snd-hda-codec-id:80862880");
-MODULE_ALIAS("snd-hda-codec-id:80862882");
-MODULE_ALIAS("snd-hda-codec-id:80862883");
-MODULE_ALIAS("snd-hda-codec-id:808629fb");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_hdmi);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("HDMI HD-audio codec");
@@ -3627,7 +3585,7 @@ MODULE_ALIAS("snd-hda-codec-nvhdmi");
MODULE_ALIAS("snd-hda-codec-atihdmi");
static struct hda_codec_driver hdmi_driver = {
- .preset = snd_hda_preset_hdmi,
+ .id = snd_hda_id_hdmi,
};
module_hda_codec_driver(hdmi_driver);
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 16b8dcba5c12..0214b7df6d9d 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -822,17 +822,7 @@ static const struct hda_codec_ops alc_patch_ops = {
};
-/* replace the codec chip_name with the given string */
-static int alc_codec_rename(struct hda_codec *codec, const char *name)
-{
- kfree(codec->core.chip_name);
- codec->core.chip_name = kstrdup(name, GFP_KERNEL);
- if (!codec->core.chip_name) {
- alc_free(codec);
- return -ENOMEM;
- }
- return 0;
-}
+#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name)
/*
* Rename codecs appropriately from COEF value or subvendor id
@@ -6627,78 +6617,70 @@ static int patch_alc680(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_realtek[] = {
- { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 },
- { .id = 0x10ec0231, .name = "ALC231", .patch = patch_alc269 },
- { .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 },
- { .id = 0x10ec0235, .name = "ALC233", .patch = patch_alc269 },
- { .id = 0x10ec0255, .name = "ALC255", .patch = patch_alc269 },
- { .id = 0x10ec0256, .name = "ALC256", .patch = patch_alc269 },
- { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
- { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
- { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
- { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
- { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 },
- { .id = 0x10ec0270, .name = "ALC270", .patch = patch_alc269 },
- { .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 },
- { .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 },
- { .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 },
- { .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 },
- { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
- { .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 },
- { .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 },
- { .id = 0x10ec0285, .name = "ALC285", .patch = patch_alc269 },
- { .id = 0x10ec0286, .name = "ALC286", .patch = patch_alc269 },
- { .id = 0x10ec0288, .name = "ALC288", .patch = patch_alc269 },
- { .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 },
- { .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 },
- { .id = 0x10ec0293, .name = "ALC293", .patch = patch_alc269 },
- { .id = 0x10ec0298, .name = "ALC298", .patch = patch_alc269 },
- { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
- .patch = patch_alc861 },
- { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
- { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
- { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd },
- { .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2",
- .patch = patch_alc882 },
- { .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
- .patch = patch_alc662 },
- { .id = 0x10ec0662, .rev = 0x100300, .name = "ALC662 rev3",
- .patch = patch_alc662 },
- { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
- { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
- { .id = 0x10ec0667, .name = "ALC667", .patch = patch_alc662 },
- { .id = 0x10ec0668, .name = "ALC668", .patch = patch_alc662 },
- { .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
- { .id = 0x10ec0671, .name = "ALC671", .patch = patch_alc662 },
- { .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 },
- { .id = 0x10ec0867, .name = "ALC891", .patch = patch_alc882 },
- { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
- { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
- { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
- { .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A",
- .patch = patch_alc882 },
- { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
- .patch = patch_alc882 },
- { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
- { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 },
- { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
- .patch = patch_alc882 },
- { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 },
- { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 },
- { .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 },
- { .id = 0x10ec0899, .name = "ALC898", .patch = patch_alc882 },
- { .id = 0x10ec0900, .name = "ALC1150", .patch = patch_alc882 },
+static const struct hda_device_id snd_hda_id_realtek[] = {
+ HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260),
+ HDA_CODEC_ENTRY(0x10ec0262, "ALC262", patch_alc262),
+ HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268),
+ HDA_CODEC_ENTRY(0x10ec0268, "ALC268", patch_alc268),
+ HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0282, "ALC282", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0283, "ALC283", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
+ HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
+ HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),
+ HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861),
+ HDA_CODEC_ENTRY(0x10ec0862, "ALC861-VD", patch_alc861vd),
+ HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100002, "ALC662 rev2", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100101, "ALC662 rev1", patch_alc662),
+ HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100300, "ALC662 rev3", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0663, "ALC663", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0665, "ALC665", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0667, "ALC667", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0668, "ALC668", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680),
+ HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880),
+ HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100101, "ALC889A", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100103, "ALC889A", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0885, "ALC885", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0887, "ALC887", patch_alc882),
+ HDA_CODEC_REV_ENTRY(0x10ec0888, 0x100101, "ALC1200", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0888, "ALC888", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0889, "ALC889", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662),
+ HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:10ec*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Realtek HD-audio codec");
static struct hda_codec_driver realtek_driver = {
- .preset = snd_hda_preset_realtek,
+ .id = snd_hda_id_realtek,
};
module_hda_codec_driver(realtek_driver);
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
index 5104bebb2286..ffda38c45509 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -289,41 +289,30 @@ static int patch_si3054(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_si3054[] = {
- { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 },
+static const struct hda_device_id snd_hda_id_si3054[] = {
+ HDA_CODEC_ENTRY(0x163c3055, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x163c3155, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x11c13026, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x11c13055, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x11c13155, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x10573055, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x10573057, "Si3054", patch_si3054),
+ HDA_CODEC_ENTRY(0x10573155, "Si3054", patch_si3054),
/* VIA HDA on Clevo m540 */
- { .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ENTRY(0x11063288, "Si3054", patch_si3054),
/* Asus A8J Modem (SM56) */
- { .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ENTRY(0x15433155, "Si3054", patch_si3054),
/* LG LW20 modem */
- { .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ENTRY(0x18540018, "Si3054", patch_si3054),
{}
};
-
-MODULE_ALIAS("snd-hda-codec-id:163c3055");
-MODULE_ALIAS("snd-hda-codec-id:163c3155");
-MODULE_ALIAS("snd-hda-codec-id:11c13026");
-MODULE_ALIAS("snd-hda-codec-id:11c13055");
-MODULE_ALIAS("snd-hda-codec-id:11c13155");
-MODULE_ALIAS("snd-hda-codec-id:10573055");
-MODULE_ALIAS("snd-hda-codec-id:10573057");
-MODULE_ALIAS("snd-hda-codec-id:10573155");
-MODULE_ALIAS("snd-hda-codec-id:11063288");
-MODULE_ALIAS("snd-hda-codec-id:15433155");
-MODULE_ALIAS("snd-hda-codec-id:18540018");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_si3054);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
static struct hda_codec_driver si3054_driver = {
- .preset = snd_hda_preset_si3054,
+ .id = snd_hda_id_si3054,
};
module_hda_codec_driver(si3054_driver);
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index def5cc8dff02..08a0f6a35cca 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -5012,121 +5012,119 @@ static int patch_stac9872(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_sigmatel[] = {
- { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
- { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
- { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
- { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
- { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
- { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
- { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
- { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x },
- { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x },
- { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x },
- { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x },
- { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x },
- { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x },
- { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x },
- { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x },
- { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x },
- { .id = 0x83847623, .name = "STAC9273D", .patch = patch_stac927x },
- { .id = 0x83847624, .name = "STAC9272X", .patch = patch_stac927x },
- { .id = 0x83847625, .name = "STAC9272D", .patch = patch_stac927x },
- { .id = 0x83847626, .name = "STAC9271X", .patch = patch_stac927x },
- { .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x },
- { .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x },
- { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x },
- { .id = 0x83847632, .name = "STAC9202", .patch = patch_stac925x },
- { .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x },
- { .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x },
- { .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x },
- { .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x },
- { .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x },
- { .id = 0x83847645, .name = "92HD206X", .patch = patch_stac927x },
- { .id = 0x83847646, .name = "92HD206D", .patch = patch_stac927x },
- /* The following does not take into account .id=0x83847661 when subsys =
- * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
- * currently not fully supported.
- */
- { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
- { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
- { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
- { .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 },
- { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
- { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
- { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
- { .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 },
- { .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 },
- { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
- { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
- { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
- { .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
- { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d4, .name = "92HD83C1C5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d1, .name = "92HD87B1/3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d9, .name = "92HD87B2/4", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7666, .name = "92HD88B3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7667, .name = "92HD88B1", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7668, .name = "92HD88B2", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7669, .name = "92HD88B4", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
- { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
- { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
- { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
- { .id = 0x111d7695, .name = "92HD95", .patch = patch_stac92hd95 },
- { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76c0, .name = "92HD89C3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c1, .name = "92HD89C2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c2, .name = "92HD89C1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c3, .name = "92HD89B3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c4, .name = "92HD89B2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c5, .name = "92HD89B1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c6, .name = "92HD89E3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c7, .name = "92HD89E2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c8, .name = "92HD89E1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c9, .name = "92HD89D3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76ca, .name = "92HD89D2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cb, .name = "92HD89D1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76df, .name = "92HD93BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e3, .name = "92HD98BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e5, .name = "92HD99BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e8, .name = "92HD66B1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e9, .name = "92HD66B2X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ea, .name = "92HD66B3X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76eb, .name = "92HD66C1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ec, .name = "92HD66C2X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ed, .name = "92HD66C3X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ee, .name = "92HD66B1X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76ef, .name = "92HD66B2X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76f0, .name = "92HD66B3X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76f1, .name = "92HD66C1X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76f2, .name = "92HD66C2X3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76f3, .name = "92HD66C3/65", .patch = patch_stac92hd83xxx},
+static const struct hda_device_id snd_hda_id_sigmatel[] = {
+ HDA_CODEC_ENTRY(0x83847690, "STAC9200", patch_stac9200),
+ HDA_CODEC_ENTRY(0x83847882, "STAC9220 A1", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847680, "STAC9221 A1", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847880, "STAC9220 A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847681, "STAC9220D/9223D A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847682, "STAC9221 A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847683, "STAC9221D A2", patch_stac922x),
+ HDA_CODEC_ENTRY(0x83847618, "STAC9227", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847619, "STAC9227", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847616, "STAC9228", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847617, "STAC9228", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847614, "STAC9229", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847615, "STAC9229", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847620, "STAC9274", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847621, "STAC9274D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847622, "STAC9273X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847623, "STAC9273D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847624, "STAC9272X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847625, "STAC9272D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847626, "STAC9271X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847627, "STAC9271D", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847628, "STAC9274X5NH", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847629, "STAC9274D5NH", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847632, "STAC9202", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847633, "STAC9202D", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847634, "STAC9250", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847635, "STAC9250D", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847636, "STAC9251", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847637, "STAC9250D", patch_stac925x),
+ HDA_CODEC_ENTRY(0x83847645, "92HD206X", patch_stac927x),
+ HDA_CODEC_ENTRY(0x83847646, "92HD206D", patch_stac927x),
+ /* The following does not take into account .id=0x83847661 when subsys =
+ * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
+ * currently not fully supported.
+ */
+ HDA_CODEC_ENTRY(0x83847661, "CXD9872RD/K", patch_stac9872),
+ HDA_CODEC_ENTRY(0x83847662, "STAC9872AK", patch_stac9872),
+ HDA_CODEC_ENTRY(0x83847664, "CXD9872AKD", patch_stac9872),
+ HDA_CODEC_ENTRY(0x83847698, "STAC9205", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a0, "STAC9205", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a1, "STAC9205D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a2, "STAC9204", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a3, "STAC9204D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a4, "STAC9255", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a5, "STAC9255D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a6, "STAC9254", patch_stac9205),
+ HDA_CODEC_ENTRY(0x838476a7, "STAC9254D", patch_stac9205),
+ HDA_CODEC_ENTRY(0x111d7603, "92HD75B3X5", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d7604, "92HD83C1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d4, "92HD83C1C5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7605, "92HD81B1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d5, "92HD81B1C5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d1, "92HD87B1/3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76d9, "92HD87B2/4", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7666, "92HD88B3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7667, "92HD88B1", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7668, "92HD88B2", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7669, "92HD88B4", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d7608, "92HD75B2X5", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d7674, "92HD73D1X5", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d7675, "92HD73C1X5", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d7676, "92HD73E1X5", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d7695, "92HD95", patch_stac92hd95),
+ HDA_CODEC_ENTRY(0x111d76b0, "92HD71B8X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b1, "92HD71B8X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b2, "92HD71B7X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b3, "92HD71B7X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b4, "92HD71B6X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b5, "92HD71B6X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b6, "92HD71B5X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76b7, "92HD71B5X", patch_stac92hd71bxx),
+ HDA_CODEC_ENTRY(0x111d76c0, "92HD89C3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c1, "92HD89C2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c2, "92HD89C1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c3, "92HD89B3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c4, "92HD89B2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c5, "92HD89B1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c6, "92HD89E3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c7, "92HD89E2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c8, "92HD89E1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76c9, "92HD89D3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76ca, "92HD89D2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76cb, "92HD89D1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76cc, "92HD89F3", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76cd, "92HD89F2", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76ce, "92HD89F1", patch_stac92hd73xx),
+ HDA_CODEC_ENTRY(0x111d76df, "92HD93BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e0, "92HD91BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e3, "92HD98BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e5, "92HD99BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e7, "92HD90BXX", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e8, "92HD66B1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76e9, "92HD66B2X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ea, "92HD66B3X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76eb, "92HD66C1X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ec, "92HD66C2X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ed, "92HD66C3X5", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ee, "92HD66B1X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76ef, "92HD66B2X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f0, "92HD66B3X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f1, "92HD66C1X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f2, "92HD66C2X3", patch_stac92hd83xxx),
+ HDA_CODEC_ENTRY(0x111d76f3, "92HD66C3/65", patch_stac92hd83xxx),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:8384*");
-MODULE_ALIAS("snd-hda-codec-id:111d*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_sigmatel);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
static struct hda_codec_driver sigmatel_driver = {
- .preset = snd_hda_preset_sigmatel,
+ .id = snd_hda_id_sigmatel,
};
module_hda_codec_driver(sigmatel_driver);
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index da5366405eda..fc30d1e8aa76 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -785,21 +785,11 @@ static int patch_vt1708S(struct hda_codec *codec)
override_mic_boost(codec, 0x1e, 0, 3, 40);
/* correct names for VT1708BCE */
- if (get_codec_type(codec) == VT1708BCE) {
- kfree(codec->core.chip_name);
- codec->core.chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
- snprintf(codec->card->mixername,
- sizeof(codec->card->mixername),
- "%s %s", codec->core.vendor_name, codec->core.chip_name);
- }
+ if (get_codec_type(codec) == VT1708BCE)
+ snd_hda_codec_set_name(codec, "VT1708BCE");
/* correct names for VT1705 */
- if (codec->core.vendor_id == 0x11064397) {
- kfree(codec->core.chip_name);
- codec->core.chip_name = kstrdup("VT1705", GFP_KERNEL);
- snprintf(codec->card->mixername,
- sizeof(codec->card->mixername),
- "%s %s", codec->core.vendor_name, codec->core.chip_name);
- }
+ if (codec->core.vendor_id == 0x11064397)
+ snd_hda_codec_set_name(codec, "VT1705");
/* automatic parse from the BIOS config */
err = via_parse_auto_config(codec);
@@ -1210,109 +1200,64 @@ static int patch_vt3476(struct hda_codec *codec)
/*
* patch entries
*/
-static const struct hda_codec_preset snd_hda_preset_via[] = {
- { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106e710, .name = "VT1709 10-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e711, .name = "VT1709 10-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e712, .name = "VT1709 10-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e713, .name = "VT1709 10-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e714, .name = "VT1709 6-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e715, .name = "VT1709 6-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e716, .name = "VT1709 6-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e717, .name = "VT1709 6-Ch",
- .patch = patch_vt1709},
- { .id = 0x1106e720, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e721, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e722, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e723, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e724, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e725, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e726, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B},
- { .id = 0x1106e727, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B},
- { .id = 0x11060397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11061397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11062397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11063397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11064397, .name = "VT1705",
- .patch = patch_vt1708S},
- { .id = 0x11065397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11066397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11067397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11060398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11061398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11062398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11063398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11064398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11065398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11066398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11067398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11060428, .name = "VT1718S",
- .patch = patch_vt1718S},
- { .id = 0x11064428, .name = "VT1718S",
- .patch = patch_vt1718S},
- { .id = 0x11060441, .name = "VT2020",
- .patch = patch_vt1718S},
- { .id = 0x11064441, .name = "VT1828S",
- .patch = patch_vt1718S},
- { .id = 0x11060433, .name = "VT1716S",
- .patch = patch_vt1716S},
- { .id = 0x1106a721, .name = "VT1716S",
- .patch = patch_vt1716S},
- { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
- { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
- { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
- { .id = 0x11060440, .name = "VT1818S",
- .patch = patch_vt1708S},
- { .id = 0x11060446, .name = "VT1802",
- .patch = patch_vt2002P},
- { .id = 0x11068446, .name = "VT1802",
- .patch = patch_vt2002P},
- { .id = 0x11064760, .name = "VT1705CF",
- .patch = patch_vt3476},
- { .id = 0x11064761, .name = "VT1708SCE",
- .patch = patch_vt3476},
- { .id = 0x11064762, .name = "VT1808",
- .patch = patch_vt3476},
+static const struct hda_device_id snd_hda_id_via[] = {
+ HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708),
+ HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709),
+ HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B),
+ HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702),
+ HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S),
+ HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S),
+ HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S),
+ HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812),
+ HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S),
+ HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P),
+ HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476),
+ HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476),
+ HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476),
{} /* terminator */
};
-
-MODULE_ALIAS("snd-hda-codec-id:1106*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
static struct hda_codec_driver via_driver = {
- .preset = snd_hda_preset_via,
+ .id = snd_hda_id_via,
};
MODULE_LICENSE("GPL");
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 7acbc21d642a..9e1ad119a3ce 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -1394,7 +1394,9 @@ static int snd_korg1212_playback_open(struct snd_pcm_substream *substream)
spin_unlock_irqrestore(&korg1212->lock, flags);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, kPlayBufferFrames, kPlayBufferFrames);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ kPlayBufferFrames);
+
return 0;
}
@@ -1422,8 +1424,8 @@ static int snd_korg1212_capture_open(struct snd_pcm_substream *substream)
spin_unlock_irqrestore(&korg1212->lock, flags);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- kPlayBufferFrames, kPlayBufferFrames);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ kPlayBufferFrames);
return 0;
}
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
index cba89beb2b38..8b8e2e54fba3 100644
--- a/sound/pci/lx6464es/lx6464es.c
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -234,8 +234,8 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
/* the clock rate cannot be changed */
board_rate = chip->board_sample_rate;
- err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
- board_rate, board_rate);
+ err = snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_RATE,
+ board_rate);
if (err < 0) {
dev_warn(chip->card->dev, "could not constrain periods\n");
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index 23d7f5d30c41..cd94ac548ba3 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -831,9 +831,9 @@ static struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = {
static void snd_rme32_set_buffer_constraint(struct rme32 *rme32, struct snd_pcm_runtime *runtime)
{
if (! rme32->fullduplex_mode) {
- snd_pcm_hw_constraint_minmax(runtime,
+ snd_pcm_hw_constraint_single(runtime,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- RME32_BUFFER_SIZE, RME32_BUFFER_SIZE);
+ RME32_BUFFER_SIZE);
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
&hw_constraints_period_bytes);
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index 2306ccf7281e..714df906249e 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -1152,13 +1152,13 @@ rme96_set_buffer_size_constraint(struct rme96 *rme96,
{
unsigned int size;
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ RME96_BUFFER_SIZE);
if ((size = rme96->playback_periodsize) != 0 ||
(size = rme96->capture_periodsize) != 0)
- snd_pcm_hw_constraint_minmax(runtime,
+ snd_pcm_hw_constraint_single(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- size, size);
+ size);
else
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 9bba275b4c9b..2875b4f6d8c9 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -5112,6 +5112,7 @@ static int hdsp_request_fw_loader(struct hdsp *hdsp)
dev_err(hdsp->card->dev,
"too short firmware size %d (expected %d)\n",
(int)fw->size, HDSP_FIRMWARE_SIZE);
+ release_firmware(fw);
return -EINVAL;
}
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index cb666c73712d..8bc8016c173d 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -6080,18 +6080,17 @@ static int snd_hdspm_open(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
32, 4096);
/* RayDAT & AIO have a fixed buffer of 16384 samples per channel */
- snd_pcm_hw_constraint_minmax(runtime,
+ snd_pcm_hw_constraint_single(runtime,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
- 16384, 16384);
+ 16384);
break;
default:
snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
64, 8192);
- snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS,
- 2, 2);
+ snd_pcm_hw_constraint_single(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS, 2);
break;
}
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index 198c924551b7..acff8d62059c 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -728,8 +728,8 @@ static int adav80x_dai_startup(struct snd_pcm_substream *substream,
if (!snd_soc_codec_is_active(codec) || !adav80x->rate)
return 0;
- return snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE, adav80x->rate, adav80x->rate);
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, adav80x->rate);
}
static void adav80x_dai_shutdown(struct snd_pcm_substream *substream,
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 2713e1845cbc..a5a4e9f75c57 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -1612,19 +1612,16 @@ static void twl4030_constraints(struct twl4030_priv *twl4030,
return;
/* Set the constraints according to the already configured stream */
- snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+ snd_pcm_hw_constraint_single(slv_substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
- twl4030->rate,
twl4030->rate);
- snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+ snd_pcm_hw_constraint_single(slv_substream->runtime,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- twl4030->sample_bits,
twl4030->sample_bits);
- snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+ snd_pcm_hw_constraint_single(slv_substream->runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
- twl4030->channels,
twl4030->channels);
}
@@ -1669,9 +1666,9 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
/* In option2 4 channel is not supported, set the
* constraint for the first stream for channels, the
* second stream will 'inherit' this cosntraint */
- snd_pcm_hw_constraint_minmax(substream->runtime,
+ snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
- 2, 2);
+ 2);
}
twl4030->master_substream = substream;
}
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index e19026380534..e4c694c758b8 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -150,14 +150,12 @@ static int uda134x_startup(struct snd_pcm_substream *substream,
master_runtime->sample_bits,
master_runtime->rate);
- snd_pcm_hw_constraint_minmax(substream->runtime,
+ snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
- master_runtime->rate,
master_runtime->rate);
- snd_pcm_hw_constraint_minmax(substream->runtime,
+ snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- master_runtime->sample_bits,
master_runtime->sample_bits);
uda134x->slave_substream = substream;
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
index 80fb1dc81f6c..7693c1129bab 100644
--- a/sound/soc/codecs/wl1273.c
+++ b/sound/soc/codecs/wl1273.c
@@ -307,11 +307,10 @@ static int wl1273_startup(struct snd_pcm_substream *substream,
switch (wl1273->mode) {
case WL1273_MODE_BT:
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE,
- 8000, 8000);
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS, 1, 1);
+ snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 8000);
+ snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 1);
break;
case WL1273_MODE_FM_RX:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index c4453120b11a..7a5c9a36c1db 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -117,20 +117,10 @@ static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-static unsigned int rates_48000[] = {
- 48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
- .count = ARRAY_SIZE(rates_48000),
- .list = rates_48000,
-};
-
static int byt_aif1_startup(struct snd_pcm_substream *substream)
{
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_48000);
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
}
static struct snd_soc_ops byt_aif1_ops = {
diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
index 49f4869cec48..4e2fcf188dd1 100644
--- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c
+++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
@@ -193,20 +193,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-static unsigned int rates_48000[] = {
- 48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
- .count = ARRAY_SIZE(rates_48000),
- .list = rates_48000,
-};
-
static int cht_aif1_startup(struct snd_pcm_substream *substream)
{
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_48000);
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
}
static int cht_max98090_headset_init(struct snd_soc_component *component)
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 7be8461e4d3b..38d65a3529c4 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -235,20 +235,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-static unsigned int rates_48000[] = {
- 48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
- .count = ARRAY_SIZE(rates_48000),
- .list = rates_48000,
-};
-
static int cht_aif1_startup(struct snd_pcm_substream *substream)
{
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_48000);
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
}
static struct snd_soc_ops cht_aif1_ops = {
diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c
index 23fe04075142..5621ccd92992 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5672.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5672.c
@@ -222,20 +222,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-static unsigned int rates_48000[] = {
- 48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
- .count = ARRAY_SIZE(rates_48000),
- .list = rates_48000,
-};
-
static int cht_aif1_startup(struct snd_pcm_substream *substream)
{
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_48000);
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
}
static struct snd_soc_ops cht_aif1_ops = {
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index dcb5336b5698..190f868e78b2 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -99,8 +99,7 @@ static int n810_startup(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
n810_ext_control(&rtd->card->dapm);
return clk_prepare_enable(sys_clkout2);
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index 99538900a253..5e21f08579d8 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -107,8 +107,7 @@ static int rx51_startup(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *card = rtd->card;
- snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
rx51_ext_control(&card->dapm);
return 0;
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 317395824cd7..c86dc96e8986 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -200,9 +200,9 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n",
soc_dai->rate);
- ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
- soc_dai->rate, soc_dai->rate);
+ soc_dai->rate);
if (ret < 0) {
dev_err(soc_dai->dev,
"ASoC: Unable to apply rate constraint: %d\n",
@@ -216,9 +216,8 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n",
soc_dai->channels);
- ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
- soc_dai->channels,
soc_dai->channels);
if (ret < 0) {
dev_err(soc_dai->dev,
@@ -233,9 +232,8 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n",
soc_dai->sample_bits);
- ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- soc_dai->sample_bits,
soc_dai->sample_bits);
if (ret < 0) {
dev_err(soc_dai->dev,
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
index 6ba8ae9ecc7a..6d5698b25bd4 100644
--- a/sound/soc/ux500/ux500_msp_dai.c
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -522,9 +522,9 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
slots_active = hweight32(mask);
dev_dbg(dai->dev, "TDM-slots active: %d", slots_active);
- snd_pcm_hw_constraint_minmax(runtime,
+ snd_pcm_hw_constraint_single(runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
- slots_active, slots_active);
+ slots_active);
break;
default:
diff --git a/sound/usb/card.h b/sound/usb/card.h
index ef580b43f1e3..71778ca4b26a 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -122,6 +122,7 @@ struct snd_usb_substream {
unsigned int buffer_periods; /* current periods per buffer */
unsigned int altset_idx; /* USB data format: index of alternate setting */
unsigned int txfr_quirk:1; /* allow sub-frame alignment */
+ unsigned int tx_length_quirk:1; /* add length specifier to transfers */
unsigned int fmt_type; /* USB audio format type (1-3) */
unsigned int pkt_offset_adj; /* Bytes to drop from beginning of packets (for non-compliant devices) */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index e6f71894ecdc..7b1cb365ffab 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -183,13 +183,53 @@ static void retire_inbound_urb(struct snd_usb_endpoint *ep,
ep->retire_data_urb(ep->data_subs, urb);
}
+static void prepare_silent_urb(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *ctx)
+{
+ struct urb *urb = ctx->urb;
+ unsigned int offs = 0;
+ unsigned int extra = 0;
+ __le32 packet_length;
+ int i;
+
+ /* For tx_length_quirk, put packet length at start of packet */
+ if (ep->chip->tx_length_quirk)
+ extra = sizeof(packet_length);
+
+ for (i = 0; i < ctx->packets; ++i) {
+ unsigned int offset;
+ unsigned int length;
+ int counts;
+
+ if (ctx->packet_size[i])
+ counts = ctx->packet_size[i];
+ else
+ counts = snd_usb_endpoint_next_packet_size(ep);
+
+ length = counts * ep->stride; /* number of silent bytes */
+ offset = offs * ep->stride + extra * i;
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = length + extra;
+ if (extra) {
+ packet_length = cpu_to_le32(length);
+ memcpy(urb->transfer_buffer + offset,
+ &packet_length, sizeof(packet_length));
+ }
+ memset(urb->transfer_buffer + offset + extra,
+ ep->silence_value, length);
+ offs += counts;
+ }
+
+ urb->number_of_packets = ctx->packets;
+ urb->transfer_buffer_length = offs * ep->stride + ctx->packets * extra;
+}
+
/*
* Prepare a PLAYBACK urb for submission to the bus.
*/
static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
struct snd_urb_ctx *ctx)
{
- int i;
struct urb *urb = ctx->urb;
unsigned char *cp = urb->transfer_buffer;
@@ -201,24 +241,7 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
ep->prepare_data_urb(ep->data_subs, urb);
} else {
/* no data provider, so send silence */
- unsigned int offs = 0;
- for (i = 0; i < ctx->packets; ++i) {
- int counts;
-
- if (ctx->packet_size[i])
- counts = ctx->packet_size[i];
- else
- counts = snd_usb_endpoint_next_packet_size(ep);
-
- urb->iso_frame_desc[i].offset = offs * ep->stride;
- urb->iso_frame_desc[i].length = counts * ep->stride;
- offs += counts;
- }
-
- urb->number_of_packets = ctx->packets;
- urb->transfer_buffer_length = offs * ep->stride;
- memset(urb->transfer_buffer, ep->silence_value,
- offs * ep->stride);
+ prepare_silent_urb(ep, ctx);
}
break;
@@ -594,6 +617,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
unsigned int max_packs_per_period, urbs_per_period, urb_packs;
unsigned int max_urbs, i;
int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels;
+ int tx_length_quirk = (ep->chip->tx_length_quirk &&
+ usb_pipeout(ep->pipe));
if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) {
/*
@@ -610,13 +635,34 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
/* assume max. frequency is 25% higher than nominal */
ep->freqmax = ep->freqn + (ep->freqn >> 2);
- maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
- >> (16 - ep->datainterval);
+ /* 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 */
- maxsize = ep->maxpacksize;
- ep->freqmax = (maxsize / (frame_bits >> 3))
+ unsigned int data_maxsize = maxsize = ep->maxpacksize;
+
+ if (tx_length_quirk)
+ /* Need to remove the length descriptor to calc freq */
+ data_maxsize -= sizeof(__le32);
+ ep->freqmax = (data_maxsize / (frame_bits >> 3))
<< (16 - ep->datainterval);
}
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 417ebb11cf48..7661616f3636 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -1903,11 +1903,14 @@ static void snd_usbmidi_switch_roland_altsetting(struct snd_usb_midi *umidi)
hostif = &intf->altsetting[1];
intfd = get_iface_desc(hostif);
+ /* If either or both of the endpoints support interrupt transfer,
+ * then use the alternate setting
+ */
if (intfd->bNumEndpoints != 2 ||
- (get_endpoint(hostif, 0)->bmAttributes &
- USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK ||
- (get_endpoint(hostif, 1)->bmAttributes &
- USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
+ !((get_endpoint(hostif, 0)->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT ||
+ (get_endpoint(hostif, 1)->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT))
return;
dev_dbg(&umidi->dev->dev, "switching to altsetting %d with int ep\n",
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index d3608c0a29f3..fe91184ce832 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -338,7 +338,7 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
struct usb_mixer_interface *mixer = list->mixer;
int index = kcontrol->private_value & 0xff;
- int value = ucontrol->value.integer.value[0];
+ unsigned int value = ucontrol->value.integer.value[0];
int old_value = kcontrol->private_value >> 8;
int err;
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index cdac5179db3f..9245f52d43bd 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -1383,6 +1383,56 @@ static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs,
subs->hwptr_done++;
}
}
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
+}
+
+static void copy_to_urb(struct snd_usb_substream *subs, struct urb *urb,
+ int offset, int stride, unsigned int bytes)
+{
+ struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+
+ if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+ /* err, the transferred area goes over buffer boundary. */
+ unsigned int bytes1 =
+ runtime->buffer_size * stride - subs->hwptr_done;
+ memcpy(urb->transfer_buffer + offset,
+ runtime->dma_area + subs->hwptr_done, bytes1);
+ memcpy(urb->transfer_buffer + offset + bytes1,
+ runtime->dma_area, bytes - bytes1);
+ } else {
+ memcpy(urb->transfer_buffer + offset,
+ runtime->dma_area + subs->hwptr_done, bytes);
+ }
+ subs->hwptr_done += bytes;
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
+}
+
+static unsigned int copy_to_urb_quirk(struct snd_usb_substream *subs,
+ struct urb *urb, int stride,
+ unsigned int bytes)
+{
+ __le32 packet_length;
+ int i;
+
+ /* Put __le32 length descriptor at start of each packet. */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int length = urb->iso_frame_desc[i].length;
+ unsigned int offset = urb->iso_frame_desc[i].offset;
+
+ packet_length = cpu_to_le32(length);
+ offset += i * sizeof(packet_length);
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length += sizeof(packet_length);
+ memcpy(urb->transfer_buffer + offset,
+ &packet_length, sizeof(packet_length));
+ copy_to_urb(subs, urb, offset + sizeof(packet_length),
+ stride, length);
+ }
+ /* Adjust transfer size accordingly. */
+ bytes += urb->number_of_packets * sizeof(packet_length);
+ return bytes;
}
static void prepare_playback_urb(struct snd_usb_substream *subs,
@@ -1460,27 +1510,17 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
}
subs->hwptr_done += bytes;
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
} else {
/* usual PCM */
- if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
- /* err, the transferred area goes over buffer boundary. */
- unsigned int bytes1 =
- runtime->buffer_size * stride - subs->hwptr_done;
- memcpy(urb->transfer_buffer,
- runtime->dma_area + subs->hwptr_done, bytes1);
- memcpy(urb->transfer_buffer + bytes1,
- runtime->dma_area, bytes - bytes1);
- } else {
- memcpy(urb->transfer_buffer,
- runtime->dma_area + subs->hwptr_done, bytes);
- }
-
- subs->hwptr_done += bytes;
+ if (!subs->tx_length_quirk)
+ copy_to_urb(subs, urb, 0, stride, bytes);
+ else
+ bytes = copy_to_urb_quirk(subs, urb, stride, bytes);
+ /* bytes is now amount of outgoing data */
}
- if (subs->hwptr_done >= runtime->buffer_size * stride)
- subs->hwptr_done -= runtime->buffer_size * stride;
-
/* update delay with exact number of samples queued */
runtime->delay = subs->last_delay;
runtime->delay += frames;
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index e4756651a52c..1a1e2e4df35e 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -2664,6 +2664,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
+ USB_DEVICE(0x1235, 0x000a),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Novation", */
+ /* .product_name = "Nocturn", */
+ .ifnum = 0,
+ .type = QUIRK_MIDI_RAW_BYTES
+ }
+},
+{
USB_DEVICE(0x1235, 0x000e),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "Novation", */
@@ -3182,10 +3191,9 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
{
/*
* ZOOM R16/24 in audio interface mode.
- * Mixer descriptors are garbage, further quirks will be needed
- * to make any of it functional, thus disabled for now.
- * Playback stream appears to start and run fine but no sound
- * is produced, so also disabled for now.
+ * Playback requires an extra four byte LE length indicator
+ * at the start of each isochronous packet. This quirk is
+ * enabled in create_standard_audio_quirk().
*/
USB_DEVICE(0x1686, 0x00dd),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
@@ -3193,14 +3201,9 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
.type = QUIRK_COMPOSITE,
.data = (const struct snd_usb_audio_quirk[]) {
{
- /* Mixer */
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE,
- },
- {
/* Playback */
.ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE,
},
{
/* Capture */
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 00ebc0ca008e..4897ea171194 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -115,6 +115,9 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip,
struct usb_interface_descriptor *altsd;
int err;
+ if (chip->usb_id == USB_ID(0x1686, 0x00dd)) /* Zoom R16/24 */
+ chip->tx_length_quirk = 1;
+
alts = &iface->altsetting[0];
altsd = get_iface_desc(alts);
err = snd_usb_parse_audio_interface(chip, altsd->bInterfaceNumber);
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 970086015cde..8ee14f2365e7 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -92,6 +92,7 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
subs->direction = stream;
subs->dev = as->chip->dev;
subs->txfr_quirk = as->chip->txfr_quirk;
+ subs->tx_length_quirk = as->chip->tx_length_quirk;
subs->speed = snd_usb_get_speed(subs->dev);
subs->pkt_offset_adj = 0;
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 33a176437e2e..15a12715bd05 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -43,6 +43,7 @@ struct snd_usb_audio {
atomic_t usage_count;
wait_queue_head_t shutdown_wait;
unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */
+ unsigned int tx_length_quirk:1; /* Put length specifier in transfers */
int num_interfaces;
int num_suspended_intf;