summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/Makefile2
-rw-r--r--sound/ac97/bus.c1
-rw-r--r--sound/ac97_bus.c1
-rw-r--r--sound/aoa/codecs/Makefile6
-rw-r--r--sound/aoa/core/Makefile2
-rw-r--r--sound/aoa/fabrics/Makefile2
-rw-r--r--sound/aoa/soundbus/Makefile2
-rw-r--r--sound/aoa/soundbus/i2sbus/Makefile2
-rw-r--r--sound/aoa/soundbus/i2sbus/pcm.c10
-rw-r--r--sound/arm/Makefile4
-rw-r--r--sound/atmel/Makefile2
-rw-r--r--sound/core/Makefile18
-rw-r--r--sound/core/control_led.c15
-rw-r--r--sound/core/init.c23
-rw-r--r--sound/core/oss/Makefile2
-rw-r--r--sound/core/pcm_dmaengine.c1
-rw-r--r--sound/core/pcm_native.c2
-rw-r--r--sound/core/seq/Makefile14
-rw-r--r--sound/core/seq/oss/Makefile2
-rw-r--r--sound/core/seq/seq_dummy.c24
-rw-r--r--sound/core/sound_kunit.c11
-rw-r--r--sound/core/timer.c8
-rw-r--r--sound/drivers/Makefile18
-rw-r--r--sound/drivers/aloop.c9
-rw-r--r--sound/drivers/mpu401/Makefile4
-rw-r--r--sound/drivers/opl3/Makefile2
-rw-r--r--sound/drivers/opl4/Makefile4
-rw-r--r--sound/drivers/pcmtest.c1
-rw-r--r--sound/drivers/pcsp/Makefile2
-rw-r--r--sound/drivers/vx/Makefile2
-rw-r--r--sound/firewire/Makefile4
-rw-r--r--sound/firewire/amdtp-stream.c10
-rw-r--r--sound/firewire/bebob/Makefile2
-rw-r--r--sound/firewire/dice/Makefile2
-rw-r--r--sound/firewire/digi00x/Makefile2
-rw-r--r--sound/firewire/fireface/Makefile2
-rw-r--r--sound/firewire/fireworks/Makefile2
-rw-r--r--sound/firewire/motu/Makefile2
-rw-r--r--sound/firewire/oxfw/Makefile2
-rw-r--r--sound/firewire/tascam/Makefile2
-rw-r--r--sound/hda/Kconfig1
-rw-r--r--sound/hda/Makefile8
-rw-r--r--sound/hda/ext/Makefile2
-rw-r--r--sound/hda/hdac_controller.c127
-rw-r--r--sound/hda/intel-dsp-config.c43
-rw-r--r--sound/hda/intel-sdw-acpi.c2
-rw-r--r--sound/i2c/Makefile6
-rw-r--r--sound/i2c/other/Makefile10
-rw-r--r--sound/isa/Makefile18
-rw-r--r--sound/isa/ad1816a/Makefile2
-rw-r--r--sound/isa/ad1848/Makefile2
-rw-r--r--sound/isa/cs423x/Makefile4
-rw-r--r--sound/isa/es1688/Makefile4
-rw-r--r--sound/isa/galaxy/Makefile4
-rw-r--r--sound/isa/gus/Makefile12
-rw-r--r--sound/isa/msnd/Makefile6
-rw-r--r--sound/isa/opti9xx/Makefile8
-rw-r--r--sound/isa/sb/Makefile18
-rw-r--r--sound/isa/sb/emu8000_patch.c13
-rw-r--r--sound/isa/wavefront/Makefile2
-rw-r--r--sound/isa/wss/Makefile2
-rw-r--r--sound/mips/Makefile4
-rw-r--r--sound/oss/dmasound/dmasound_atari.c2
-rw-r--r--sound/oss/dmasound/dmasound_paula.c1
-rw-r--r--sound/parisc/Makefile2
-rw-r--r--sound/pci/Makefile48
-rw-r--r--sound/pci/ali5451/Makefile2
-rw-r--r--sound/pci/asihpi/Makefile2
-rw-r--r--sound/pci/au88x0/Makefile6
-rw-r--r--sound/pci/aw2/Makefile2
-rw-r--r--sound/pci/ca0106/Makefile2
-rw-r--r--sound/pci/ctxfi/Makefile2
-rw-r--r--sound/pci/echoaudio/Makefile28
-rw-r--r--sound/pci/emu10k1/Makefile6
-rw-r--r--sound/pci/emu10k1/emu10k1.c3
-rw-r--r--sound/pci/emu10k1/emu10k1_callback.c13
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c225
-rw-r--r--sound/pci/emu10k1/emu10k1_patch.c209
-rw-r--r--sound/pci/emu10k1/emumixer.c18
-rw-r--r--sound/pci/emu10k1/emuproc.c9
-rw-r--r--sound/pci/emu10k1/io.c102
-rw-r--r--sound/pci/emu10k1/memory.c55
-rw-r--r--sound/pci/hda/Kconfig2
-rw-r--r--sound/pci/hda/Makefile52
-rw-r--r--sound/pci/hda/cirrus_scodec_test.c1
-rw-r--r--sound/pci/hda/cs35l41_hda.c416
-rw-r--r--sound/pci/hda/cs35l41_hda.h6
-rw-r--r--sound/pci/hda/cs35l41_hda_property.c24
-rw-r--r--sound/pci/hda/cs35l56_hda.c8
-rw-r--r--sound/pci/hda/hda_codec.c37
-rw-r--r--sound/pci/hda/hda_component.c16
-rw-r--r--sound/pci/hda/hda_component.h7
-rw-r--r--sound/pci/hda/hda_controller.c11
-rw-r--r--sound/pci/hda/hda_controller.h1
-rw-r--r--sound/pci/hda/hda_cs_dsp_ctl.c47
-rw-r--r--sound/pci/hda/hda_generic.c4
-rw-r--r--sound/pci/hda/hda_generic.h2
-rw-r--r--sound/pci/hda/hda_intel.c55
-rw-r--r--sound/pci/hda/hda_intel_trace.h2
-rw-r--r--sound/pci/hda/hda_sysfs.c4
-rw-r--r--sound/pci/hda/patch_analog.c4
-rw-r--r--sound/pci/hda/patch_ca0132.c4
-rw-r--r--sound/pci/hda/patch_cirrus.c4
-rw-r--r--sound/pci/hda/patch_conexant.c4
-rw-r--r--sound/pci/hda/patch_cs8409.c8
-rw-r--r--sound/pci/hda/patch_hdmi.c11
-rw-r--r--sound/pci/hda/patch_realtek.c133
-rw-r--r--sound/pci/hda/patch_sigmatel.c8
-rw-r--r--sound/pci/hda/patch_via.c6
-rw-r--r--sound/pci/ice1712/Makefile6
-rw-r--r--sound/pci/korg1212/Makefile2
-rw-r--r--sound/pci/lx6464es/Makefile2
-rw-r--r--sound/pci/mixart/Makefile2
-rw-r--r--sound/pci/nm256/Makefile2
-rw-r--r--sound/pci/oxygen/Makefile8
-rw-r--r--sound/pci/pcxhr/Makefile2
-rw-r--r--sound/pci/riptide/Makefile2
-rw-r--r--sound/pci/rme9652/Makefile6
-rw-r--r--sound/pci/trident/Makefile2
-rw-r--r--sound/pci/vx222/Makefile2
-rw-r--r--sound/pci/ymfpci/Makefile2
-rw-r--r--sound/pcmcia/pdaudiocf/Makefile2
-rw-r--r--sound/pcmcia/vx/Makefile2
-rw-r--r--sound/ppc/Makefile2
-rw-r--r--sound/sh/Makefile4
-rw-r--r--sound/soc/amd/acp/acp-legacy-common.c96
-rw-r--r--sound/soc/amd/acp/acp-pci.c9
-rw-r--r--sound/soc/amd/acp/amd.h10
-rw-r--r--sound/soc/amd/acp/chip_offset_byte.h1
-rw-r--r--sound/soc/codecs/cs35l56.c13
-rw-r--r--sound/soc/codecs/da7219-aad.c6
-rw-r--r--sound/soc/generic/audio-graph-card2.c2
-rw-r--r--sound/soc/intel/avs/boards/es8336.c2
-rw-r--r--sound/soc/intel/avs/boards/nau8825.c2
-rw-r--r--sound/soc/intel/avs/boards/rt274.c3
-rw-r--r--sound/soc/intel/avs/boards/rt286.c4
-rw-r--r--sound/soc/intel/avs/boards/rt298.c4
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-tdm.c4
-rw-r--r--sound/soc/meson/Kconfig1
-rw-r--r--sound/soc/meson/axg-card.c1
-rw-r--r--sound/soc/meson/axg-fifo.c29
-rw-r--r--sound/soc/meson/axg-tdm-formatter.c40
-rw-r--r--sound/soc/meson/axg-tdm-interface.c38
-rw-r--r--sound/soc/meson/axg-tdm.h5
-rw-r--r--sound/soc/sof/amd/Makefile10
-rw-r--r--sound/soc/sof/intel/hda-bus.c5
-rw-r--r--sound/sparc/Makefile6
-rw-r--r--sound/spi/Makefile2
-rw-r--r--sound/synth/Makefile2
-rw-r--r--sound/synth/emux/Makefile2
-rw-r--r--sound/synth/emux/emux.c6
-rw-r--r--sound/synth/emux/emux_hwdep.c3
-rw-r--r--sound/synth/emux/emux_oss.c3
-rw-r--r--sound/synth/emux/emux_proc.c1
-rw-r--r--sound/synth/emux/emux_seq.c6
-rw-r--r--sound/synth/emux/soundfont.c73
-rw-r--r--sound/usb/6fire/Makefile2
-rw-r--r--sound/usb/Makefile4
-rw-r--r--sound/usb/card.c4
-rw-r--r--sound/usb/hiface/Makefile2
-rw-r--r--sound/usb/misc/Makefile2
-rw-r--r--sound/usb/mixer_quirks.c2
-rw-r--r--sound/usb/mixer_scarlett2.c2843
-rw-r--r--sound/usb/quirks-table.h38
-rw-r--r--sound/usb/quirks.c74
-rw-r--r--sound/usb/usx2y/Makefile4
-rw-r--r--sound/virtio/Makefile2
-rw-r--r--sound/x86/Makefile2
-rw-r--r--sound/xen/Makefile2
169 files changed, 4057 insertions, 1499 deletions
diff --git a/sound/Makefile b/sound/Makefile
index 04ef04b1168f..5942311a4232 100644
--- a/sound/Makefile
+++ b/sound/Makefile
@@ -17,4 +17,4 @@ ifeq ($(CONFIG_SND),y)
obj-y += last.o
endif
-soundcore-objs := sound_core.o
+soundcore-y := sound_core.o
diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c
index 5e46b972a3da..40e88d79c483 100644
--- a/sound/ac97/bus.c
+++ b/sound/ac97/bus.c
@@ -551,5 +551,6 @@ static void __exit ac97_bus_exit(void)
}
module_exit(ac97_bus_exit);
+MODULE_DESCRIPTION("AC97 bus interface");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
diff --git a/sound/ac97_bus.c b/sound/ac97_bus.c
index 7ea274c55900..1484fc178fa4 100644
--- a/sound/ac97_bus.c
+++ b/sound/ac97_bus.c
@@ -95,4 +95,5 @@ module_exit(ac97_bus_exit);
EXPORT_SYMBOL(ac97_bus_type);
+MODULE_DESCRIPTION("Legacy AC97 bus interface");
MODULE_LICENSE("GPL");
diff --git a/sound/aoa/codecs/Makefile b/sound/aoa/codecs/Makefile
index 95f4c3849d55..8feedc771bd9 100644
--- a/sound/aoa/codecs/Makefile
+++ b/sound/aoa/codecs/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
-snd-aoa-codec-onyx-objs := onyx.o
-snd-aoa-codec-tas-objs := tas.o
-snd-aoa-codec-toonie-objs := toonie.o
+snd-aoa-codec-onyx-y := onyx.o
+snd-aoa-codec-tas-y := tas.o
+snd-aoa-codec-toonie-y := toonie.o
obj-$(CONFIG_SND_AOA_ONYX) += snd-aoa-codec-onyx.o
obj-$(CONFIG_SND_AOA_TAS) += snd-aoa-codec-tas.o
diff --git a/sound/aoa/core/Makefile b/sound/aoa/core/Makefile
index 056d69683b1e..f586c340fe12 100644
--- a/sound/aoa/core/Makefile
+++ b/sound/aoa/core/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SND_AOA) += snd-aoa.o
-snd-aoa-objs := core.o \
+snd-aoa-y := core.o \
alsa.o \
gpio-pmf.o \
gpio-feature.o
diff --git a/sound/aoa/fabrics/Makefile b/sound/aoa/fabrics/Makefile
index 3f1d55f3f1fc..2c3bee6cfa2c 100644
--- a/sound/aoa/fabrics/Makefile
+++ b/sound/aoa/fabrics/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-aoa-fabric-layout-objs += layout.o
+snd-aoa-fabric-layout-y += layout.o
obj-$(CONFIG_SND_AOA_FABRIC_LAYOUT) += snd-aoa-fabric-layout.o
diff --git a/sound/aoa/soundbus/Makefile b/sound/aoa/soundbus/Makefile
index e0b61cf5518e..a10b102daf81 100644
--- a/sound/aoa/soundbus/Makefile
+++ b/sound/aoa/soundbus/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SND_AOA_SOUNDBUS) += snd-aoa-soundbus.o
-snd-aoa-soundbus-objs := core.o sysfs.o
+snd-aoa-soundbus-y := core.o sysfs.o
obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += i2sbus/
diff --git a/sound/aoa/soundbus/i2sbus/Makefile b/sound/aoa/soundbus/i2sbus/Makefile
index 1b38c87fef09..1ddaa0e17d67 100644
--- a/sound/aoa/soundbus/i2sbus/Makefile
+++ b/sound/aoa/soundbus/i2sbus/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += snd-aoa-i2sbus.o
-snd-aoa-i2sbus-objs := core.o pcm.o control.o
+snd-aoa-i2sbus-y := core.o pcm.o control.o
diff --git a/sound/aoa/soundbus/i2sbus/pcm.c b/sound/aoa/soundbus/i2sbus/pcm.c
index 07df5cc0f2d7..98b812ffbde6 100644
--- a/sound/aoa/soundbus/i2sbus/pcm.c
+++ b/sound/aoa/soundbus/i2sbus/pcm.c
@@ -255,24 +255,24 @@ static void i2sbus_wait_for_stop(struct i2sbus_dev *i2sdev,
{
unsigned long flags;
DECLARE_COMPLETION_ONSTACK(done);
- long timeout;
+ unsigned long time_left;
spin_lock_irqsave(&i2sdev->low_lock, flags);
if (pi->dbdma_ring.stopping) {
pi->stop_completion = &done;
spin_unlock_irqrestore(&i2sdev->low_lock, flags);
- timeout = wait_for_completion_timeout(&done, HZ);
+ time_left = wait_for_completion_timeout(&done, HZ);
spin_lock_irqsave(&i2sdev->low_lock, flags);
pi->stop_completion = NULL;
- if (timeout == 0) {
+ if (time_left == 0) {
/* timeout expired, stop dbdma forcefully */
printk(KERN_ERR "i2sbus_wait_for_stop: timed out\n");
/* make sure RUN, PAUSE and S0 bits are cleared */
out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16);
pi->dbdma_ring.stopping = 0;
- timeout = 10;
+ time_left = 10;
while (in_le32(&pi->dbdma->status) & ACTIVE) {
- if (--timeout <= 0)
+ if (--time_left <= 0)
break;
udelay(1);
}
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index 34c769489877..899edb4bb278 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -4,11 +4,11 @@
#
obj-$(CONFIG_SND_ARMAACI) += snd-aaci.o
-snd-aaci-objs := aaci.o
+snd-aaci-y := aaci.o
obj-$(CONFIG_SND_PXA2XX_LIB) += snd-pxa2xx-lib.o
snd-pxa2xx-lib-y := pxa2xx-pcm-lib.o
snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97) += pxa2xx-ac97-lib.o
obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o
-snd-pxa2xx-ac97-objs := pxa2xx-ac97.o
+snd-pxa2xx-ac97-y := pxa2xx-ac97.o
diff --git a/sound/atmel/Makefile b/sound/atmel/Makefile
index 57bc6f65be19..a8917d1854c7 100644
--- a/sound/atmel/Makefile
+++ b/sound/atmel/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-atmel-ac97c-objs := ac97c.o
+snd-atmel-ac97c-y := ac97c.o
obj-$(CONFIG_SND_ATMEL_AC97C) += snd-atmel-ac97c.o
diff --git a/sound/core/Makefile b/sound/core/Makefile
index b8aa886198ab..31a0623cc89d 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -24,18 +24,18 @@ snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
CFLAGS_pcm_lib.o := -I$(src)
CFLAGS_pcm_native.o := -I$(src)
-snd-pcm-dmaengine-objs := pcm_dmaengine.o
+snd-pcm-dmaengine-y := pcm_dmaengine.o
-snd-ctl-led-objs := control_led.o
-snd-rawmidi-objs := rawmidi.o
-snd-ump-objs := ump.o
+snd-ctl-led-y := control_led.o
+snd-rawmidi-y := rawmidi.o
+snd-ump-y := ump.o
snd-ump-$(CONFIG_SND_UMP_LEGACY_RAWMIDI) += ump_convert.o
-snd-timer-objs := timer.o
-snd-hrtimer-objs := hrtimer.o
-snd-hwdep-objs := hwdep.o
-snd-seq-device-objs := seq_device.o
+snd-timer-y := timer.o
+snd-hrtimer-y := hrtimer.o
+snd-hwdep-y := hwdep.o
+snd-seq-device-y := seq_device.o
-snd-compress-objs := compress_offload.o
+snd-compress-y := compress_offload.o
obj-$(CONFIG_SND) += snd.o
obj-$(CONFIG_SND_CTL_LED) += snd-ctl-led.o
diff --git a/sound/core/control_led.c b/sound/core/control_led.c
index 3d37e9fa7b9c..ac19d14bd574 100644
--- a/sound/core/control_led.c
+++ b/sound/core/control_led.c
@@ -285,25 +285,22 @@ static void snd_ctl_led_ctl_destroy(struct snd_ctl_led_ctl *lctl)
static void snd_ctl_led_clean(struct snd_card *card)
{
unsigned int group;
+ struct snd_ctl_led_ctl *lctl, *_lctl;
struct snd_ctl_led *led;
- struct snd_ctl_led_ctl *lctl;
for (group = 0; group < MAX_LED; group++) {
led = &snd_ctl_leds[group];
-repeat:
- list_for_each_entry(lctl, &led->controls, list)
- if (!card || lctl->card == card) {
+ list_for_each_entry_safe(lctl, _lctl, &led->controls, list)
+ if (!card || lctl->card == card)
snd_ctl_led_ctl_destroy(lctl);
- goto repeat;
- }
}
}
static int snd_ctl_led_reset(int card_number, unsigned int group)
{
struct snd_card *card __free(snd_card_unref) = NULL;
+ struct snd_ctl_led_ctl *lctl, *_lctl;
struct snd_ctl_led *led;
- struct snd_ctl_led_ctl *lctl;
struct snd_kcontrol_volatile *vd;
bool change = false;
@@ -315,14 +312,12 @@ static int snd_ctl_led_reset(int card_number, unsigned int group)
if (!snd_ctl_led_card_valid[card_number])
return -ENXIO;
led = &snd_ctl_leds[group];
-repeat:
- list_for_each_entry(lctl, &led->controls, list)
+ list_for_each_entry_safe(lctl, _lctl, &led->controls, list)
if (lctl->card == card) {
vd = &lctl->kctl->vd[lctl->index_offset];
vd->access &= ~group_to_access(group);
snd_ctl_led_ctl_destroy(lctl);
change = true;
- goto repeat;
}
}
if (change)
diff --git a/sound/core/init.c b/sound/core/init.c
index 4ed5037d8693..4e52bbe32786 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -50,7 +50,7 @@ MODULE_PARM_DESC(slots, "Module names assigned to the slots.");
static int module_slot_match(struct module *module, int idx)
{
int match = 1;
-#ifdef MODULE
+#ifdef CONFIG_MODULES
const char *s1, *s2;
if (!module || !*module->name || !slots[idx])
@@ -77,7 +77,7 @@ static int module_slot_match(struct module *module, int idx)
if (!c1)
break;
}
-#endif /* MODULE */
+#endif /* CONFIG_MODULES */
return match;
}
@@ -311,10 +311,8 @@ static int snd_card_init(struct snd_card *card, struct device *parent,
}
card->dev = parent;
card->number = idx;
-#ifdef MODULE
- WARN_ON(!module);
+ WARN_ON(IS_MODULE(CONFIG_SND) && !module);
card->module = module;
-#endif
INIT_LIST_HEAD(&card->devices);
init_rwsem(&card->controls_rwsem);
rwlock_init(&card->ctl_files_rwlock);
@@ -516,6 +514,14 @@ void snd_card_disconnect(struct snd_card *card)
}
}
+#ifdef CONFIG_PM
+ /* wake up sleepers here before other callbacks for avoiding potential
+ * deadlocks with other locks (e.g. in kctls);
+ * then this notifies the shutdown and sleepers would abort immediately
+ */
+ wake_up_all(&card->power_sleep);
+#endif
+
/* notify all connected devices about disconnection */
/* at this point, they cannot respond to any calls except release() */
@@ -542,10 +548,7 @@ void snd_card_disconnect(struct snd_card *card)
clear_bit(card->number, snd_cards_lock);
}
-#ifdef CONFIG_PM
- wake_up(&card->power_sleep);
snd_power_sync_ref(card);
-#endif
}
EXPORT_SYMBOL(snd_card_disconnect);
@@ -964,7 +967,7 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer)
#endif
-#ifdef MODULE
+#ifdef CONFIG_MODULES
static void snd_card_module_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -992,7 +995,7 @@ int __init snd_card_info_init(void)
if (snd_info_register(entry) < 0)
return -ENOMEM; /* freed in error path */
-#ifdef MODULE
+#ifdef CONFIG_MODULES
entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL);
if (!entry)
return -ENOMEM;
diff --git a/sound/core/oss/Makefile b/sound/core/oss/Makefile
index ae25edcc3b42..d5f48ae6ba96 100644
--- a/sound/core/oss/Makefile
+++ b/sound/core/oss/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
-snd-mixer-oss-objs := mixer_oss.o
+snd-mixer-oss-y := mixer_oss.o
snd-pcm-oss-y := pcm_oss.o
snd-pcm-oss-$(CONFIG_SND_PCM_OSS_PLUGINS) += pcm_plugin.o \
diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c
index 494ec0c207fa..12aa1cef11a1 100644
--- a/sound/core/pcm_dmaengine.c
+++ b/sound/core/pcm_dmaengine.c
@@ -470,4 +470,5 @@ int snd_dmaengine_pcm_refine_runtime_hwparams(
}
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_refine_runtime_hwparams);
+MODULE_DESCRIPTION("PCM dmaengine helper APIs");
MODULE_LICENSE("GPL");
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 0b76e76823d2..521ba56392a0 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -2416,7 +2416,7 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
static const unsigned int rates[] = {
5512, 8000, 11025, 16000, 22050, 32000, 44100,
- 48000, 64000, 88200, 96000, 176400, 192000, 352800, 384000
+ 48000, 64000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, 768000
};
const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile
index 990eec7c83ad..0904aa48d88b 100644
--- a/sound/core/seq/Makefile
+++ b/sound/core/seq/Makefile
@@ -4,17 +4,17 @@
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
-snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
+snd-seq-y := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
seq_fifo.o seq_prioq.o seq_timer.o \
seq_system.o seq_ports.o
snd-seq-$(CONFIG_SND_PROC_FS) += seq_info.o
snd-seq-$(CONFIG_SND_SEQ_UMP) += seq_ump_convert.o
-snd-seq-midi-objs := seq_midi.o
-snd-seq-midi-emul-objs := seq_midi_emul.o
-snd-seq-midi-event-objs := seq_midi_event.o
-snd-seq-dummy-objs := seq_dummy.o
-snd-seq-virmidi-objs := seq_virmidi.o
-snd-seq-ump-client-objs := seq_ump_client.o
+snd-seq-midi-y := seq_midi.o
+snd-seq-midi-emul-y := seq_midi_emul.o
+snd-seq-midi-event-y := seq_midi_event.o
+snd-seq-dummy-y := seq_dummy.o
+snd-seq-virmidi-y := seq_virmidi.o
+snd-seq-ump-client-y := seq_ump_client.o
obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o
obj-$(CONFIG_SND_SEQUENCER_OSS) += oss/
diff --git a/sound/core/seq/oss/Makefile b/sound/core/seq/oss/Makefile
index f1a60878549a..4e4741834208 100644
--- a/sound/core/seq/oss/Makefile
+++ b/sound/core/seq/oss/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
-snd-seq-oss-objs := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \
+snd-seq-oss-y := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \
seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \
seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o
diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c
index 9308194b2d9a..783fc72c2ef6 100644
--- a/sound/core/seq/seq_dummy.c
+++ b/sound/core/seq/seq_dummy.c
@@ -58,6 +58,12 @@ MODULE_PARM_DESC(ports, "number of ports to be created");
module_param(duplex, bool, 0444);
MODULE_PARM_DESC(duplex, "create DUPLEX ports");
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+static int ump;
+module_param(ump, int, 0444);
+MODULE_PARM_DESC(ump, "UMP conversion (0: no convert, 1: MIDI 1.0, 2: MIDI 2.0)");
+#endif
+
struct snd_seq_dummy_port {
int client;
int port;
@@ -152,7 +158,9 @@ static int __init
register_client(void)
{
struct snd_seq_dummy_port *rec1, *rec2;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
struct snd_seq_client *client;
+#endif
int i;
if (ports < 1) {
@@ -166,12 +174,24 @@ register_client(void)
if (my_client < 0)
return my_client;
- /* don't convert events but just pass-through */
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
client = snd_seq_kernel_client_get(my_client);
if (!client)
return -EINVAL;
- client->filter = SNDRV_SEQ_FILTER_NO_CONVERT;
+ switch (ump) {
+ case 1:
+ client->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_1_0;
+ break;
+ case 2:
+ client->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_2_0;
+ break;
+ default:
+ /* don't convert events but just pass-through */
+ client->filter = SNDRV_SEQ_FILTER_NO_CONVERT;
+ break;
+ }
snd_seq_kernel_client_put(client);
+#endif
/* create ports */
for (i = 0; i < ports; i++) {
diff --git a/sound/core/sound_kunit.c b/sound/core/sound_kunit.c
index eb90f62228c0..bfed1a25fc8f 100644
--- a/sound/core/sound_kunit.c
+++ b/sound/core/sound_kunit.c
@@ -45,7 +45,7 @@ struct avail_test_data {
snd_pcm_uframes_t expected_avail;
};
-static struct snd_format_test_data valid_fmt[] = {
+static const struct snd_format_test_data valid_fmt[] = {
DEFINE_FORMAT(S8, 8, 8, -1, 1, SILENCE()),
DEFINE_FORMAT(U8, 8, 8, -1, 0, SILENCE(0x80)),
DEFINE_FORMAT(S16_LE, 16, 16, 1, 1, SILENCE()),
@@ -154,7 +154,7 @@ static void test_format_endianness(struct kunit *test)
KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(WRONG_FORMAT_2), -EINVAL);
}
-static void _test_fill_silence(struct kunit *test, struct snd_format_test_data *data,
+static void _test_fill_silence(struct kunit *test, const struct snd_format_test_data *data,
u8 *buffer, size_t samples_count)
{
size_t sample_bytes = data->physical_bits >> 3;
@@ -167,7 +167,7 @@ static void _test_fill_silence(struct kunit *test, struct snd_format_test_data *
static void test_format_fill_silence(struct kunit *test)
{
- u32 buf_samples[] = { 10, 20, 32, 64, 129, SILENCE_BUFFER_MAX_FRAMES };
+ static const u32 buf_samples[] = { 10, 20, 32, 64, 129, SILENCE_BUFFER_MAX_FRAMES };
u8 *buffer;
u32 i, j;
@@ -191,7 +191,7 @@ static snd_pcm_uframes_t calculate_boundary(snd_pcm_uframes_t buffer_size)
return boundary;
}
-static struct avail_test_data p_avail_data[] = {
+static const struct avail_test_data p_avail_data[] = {
/* buf_size + hw_ptr < appl_ptr => avail = buf_size + hw_ptr - appl_ptr + boundary */
{ 128, 1000, 1129, 1073741824UL - 1 },
/*
@@ -220,7 +220,7 @@ static void test_playback_avail(struct kunit *test)
}
}
-static struct avail_test_data c_avail_data[] = {
+static const struct avail_test_data c_avail_data[] = {
/* hw_ptr - appl_ptr < 0 => avail = hw_ptr - appl_ptr + boundary */
{ 128, 1000, 1001, 1073741824UL - 1 },
/* standard case: avail = hw_ptr - appl_ptr */
@@ -308,5 +308,6 @@ static struct kunit_suite sound_utils_suite = {
};
kunit_test_suite(sound_utils_suite);
+MODULE_DESCRIPTION("Sound core KUnit test");
MODULE_AUTHOR("Ivan Orlov");
MODULE_LICENSE("GPL");
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 4d2ee99c12a3..d104adc75a8b 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -544,6 +544,14 @@ static int snd_timer_start1(struct snd_timer_instance *timeri,
SNDRV_TIMER_IFLG_START))
return -EBUSY;
+ /* check the actual time for the start tick;
+ * bail out as error if it's way too low (< 100us)
+ */
+ if (start) {
+ if ((u64)snd_timer_hw_resolution(timer) * ticks < 100000)
+ return -EINVAL;
+ }
+
if (start)
timeri->ticks = timeri->cticks = ticks;
else if (!timeri->cticks)
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index 2c0c7092d396..a08bdd70ec9c 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -4,15 +4,15 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-dummy-objs := dummy.o
-snd-aloop-objs := aloop.o
-snd-mtpav-objs := mtpav.o
-snd-mts64-objs := mts64.o
-snd-pcmtest-objs := pcmtest.o
-snd-portman2x4-objs := portman2x4.o
-snd-serial-u16550-objs := serial-u16550.o
-snd-serial-generic-objs := serial-generic.o
-snd-virmidi-objs := virmidi.o
+snd-dummy-y := dummy.o
+snd-aloop-y := aloop.o
+snd-mtpav-y := mtpav.o
+snd-mts64-y := mts64.o
+snd-pcmtest-y := pcmtest.o
+snd-portman2x4-y := portman2x4.o
+snd-serial-u16550-y := serial-u16550.o
+snd-serial-generic-y := serial-generic.o
+snd-virmidi-y := virmidi.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index 892c4e29c0a3..d6dd4b8c750a 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -927,10 +927,13 @@ static const struct snd_pcm_hardware loopback_pcm_hardware =
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |
- SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE),
- .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,
+ SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE |
+ SNDRV_PCM_FMTBIT_DSD_U8 |
+ SNDRV_PCM_FMTBIT_DSD_U16_LE | SNDRV_PCM_FMTBIT_DSD_U16_BE |
+ SNDRV_PCM_FMTBIT_DSD_U32_LE | SNDRV_PCM_FMTBIT_DSD_U32_BE),
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_768000,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 768000,
.channels_min = 1,
.channels_max = 32,
.buffer_bytes_max = 2 * 1024 * 1024,
diff --git a/sound/drivers/mpu401/Makefile b/sound/drivers/mpu401/Makefile
index 3dfd5b374c4f..0a96e238ee92 100644
--- a/sound/drivers/mpu401/Makefile
+++ b/sound/drivers/mpu401/Makefile
@@ -4,8 +4,8 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-mpu401-objs := mpu401.o
-snd-mpu401-uart-objs := mpu401_uart.o
+snd-mpu401-y := mpu401.o
+snd-mpu401-uart-y := mpu401_uart.o
obj-$(CONFIG_SND_MPU401_UART) += snd-mpu401-uart.o
diff --git a/sound/drivers/opl3/Makefile b/sound/drivers/opl3/Makefile
index 83bca9f1fbdf..cf4826308365 100644
--- a/sound/drivers/opl3/Makefile
+++ b/sound/drivers/opl3/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-opl3-lib-objs := opl3_lib.o opl3_synth.o
+snd-opl3-lib-y := opl3_lib.o opl3_synth.o
snd-opl3-synth-y := opl3_seq.o opl3_midi.o opl3_drums.o
ifneq ($(CONFIG_SND_SEQUENCER_OSS),)
snd-opl3-synth-y += opl3_oss.o
diff --git a/sound/drivers/opl4/Makefile b/sound/drivers/opl4/Makefile
index 6e86a4092b4c..a841630b45c2 100644
--- a/sound/drivers/opl4/Makefile
+++ b/sound/drivers/opl4/Makefile
@@ -4,9 +4,9 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o
+snd-opl4-lib-y := opl4_lib.o opl4_mixer.o
snd-opl4-lib-$(CONFIG_SND_PROC_FS) += opl4_proc.o
-snd-opl4-synth-objs := opl4_seq.o opl4_synth.o yrw801.o
+snd-opl4-synth-y := opl4_seq.o opl4_synth.o yrw801.o
obj-$(CONFIG_SND_OPL4_LIB) += snd-opl4-lib.o
obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-opl4-synth.o
diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c
index b8bff5522bce..21cefaf5419a 100644
--- a/sound/drivers/pcmtest.c
+++ b/sound/drivers/pcmtest.c
@@ -772,6 +772,7 @@ static void __exit mod_exit(void)
platform_device_unregister(&pcmtst_pdev);
}
+MODULE_DESCRIPTION("Virtual ALSA driver for PCM testing/fuzzing");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ivan Orlov");
module_init(mod_init);
diff --git a/sound/drivers/pcsp/Makefile b/sound/drivers/pcsp/Makefile
index 77dc0ee1b598..309c09497261 100644
--- a/sound/drivers/pcsp/Makefile
+++ b/sound/drivers/pcsp/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-pcsp-objs := pcsp.o pcsp_lib.o pcsp_mixer.o pcsp_input.o
+snd-pcsp-y := pcsp.o pcsp_lib.o pcsp_mixer.o pcsp_input.o
obj-$(CONFIG_SND_PCSP) += snd-pcsp.o
diff --git a/sound/drivers/vx/Makefile b/sound/drivers/vx/Makefile
index d9f9ac670378..ae1b3e09283f 100644
--- a/sound/drivers/vx/Makefile
+++ b/sound/drivers/vx/Makefile
@@ -4,6 +4,6 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-vx-lib-objs := vx_core.o vx_hwdep.o vx_pcm.o vx_mixer.o vx_cmd.o vx_uer.o
+snd-vx-lib-y := vx_core.o vx_hwdep.o vx_pcm.o vx_mixer.o vx_cmd.o vx_uer.o
obj-$(CONFIG_SND_VX_LIB) += snd-vx-lib.o
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index 44a7b510b75b..45018a5c224f 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -2,9 +2,9 @@
# To find a header included by define_trace.h.
CFLAGS_amdtp-stream.o := -I$(src)
-snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
+snd-firewire-lib-y := lib.o iso-resources.o packets-buffer.o \
fcp.o cmp.o amdtp-stream.o amdtp-am824.o
-snd-isight-objs := isight.o
+snd-isight-y := isight.o
obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
obj-$(CONFIG_SND_DICE) += dice/
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index c9f153f85ae6..d35d0a420ee0 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -1180,13 +1180,11 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_
(void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time);
for (i = 0; i < packets; ++i) {
- struct {
- struct fw_iso_packet params;
- __be32 header[CIP_HEADER_QUADLETS];
- } template = { {0}, {0} };
+ DEFINE_FLEX(struct fw_iso_packet, template, header,
+ header_length, CIP_HEADER_QUADLETS);
bool sched_irq = false;
- build_it_pkt_header(s, desc->cycle, &template.params, pkt_header_length,
+ build_it_pkt_header(s, desc->cycle, template, pkt_header_length,
desc->data_blocks, desc->data_block_counter,
desc->syt, i, curr_cycle_time);
@@ -1198,7 +1196,7 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_
}
}
- if (queue_out_packet(s, &template.params, sched_irq) < 0) {
+ if (queue_out_packet(s, template, sched_irq) < 0) {
cancel_stream(s);
return;
}
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
index 14bc84c51ef5..b913e805bd7a 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
+snd-bebob-y := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
bebob_pcm.o bebob_hwdep.o bebob_terratec.o \
bebob_yamaha_terratec.o bebob_focusrite.o bebob_maudio.o \
bebob.o
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile
index bac8712f9014..36e25a3cf3c6 100644
--- a/sound/firewire/dice/Makefile
+++ b/sound/firewire/dice/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
+snd-dice-y := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
dice-pcm.o dice-hwdep.o dice.o dice-tcelectronic.o \
dice-alesis.o dice-extension.o dice-mytek.o dice-presonus.o \
dice-harman.o dice-focusrite.o dice-weiss.o
diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile
index 8add0cd9af3a..6dc18bd2e186 100644
--- a/sound/firewire/digi00x/Makefile
+++ b/sound/firewire/digi00x/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \
+snd-firewire-digi00x-y := 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/fireface/Makefile b/sound/firewire/fireface/Makefile
index 3aef221ce4b0..b397d95877a0 100644
--- a/sound/firewire/fireface/Makefile
+++ b/sound/firewire/fireface/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \
+snd-fireface-y := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \
ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-former.o \
ff-protocol-latter.o
obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
index 3386121b2a04..baaf3066c9b1 100644
--- a/sound/firewire/fireworks/Makefile
+++ b/sound/firewire/fireworks/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
+snd-fireworks-y := fireworks_transaction.o fireworks_command.o \
fireworks_stream.o fireworks_proc.o fireworks_midi.o \
fireworks_pcm.o fireworks_hwdep.o fireworks.o
obj-$(CONFIG_SND_FIREWORKS) += snd-fireworks.o
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index 3bef2a0b1e2e..df0fe886dbc0 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS_amdtp-motu.o := -I$(src)
-snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
+snd-firewire-motu-y := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o \
motu-protocol-v2.o motu-protocol-v3.o \
motu-protocol-v1.o motu-register-dsp-message-parser.o \
diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
index 669d1e8238df..9ac8893a926f 100644
--- a/sound/firewire/oxfw/Makefile
+++ b/sound/firewire/oxfw/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-pcm.o oxfw-proc.o \
+snd-oxfw-y := oxfw-command.o oxfw-stream.o oxfw-pcm.o oxfw-proc.o \
oxfw-midi.o oxfw-hwdep.o oxfw-spkr.o oxfw-scs1x.o oxfw.o
obj-$(CONFIG_SND_OXFW) += snd-oxfw.o
diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile
index a1d21f244d64..43fed14cf172 100644
--- a/sound/firewire/tascam/Makefile
+++ b/sound/firewire/tascam/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \
+snd-firewire-tascam-y := 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/hda/Kconfig b/sound/hda/Kconfig
index 741179ccbd4e..e2ac247fc1d4 100644
--- a/sound/hda/Kconfig
+++ b/sound/hda/Kconfig
@@ -42,6 +42,7 @@ config SND_INTEL_NHLT
config SND_INTEL_DSP_CONFIG
tristate
+ select ACPI_NHLT if ACPI
select SND_INTEL_NHLT if ACPI
select SND_INTEL_SOUNDWIRE_ACPI if ACPI
# this config should be selected only for Intel DSP platforms.
diff --git a/sound/hda/Makefile b/sound/hda/Makefile
index 78f487a635f8..83cceafe0d4c 100644
--- a/sound/hda/Makefile
+++ b/sound/hda/Makefile
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
-snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \
+snd-hda-core-y := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \
hdac_regmap.o hdac_controller.o hdac_stream.o array.o hdmi_chmap.o
-snd-hda-core-objs += trace.o
+snd-hda-core-y += trace.o
CFLAGS_trace.o := -I$(src)
# for sync with i915 gfx driver
@@ -14,9 +14,9 @@ obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
#extended hda
obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/
-snd-intel-dspcfg-objs := intel-dsp-config.o
+snd-intel-dspcfg-y := intel-dsp-config.o
snd-intel-dspcfg-$(CONFIG_SND_INTEL_NHLT) += intel-nhlt.o
obj-$(CONFIG_SND_INTEL_DSP_CONFIG) += snd-intel-dspcfg.o
-snd-intel-sdw-acpi-objs := intel-sdw-acpi.o
+snd-intel-sdw-acpi-y := intel-sdw-acpi.o
obj-$(CONFIG_SND_INTEL_SOUNDWIRE_ACPI) += snd-intel-sdw-acpi.o
diff --git a/sound/hda/ext/Makefile b/sound/hda/ext/Makefile
index 154779bdc0ba..05883fb28d28 100644
--- a/sound/hda/ext/Makefile
+++ b/sound/hda/ext/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-hda-ext-core-objs := hdac_ext_bus.o hdac_ext_controller.o hdac_ext_stream.o
+snd-hda-ext-core-y := hdac_ext_bus.o hdac_ext_controller.o hdac_ext_stream.o
obj-$(CONFIG_SND_HDA_EXT_CORE) += snd-hda-ext-core.o
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
index 7f3a000fab0c..b5c833b9f8b9 100644
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/hdac_controller.c
@@ -62,7 +62,8 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
azx_clear_corbrp(bus);
/* enable corb dma */
- snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN);
+ if (!bus->use_pio_for_commands)
+ snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN);
/* RIRB set up */
bus->rirb.addr = bus->rb.addr + 2048;
@@ -135,14 +136,94 @@ static unsigned int azx_command_addr(u32 cmd)
return addr;
}
+/* receive an Immediate Response with PIO */
+static int snd_hdac_bus_wait_for_pio_response(struct hdac_bus *bus,
+ unsigned int addr)
+{
+ int timeout = 50;
+
+ while (timeout--) {
+ /* check IRV bit */
+ if (snd_hdac_chip_readw(bus, IRS) & AZX_IRS_VALID) {
+ /* reuse rirb.res as the response return value */
+ bus->rirb.res[addr] = snd_hdac_chip_readl(bus, IR);
+ return 0;
+ }
+ udelay(1);
+ }
+
+ dev_dbg_ratelimited(bus->dev, "get_response_pio timeout: IRS=%#x\n",
+ snd_hdac_chip_readw(bus, IRS));
+
+ bus->rirb.res[addr] = -1;
+
+ return -EIO;
+}
+
/**
- * snd_hdac_bus_send_cmd - send a command verb via CORB
+ * snd_hdac_bus_send_cmd_pio - send a command verb via Immediate Command
* @bus: HD-audio core bus
* @val: encoded verb value to send
*
* Returns zero for success or a negative error code.
*/
-int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val)
+static int snd_hdac_bus_send_cmd_pio(struct hdac_bus *bus, unsigned int val)
+{
+ unsigned int addr = azx_command_addr(val);
+ int timeout = 50;
+ int ret = -EIO;
+
+ spin_lock_irq(&bus->reg_lock);
+
+ while (timeout--) {
+ /* check ICB bit */
+ if (!((snd_hdac_chip_readw(bus, IRS) & AZX_IRS_BUSY))) {
+ /* Clear IRV bit */
+ snd_hdac_chip_updatew(bus, IRS, AZX_IRS_VALID, AZX_IRS_VALID);
+ snd_hdac_chip_writel(bus, IC, val);
+ /* Set ICB bit */
+ snd_hdac_chip_updatew(bus, IRS, AZX_IRS_BUSY, AZX_IRS_BUSY);
+
+ ret = snd_hdac_bus_wait_for_pio_response(bus, addr);
+ goto out;
+ }
+ udelay(1);
+ }
+
+ dev_dbg_ratelimited(bus->dev, "send_cmd_pio timeout: IRS=%#x, val=%#x\n",
+ snd_hdac_chip_readw(bus, IRS), val);
+
+out:
+ spin_unlock_irq(&bus->reg_lock);
+
+ return ret;
+}
+
+/**
+ * snd_hdac_bus_get_response_pio - receive a response via Immediate Response
+ * @bus: HD-audio core bus
+ * @addr: codec address
+ * @res: pointer to store the value, NULL when not needed
+ *
+ * Returns zero if a value is read, or a negative error code.
+ */
+static int snd_hdac_bus_get_response_pio(struct hdac_bus *bus,
+ unsigned int addr, unsigned int *res)
+{
+ if (res)
+ *res = bus->rirb.res[addr];
+
+ return 0;
+}
+
+/**
+ * snd_hdac_bus_send_cmd_corb - send a command verb via CORB
+ * @bus: HD-audio core bus
+ * @val: encoded verb value to send
+ *
+ * Returns zero for success or a negative error code.
+ */
+static int snd_hdac_bus_send_cmd_corb(struct hdac_bus *bus, unsigned int val)
{
unsigned int addr = azx_command_addr(val);
unsigned int wp, rp;
@@ -176,7 +257,6 @@ int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val)
return 0;
}
-EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd);
#define AZX_RIRB_EX_UNSOL_EV (1<<4)
@@ -234,15 +314,15 @@ void snd_hdac_bus_update_rirb(struct hdac_bus *bus)
EXPORT_SYMBOL_GPL(snd_hdac_bus_update_rirb);
/**
- * snd_hdac_bus_get_response - receive a response via RIRB
+ * snd_hdac_bus_get_response_rirb - receive a response via RIRB
* @bus: HD-audio core bus
* @addr: codec address
* @res: pointer to store the value, NULL when not needed
*
* Returns zero if a value is read, or a negative error code.
*/
-int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
- unsigned int *res)
+static int snd_hdac_bus_get_response_rirb(struct hdac_bus *bus,
+ unsigned int addr, unsigned int *res)
{
unsigned long timeout;
unsigned long loopcounter;
@@ -293,6 +373,39 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
return -EIO;
}
+
+/**
+ * snd_hdac_bus_send_cmd - send a command verb via CORB or PIO
+ * @bus: HD-audio core bus
+ * @val: encoded verb value to send
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val)
+{
+ if (bus->use_pio_for_commands)
+ return snd_hdac_bus_send_cmd_pio(bus, val);
+
+ return snd_hdac_bus_send_cmd_corb(bus, val);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd);
+
+/**
+ * snd_hdac_bus_get_response - receive a response via RIRB or PIO
+ * @bus: HD-audio core bus
+ * @addr: codec address
+ * @res: pointer to store the value, NULL when not needed
+ *
+ * Returns zero if a value is read, or a negative error code.
+ */
+int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
+ unsigned int *res)
+{
+ if (bus->use_pio_for_commands)
+ return snd_hdac_bus_get_response_pio(bus, addr, res);
+
+ return snd_hdac_bus_get_response_rirb(bus, addr, res);
+}
EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response);
#define HDAC_MAX_CAPS 10
diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c
index 6a384b922e4f..cfdb1b73c88c 100644
--- a/sound/hda/intel-dsp-config.c
+++ b/sound/hda/intel-dsp-config.c
@@ -13,6 +13,8 @@
#include <sound/intel-nhlt.h>
#include <sound/soc-acpi.h>
+#include <acpi/nhlt.h>
+
static int dsp_driver;
module_param(dsp_driver, int, 0444);
@@ -557,9 +559,32 @@ static const struct config_entry *snd_intel_dsp_find_config
if (table->codec_hid) {
int i;
- for (i = 0; i < table->codec_hid->num_codecs; i++)
- if (acpi_dev_present(table->codec_hid->codecs[i], NULL, -1))
+ for (i = 0; i < table->codec_hid->num_codecs; i++) {
+ struct nhlt_acpi_table *nhlt;
+ bool ssp_found = false;
+
+ if (!acpi_dev_present(table->codec_hid->codecs[i], NULL, -1))
+ continue;
+
+ nhlt = intel_nhlt_init(&pci->dev);
+ if (!nhlt) {
+ dev_warn(&pci->dev, "%s: NHLT table not found, skipped HID %s\n",
+ __func__, table->codec_hid->codecs[i]);
+ continue;
+ }
+
+ if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_SSP) &&
+ intel_nhlt_ssp_endpoint_mask(nhlt, NHLT_DEVICE_I2S))
+ ssp_found = true;
+
+ intel_nhlt_free(nhlt);
+
+ if (ssp_found)
break;
+
+ dev_warn(&pci->dev, "%s: no valid SSP found for HID %s, skipped\n",
+ __func__, table->codec_hid->codecs[i]);
+ }
if (i == table->codec_hid->num_codecs)
continue;
}
@@ -570,15 +595,15 @@ static const struct config_entry *snd_intel_dsp_find_config
static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
{
- struct nhlt_acpi_table *nhlt;
int ret = 0;
- nhlt = intel_nhlt_init(&pci->dev);
- if (nhlt) {
- if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_DMIC))
- ret = 1;
- intel_nhlt_free(nhlt);
- }
+ acpi_nhlt_get_gbl_table();
+
+ if (acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, -1, -1))
+ ret = 1;
+
+ acpi_nhlt_put_gbl_table();
+
return ret;
}
diff --git a/sound/hda/intel-sdw-acpi.c b/sound/hda/intel-sdw-acpi.c
index 5f60658c6051..d7417a40392b 100644
--- a/sound/hda/intel-sdw-acpi.c
+++ b/sound/hda/intel-sdw-acpi.c
@@ -45,6 +45,8 @@ static bool is_link_enabled(struct fwnode_handle *fw_node, u8 idx)
"intel-quirk-mask",
&quirk_mask);
+ fwnode_handle_put(link);
+
if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)
return false;
diff --git a/sound/i2c/Makefile b/sound/i2c/Makefile
index 09978855e08e..c827f9f70a33 100644
--- a/sound/i2c/Makefile
+++ b/sound/i2c/Makefile
@@ -4,9 +4,9 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-i2c-objs := i2c.o
-snd-cs8427-objs := cs8427.o
-snd-tea6330t-objs := tea6330t.o
+snd-i2c-y := i2c.o
+snd-cs8427-y := cs8427.o
+snd-tea6330t-y := tea6330t.o
obj-$(CONFIG_SND) += other/
diff --git a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile
index 1a4ce1236146..0a2c0d147ab8 100644
--- a/sound/i2c/other/Makefile
+++ b/sound/i2c/other/Makefile
@@ -4,11 +4,11 @@
# Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ak4114-objs := ak4114.o
-snd-ak4117-objs := ak4117.o
-snd-ak4113-objs := ak4113.o
-snd-ak4xxx-adda-objs := ak4xxx-adda.o
-snd-pt2258-objs := pt2258.o
+snd-ak4114-y := ak4114.o
+snd-ak4117-y := ak4117.o
+snd-ak4113-y := ak4113.o
+snd-ak4xxx-adda-y := ak4xxx-adda.o
+snd-pt2258-y := pt2258.o
# Module Dependency
obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
diff --git a/sound/isa/Makefile b/sound/isa/Makefile
index 5eaddbf4a712..2135d68a15ac 100644
--- a/sound/isa/Makefile
+++ b/sound/isa/Makefile
@@ -4,15 +4,15 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-adlib-objs := adlib.o
-snd-als100-objs := als100.o
-snd-azt2320-objs := azt2320.o
-snd-cmi8328-objs := cmi8328.o
-snd-cmi8330-objs := cmi8330.o
-snd-es18xx-objs := es18xx.o
-snd-opl3sa2-objs := opl3sa2.o
-snd-sc6000-objs := sc6000.o
-snd-sscape-objs := sscape.o
+snd-adlib-y := adlib.o
+snd-als100-y := als100.o
+snd-azt2320-y := azt2320.o
+snd-cmi8328-y := cmi8328.o
+snd-cmi8330-y := cmi8330.o
+snd-es18xx-y := es18xx.o
+snd-opl3sa2-y := opl3sa2.o
+snd-sc6000-y := sc6000.o
+snd-sscape-y := sscape.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ADLIB) += snd-adlib.o
diff --git a/sound/isa/ad1816a/Makefile b/sound/isa/ad1816a/Makefile
index 93def7f16933..573325228534 100644
--- a/sound/isa/ad1816a/Makefile
+++ b/sound/isa/ad1816a/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ad1816a-objs := ad1816a.o ad1816a_lib.o
+snd-ad1816a-y := ad1816a.o ad1816a_lib.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AD1816A) += snd-ad1816a.o
diff --git a/sound/isa/ad1848/Makefile b/sound/isa/ad1848/Makefile
index 4eab89bbc845..5fdfc1c9f059 100644
--- a/sound/isa/ad1848/Makefile
+++ b/sound/isa/ad1848/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ad1848-objs := ad1848.o
+snd-ad1848-y := ad1848.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AD1848) += snd-ad1848.o
diff --git a/sound/isa/cs423x/Makefile b/sound/isa/cs423x/Makefile
index 91c6b8d64424..013a777d23fa 100644
--- a/sound/isa/cs423x/Makefile
+++ b/sound/isa/cs423x/Makefile
@@ -4,8 +4,8 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-cs4231-objs := cs4231.o
-snd-cs4236-objs := cs4236.o cs4236_lib.o
+snd-cs4231-y := cs4231.o
+snd-cs4236-y := cs4236.o cs4236_lib.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_CS4231) += snd-cs4231.o
diff --git a/sound/isa/es1688/Makefile b/sound/isa/es1688/Makefile
index c683ac36c50e..7d6c44a8eaad 100644
--- a/sound/isa/es1688/Makefile
+++ b/sound/isa/es1688/Makefile
@@ -4,8 +4,8 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-es1688-lib-objs := es1688_lib.o
-snd-es1688-objs := es1688.o
+snd-es1688-lib-y := es1688_lib.o
+snd-es1688-y := es1688.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ES1688) += snd-es1688.o snd-es1688-lib.o
diff --git a/sound/isa/galaxy/Makefile b/sound/isa/galaxy/Makefile
index ff861f238093..2dbd519860a6 100644
--- a/sound/isa/galaxy/Makefile
+++ b/sound/isa/galaxy/Makefile
@@ -4,8 +4,8 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
#
-snd-azt1605-objs := azt1605.o
-snd-azt2316-objs := azt2316.o
+snd-azt1605-y := azt1605.o
+snd-azt2316-y := azt2316.o
obj-$(CONFIG_SND_AZT1605) += snd-azt1605.o
obj-$(CONFIG_SND_AZT2316) += snd-azt2316.o
diff --git a/sound/isa/gus/Makefile b/sound/isa/gus/Makefile
index c6f32ffd3420..4924c1904fa4 100644
--- a/sound/isa/gus/Makefile
+++ b/sound/isa/gus/Makefile
@@ -4,18 +4,18 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-gus-lib-objs := gus_main.o \
+snd-gus-lib-y := gus_main.o \
gus_io.o gus_irq.o gus_timer.o \
gus_mem.o gus_mem_proc.o gus_dram.o gus_dma.o gus_volume.o \
gus_pcm.o gus_mixer.o \
gus_uart.o \
gus_reset.o
-snd-gusclassic-objs := gusclassic.o
-snd-gusextreme-objs := gusextreme.o
-snd-gusmax-objs := gusmax.o
-snd-interwave-objs := interwave.o
-snd-interwave-stb-objs := interwave-stb.o
+snd-gusclassic-y := gusclassic.o
+snd-gusextreme-y := gusextreme.o
+snd-gusmax-y := gusmax.o
+snd-interwave-y := interwave.o
+snd-interwave-stb-y := interwave-stb.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o
diff --git a/sound/isa/msnd/Makefile b/sound/isa/msnd/Makefile
index ec231a7b1d5e..5f8d6b472722 100644
--- a/sound/isa/msnd/Makefile
+++ b/sound/isa/msnd/Makefile
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
-snd-msnd-lib-objs := msnd.o msnd_midi.o msnd_pinnacle_mixer.o
-snd-msnd-pinnacle-objs := msnd_pinnacle.o
-snd-msnd-classic-objs := msnd_classic.o
+snd-msnd-lib-y := msnd.o msnd_midi.o msnd_pinnacle_mixer.o
+snd-msnd-pinnacle-y := msnd_pinnacle.o
+snd-msnd-classic-y := msnd_classic.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_MSND_PINNACLE) += snd-msnd-pinnacle.o snd-msnd-lib.o
diff --git a/sound/isa/opti9xx/Makefile b/sound/isa/opti9xx/Makefile
index a9dcdeb502bd..44a2fb220456 100644
--- a/sound/isa/opti9xx/Makefile
+++ b/sound/isa/opti9xx/Makefile
@@ -4,10 +4,10 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-opti92x-ad1848-objs := opti92x-ad1848.o
-snd-opti92x-cs4231-objs := opti92x-cs4231.o
-snd-opti93x-objs := opti93x.o
-snd-miro-objs := miro.o
+snd-opti92x-ad1848-y := opti92x-ad1848.o
+snd-opti92x-cs4231-y := opti92x-cs4231.o
+snd-opti93x-y := opti93x.o
+snd-miro-y := miro.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opti92x-ad1848.o
diff --git a/sound/isa/sb/Makefile b/sound/isa/sb/Makefile
index f174a5b3c8e4..96a926feb17a 100644
--- a/sound/isa/sb/Makefile
+++ b/sound/isa/sb/Makefile
@@ -4,15 +4,15 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-sb-common-objs := sb_common.o sb_mixer.o
-snd-sb8-dsp-objs := sb8_main.o sb8_midi.o
-snd-sb16-dsp-objs := sb16_main.o
-snd-sb16-csp-objs := sb16_csp.o
-snd-sb8-objs := sb8.o
-snd-sb16-objs := sb16.o
-snd-sbawe-objs := sbawe.o emu8000.o
-snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o
-snd-jazz16-objs := jazz16.o
+snd-sb-common-y := sb_common.o sb_mixer.o
+snd-sb8-dsp-y := sb8_main.o sb8_midi.o
+snd-sb16-dsp-y := sb16_main.o
+snd-sb16-csp-y := sb16_csp.o
+snd-sb8-y := sb8.o
+snd-sb16-y := sb16.o
+snd-sbawe-y := sbawe.o emu8000.o
+snd-emu8000-synth-y := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o
+snd-jazz16-y := jazz16.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_SB_COMMON) += snd-sb-common.o
diff --git a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c
index 8c1e7f2bfc34..ab4f988f080d 100644
--- a/sound/isa/sb/emu8000_patch.c
+++ b/sound/isa/sb/emu8000_patch.c
@@ -148,13 +148,6 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
if (snd_BUG_ON(!sp))
return -EINVAL;
- if (sp->v.size == 0)
- return 0;
-
- /* be sure loop points start < end */
- if (sp->v.loopstart > sp->v.loopend)
- swap(sp->v.loopstart, sp->v.loopend);
-
/* compute true data size to be loaded */
truesize = sp->v.size;
if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
@@ -177,12 +170,6 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
return -EFAULT;
}
- /* recalculate address offset */
- sp->v.end -= sp->v.start;
- sp->v.loopstart -= sp->v.start;
- sp->v.loopend -= sp->v.start;
- sp->v.start = 0;
-
/* dram position (in word) -- mem_offset is byte */
dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1);
dram_start = dram_offset;
diff --git a/sound/isa/wavefront/Makefile b/sound/isa/wavefront/Makefile
index b8406dce81f5..3ba85fb2e6cd 100644
--- a/sound/isa/wavefront/Makefile
+++ b/sound/isa/wavefront/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-wavefront-objs := wavefront.o wavefront_fx.o wavefront_synth.o wavefront_midi.o
+snd-wavefront-y := wavefront.o wavefront_fx.o wavefront_synth.o wavefront_midi.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_WAVEFRONT) += snd-wavefront.o
diff --git a/sound/isa/wss/Makefile b/sound/isa/wss/Makefile
index 34d0636b3dc3..f23e71d0d5d4 100644
--- a/sound/isa/wss/Makefile
+++ b/sound/isa/wss/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 2008 by Jaroslav Kysela <perex@perex.cz>
#
-snd-wss-lib-objs := wss_lib.o
+snd-wss-lib-y := wss_lib.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_WSS_LIB) += snd-wss-lib.o
diff --git a/sound/mips/Makefile b/sound/mips/Makefile
index 7c86268b2bf3..bfbf3bda487b 100644
--- a/sound/mips/Makefile
+++ b/sound/mips/Makefile
@@ -3,8 +3,8 @@
# Makefile for ALSA
#
-snd-sgi-o2-objs := sgio2audio.o ad1843.o
-snd-sgi-hal2-objs := hal2.o
+snd-sgi-o2-y := sgio2audio.o ad1843.o
+snd-sgi-hal2-y := hal2.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o
diff --git a/sound/oss/dmasound/dmasound_atari.c b/sound/oss/dmasound/dmasound_atari.c
index 81c6a9830727..6188469de8af 100644
--- a/sound/oss/dmasound/dmasound_atari.c
+++ b/sound/oss/dmasound/dmasound_atari.c
@@ -1618,4 +1618,6 @@ static void __exit dmasound_atari_cleanup(void)
module_init(dmasound_atari_init);
module_exit(dmasound_atari_cleanup);
+
+MODULE_DESCRIPTION("Atari TT and Falcon DMA Sound Driver");
MODULE_LICENSE("GPL");
diff --git a/sound/oss/dmasound/dmasound_paula.c b/sound/oss/dmasound/dmasound_paula.c
index 3a593da09280..b8fad12f9e5f 100644
--- a/sound/oss/dmasound/dmasound_paula.c
+++ b/sound/oss/dmasound/dmasound_paula.c
@@ -740,5 +740,6 @@ static struct platform_driver amiga_audio_driver __refdata = {
module_platform_driver_probe(amiga_audio_driver, amiga_audio_probe);
+MODULE_DESCRIPTION("Amiga Paula DMA Sound Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:amiga-audio");
diff --git a/sound/parisc/Makefile b/sound/parisc/Makefile
index 10891c3b7d91..84c71490fb72 100644
--- a/sound/parisc/Makefile
+++ b/sound/parisc/Makefile
@@ -3,7 +3,7 @@
# Makefile for ALSA
#
-snd-harmony-objs := harmony.o
+snd-harmony-y := harmony.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_HARMONY) += snd-harmony.o
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index 04cac7469139..18b673018dfd 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -4,30 +4,30 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ad1889-objs := ad1889.o
-snd-als300-objs := als300.o
-snd-als4000-objs := als4000.o
-snd-atiixp-objs := atiixp.o
-snd-atiixp-modem-objs := atiixp_modem.o
-snd-azt3328-objs := azt3328.o
-snd-bt87x-objs := bt87x.o
-snd-cmipci-objs := cmipci.o
-snd-cs4281-objs := cs4281.o
-snd-cs5530-objs := cs5530.o
-snd-ens1370-objs := ens1370.o ak4531_codec.o
-snd-ens1371-objs := ens1371.o
-snd-es1938-objs := es1938.o
-snd-es1968-objs := es1968.o
-snd-fm801-objs := fm801.o
-snd-intel8x0-objs := intel8x0.o
-snd-intel8x0m-objs := intel8x0m.o
-snd-maestro3-objs := maestro3.o
-snd-rme32-objs := rme32.o
-snd-rme96-objs := rme96.o
-snd-sis7019-objs := sis7019.o
-snd-sonicvibes-objs := sonicvibes.o
-snd-via82xx-objs := via82xx.o
-snd-via82xx-modem-objs := via82xx_modem.o
+snd-ad1889-y := ad1889.o
+snd-als300-y := als300.o
+snd-als4000-y := als4000.o
+snd-atiixp-y := atiixp.o
+snd-atiixp-modem-y := atiixp_modem.o
+snd-azt3328-y := azt3328.o
+snd-bt87x-y := bt87x.o
+snd-cmipci-y := cmipci.o
+snd-cs4281-y := cs4281.o
+snd-cs5530-y := cs5530.o
+snd-ens1370-y := ens1370.o ak4531_codec.o
+snd-ens1371-y := ens1371.o
+snd-es1938-y := es1938.o
+snd-es1968-y := es1968.o
+snd-fm801-y := fm801.o
+snd-intel8x0-y := intel8x0.o
+snd-intel8x0m-y := intel8x0m.o
+snd-maestro3-y := maestro3.o
+snd-rme32-y := rme32.o
+snd-rme96-y := rme96.o
+snd-sis7019-y := sis7019.o
+snd-sonicvibes-y := sonicvibes.o
+snd-via82xx-y := via82xx.o
+snd-via82xx-modem-y := via82xx_modem.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AD1889) += snd-ad1889.o
diff --git a/sound/pci/ali5451/Makefile b/sound/pci/ali5451/Makefile
index 8156198fbaeb..e319a4c1d6b2 100644
--- a/sound/pci/ali5451/Makefile
+++ b/sound/pci/ali5451/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ali5451-objs := ali5451.o
+snd-ali5451-y := ali5451.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ALI5451) += snd-ali5451.o
diff --git a/sound/pci/asihpi/Makefile b/sound/pci/asihpi/Makefile
index 8351f8f5b523..d558a974fa7e 100644
--- a/sound/pci/asihpi/Makefile
+++ b/sound/pci/asihpi/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-asihpi-objs := asihpi.o hpioctl.o hpimsginit.o\
+snd-asihpi-y := asihpi.o hpioctl.o hpimsginit.o\
hpicmn.o hpifunc.o hpidebug.o hpidspcd.o\
hpios.o hpi6000.o hpi6205.o hpimsgx.o
diff --git a/sound/pci/au88x0/Makefile b/sound/pci/au88x0/Makefile
index 78ab11562f4d..5ec5abdee28d 100644
--- a/sound/pci/au88x0/Makefile
+++ b/sound/pci/au88x0/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
-snd-au8810-objs := au8810.o
-snd-au8820-objs := au8820.o
-snd-au8830-objs := au8830.o
+snd-au8810-y := au8810.o
+snd-au8820-y := au8820.o
+snd-au8830-y := au8830.o
obj-$(CONFIG_SND_AU8810) += snd-au8810.o
obj-$(CONFIG_SND_AU8820) += snd-au8820.o
diff --git a/sound/pci/aw2/Makefile b/sound/pci/aw2/Makefile
index f9045afb4cda..c246f7c7f2bf 100644
--- a/sound/pci/aw2/Makefile
+++ b/sound/pci/aw2/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-aw2-objs := aw2-alsa.o aw2-saa7146.o
+snd-aw2-y := aw2-alsa.o aw2-saa7146.o
obj-$(CONFIG_SND_AW2) += snd-aw2.o
diff --git a/sound/pci/ca0106/Makefile b/sound/pci/ca0106/Makefile
index 9e51d3df3ee8..693dc4d80925 100644
--- a/sound/pci/ca0106/Makefile
+++ b/sound/pci/ca0106/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-ca0106-objs := ca0106_main.o ca0106_mixer.o ca_midi.o
+snd-ca0106-y := ca0106_main.o ca0106_mixer.o ca_midi.o
snd-ca0106-$(CONFIG_SND_PROC_FS) += ca0106_proc.o
obj-$(CONFIG_SND_CA0106) += snd-ca0106.o
diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile
index 70888706a0af..ff2b1cba3a3c 100644
--- a/sound/pci/ctxfi/Makefile
+++ b/sound/pci/ctxfi/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \
+snd-ctxfi-y := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \
ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o cttimer.o \
cthw20k2.o cthw20k1.o
diff --git a/sound/pci/echoaudio/Makefile b/sound/pci/echoaudio/Makefile
index 4865b8fe7434..96667641c7cf 100644
--- a/sound/pci/echoaudio/Makefile
+++ b/sound/pci/echoaudio/Makefile
@@ -4,20 +4,20 @@
# Copyright (c) 2003 by Giuliano Pochini <pochini@shiny.it>
#
-snd-darla20-objs := darla20.o
-snd-gina20-objs := gina20.o
-snd-layla20-objs := layla20.o
-snd-darla24-objs := darla24.o
-snd-gina24-objs := gina24.o
-snd-layla24-objs := layla24.o
-snd-mona-objs := mona.o
-snd-mia-objs := mia.o
-snd-echo3g-objs := echo3g.o
-snd-indigo-objs := indigo.o
-snd-indigoio-objs := indigoio.o
-snd-indigodj-objs := indigodj.o
-snd-indigoiox-objs := indigoiox.o
-snd-indigodjx-objs := indigodjx.o
+snd-darla20-y := darla20.o
+snd-gina20-y := gina20.o
+snd-layla20-y := layla20.o
+snd-darla24-y := darla24.o
+snd-gina24-y := gina24.o
+snd-layla24-y := layla24.o
+snd-mona-y := mona.o
+snd-mia-y := mia.o
+snd-echo3g-y := echo3g.o
+snd-indigo-y := indigo.o
+snd-indigoio-y := indigoio.o
+snd-indigodj-y := indigodj.o
+snd-indigoiox-y := indigoiox.o
+snd-indigodjx-y := indigodjx.o
obj-$(CONFIG_SND_DARLA20) += snd-darla20.o
obj-$(CONFIG_SND_GINA20) += snd-gina20.o
diff --git a/sound/pci/emu10k1/Makefile b/sound/pci/emu10k1/Makefile
index 17d5527be319..1f325abcb3ef 100644
--- a/sound/pci/emu10k1/Makefile
+++ b/sound/pci/emu10k1/Makefile
@@ -4,12 +4,12 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-emu10k1-objs := emu10k1.o emu10k1_main.o \
+snd-emu10k1-y := emu10k1.o emu10k1_main.o \
irq.o memory.o voice.o emumpu401.o emupcm.o io.o \
emumixer.o emufx.o timer.o p16v.o
snd-emu10k1-$(CONFIG_SND_PROC_FS) += emuproc.o
-snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o
-snd-emu10k1x-objs := emu10k1x.o
+snd-emu10k1-synth-y := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o
+snd-emu10k1x-y := emu10k1x.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index fe72e7d77241..dadeda7758ce 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -189,8 +189,7 @@ static int snd_emu10k1_suspend(struct device *dev)
emu->suspend = 1;
- cancel_work_sync(&emu->emu1010.firmware_work);
- cancel_work_sync(&emu->emu1010.clock_work);
+ cancel_work_sync(&emu->emu1010.work);
snd_ac97_suspend(emu->ac97);
diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
index 941bfbf812ed..ef26e4d3e2a3 100644
--- a/sound/pci/emu10k1/emu10k1_callback.c
+++ b/sound/pci/emu10k1/emu10k1_callback.c
@@ -255,7 +255,7 @@ lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
/* check if sample is finished playing (non-looping only) */
if (bp != best + V_OFF && bp != best + V_FREE &&
(vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
- val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch);
+ val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch) - 64 + 3;
if (val >= vp->reg.loopstart)
bp = best + V_OFF;
}
@@ -310,6 +310,7 @@ start_voice(struct snd_emux_voice *vp)
{
unsigned int temp;
int ch;
+ bool w_16;
u32 psst, dsl, map, ccca, vtarget;
unsigned int addr, mapped_offset;
struct snd_midi_channel *chan;
@@ -321,6 +322,7 @@ start_voice(struct snd_emux_voice *vp)
if (snd_BUG_ON(ch < 0))
return -EINVAL;
chan = vp->chan;
+ w_16 = !(vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS);
emem = (struct snd_emu10k1_memblk *)vp->block;
if (emem == NULL)
@@ -330,7 +332,7 @@ start_voice(struct snd_emux_voice *vp)
/* dev_err(hw->card->devK, "emu: cannot map!\n"); */
return -ENOMEM;
}
- mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1;
+ mapped_offset = snd_emu10k1_memblk_offset(emem) >> w_16;
vp->reg.start += mapped_offset;
vp->reg.end += mapped_offset;
vp->reg.loopstart += mapped_offset;
@@ -362,7 +364,7 @@ start_voice(struct snd_emux_voice *vp)
map = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
- addr = vp->reg.start;
+ addr = vp->reg.start + 64 - 3;
temp = vp->reg.parm.filterQ;
ccca = (temp << 28) | addr;
if (vp->apitch < 0xe400)
@@ -371,7 +373,7 @@ start_voice(struct snd_emux_voice *vp)
unsigned int shift = (vp->apitch - 0xe000) >> 10;
ccca |= shift << 25;
}
- if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
+ if (!w_16)
ccca |= CCCA_8BITSELECT;
vtarget = (unsigned int)vp->vtarget << 16;
@@ -430,6 +432,9 @@ start_voice(struct snd_emux_voice *vp)
/* Q & current address (Q 4bit value, MSB) */
CCCA, ccca,
+ /* cache */
+ CCR, REG_VAL_PUT(CCR_CACHEINVALIDSIZE, 64),
+
/* reset volume */
VTFT, vtarget | vp->ftarget,
CVCF, vtarget | CVCF_CURRENTFILTER_MASK,
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index de5c41e578e1..5b8a5ba825bd 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -652,52 +652,6 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 *emu)
return 0;
}
-static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu,
- const struct firmware *fw_entry)
-{
- int n, i;
- u16 reg;
- u8 value;
- __always_unused u16 write_post;
-
- if (!fw_entry)
- return -EIO;
-
- /* The FPGA is a Xilinx Spartan IIE XC2S50E */
- /* On E-MU 0404b it is a Xilinx Spartan III XC3S50 */
- /* GPIO7 -> FPGA PGMN
- * GPIO6 -> FPGA CCLK
- * GPIO5 -> FPGA DIN
- * FPGA CONFIG OFF -> FPGA PGMN
- */
- spin_lock_irq(&emu->emu_lock);
- outw(0x00, emu->port + A_GPIO); /* Set PGMN low for 100uS. */
- write_post = inw(emu->port + A_GPIO);
- udelay(100);
- outw(0x80, emu->port + A_GPIO); /* Leave bit 7 set during netlist setup. */
- write_post = inw(emu->port + A_GPIO);
- udelay(100); /* Allow FPGA memory to clean */
- for (n = 0; n < fw_entry->size; n++) {
- value = fw_entry->data[n];
- for (i = 0; i < 8; i++) {
- reg = 0x80;
- if (value & 0x1)
- reg = reg | 0x20;
- value = value >> 1;
- outw(reg, emu->port + A_GPIO);
- write_post = inw(emu->port + A_GPIO);
- outw(reg | 0x40, emu->port + A_GPIO);
- write_post = inw(emu->port + A_GPIO);
- }
- }
- /* After programming, set GPIO bit 4 high again. */
- outw(0x10, emu->port + A_GPIO);
- write_post = inw(emu->port + A_GPIO);
- spin_unlock_irq(&emu->emu_lock);
-
- return 0;
-}
-
/* firmware file names, per model, init-fw and dock-fw (optional) */
static const char * const firmware_names[5][2] = {
[EMU_MODEL_EMU1010] = {
@@ -729,72 +683,68 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, int dock,
return err;
}
- return snd_emu1010_load_firmware_entry(emu, *fw);
+ snd_emu1010_load_firmware_entry(emu, dock, *fw);
+ return 0;
}
-static void emu1010_firmware_work(struct work_struct *work)
+static void snd_emu1010_load_dock_firmware(struct snd_emu10k1 *emu)
{
- struct snd_emu10k1 *emu;
- u32 tmp, tmp2, reg;
+ u32 tmp, tmp2;
int err;
- emu = container_of(work, struct snd_emu10k1,
- emu1010.firmware_work);
- if (emu->card->shutdown)
+ // The docking events clearly arrive prematurely - while the
+ // Dock's FPGA seems to be successfully programmed, the Dock
+ // fails to initialize subsequently if we don't give it some
+ // time to "warm up" here.
+ msleep(200);
+
+ dev_info(emu->card->dev, "emu1010: Loading Audio Dock Firmware\n");
+ err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw);
+ if (err < 0)
return;
-#ifdef CONFIG_PM_SLEEP
- if (emu->suspend)
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
+
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp);
+ dev_dbg(emu->card->dev, "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp);
+ if ((tmp & 0x1f) != 0x15) {
+ /* FPGA failed to be programmed */
+ dev_err(emu->card->dev,
+ "emu1010: Loading Audio Dock Firmware failed, reg = 0x%x\n",
+ tmp);
return;
-#endif
+ }
+ dev_info(emu->card->dev, "emu1010: Audio Dock Firmware loaded\n");
+
+ snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp);
+ snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2);
+ dev_info(emu->card->dev, "Audio Dock ver: %u.%u\n", tmp, tmp2);
+
+ /* Allow DLL to settle, to sync clocking between 1010 and Dock */
+ msleep(10);
+}
+
+static void emu1010_dock_event(struct snd_emu10k1 *emu)
+{
+ u32 reg;
+
snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg); /* OPTIONS: Which cards are attached to the EMU */
if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
/* Audio Dock attached */
- /* Return to Audio Dock programming mode */
- dev_info(emu->card->dev,
- "emu1010: Loading Audio Dock Firmware\n");
- snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG,
- EMU_HANA_FPGA_CONFIG_AUDIODOCK);
- err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw);
- if (err < 0)
- return;
- snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
- snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp);
- dev_info(emu->card->dev,
- "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp);
- if ((tmp & 0x1f) != 0x15) {
- /* FPGA failed to be programmed */
- dev_info(emu->card->dev,
- "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n",
- tmp);
- return;
- }
- dev_info(emu->card->dev,
- "emu1010: Audio Dock Firmware loaded\n");
- snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp);
- snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2);
- dev_info(emu->card->dev, "Audio Dock ver: %u.%u\n", tmp, tmp2);
- /* Sync clocking between 1010 and Dock */
- /* Allow DLL to settle */
- msleep(10);
+ snd_emu1010_load_dock_firmware(emu);
/* Unmute all. Default is muted after a firmware load */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
+ } else if (!(reg & EMU_HANA_OPTION_DOCK_ONLINE)) {
+ /* Audio Dock removed */
+ dev_info(emu->card->dev, "emu1010: Audio Dock detached\n");
+ /* The hardware auto-mutes all, so we unmute again */
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
}
}
-static void emu1010_clock_work(struct work_struct *work)
+static void emu1010_clock_event(struct snd_emu10k1 *emu)
{
- struct snd_emu10k1 *emu;
struct snd_ctl_elem_id id;
- emu = container_of(work, struct snd_emu10k1,
- emu1010.clock_work);
- if (emu->card->shutdown)
- return;
-#ifdef CONFIG_PM_SLEEP
- if (emu->suspend)
- return;
-#endif
-
spin_lock_irq(&emu->reg_lock);
// This is the only thing that can actually happen.
emu->emu1010.clock_source = emu->emu1010.clock_fallback;
@@ -805,21 +755,44 @@ static void emu1010_clock_work(struct work_struct *work)
snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE, &id);
}
-static void emu1010_interrupt(struct snd_emu10k1 *emu)
+static void emu1010_work(struct work_struct *work)
{
+ struct snd_emu10k1 *emu;
u32 sts;
+ emu = container_of(work, struct snd_emu10k1, emu1010.work);
+ if (emu->card->shutdown)
+ return;
+#ifdef CONFIG_PM_SLEEP
+ if (emu->suspend)
+ return;
+#endif
+
+ snd_emu1010_fpga_lock(emu);
+
snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &sts);
- if (sts & EMU_HANA_IRQ_DOCK_LOST) {
- /* Audio Dock removed */
- dev_info(emu->card->dev, "emu1010: Audio Dock detached\n");
- /* The hardware auto-mutes all, so we unmute again */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
- } else if (sts & EMU_HANA_IRQ_DOCK) {
- schedule_work(&emu->emu1010.firmware_work);
- }
+
+ // The distinction of the IRQ status bits is unreliable,
+ // so we dispatch later based on option card status.
+ if (sts & (EMU_HANA_IRQ_DOCK | EMU_HANA_IRQ_DOCK_LOST))
+ emu1010_dock_event(emu);
+
if (sts & EMU_HANA_IRQ_WCLK_CHANGED)
- schedule_work(&emu->emu1010.clock_work);
+ emu1010_clock_event(emu);
+
+ snd_emu1010_fpga_unlock(emu);
+}
+
+static void emu1010_interrupt(struct snd_emu10k1 *emu)
+{
+ // We get an interrupt on each GPIO input pin change, but we
+ // care only about the ones triggered by the dedicated pin.
+ u16 sts = inw(emu->port + A_GPIO);
+ u16 bit = emu->card_capabilities->ca0108_chip ? 0x2000 : 0x8000;
+ if (!(sts & bit))
+ return;
+
+ schedule_work(&emu->emu1010.work);
}
/*
@@ -841,32 +814,13 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
* Proper init follows in snd_emu10k1_init(). */
outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
- /* Disable 48Volt power to Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
-
- /* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
- snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
- dev_dbg(emu->card->dev, "reg1 = 0x%x\n", reg);
- if ((reg & 0x3f) == 0x15) {
- /* FPGA netlist already present so clear it */
- /* Return to programming mode */
-
- snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_HANA);
- }
- snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
- dev_dbg(emu->card->dev, "reg2 = 0x%x\n", reg);
- if ((reg & 0x3f) == 0x15) {
- /* FPGA failed to return to programming mode */
- dev_info(emu->card->dev,
- "emu1010: FPGA failed to return to programming mode\n");
- return -ENODEV;
- }
- dev_info(emu->card->dev, "emu1010: EMU_HANA_ID = 0x%x\n", reg);
+ snd_emu1010_fpga_lock(emu);
+ dev_info(emu->card->dev, "emu1010: Loading Hana Firmware\n");
err = snd_emu1010_load_firmware(emu, 0, &emu->firmware);
if (err < 0) {
dev_info(emu->card->dev, "emu1010: Loading Firmware failed\n");
- return err;
+ goto fail;
}
/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
@@ -876,7 +830,8 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
dev_info(emu->card->dev,
"emu1010: Loading Hana Firmware file failed, reg = 0x%x\n",
reg);
- return -ENODEV;
+ err = -ENODEV;
+ goto fail;
}
dev_info(emu->card->dev, "emu1010: Hana Firmware loaded\n");
@@ -889,7 +844,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg);
if (reg & EMU_HANA_OPTION_DOCK_OFFLINE)
- schedule_work(&emu->emu1010.firmware_work);
+ snd_emu1010_load_dock_firmware(emu);
if (emu->card_capabilities->no_adat) {
emu->emu1010.optical_in = 0; /* IN_SPDIF */
emu->emu1010.optical_out = 0; /* OUT_SPDIF */
@@ -936,7 +891,9 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
// so it is safe to simply enable the outputs.
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
- return 0;
+fail:
+ snd_emu1010_fpga_unlock(emu);
+ return err;
}
/*
* Create the EMU10K1 instance
@@ -958,10 +915,10 @@ static void snd_emu10k1_free(struct snd_card *card)
}
if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) {
/* Disable 48Volt power to Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_DOCK_PWR, 0);
}
- cancel_work_sync(&emu->emu1010.firmware_work);
- cancel_work_sync(&emu->emu1010.clock_work);
+ cancel_work_sync(&emu->emu1010.work);
+ mutex_destroy(&emu->emu1010.lock);
release_firmware(emu->firmware);
release_firmware(emu->dock_fw);
snd_util_memhdr_free(emu->memhdr);
@@ -1540,8 +1497,8 @@ int snd_emu10k1_create(struct snd_card *card,
emu->irq = -1;
emu->synth = NULL;
emu->get_synth_voice = NULL;
- INIT_WORK(&emu->emu1010.firmware_work, emu1010_firmware_work);
- INIT_WORK(&emu->emu1010.clock_work, emu1010_clock_work);
+ INIT_WORK(&emu->emu1010.work, emu1010_work);
+ mutex_init(&emu->emu1010.lock);
/* read revision & serial */
emu->revision = pci->revision;
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
diff --git a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c
index 89890f24509f..dbfa89435ac2 100644
--- a/sound/pci/emu10k1/emu10k1_patch.c
+++ b/sound/pci/emu10k1/emu10k1_patch.c
@@ -16,7 +16,7 @@
#define BLANK_LOOP_START 4
#define BLANK_LOOP_END 8
#define BLANK_LOOP_SIZE 12
-#define BLANK_HEAD_SIZE 32
+#define BLANK_HEAD_SIZE 3
/*
* allocate a sample block and copy data from userspace
@@ -26,56 +26,76 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
struct snd_util_memhdr *hdr,
const void __user *data, long count)
{
+ u8 fill;
+ u32 xor;
+ int shift;
int offset;
int truesize, size, blocksize;
- __maybe_unused int loopsize;
- int loopend, sampleend;
- unsigned int start_addr;
+ int loop_start, loop_end, loop_size, data_end, unroll;
struct snd_emu10k1 *emu;
emu = rec->hw;
if (snd_BUG_ON(!sp || !hdr))
return -EINVAL;
- if (sp->v.size == 0) {
- dev_dbg(emu->card->dev,
- "emu: rom font for sample %d\n", sp->v.sample);
- return 0;
+ if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP | SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
+ /* should instead return -ENOTSUPP; but compatibility */
+ printk(KERN_WARNING "Emu10k1 wavetable patch %d with unsupported loop feature\n",
+ sp->v.sample);
}
- /* recalculate address offset */
- sp->v.end -= sp->v.start;
- sp->v.loopstart -= sp->v.start;
- sp->v.loopend -= sp->v.start;
- sp->v.start = 0;
-
- /* some samples have invalid data. the addresses are corrected in voice info */
- sampleend = sp->v.end;
- if (sampleend > sp->v.size)
- sampleend = sp->v.size;
- loopend = sp->v.loopend;
- if (loopend > sampleend)
- loopend = sampleend;
-
- /* be sure loop points start < end */
- if (sp->v.loopstart >= sp->v.loopend)
- swap(sp->v.loopstart, sp->v.loopend);
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) {
+ shift = 0;
+ fill = 0x80;
+ xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0 : 0x80808080;
+ } else {
+ shift = 1;
+ fill = 0;
+ xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0x80008000 : 0;
+ }
/* compute true data size to be loaded */
truesize = sp->v.size + BLANK_HEAD_SIZE;
- loopsize = 0;
-#if 0 /* not supported */
- if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
- loopsize = sp->v.loopend - sp->v.loopstart;
- truesize += loopsize;
-#endif
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
truesize += BLANK_LOOP_SIZE;
+ /* if no blank loop is attached in the sample, add it */
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
+ sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
+ sp->v.loopend = sp->v.end + BLANK_LOOP_END;
+ }
+ }
+
+ loop_start = sp->v.loopstart;
+ loop_end = sp->v.loopend;
+ loop_size = loop_end - loop_start;
+ if (!loop_size)
+ return -EINVAL;
+ data_end = sp->v.end;
+
+ /* recalculate offset */
+ sp->v.start += BLANK_HEAD_SIZE;
+ sp->v.end += BLANK_HEAD_SIZE;
+ sp->v.loopstart += BLANK_HEAD_SIZE;
+ sp->v.loopend += BLANK_HEAD_SIZE;
+
+ // Automatic pre-filling of the cache does not work in the presence
+ // of loops (*), and we don't want to fill it manually, as that is
+ // fiddly and slow. So we unroll the loop until the loop end is
+ // beyond the cache size.
+ // (*) Strictly speaking, a single iteration is supported (that's
+ // how it works when the playback engine runs), but handling this
+ // special case is not worth it.
+ unroll = 0;
+ while (sp->v.loopend < 64) {
+ truesize += loop_size;
+ sp->v.loopstart += loop_size;
+ sp->v.loopend += loop_size;
+ sp->v.end += loop_size;
+ unroll++;
+ }
/* try to allocate a memory block */
- blocksize = truesize;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- blocksize *= 2;
+ blocksize = truesize << shift;
sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
if (sp->block == NULL) {
dev_dbg(emu->card->dev,
@@ -88,110 +108,43 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
/* write blank samples at head */
offset = 0;
- size = BLANK_HEAD_SIZE;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- size *= 2;
- if (offset + size > blocksize)
- return -EINVAL;
- snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
- offset += size;
-
- /* copy start->loopend */
- size = loopend;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- size *= 2;
- if (offset + size > blocksize)
- return -EINVAL;
- if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
- snd_emu10k1_synth_free(emu, sp->block);
- sp->block = NULL;
- return -EFAULT;
- }
+ size = BLANK_HEAD_SIZE << shift;
+ snd_emu10k1_synth_memset(emu, sp->block, offset, size, fill);
offset += size;
- data += size;
-
-#if 0 /* not supported yet */
- /* handle reverse (or bidirectional) loop */
- if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
- /* copy loop in reverse */
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
- int woffset;
- unsigned short *wblock = (unsigned short*)block;
- woffset = offset / 2;
- if (offset + loopsize * 2 > blocksize)
- return -EINVAL;
- for (i = 0; i < loopsize; i++)
- wblock[woffset + i] = wblock[woffset - i -1];
- offset += loopsize * 2;
- } else {
- if (offset + loopsize > blocksize)
- return -EINVAL;
- for (i = 0; i < loopsize; i++)
- block[offset + i] = block[offset - i -1];
- offset += loopsize;
- }
- /* modify loop pointers */
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
- sp->v.loopend += loopsize;
- } else {
- sp->v.loopstart += loopsize;
- sp->v.loopend += loopsize;
+ /* copy provided samples */
+ if (unroll && loop_end <= data_end) {
+ size = loop_end << shift;
+ if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor))
+ goto faulty;
+ offset += size;
+
+ data += loop_start << shift;
+ while (--unroll > 0) {
+ size = loop_size << shift;
+ if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor))
+ goto faulty;
+ offset += size;
}
- /* add sample pointer */
- sp->v.end += loopsize;
- }
-#endif
- /* loopend -> sample end */
- size = sp->v.size - loopend;
- if (size < 0)
- return -EINVAL;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- size *= 2;
- if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
- snd_emu10k1_synth_free(emu, sp->block);
- sp->block = NULL;
- return -EFAULT;
+ size = (data_end - loop_start) << shift;
+ } else {
+ size = data_end << shift;
}
+ if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor))
+ goto faulty;
offset += size;
/* clear rest of samples (if any) */
if (offset < blocksize)
- snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
-
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
- /* if no blank loop is attached in the sample, add it */
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
- sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
- sp->v.loopend = sp->v.end + BLANK_LOOP_END;
- }
- }
-
-#if 0 /* not supported yet */
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
- /* unsigned -> signed */
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
- unsigned short *wblock = (unsigned short*)block;
- for (i = 0; i < truesize; i++)
- wblock[i] ^= 0x8000;
- } else {
- for (i = 0; i < truesize; i++)
- block[i] ^= 0x80;
- }
- }
-#endif
-
- /* recalculate offset */
- start_addr = BLANK_HEAD_SIZE * 2;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- start_addr >>= 1;
- sp->v.start += start_addr;
- sp->v.end += start_addr;
- sp->v.loopstart += start_addr;
- sp->v.loopend += start_addr;
+ snd_emu10k1_synth_memset(emu, sp->block, offset, blocksize - offset, fill);
return 0;
+
+faulty:
+ snd_emu10k1_synth_free(emu, sp->block);
+ sp->block = NULL;
+ return -EFAULT;
}
/*
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index 0a32ea53d8c6..05b98d9b547b 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -661,7 +661,9 @@ static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol,
change = (emu->emu1010.output_source[channel] != val);
if (change) {
emu->emu1010.output_source[channel] = val;
+ snd_emu1010_fpga_lock(emu);
snd_emu1010_output_source_apply(emu, channel, val);
+ snd_emu1010_fpga_unlock(emu);
}
return change;
}
@@ -705,7 +707,9 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol,
change = (emu->emu1010.input_source[channel] != val);
if (change) {
emu->emu1010.input_source[channel] = val;
+ snd_emu1010_fpga_lock(emu);
snd_emu1010_input_source_apply(emu, channel, val);
+ snd_emu1010_fpga_unlock(emu);
}
return change;
}
@@ -774,7 +778,7 @@ static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ct
cache = cache & ~mask;
change = (cache != emu->emu1010.adc_pads);
if (change) {
- snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, cache );
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_ADC_PADS, cache );
emu->emu1010.adc_pads = cache;
}
@@ -832,7 +836,7 @@ static int snd_emu1010_dac_pads_put(struct snd_kcontrol *kcontrol, struct snd_ct
cache = cache & ~mask;
change = (cache != emu->emu1010.dac_pads);
if (change) {
- snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, cache );
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_DAC_PADS, cache );
emu->emu1010.dac_pads = cache;
}
@@ -980,6 +984,7 @@ static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol,
val = ucontrol->value.enumerated.item[0] ;
if (val >= emu_ci->num)
return -EINVAL;
+ snd_emu1010_fpga_lock(emu);
spin_lock_irq(&emu->reg_lock);
change = (emu->emu1010.clock_source != val);
if (change) {
@@ -996,6 +1001,7 @@ static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol,
} else {
spin_unlock_irq(&emu->reg_lock);
}
+ snd_emu1010_fpga_unlock(emu);
return change;
}
@@ -1041,7 +1047,7 @@ static int snd_emu1010_clock_fallback_put(struct snd_kcontrol *kcontrol,
change = (emu->emu1010.clock_fallback != val);
if (change) {
emu->emu1010.clock_fallback = val;
- snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 1 - val);
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_DEFCLOCK, 1 - val);
}
return change;
}
@@ -1093,7 +1099,7 @@ static int snd_emu1010_optical_out_put(struct snd_kcontrol *kcontrol,
emu->emu1010.optical_out = val;
tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) |
(emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF);
- snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_OPTICAL_TYPE, tmp);
}
return change;
}
@@ -1144,7 +1150,7 @@ static int snd_emu1010_optical_in_put(struct snd_kcontrol *kcontrol,
emu->emu1010.optical_in = val;
tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) |
(emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF);
- snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_OPTICAL_TYPE, tmp);
}
return change;
}
@@ -2323,7 +2329,9 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
for (i = 0; i < emu_ri->n_outs; i++)
emu->emu1010.output_source[i] =
emu1010_map_source(emu_ri, emu_ri->out_dflts[i]);
+ snd_emu1010_fpga_lock(emu);
snd_emu1010_apply_sources(emu);
+ snd_emu1010_fpga_unlock(emu);
kctl = emu->ctl_clock_source = snd_ctl_new1(&snd_emu1010_clock_source, emu);
err = snd_ctl_add(card, kctl);
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index 2f80fd91017c..737c28d31b41 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -165,6 +165,8 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
u32 value2;
if (emu->card_capabilities->emu_model) {
+ snd_emu1010_fpga_lock(emu);
+
// This represents the S/PDIF lock status on 0404b, which is
// kinda weird and unhelpful, because monitoring it via IRQ is
// impractical (one gets an IRQ flood as long as it is desynced).
@@ -197,6 +199,8 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "\nS/PDIF mode: %s%s\n",
value & EMU_HANA_SPDIF_MODE_RX_PRO ? "professional" : "consumer",
value & EMU_HANA_SPDIF_MODE_RX_NOCOPY ? ", no copy" : "");
+
+ snd_emu1010_fpga_unlock(emu);
} else {
snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS);
snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS);
@@ -458,6 +462,9 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry,
struct snd_emu10k1 *emu = entry->private_data;
u32 value;
int i;
+
+ snd_emu1010_fpga_lock(emu);
+
snd_iprintf(buffer, "EMU1010 Registers:\n\n");
for(i = 0; i < 0x40; i+=1) {
@@ -496,6 +503,8 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry,
snd_emu_proc_emu1010_link_read(emu, buffer, 0x701);
}
}
+
+ snd_emu1010_fpga_unlock(emu);
}
static void snd_emu_proc_io_reg_read(struct snd_info_entry *entry,
diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
index 74df2330015f..b60ab5671e00 100644
--- a/sound/pci/emu10k1/io.c
+++ b/sound/pci/emu10k1/io.c
@@ -285,24 +285,33 @@ static void snd_emu1010_fpga_write_locked(struct snd_emu10k1 *emu, u32 reg, u32
outw(value, emu->port + A_GPIO);
udelay(10);
outw(value | 0x80 , emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
+ udelay(10);
}
void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value)
{
- unsigned long flags;
+ if (snd_BUG_ON(!mutex_is_locked(&emu->emu1010.lock)))
+ return;
+ snd_emu1010_fpga_write_locked(emu, reg, value);
+}
- spin_lock_irqsave(&emu->emu_lock, flags);
+void snd_emu1010_fpga_write_lock(struct snd_emu10k1 *emu, u32 reg, u32 value)
+{
+ snd_emu1010_fpga_lock(emu);
snd_emu1010_fpga_write_locked(emu, reg, value);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ snd_emu1010_fpga_unlock(emu);
}
-static void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 *value)
+void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
{
// The higest input pin is used as the designated interrupt trigger,
// so it needs to be masked out.
// But note that any other input pin change will also cause an IRQ,
// so using this function often causes an IRQ as a side effect.
u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f;
+
+ if (snd_BUG_ON(!mutex_is_locked(&emu->emu1010.lock)))
+ return;
if (snd_BUG_ON(reg > 0x3f))
return;
reg += 0x40; /* 0x40 upwards are registers. */
@@ -313,47 +322,31 @@ static void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 *
*value = ((inw(emu->port + A_GPIO) >> 8) & mask);
}
-void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&emu->emu_lock, flags);
- snd_emu1010_fpga_read_locked(emu, reg, value);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
-}
-
/* Each Destination has one and only one Source,
* but one Source can feed any number of Destinations simultaneously.
*/
void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src)
{
- unsigned long flags;
-
if (snd_BUG_ON(dst & ~0x71f))
return;
if (snd_BUG_ON(src & ~0x71f))
return;
- spin_lock_irqsave(&emu->emu_lock, flags);
- snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8);
- snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f);
- snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCHI, src >> 8);
- snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCLO, src & 0x1f);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f);
+ snd_emu1010_fpga_write(emu, EMU_HANA_SRCHI, src >> 8);
+ snd_emu1010_fpga_write(emu, EMU_HANA_SRCLO, src & 0x1f);
}
u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst)
{
- unsigned long flags;
u32 hi, lo;
if (snd_BUG_ON(dst & ~0x71f))
return 0;
- spin_lock_irqsave(&emu->emu_lock, flags);
- snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8);
- snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f);
- snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCHI, &hi);
- snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCLO, &lo);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f);
+ snd_emu1010_fpga_read(emu, EMU_HANA_SRCHI, &hi);
+ snd_emu1010_fpga_read(emu, EMU_HANA_SRCLO, &lo);
return (hi << 8) | lo;
}
@@ -429,6 +422,59 @@ void snd_emu1010_update_clock(struct snd_emu10k1 *emu)
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, leds);
}
+void snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, int dock,
+ const struct firmware *fw_entry)
+{
+ __always_unused u16 write_post;
+
+ // On E-MU 1010 rev1 the FPGA is a Xilinx Spartan IIE XC2S50E.
+ // On E-MU 0404b it is a Xilinx Spartan III XC3S50.
+ // The wiring is as follows:
+ // GPO7 -> FPGA input & 1K resistor -> FPGA /PGMN <- FPGA output
+ // In normal operation, the active low reset line is held up by
+ // an FPGA output, while the GPO pin performs its duty as control
+ // register access strobe signal. Writing the respective bit to
+ // EMU_HANA_FPGA_CONFIG puts the FPGA output into high-Z mode, at
+ // which point the GPO pin can control the reset line through the
+ // resistor.
+ // GPO6 -> FPGA CCLK & FPGA input
+ // GPO5 -> FPGA DIN (dual function)
+
+ // If the FPGA is already programmed, return it to programming mode
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG,
+ dock ? EMU_HANA_FPGA_CONFIG_AUDIODOCK :
+ EMU_HANA_FPGA_CONFIG_HANA);
+
+ // Assert reset line for 100uS
+ outw(0x00, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
+ udelay(100);
+ outw(0x80, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
+ udelay(100); // Allow FPGA memory to clean
+
+ // Upload the netlist. Keep reset line high!
+ for (int n = 0; n < fw_entry->size; n++) {
+ u8 value = fw_entry->data[n];
+ for (int i = 0; i < 8; i++) {
+ u16 reg = 0x80;
+ if (value & 1)
+ reg |= 0x20;
+ value >>= 1;
+ outw(reg, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
+ outw(reg | 0x40, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
+ }
+ }
+
+ // After programming, set GPIO bit 4 high again.
+ // This appears to be a config word that the rev1 Hana
+ // firmware reads; weird things happen without this.
+ outw(0x10, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
+}
+
void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
{
unsigned long flags;
diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
index 20b07117574b..d29711777161 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -565,15 +565,18 @@ static inline void *offset_ptr(struct snd_emu10k1 *emu, int page, int offset)
}
/*
- * bzero(blk + offset, size)
+ * memset(blk + offset, value, size)
*/
-int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk,
- int offset, int size)
+int snd_emu10k1_synth_memset(struct snd_emu10k1 *emu, struct snd_util_memblk *blk,
+ int offset, int size, u8 value)
{
int page, nextofs, end_offset, temp, temp1;
void *ptr;
struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk;
+ if (snd_BUG_ON(offset + size > p->mem.size))
+ return -EFAULT;
+
offset += blk->offset & (PAGE_SIZE - 1);
end_offset = offset + size;
page = get_aligned_page(offset);
@@ -585,25 +588,55 @@ int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk
temp = temp1;
ptr = offset_ptr(emu, page + p->first_page, offset);
if (ptr)
- memset(ptr, 0, temp);
+ memset(ptr, value, temp);
offset = nextofs;
page++;
} while (offset < end_offset);
return 0;
}
-EXPORT_SYMBOL(snd_emu10k1_synth_bzero);
+EXPORT_SYMBOL(snd_emu10k1_synth_memset);
+
+// Note that the value is assumed to be suitably repetitive.
+static void xor_range(void *ptr, int size, u32 value)
+{
+ if ((long)ptr & 1) {
+ *(u8 *)ptr ^= (u8)value;
+ ptr++;
+ size--;
+ }
+ if (size > 1 && ((long)ptr & 2)) {
+ *(u16 *)ptr ^= (u16)value;
+ ptr += 2;
+ size -= 2;
+ }
+ while (size > 3) {
+ *(u32 *)ptr ^= value;
+ ptr += 4;
+ size -= 4;
+ }
+ if (size > 1) {
+ *(u16 *)ptr ^= (u16)value;
+ ptr += 2;
+ size -= 2;
+ }
+ if (size > 0)
+ *(u8 *)ptr ^= (u8)value;
+}
/*
- * copy_from_user(blk + offset, data, size)
+ * copy_from_user(blk + offset, data, size) ^ xor
*/
int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_memblk *blk,
- int offset, const char __user *data, int size)
+ int offset, const char __user *data, int size, u32 xor)
{
int page, nextofs, end_offset, temp, temp1;
void *ptr;
struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk;
+ if (snd_BUG_ON(offset + size > p->mem.size))
+ return -EFAULT;
+
offset += blk->offset & (PAGE_SIZE - 1);
end_offset = offset + size;
page = get_aligned_page(offset);
@@ -614,8 +647,12 @@ int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_me
if (temp1 < temp)
temp = temp1;
ptr = offset_ptr(emu, page + p->first_page, offset);
- if (ptr && copy_from_user(ptr, data, temp))
- return -EFAULT;
+ if (ptr) {
+ if (copy_from_user(ptr, data, temp))
+ return -EFAULT;
+ if (xor)
+ xor_range(ptr, temp, xor);
+ }
offset = nextofs;
data += temp;
page++;
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index f806636242ee..0da625533afc 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -128,6 +128,7 @@ config SND_HDA_SCODEC_CS35L41_I2C
select SND_SOC_CS35L41_LIB
select SND_HDA_SCODEC_CS35L41
select SND_HDA_CS_DSP_CONTROLS
+ select SND_SOC_CS_AMP_LIB
help
Say Y or M here to include CS35L41 I2C HD-audio side codec support
in snd-hda-intel driver, such as ALC287.
@@ -144,6 +145,7 @@ config SND_HDA_SCODEC_CS35L41_SPI
select SND_SOC_CS35L41_LIB
select SND_HDA_SCODEC_CS35L41
select SND_HDA_CS_DSP_CONTROLS
+ select SND_SOC_CS_AMP_LIB
help
Say Y or M here to include CS35L41 SPI HD-audio side codec support
in snd-hda-intel driver, such as ALC287.
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 13e04e1f65de..058ca0a289e4 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
-snd-hda-intel-objs := hda_intel.o
-snd-hda-tegra-objs := hda_tegra.o
+snd-hda-intel-y := hda_intel.o
+snd-hda-tegra-y := hda_tegra.o
snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
snd-hda-codec-y += hda_controller.o
@@ -13,32 +13,32 @@ snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
CFLAGS_hda_controller.o := -I$(src)
CFLAGS_hda_intel.o := -I$(src)
-snd-hda-codec-generic-objs := hda_generic.o
-snd-hda-codec-realtek-objs := patch_realtek.o
-snd-hda-codec-cmedia-objs := patch_cmedia.o
-snd-hda-codec-analog-objs := patch_analog.o
-snd-hda-codec-idt-objs := patch_sigmatel.o
-snd-hda-codec-si3054-objs := patch_si3054.o
-snd-hda-codec-cirrus-objs := patch_cirrus.o
-snd-hda-codec-cs8409-objs := patch_cs8409.o patch_cs8409-tables.o
-snd-hda-codec-ca0110-objs := patch_ca0110.o
-snd-hda-codec-ca0132-objs := patch_ca0132.o
-snd-hda-codec-conexant-objs := patch_conexant.o
-snd-hda-codec-via-objs := patch_via.o
-snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
+snd-hda-codec-generic-y := hda_generic.o
+snd-hda-codec-realtek-y := patch_realtek.o
+snd-hda-codec-cmedia-y := patch_cmedia.o
+snd-hda-codec-analog-y := patch_analog.o
+snd-hda-codec-idt-y := patch_sigmatel.o
+snd-hda-codec-si3054-y := patch_si3054.o
+snd-hda-codec-cirrus-y := patch_cirrus.o
+snd-hda-codec-cs8409-y := patch_cs8409.o patch_cs8409-tables.o
+snd-hda-codec-ca0110-y := patch_ca0110.o
+snd-hda-codec-ca0132-y := patch_ca0132.o
+snd-hda-codec-conexant-y := patch_conexant.o
+snd-hda-codec-via-y := patch_via.o
+snd-hda-codec-hdmi-y := patch_hdmi.o hda_eld.o
# side codecs
-snd-hda-cirrus-scodec-objs := cirrus_scodec.o
-snd-hda-cirrus-scodec-test-objs := cirrus_scodec_test.o
-snd-hda-scodec-cs35l41-objs := cs35l41_hda.o cs35l41_hda_property.o
-snd-hda-scodec-cs35l41-i2c-objs := cs35l41_hda_i2c.o
-snd-hda-scodec-cs35l41-spi-objs := cs35l41_hda_spi.o
-snd-hda-scodec-cs35l56-objs := cs35l56_hda.o
-snd-hda-scodec-cs35l56-i2c-objs := cs35l56_hda_i2c.o
-snd-hda-scodec-cs35l56-spi-objs := cs35l56_hda_spi.o
-snd-hda-cs-dsp-ctls-objs := hda_cs_dsp_ctl.o
-snd-hda-scodec-component-objs := hda_component.o
-snd-hda-scodec-tas2781-i2c-objs := tas2781_hda_i2c.o
+snd-hda-cirrus-scodec-y := cirrus_scodec.o
+snd-hda-cirrus-scodec-test-y := cirrus_scodec_test.o
+snd-hda-scodec-cs35l41-y := cs35l41_hda.o cs35l41_hda_property.o
+snd-hda-scodec-cs35l41-i2c-y := cs35l41_hda_i2c.o
+snd-hda-scodec-cs35l41-spi-y := cs35l41_hda_spi.o
+snd-hda-scodec-cs35l56-y := cs35l56_hda.o
+snd-hda-scodec-cs35l56-i2c-y := cs35l56_hda_i2c.o
+snd-hda-scodec-cs35l56-spi-y := cs35l56_hda_spi.o
+snd-hda-cs-dsp-ctls-y := hda_cs_dsp_ctl.o
+snd-hda-scodec-component-y := hda_component.o
+snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o
# common driver
obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
diff --git a/sound/pci/hda/cirrus_scodec_test.c b/sound/pci/hda/cirrus_scodec_test.c
index 8ae373676bd1..e925ebe21ccb 100644
--- a/sound/pci/hda/cirrus_scodec_test.c
+++ b/sound/pci/hda/cirrus_scodec_test.c
@@ -366,5 +366,6 @@ static struct kunit_suite cirrus_scodec_test_suite = {
kunit_test_suite(cirrus_scodec_test_suite);
MODULE_IMPORT_NS(SND_HDA_CIRRUS_SCODEC);
+MODULE_DESCRIPTION("KUnit test for the Cirrus side-codec library");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c
index d3fa6e136744..2b0404f6e271 100644
--- a/sound/pci/hda/cs35l41_hda.c
+++ b/sound/pci/hda/cs35l41_hda.c
@@ -22,7 +22,6 @@
#include "hda_cs_dsp_ctl.h"
#include "cs35l41_hda_property.h"
-#define CS35L41_FIRMWARE_ROOT "cirrus/"
#define CS35L41_PART "cs35l41"
#define HALO_STATE_DSP_CTL_NAME "HALO_STATE"
@@ -37,6 +36,42 @@
#define CS35L41_UUID "50d90cdc-3de4-4f18-b528-c7fe3b71f40d"
#define CS35L41_DSM_GET_MUTE 5
#define CS35L41_NOTIFY_EVENT 0x91
+#define CS35L41_TUNING_SIG 0x109A4A35
+
+enum cs35l41_tuning_param_types {
+ TUNING_PARAM_GAIN,
+};
+
+struct cs35l41_tuning_param_hdr {
+ __le32 tuning_index;
+ __le32 type;
+ __le32 size;
+} __packed;
+
+struct cs35l41_tuning_param {
+ struct cs35l41_tuning_param_hdr hdr;
+ union {
+ __le32 gain;
+ };
+} __packed;
+
+struct cs35l41_tuning_params {
+ __le32 signature;
+ __le32 version;
+ __le32 size;
+ __le32 num_entries;
+ u8 data[];
+} __packed;
+
+/* Firmware calibration controls */
+static const struct cirrus_amp_cal_controls cs35l41_calibration_controls = {
+ .alg_id = CAL_DSP_CTL_ALG,
+ .mem_region = CAL_DSP_CTL_TYPE,
+ .ambient = CAL_AMBIENT_DSP_CTL_NAME,
+ .calr = CAL_R_DSP_CTL_NAME,
+ .status = CAL_STATUS_DSP_CTL_NAME,
+ .checksum = CAL_CHECKSUM_DSP_CTL_NAME,
+};
static bool firmware_autostart = 1;
module_param(firmware_autostart, bool, 0444);
@@ -75,7 +110,7 @@ static const struct reg_sequence cs35l41_hda_config_dsp[] = {
{ CS35L41_SP_HIZ_CTRL, 0x00000003 }, // Hi-Z unused/disabled
{ CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot
{ CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot
- { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = ERR_VOL
+ { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = DSP1TX1
{ CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON
{ CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON
{ CS35L41_ASP_TX3_SRC, 0x00000028 }, // ASPTX3 SRC = VPMON
@@ -84,7 +119,7 @@ static const struct reg_sequence cs35l41_hda_config_dsp[] = {
{ CS35L41_DSP1_RX2_SRC, 0x00000008 }, // DSP1RX2 SRC = ASPRX1
{ CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON
{ CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON
- { CS35L41_DSP1_RX5_SRC, 0x00000029 }, // DSP1RX5 SRC = VBSTMON
+ { CS35L41_DSP1_RX6_SRC, 0x00000029 }, // DSP1RX6 SRC = VBSTMON
};
static const struct reg_sequence cs35l41_hda_unmute[] = {
@@ -92,11 +127,6 @@ static const struct reg_sequence cs35l41_hda_unmute[] = {
{ CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB
};
-static const struct reg_sequence cs35l41_hda_unmute_dsp[] = {
- { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB
- { CS35L41_AMP_GAIN_CTRL, 0x00000233 }, // AMP_GAIN_PCM = 17.5dB AMP_GAIN_PDM = 19.5dB
-};
-
static const struct reg_sequence cs35l41_hda_mute[] = {
{ CS35L41_AMP_GAIN_CTRL, 0x00000000 }, // AMP_GAIN_PCM 0.5 dB
{ CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM Mute
@@ -117,9 +147,30 @@ static const struct cs_dsp_client_ops client_ops = {
.control_remove = hda_cs_dsp_control_remove,
};
+static int cs35l41_request_tuning_param_file(struct cs35l41_hda *cs35l41, char *tuning_filename,
+ const struct firmware **firmware, char **filename,
+ const char *ssid)
+{
+ int ret = 0;
+
+ /* Filename is the same as the tuning file with "cfg" suffix */
+ *filename = kasprintf(GFP_KERNEL, "%scfg", tuning_filename);
+ if (*filename == NULL)
+ return -ENOMEM;
+
+ ret = firmware_request_nowarn(firmware, *filename, cs35l41->dev);
+ if (ret != 0) {
+ dev_dbg(cs35l41->dev, "Failed to request '%s'\n", *filename);
+ kfree(*filename);
+ *filename = NULL;
+ }
+
+ return ret;
+}
+
static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41,
const struct firmware **firmware, char **filename,
- const char *dir, const char *ssid, const char *amp_name,
+ const char *ssid, const char *amp_name,
int spkid, const char *filetype)
{
const char * const dsp_name = cs35l41->cs_dsp.name;
@@ -127,23 +178,23 @@ static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41,
int ret = 0;
if (spkid > -1 && ssid && amp_name)
- *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d-%s.%s", dir, CS35L41_PART,
+ *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d-%s.%s", CS35L41_PART,
dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
ssid, spkid, amp_name, filetype);
else if (spkid > -1 && ssid)
- *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d.%s", dir, CS35L41_PART,
+ *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d.%s", CS35L41_PART,
dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
ssid, spkid, filetype);
else if (ssid && amp_name)
- *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, CS35L41_PART,
+ *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-%s.%s", CS35L41_PART,
dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
ssid, amp_name, filetype);
else if (ssid)
- *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, CS35L41_PART,
+ *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s.%s", CS35L41_PART,
dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
ssid, filetype);
else
- *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, CS35L41_PART,
+ *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s.%s", CS35L41_PART,
dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
filetype);
@@ -184,13 +235,11 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.wmfw */
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
- CS35L41_FIRMWARE_ROOT,
cs35l41->acpi_subsystem_id, cs35l41->amp_name,
cs35l41->speaker_id, "wmfw");
if (!ret) {
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT,
cs35l41->acpi_subsystem_id, cs35l41->amp_name,
cs35l41->speaker_id, "bin");
if (ret)
@@ -201,12 +250,11 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
/* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
- CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
+ cs35l41->acpi_subsystem_id,
cs35l41->amp_name, -1, "wmfw");
if (!ret) {
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT,
cs35l41->acpi_subsystem_id, cs35l41->amp_name,
cs35l41->speaker_id, "bin");
if (ret)
@@ -217,18 +265,17 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
/* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
- CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
+ cs35l41->acpi_subsystem_id,
NULL, cs35l41->speaker_id, "wmfw");
if (!ret) {
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT,
cs35l41->acpi_subsystem_id,
cs35l41->amp_name, cs35l41->speaker_id, "bin");
if (ret)
/* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware,
- coeff_filename, CS35L41_FIRMWARE_ROOT,
+ coeff_filename,
cs35l41->acpi_subsystem_id, NULL,
cs35l41->speaker_id, "bin");
if (ret)
@@ -239,18 +286,17 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
/* try cirrus/part-dspN-fwtype-sub.wmfw */
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
- CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
+ cs35l41->acpi_subsystem_id,
NULL, -1, "wmfw");
if (!ret) {
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT,
cs35l41->acpi_subsystem_id, cs35l41->amp_name,
cs35l41->speaker_id, "bin");
if (ret)
/* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware,
- coeff_filename, CS35L41_FIRMWARE_ROOT,
+ coeff_filename,
cs35l41->acpi_subsystem_id, NULL,
cs35l41->speaker_id, "bin");
if (ret)
@@ -277,13 +323,13 @@ static int cs35l41_fallback_firmware_file(struct cs35l41_hda *cs35l41,
/* fallback try cirrus/part-dspN-fwtype.wmfw */
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
- CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw");
+ NULL, NULL, -1, "wmfw");
if (ret)
goto err;
/* fallback try cirrus/part-dspN-fwtype.bin */
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin");
+ NULL, NULL, -1, "bin");
if (ret) {
release_firmware(*wmfw_firmware);
kfree(*wmfw_filename);
@@ -312,12 +358,11 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41,
/* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
- CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
+ cs35l41->acpi_subsystem_id,
cs35l41->amp_name, -1, "wmfw");
if (!ret) {
/* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT,
cs35l41->acpi_subsystem_id, cs35l41->amp_name,
-1, "bin");
if (ret)
@@ -328,18 +373,16 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41,
/* try cirrus/part-dspN-fwtype-sub.wmfw */
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
- CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
+ cs35l41->acpi_subsystem_id,
NULL, -1, "wmfw");
if (!ret) {
/* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT,
cs35l41->acpi_subsystem_id,
cs35l41->amp_name, -1, "bin");
if (ret)
/* try cirrus/part-dspN-fwtype-sub.bin */
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT,
cs35l41->acpi_subsystem_id, NULL, -1,
"bin");
if (ret)
@@ -361,95 +404,162 @@ fallback:
coeff_firmware, coeff_filename);
}
-#if IS_ENABLED(CONFIG_EFI)
-static int cs35l41_apply_calibration(struct cs35l41_hda *cs35l41, __be32 ambient, __be32 r0,
- __be32 status, __be32 checksum)
+
+static void cs35l41_hda_apply_calibration(struct cs35l41_hda *cs35l41)
+{
+ int ret;
+
+ if (!cs35l41->cal_data_valid)
+ return;
+
+ ret = cs_amp_write_cal_coeffs(&cs35l41->cs_dsp, &cs35l41_calibration_controls,
+ &cs35l41->cal_data);
+ if (ret < 0)
+ dev_warn(cs35l41->dev, "Failed to apply calibration: %d\n", ret);
+ else
+ dev_info(cs35l41->dev, "Calibration applied: R0=%d\n", cs35l41->cal_data.calR);
+}
+
+static int cs35l41_read_silicon_uid(struct cs35l41_hda *cs35l41, u64 *uid)
{
+ u32 tmp;
int ret;
- ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_AMBIENT_DSP_CTL_NAME, CAL_DSP_CTL_TYPE,
- CAL_DSP_CTL_ALG, &ambient, 4);
+ ret = regmap_read(cs35l41->regmap, CS35L41_DIE_STS2, &tmp);
if (ret) {
- dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_AMBIENT_DSP_CTL_NAME,
- ret);
+ dev_err(cs35l41->dev, "Cannot obtain CS35L41_DIE_STS2: %d\n", ret);
return ret;
}
- ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_R_DSP_CTL_NAME, CAL_DSP_CTL_TYPE,
- CAL_DSP_CTL_ALG, &r0, 4);
+
+ *uid = tmp;
+ *uid <<= 32;
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DIE_STS1, &tmp);
if (ret) {
- dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_R_DSP_CTL_NAME, ret);
+ dev_err(cs35l41->dev, "Cannot obtain CS35L41_DIE_STS1: %d\n", ret);
return ret;
}
- ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_STATUS_DSP_CTL_NAME, CAL_DSP_CTL_TYPE,
- CAL_DSP_CTL_ALG, &status, 4);
- if (ret) {
- dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_STATUS_DSP_CTL_NAME,
- ret);
+
+ *uid |= tmp;
+
+ dev_dbg(cs35l41->dev, "UniqueID = %#llx\n", *uid);
+
+ return 0;
+}
+
+static int cs35l41_get_calibration(struct cs35l41_hda *cs35l41)
+{
+ u64 silicon_uid;
+ int ret;
+
+ ret = cs35l41_read_silicon_uid(cs35l41, &silicon_uid);
+ if (ret < 0)
return ret;
- }
- ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_CHECKSUM_DSP_CTL_NAME, CAL_DSP_CTL_TYPE,
- CAL_DSP_CTL_ALG, &checksum, 4);
- if (ret) {
- dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_CHECKSUM_DSP_CTL_NAME,
- ret);
+
+ ret = cs_amp_get_efi_calibration_data(cs35l41->dev, silicon_uid,
+ cs35l41->index,
+ &cs35l41->cal_data);
+
+ /* Only return an error status if probe should be aborted */
+ if ((ret == -ENOENT) || (ret == -EOVERFLOW))
+ return 0;
+
+ if (ret < 0)
return ret;
- }
+
+ cs35l41->cal_data_valid = true;
return 0;
}
-static int cs35l41_save_calibration(struct cs35l41_hda *cs35l41)
+
+static void cs35l41_set_default_tuning_params(struct cs35l41_hda *cs35l41)
{
- static efi_guid_t efi_guid = EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe,
- 0x5a, 0xa3, 0x5d, 0xb3);
- static efi_char16_t efi_name[] = L"CirrusSmartAmpCalibrationData";
- const struct cs35l41_amp_efi_data *efi_data;
- const struct cs35l41_amp_cal_data *cl;
- unsigned long data_size = 0;
- efi_status_t status;
- int ret = 0;
- u8 *data = NULL;
- u32 attr;
+ cs35l41->tuning_gain = DEFAULT_AMP_GAIN_PCM;
+}
- /* Get real size of UEFI variable */
- status = efi.get_variable(efi_name, &efi_guid, &attr, &data_size, data);
- if (status == EFI_BUFFER_TOO_SMALL) {
- ret = -ENODEV;
- /* Allocate data buffer of data_size bytes */
- data = vmalloc(data_size);
- if (!data)
- return -ENOMEM;
- /* Get variable contents into buffer */
- status = efi.get_variable(efi_name, &efi_guid, &attr, &data_size, data);
- if (status == EFI_SUCCESS) {
- efi_data = (struct cs35l41_amp_efi_data *)data;
- dev_dbg(cs35l41->dev, "Calibration: Size=%d, Amp Count=%d\n",
- efi_data->size, efi_data->count);
- if (efi_data->count > cs35l41->index) {
- cl = &efi_data->data[cs35l41->index];
- dev_dbg(cs35l41->dev,
- "Calibration: Ambient=%02x, Status=%02x, R0=%d\n",
- cl->calAmbient, cl->calStatus, cl->calR);
-
- /* Calibration can only be applied whilst the DSP is not running */
- ret = cs35l41_apply_calibration(cs35l41,
- cpu_to_be32(cl->calAmbient),
- cpu_to_be32(cl->calR),
- cpu_to_be32(cl->calStatus),
- cpu_to_be32(cl->calR + 1));
- }
+static int cs35l41_read_tuning_params(struct cs35l41_hda *cs35l41, const struct firmware *firmware)
+{
+ struct cs35l41_tuning_params *params;
+ unsigned int offset = 0;
+ unsigned int end;
+ int i;
+
+ params = (void *)&firmware->data[0];
+
+ if (le32_to_cpu(params->size) != firmware->size) {
+ dev_err(cs35l41->dev, "Wrong Size for Tuning Param file. Expected %d got %zu\n",
+ le32_to_cpu(params->size), firmware->size);
+ return -EINVAL;
+ }
+
+ if (le32_to_cpu(params->version) != 1) {
+ dev_err(cs35l41->dev, "Unsupported Tuning Param Version: %d\n",
+ le32_to_cpu(params->version));
+ return -EINVAL;
+ }
+
+ if (le32_to_cpu(params->signature) != CS35L41_TUNING_SIG) {
+ dev_err(cs35l41->dev,
+ "Mismatched Signature for Tuning Param file. Expected %#x got %#x\n",
+ CS35L41_TUNING_SIG, le32_to_cpu(params->signature));
+ return -EINVAL;
+ }
+
+ end = firmware->size - sizeof(struct cs35l41_tuning_params);
+
+ for (i = 0; i < le32_to_cpu(params->num_entries); i++) {
+ struct cs35l41_tuning_param *param;
+
+ if ((offset >= end) || ((offset + sizeof(struct cs35l41_tuning_param_hdr)) >= end))
+ return -EFAULT;
+
+ param = (void *)&params->data[offset];
+ offset += le32_to_cpu(param->hdr.size);
+
+ if (offset > end)
+ return -EFAULT;
+
+ switch (le32_to_cpu(param->hdr.type)) {
+ case TUNING_PARAM_GAIN:
+ cs35l41->tuning_gain = le32_to_cpu(param->gain);
+ dev_dbg(cs35l41->dev, "Applying Gain: %d\n", cs35l41->tuning_gain);
+ break;
+ default:
+ break;
}
- vfree(data);
}
- return ret;
+
+ return 0;
}
-#else
-static int cs35l41_save_calibration(struct cs35l41_hda *cs35l41)
+
+static int cs35l41_load_tuning_params(struct cs35l41_hda *cs35l41, char *tuning_filename)
{
- dev_warn(cs35l41->dev, "Calibration not supported without EFI support.\n");
- return 0;
+ const struct firmware *tuning_param_file = NULL;
+ char *tuning_param_filename = NULL;
+ int ret;
+
+ ret = cs35l41_request_tuning_param_file(cs35l41, tuning_filename, &tuning_param_file,
+ &tuning_param_filename, cs35l41->acpi_subsystem_id);
+ if (ret) {
+ dev_dbg(cs35l41->dev, "Missing Tuning Param for file: %s: %d\n", tuning_filename,
+ ret);
+ return 0;
+ }
+
+ ret = cs35l41_read_tuning_params(cs35l41, tuning_param_file);
+ if (ret) {
+ dev_err(cs35l41->dev, "Error reading Tuning Params from file: %s: %d\n",
+ tuning_param_filename, ret);
+ /* Reset to default Tuning Parameters */
+ cs35l41_set_default_tuning_params(cs35l41);
+ }
+
+ release_firmware(tuning_param_file);
+ kfree(tuning_param_filename);
+
+ return ret;
}
-#endif
static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41)
{
@@ -470,27 +580,35 @@ static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41)
cs35l41->halo_initialized = true;
}
+ cs35l41_set_default_tuning_params(cs35l41);
+
ret = cs35l41_request_firmware_files(cs35l41, &wmfw_firmware, &wmfw_filename,
&coeff_firmware, &coeff_filename);
if (ret < 0)
return ret;
dev_dbg(cs35l41->dev, "Loading WMFW Firmware: %s\n", wmfw_filename);
- if (coeff_filename)
+ if (coeff_filename) {
dev_dbg(cs35l41->dev, "Loading Coefficient File: %s\n", coeff_filename);
- else
+ ret = cs35l41_load_tuning_params(cs35l41, coeff_filename);
+ if (ret)
+ dev_warn(cs35l41->dev, "Unable to load Tuning Parameters: %d\n", ret);
+ } else {
dev_warn(cs35l41->dev, "No Coefficient File available.\n");
+ }
ret = cs_dsp_power_up(dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename,
hda_cs_dsp_fw_ids[cs35l41->firmware_type]);
if (ret)
- goto err_release;
+ goto err;
cs35l41_add_controls(cs35l41);
- ret = cs35l41_save_calibration(cs35l41);
+ cs35l41_hda_apply_calibration(cs35l41);
-err_release:
+err:
+ if (ret)
+ cs35l41_set_default_tuning_params(cs35l41);
release_firmware(wmfw_firmware);
release_firmware(coeff_firmware);
kfree(wmfw_filename);
@@ -503,6 +621,7 @@ static void cs35l41_shutdown_dsp(struct cs35l41_hda *cs35l41)
{
struct cs_dsp *dsp = &cs35l41->cs_dsp;
+ cs35l41_set_default_tuning_params(cs35l41);
cs_dsp_stop(dsp);
cs_dsp_power_down(dsp);
dev_dbg(cs35l41->dev, "Unloaded Firmware\n");
@@ -553,6 +672,10 @@ static void cs35l41_hda_play_start(struct device *dev)
if (cs35l41->cs_dsp.running) {
regmap_multi_reg_write(reg, cs35l41_hda_config_dsp,
ARRAY_SIZE(cs35l41_hda_config_dsp));
+ if (cs35l41->hw_cfg.bst_type == CS35L41_INT_BOOST)
+ regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VPMON);
+ else
+ regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VBSTMON);
regmap_update_bits(reg, CS35L41_PWR_CTRL2,
CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK,
1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT);
@@ -570,6 +693,7 @@ static void cs35l41_mute(struct device *dev, bool mute)
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
struct regmap *reg = cs35l41->regmap;
+ unsigned int amp_gain;
dev_dbg(dev, "Mute(%d:%d) Playback Started: %d\n", mute, cs35l41->mute_override,
cs35l41->playback_started);
@@ -581,8 +705,13 @@ static void cs35l41_mute(struct device *dev, bool mute)
} else {
dev_dbg(dev, "Unmuting\n");
if (cs35l41->cs_dsp.running) {
- regmap_multi_reg_write(reg, cs35l41_hda_unmute_dsp,
- ARRAY_SIZE(cs35l41_hda_unmute_dsp));
+ dev_dbg(dev, "Using Tuned Gain: %d\n", cs35l41->tuning_gain);
+ amp_gain = (cs35l41->tuning_gain << CS35L41_AMP_GAIN_PCM_SHIFT) |
+ (DEFAULT_AMP_GAIN_PDM << CS35L41_AMP_GAIN_PDM_SHIFT);
+
+ /* AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB */
+ regmap_write(reg, CS35L41_AMP_DIG_VOL_CTRL, 0x00008000);
+ regmap_write(reg, CS35L41_AMP_GAIN_CTRL, amp_gain);
} else {
regmap_multi_reg_write(reg, cs35l41_hda_unmute,
ARRAY_SIZE(cs35l41_hda_unmute));
@@ -1056,6 +1185,9 @@ static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41)
goto clean_dsp;
}
+ dev_info(cs35l41->dev, "Firmware Loaded - Type: %s, Gain: %d\n",
+ hda_cs_dsp_fw_ids[cs35l41->firmware_type], cs35l41->tuning_gain);
+
return 0;
clean_dsp:
@@ -1461,13 +1593,56 @@ static struct regmap_irq_chip cs35l41_regmap_irq_chip = {
.runtime_pm = true,
};
+static void cs35l41_configure_interrupt(struct cs35l41_hda *cs35l41, int irq_pol)
+{
+ int irq;
+ int ret;
+ int i;
+
+ if (!cs35l41->irq) {
+ dev_warn(cs35l41->dev, "No Interrupt Found");
+ goto err;
+ }
+
+ ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq,
+ IRQF_ONESHOT | IRQF_SHARED | irq_pol,
+ 0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data);
+ if (ret) {
+ dev_dbg(cs35l41->dev, "Unable to add IRQ Chip: %d.", ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) {
+ irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq);
+ if (irq < 0) {
+ ret = irq;
+ dev_dbg(cs35l41->dev, "Unable to map IRQ %s: %d.", cs35l41_irqs[i].name,
+ ret);
+ goto err;
+ }
+
+ ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL,
+ cs35l41_irqs[i].handler,
+ IRQF_ONESHOT | IRQF_SHARED | irq_pol,
+ cs35l41_irqs[i].name, cs35l41);
+ if (ret) {
+ dev_dbg(cs35l41->dev, "Unable to allocate IRQ %s:: %d.",
+ cs35l41_irqs[i].name, ret);
+ goto err;
+ }
+ }
+ return;
+err:
+ dev_warn(cs35l41->dev,
+ "IRQ Config Failed. Amp errors may not be recoverable without reboot.");
+}
+
static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41)
{
struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
bool using_irq = false;
- int irq, irq_pol;
+ int irq_pol;
int ret;
- int i;
if (!cs35l41->hw_cfg.valid)
return -EINVAL;
@@ -1510,26 +1685,8 @@ static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41)
irq_pol = cs35l41_gpio_config(cs35l41->regmap, hw_cfg);
- if (cs35l41->irq && using_irq) {
- ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq,
- IRQF_ONESHOT | IRQF_SHARED | irq_pol,
- 0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data);
- if (ret)
- return ret;
-
- for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) {
- irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq);
- if (irq < 0)
- return irq;
-
- ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL,
- cs35l41_irqs[i].handler,
- IRQF_ONESHOT | IRQF_SHARED | irq_pol,
- cs35l41_irqs[i].name, cs35l41);
- if (ret)
- return ret;
- }
- }
+ if (using_irq)
+ cs35l41_configure_interrupt(cs35l41, irq_pol);
return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos);
}
@@ -1808,6 +1965,10 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
if (ret)
goto err;
+ ret = cs35l41_get_calibration(cs35l41);
+ if (ret && ret != -ENOENT)
+ goto err;
+
cs35l41_mute(cs35l41->dev, true);
INIT_WORK(&cs35l41->fw_load_work, cs35l41_fw_load_work);
@@ -1888,6 +2049,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, SND_HDA_SCODEC_CS35L41);
MODULE_DESCRIPTION("CS35L41 HDA Driver");
MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS);
+MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB);
MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(FW_CS_DSP);
diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h
index 43d55292b327..b0bebb778462 100644
--- a/sound/pci/hda/cs35l41_hda.h
+++ b/sound/pci/hda/cs35l41_hda.h
@@ -16,11 +16,14 @@
#include <linux/gpio/consumer.h>
#include <linux/device.h>
#include <sound/cs35l41.h>
+#include <sound/cs-amp-lib.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/wmfw.h>
#define CS35L41_MAX_ACCEPTABLE_SPI_SPEED_HZ 1000000
+#define DEFAULT_AMP_GAIN_PCM 17 /* 17.5dB Gain */
+#define DEFAULT_AMP_GAIN_PDM 19 /* 19.5dB Gain */
struct cs35l41_amp_cal_data {
u32 calTarget[2];
@@ -83,6 +86,9 @@ struct cs35l41_hda {
bool mute_override;
enum control_bus control_bus;
bool bypass_fw;
+ unsigned int tuning_gain;
+ struct cirrus_amp_cal_data cal_data;
+ bool cal_data_valid;
};
diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c
index 8fb688e41414..6a7a6d486916 100644
--- a/sound/pci/hda/cs35l41_hda_property.c
+++ b/sound/pci/hda/cs35l41_hda_property.c
@@ -70,6 +70,8 @@ static const struct cs35l41_config cs35l41_config_table[] = {
{ "103C8C15", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
{ "103C8C16", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
{ "103C8C17", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
+ { "103C8C4D", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8C4E", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
{ "103C8C4F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
{ "103C8C50", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
{ "103C8C51", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
@@ -95,6 +97,7 @@ static const struct cs35l41_config cs35l41_config_table[] = {
{ "10431863", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
{ "104318D3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
{ "10431A83", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431B93", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
{ "10431C9F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
{ "10431CAF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
{ "10431CCF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
@@ -108,10 +111,17 @@ static const struct cs35l41_config cs35l41_config_table[] = {
{ "10431F12", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
{ "10431F1F", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 },
{ "10431F62", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "10433A20", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10433A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10433A40", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10433A50", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
{ "10433A60", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "17AA3865", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "17AA3866", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "17AA386E", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
{ "17AA386F", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
- { "17AA3877", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
- { "17AA3878", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "17AA3877", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "17AA3878", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
{ "17AA38A9", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
{ "17AA38AB", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
{ "17AA38B4", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
@@ -457,6 +467,8 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
{ "CSC3551", "103C8C15", generic_dsd_config },
{ "CSC3551", "103C8C16", generic_dsd_config },
{ "CSC3551", "103C8C17", generic_dsd_config },
+ { "CSC3551", "103C8C4D", generic_dsd_config },
+ { "CSC3551", "103C8C4E", generic_dsd_config },
{ "CSC3551", "103C8C4F", generic_dsd_config },
{ "CSC3551", "103C8C50", generic_dsd_config },
{ "CSC3551", "103C8C51", generic_dsd_config },
@@ -486,6 +498,7 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
{ "CSC3551", "10431863", generic_dsd_config },
{ "CSC3551", "104318D3", generic_dsd_config },
{ "CSC3551", "10431A83", generic_dsd_config },
+ { "CSC3551", "10431B93", generic_dsd_config },
{ "CSC3551", "10431C9F", generic_dsd_config },
{ "CSC3551", "10431CAF", generic_dsd_config },
{ "CSC3551", "10431CCF", generic_dsd_config },
@@ -499,7 +512,14 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
{ "CSC3551", "10431F12", generic_dsd_config },
{ "CSC3551", "10431F1F", generic_dsd_config },
{ "CSC3551", "10431F62", generic_dsd_config },
+ { "CSC3551", "10433A20", generic_dsd_config },
+ { "CSC3551", "10433A30", generic_dsd_config },
+ { "CSC3551", "10433A40", generic_dsd_config },
+ { "CSC3551", "10433A50", generic_dsd_config },
{ "CSC3551", "10433A60", generic_dsd_config },
+ { "CSC3551", "17AA3865", generic_dsd_config },
+ { "CSC3551", "17AA3866", generic_dsd_config },
+ { "CSC3551", "17AA386E", generic_dsd_config },
{ "CSC3551", "17AA386F", generic_dsd_config },
{ "CSC3551", "17AA3877", generic_dsd_config },
{ "CSC3551", "17AA3878", generic_dsd_config },
diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c
index 558c1f38fe97..11b0570ff56d 100644
--- a/sound/pci/hda/cs35l56_hda.c
+++ b/sound/pci/hda/cs35l56_hda.c
@@ -732,8 +732,6 @@ static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *
if (cs35l56->base.fw_patched)
cs_dsp_power_down(&cs35l56->cs_dsp);
- cs_dsp_remove(&cs35l56->cs_dsp);
-
if (comps[cs35l56->index].dev == dev)
memset(&comps[cs35l56->index], 0, sizeof(*comps));
@@ -1035,7 +1033,7 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id)
ARRAY_SIZE(cs35l56_hda_dai_config));
ret = cs35l56_force_sync_asp1_registers_from_cache(&cs35l56->base);
if (ret)
- goto err;
+ goto dsp_err;
/*
* By default only enable one ASP1TXn, where n=amplifier index,
@@ -1061,6 +1059,8 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id)
pm_err:
pm_runtime_disable(cs35l56->base.dev);
+dsp_err:
+ cs_dsp_remove(&cs35l56->cs_dsp);
err:
gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
@@ -1078,6 +1078,8 @@ void cs35l56_hda_remove(struct device *dev)
component_del(cs35l56->base.dev, &cs35l56_hda_comp_ops);
+ cs_dsp_remove(&cs35l56->cs_dsp);
+
kfree(cs35l56->system_name);
pm_runtime_put_noidle(cs35l56->base.dev);
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 2cac337f5263..325e8f0b99a8 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -616,7 +616,6 @@ void snd_hda_shutup_pins(struct hda_codec *codec)
}
EXPORT_SYMBOL_GPL(snd_hda_shutup_pins);
-#ifdef CONFIG_PM
/* Restore the pin controls cleared previously via snd_hda_shutup_pins() */
static void restore_shutup_pins(struct hda_codec *codec)
{
@@ -634,7 +633,6 @@ static void restore_shutup_pins(struct hda_codec *codec)
}
codec->pins_shutup = 0;
}
-#endif
static void hda_jackpoll_work(struct work_struct *work)
{
@@ -1001,9 +999,7 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
codec->card = card;
codec->addr = codec_addr;
-#ifdef CONFIG_PM
codec->power_jiffies = jiffies;
-#endif
snd_hda_sysfs_init(codec);
@@ -1238,7 +1234,6 @@ static void purify_inactive_streams(struct hda_codec *codec)
}
}
-#ifdef CONFIG_PM
/* clean up all streams; called from suspend */
static void hda_cleanup_all_streams(struct hda_codec *codec)
{
@@ -1250,7 +1245,6 @@ static void hda_cleanup_all_streams(struct hda_codec *codec)
really_cleanup_stream(codec, p);
}
}
-#endif
/*
* amp access functions
@@ -2858,7 +2852,6 @@ static void hda_exec_init_verbs(struct hda_codec *codec)
static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
#endif
-#ifdef CONFIG_PM
/* update the power on/off account with the current jiffies */
static void update_power_acct(struct hda_codec *codec, bool on)
{
@@ -2966,9 +2959,6 @@ static int hda_codec_runtime_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM */
-
-#ifdef CONFIG_PM_SLEEP
static int hda_codec_pm_prepare(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
@@ -3023,22 +3013,19 @@ static int hda_codec_pm_restore(struct device *dev)
dev->power.power_state = PMSG_RESTORE;
return pm_runtime_force_resume(dev);
}
-#endif /* CONFIG_PM_SLEEP */
/* referred in hda_bind.c */
const struct dev_pm_ops hda_codec_driver_pm = {
-#ifdef CONFIG_PM_SLEEP
- .prepare = hda_codec_pm_prepare,
- .complete = hda_codec_pm_complete,
- .suspend = hda_codec_pm_suspend,
- .resume = hda_codec_pm_resume,
- .freeze = hda_codec_pm_freeze,
- .thaw = hda_codec_pm_thaw,
- .poweroff = hda_codec_pm_suspend,
- .restore = hda_codec_pm_restore,
-#endif /* CONFIG_PM_SLEEP */
- SET_RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume,
- NULL)
+ .prepare = pm_sleep_ptr(hda_codec_pm_prepare),
+ .complete = pm_sleep_ptr(hda_codec_pm_complete),
+ .suspend = pm_sleep_ptr(hda_codec_pm_suspend),
+ .resume = pm_sleep_ptr(hda_codec_pm_resume),
+ .freeze = pm_sleep_ptr(hda_codec_pm_freeze),
+ .thaw = pm_sleep_ptr(hda_codec_pm_thaw),
+ .poweroff = pm_sleep_ptr(hda_codec_pm_suspend),
+ .restore = pm_sleep_ptr(hda_codec_pm_restore),
+ .runtime_suspend = pm_ptr(hda_codec_runtime_suspend),
+ .runtime_resume = pm_ptr(hda_codec_runtime_resume),
};
/* suspend the codec at shutdown; called from driver's shutdown callback */
@@ -3425,7 +3412,6 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls);
-#ifdef CONFIG_PM
/**
* snd_hda_codec_set_power_save - Configure codec's runtime PM
* @codec: codec device to configure
@@ -3516,7 +3502,6 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power);
-#endif
/*
* input MUX helper
@@ -4060,12 +4045,10 @@ void snd_hda_bus_reset_codecs(struct hda_bus *bus)
/* FIXME: maybe a better way needed for forced reset */
if (current_work() != &codec->jackpoll_work.work)
cancel_delayed_work_sync(&codec->jackpoll_work);
-#ifdef CONFIG_PM
if (hda_codec_is_power_on(codec)) {
hda_call_codec_suspend(codec);
hda_call_codec_resume(codec);
}
-#endif
}
}
diff --git a/sound/pci/hda/hda_component.c b/sound/pci/hda/hda_component.c
index cd299d7d84ba..d02589014a3f 100644
--- a/sound/pci/hda/hda_component.c
+++ b/sound/pci/hda/hda_component.c
@@ -123,6 +123,21 @@ static int hda_comp_match_dev_name(struct device *dev, void *data)
return !strcmp(d + n, tmp);
}
+int hda_component_manager_bind(struct hda_codec *cdc,
+ struct hda_component *comps, int count)
+{
+ int i;
+
+ /* Init shared data */
+ for (i = 0; i < count; ++i) {
+ memset(&comps[i], 0, sizeof(comps[i]));
+ comps[i].codec = cdc;
+ }
+
+ return component_bind_all(hda_codec_dev(cdc), comps);
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind, SND_HDA_SCODEC_COMPONENT);
+
int hda_component_manager_init(struct hda_codec *cdc,
struct hda_component *comps, int count,
const char *bus, const char *hid,
@@ -143,7 +158,6 @@ int hda_component_manager_init(struct hda_codec *cdc,
sm->hid = hid;
sm->match_str = match_str;
sm->index = i;
- comps[i].codec = cdc;
component_match_add(dev, &match, hda_comp_match_dev_name, sm);
}
diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h
index c80a66691b5d..c70b3de68ab2 100644
--- a/sound/pci/hda/hda_component.h
+++ b/sound/pci/hda/hda_component.h
@@ -75,11 +75,8 @@ int hda_component_manager_init(struct hda_codec *cdc,
void hda_component_manager_free(struct hda_codec *cdc,
const struct component_master_ops *ops);
-static inline int hda_component_manager_bind(struct hda_codec *cdc,
- struct hda_component *comps)
-{
- return component_bind_all(hda_codec_dev(cdc), comps);
-}
+int hda_component_manager_bind(struct hda_codec *cdc,
+ struct hda_component *comps, int count);
static inline void hda_component_manager_unbind(struct hda_codec *cdc,
struct hda_component *comps)
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index 206306a0eb82..766734dc5be2 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -3,7 +3,7 @@
*
* Implementation of primary alsa driver code base for Intel HD Audio.
*
- * Copyright(c) 2004 Intel Corporation. All rights reserved.
+ * Copyright(c) 2004 Intel Corporation
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
* PeiSen Hou <pshou@realtek.com.tw>
@@ -914,7 +914,7 @@ static int azx_send_cmd(struct hdac_bus *bus, unsigned int val)
if (chip->disabled)
return 0;
- if (chip->single_cmd)
+ if (chip->single_cmd || bus->use_pio_for_commands)
return azx_single_send_cmd(bus, val);
else
return snd_hdac_bus_send_cmd(bus, val);
@@ -928,7 +928,7 @@ static int azx_get_response(struct hdac_bus *bus, unsigned int addr,
if (chip->disabled)
return 0;
- if (chip->single_cmd)
+ if (chip->single_cmd || bus->use_pio_for_commands)
return azx_single_get_response(bus, addr, res);
else
return azx_rirb_get_response(bus, addr, res);
@@ -1075,11 +1075,9 @@ irqreturn_t azx_interrupt(int irq, void *dev_id)
bool active, handled = false;
int repeat = 0; /* count for avoiding endless loop */
-#ifdef CONFIG_PM
if (azx_has_pm_runtime(chip))
if (!pm_runtime_active(chip->card->dev))
return IRQ_NONE;
-#endif
spin_lock(&bus->reg_lock);
@@ -1188,6 +1186,9 @@ int azx_bus_init(struct azx *chip, const char *model)
if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY)
bus->core.align_bdle_4k = true;
+ if (chip->driver_caps & AZX_DCAPS_PIO_COMMANDS)
+ bus->core.use_pio_for_commands = true;
+
/* enable sync_write flag for stable communication as default */
bus->core.sync_write = 1;
diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h
index 8556031bcd68..c2d0109866e6 100644
--- a/sound/pci/hda/hda_controller.h
+++ b/sound/pci/hda/hda_controller.h
@@ -45,6 +45,7 @@
#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */
#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */
#define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */
+#define AZX_DCAPS_PIO_COMMANDS (1 << 31) /* Use PIO instead of CORB for commands */
enum {
AZX_SNOOP_TYPE_NONE,
diff --git a/sound/pci/hda/hda_cs_dsp_ctl.c b/sound/pci/hda/hda_cs_dsp_ctl.c
index 7f2d35cf245b..e6e876998e71 100644
--- a/sound/pci/hda/hda_cs_dsp_ctl.c
+++ b/sound/pci/hda/hda_cs_dsp_ctl.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <sound/soc.h>
+#include <linux/cleanup.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/wmfw.h>
#include "hda_cs_dsp_ctl.h"
@@ -87,11 +88,23 @@ static unsigned int wmfw_convert_flags(unsigned int in)
return out;
}
-static void hda_cs_dsp_add_kcontrol(struct hda_cs_dsp_coeff_ctl *ctl, const char *name)
+static void hda_cs_dsp_free_kcontrol(struct snd_kcontrol *kctl)
{
+ struct hda_cs_dsp_coeff_ctl *ctl = (struct hda_cs_dsp_coeff_ctl *)snd_kcontrol_chip(kctl);
struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
+
+ /* NULL priv to prevent a double-free in hda_cs_dsp_control_remove() */
+ cs_ctl->priv = NULL;
+ kfree(ctl);
+}
+
+static void hda_cs_dsp_add_kcontrol(struct cs_dsp_coeff_ctl *cs_ctl,
+ const struct hda_cs_dsp_ctl_info *info,
+ const char *name)
+{
struct snd_kcontrol_new kcontrol = {0};
struct snd_kcontrol *kctl;
+ struct hda_cs_dsp_coeff_ctl *ctl __free(kfree) = NULL;
int ret = 0;
if (cs_ctl->len > ADSP_MAX_STD_CTRL_SIZE) {
@@ -100,6 +113,13 @@ static void hda_cs_dsp_add_kcontrol(struct hda_cs_dsp_coeff_ctl *ctl, const char
return;
}
+ ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
+ if (!ctl)
+ return;
+
+ ctl->cs_ctl = cs_ctl;
+ ctl->card = info->card;
+
kcontrol.name = name;
kcontrol.info = hda_cs_dsp_coeff_info;
kcontrol.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
@@ -107,20 +127,22 @@ static void hda_cs_dsp_add_kcontrol(struct hda_cs_dsp_coeff_ctl *ctl, const char
kcontrol.get = hda_cs_dsp_coeff_get;
kcontrol.put = hda_cs_dsp_coeff_put;
- /* Save ctl inside private_data, ctl is owned by cs_dsp,
- * and will be freed when cs_dsp removes the control */
kctl = snd_ctl_new1(&kcontrol, (void *)ctl);
if (!kctl)
return;
- ret = snd_ctl_add(ctl->card, kctl);
+ kctl->private_free = hda_cs_dsp_free_kcontrol;
+ ctl->kctl = kctl;
+
+ /* snd_ctl_add() calls our private_free on error, which will kfree(ctl) */
+ cs_ctl->priv = no_free_ptr(ctl);
+ ret = snd_ctl_add(info->card, kctl);
if (ret) {
dev_err(cs_ctl->dsp->dev, "Failed to add KControl %s = %d\n", kcontrol.name, ret);
return;
}
dev_dbg(cs_ctl->dsp->dev, "Added KControl: %s\n", kcontrol.name);
- ctl->kctl = kctl;
}
static void hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl,
@@ -128,7 +150,6 @@ static void hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl,
{
struct cs_dsp *cs_dsp = cs_ctl->dsp;
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
- struct hda_cs_dsp_coeff_ctl *ctl;
const char *region_name;
int ret;
@@ -153,15 +174,7 @@ static void hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl,
" %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip);
}
- ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
- if (!ctl)
- return;
-
- ctl->cs_ctl = cs_ctl;
- ctl->card = info->card;
- cs_ctl->priv = ctl;
-
- hda_cs_dsp_add_kcontrol(ctl, name);
+ hda_cs_dsp_add_kcontrol(cs_ctl, info, name);
}
void hda_cs_dsp_add_controls(struct cs_dsp *dsp, const struct hda_cs_dsp_ctl_info *info)
@@ -193,7 +206,9 @@ void hda_cs_dsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl)
{
struct hda_cs_dsp_coeff_ctl *ctl = cs_ctl->priv;
- kfree(ctl);
+ /* ctl and kctl may already have been removed by ALSA private_free */
+ if (ctl && ctl->kctl)
+ snd_ctl_remove(ctl->card, ctl->kctl);
}
EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_control_remove, SND_HDA_CS_DSP_CONTROLS);
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index de2a3d08c73c..f64d9dc197a3 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -6021,7 +6021,6 @@ void snd_hda_gen_free(struct hda_codec *codec)
}
EXPORT_SYMBOL_GPL(snd_hda_gen_free);
-#ifdef CONFIG_PM
/**
* snd_hda_gen_check_power_status - check the loopback power save state
* @codec: the HDA codec
@@ -6035,7 +6034,6 @@ int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid)
return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
}
EXPORT_SYMBOL_GPL(snd_hda_gen_check_power_status);
-#endif
/*
@@ -6048,9 +6046,7 @@ static const struct hda_codec_ops generic_patch_ops = {
.init = snd_hda_gen_init,
.free = snd_hda_gen_free,
.unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
.check_power_status = snd_hda_gen_check_power_status,
-#endif
};
/*
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index a8eea8367629..8f5ecf740c49 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -340,9 +340,7 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec,
struct hda_jack_callback *jack);
void snd_hda_gen_update_outputs(struct hda_codec *codec);
-#ifdef CONFIG_PM
int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid);
-#endif
unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec,
hda_nid_t nid,
unsigned int power_state);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 1b550c42db09..3500108f6ba3 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -4,7 +4,7 @@
* hda_intel.c - Implementation of primary alsa driver code base
* for Intel HD Audio.
*
- * Copyright(c) 2004 Intel Corporation. All rights reserved.
+ * Copyright(c) 2004 Intel Corporation
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
* PeiSen Hou <pshou@realtek.com.tw>
@@ -186,8 +186,10 @@ MODULE_PARM_DESC(pm_blacklist, "Enable power-management denylist");
static bool power_save_controller = 1;
module_param(power_save_controller, bool, 0644);
MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
-#else
+#else /* CONFIG_PM */
#define power_save 0
+#define pm_blacklist false
+#define power_save_controller false
#endif /* CONFIG_PM */
static int align_buffer_size = -1;
@@ -289,6 +291,9 @@ enum {
#define AZX_DCAPS_INTEL_BROXTON AZX_DCAPS_INTEL_SKYLAKE
+#define AZX_DCAPS_INTEL_LNL \
+ (AZX_DCAPS_INTEL_SKYLAKE | AZX_DCAPS_PIO_COMMANDS)
+
/* quirks for ATI SB / AMD Hudson */
#define AZX_DCAPS_PRESET_ATI_SB \
(AZX_DCAPS_NO_TCSEL | AZX_DCAPS_POSFIX_LPIB |\
@@ -890,7 +895,6 @@ static void __azx_shutdown_chip(struct azx *chip, bool skip_link_reset)
display_power(chip, false);
}
-#ifdef CONFIG_PM
static DEFINE_MUTEX(card_list_lock);
static LIST_HEAD(card_list);
@@ -916,7 +920,7 @@ static void azx_del_card_list(struct azx *chip)
}
/* trigger power-save check at writing parameter */
-static int param_set_xint(const char *val, const struct kernel_param *kp)
+static int __maybe_unused param_set_xint(const char *val, const struct kernel_param *kp)
{
struct hda_intel *hda;
struct azx *chip;
@@ -987,7 +991,6 @@ static void __azx_runtime_resume(struct azx *chip)
display_power(chip, false);
}
-#ifdef CONFIG_PM_SLEEP
static int azx_prepare(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
@@ -1046,7 +1049,7 @@ static int azx_suspend(struct device *dev)
return 0;
}
-static int azx_resume(struct device *dev)
+static int __maybe_unused azx_resume(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
@@ -1097,9 +1100,8 @@ static int azx_thaw_noirq(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
-static int azx_runtime_suspend(struct device *dev)
+static int __maybe_unused azx_runtime_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
@@ -1116,7 +1118,7 @@ static int azx_runtime_suspend(struct device *dev)
return 0;
}
-static int azx_runtime_resume(struct device *dev)
+static int __maybe_unused azx_runtime_resume(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
@@ -1133,7 +1135,7 @@ static int azx_runtime_resume(struct device *dev)
return 0;
}
-static int azx_runtime_idle(struct device *dev)
+static int __maybe_unused azx_runtime_idle(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
@@ -1159,23 +1161,14 @@ static int azx_runtime_idle(struct device *dev)
}
static const struct dev_pm_ops azx_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
-#ifdef CONFIG_PM_SLEEP
- .prepare = azx_prepare,
- .complete = azx_complete,
- .freeze_noirq = azx_freeze_noirq,
- .thaw_noirq = azx_thaw_noirq,
-#endif
+ SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
+ .prepare = pm_sleep_ptr(azx_prepare),
+ .complete = pm_sleep_ptr(azx_complete),
+ .freeze_noirq = pm_sleep_ptr(azx_freeze_noirq),
+ .thaw_noirq = pm_sleep_ptr(azx_thaw_noirq),
SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle)
};
-#define AZX_PM_OPS &azx_pm
-#else
-#define azx_add_card_list(chip) /* NOP */
-#define azx_del_card_list(chip) /* NOP */
-#define AZX_PM_OPS NULL
-#endif /* CONFIG_PM */
-
static int azx_probe_continue(struct azx *chip);
@@ -2206,7 +2199,6 @@ out_free:
return err;
}
-#ifdef CONFIG_PM
/* On some boards setting power_save to a non 0 value leads to clicking /
* popping sounds when ever we enter/leave powersaving mode. Ideally we would
* figure out how to avoid these sounds, but that is not always feasible.
@@ -2248,13 +2240,11 @@ static const struct snd_pci_quirk power_save_denylist[] = {
SND_PCI_QUIRK(0x1734, 0x1232, "KONTRON SinglePC", 0),
{}
};
-#endif /* CONFIG_PM */
static void set_default_power_save(struct azx *chip)
{
int val = power_save;
-#ifdef CONFIG_PM
if (pm_blacklist) {
const struct snd_pci_quirk *q;
@@ -2265,7 +2255,6 @@ static void set_default_power_save(struct azx *chip)
val = 0;
}
}
-#endif /* CONFIG_PM */
snd_hda_set_power_save(&chip->bus, val * 1000);
}
@@ -2321,10 +2310,6 @@ static int azx_probe_continue(struct azx *chip)
chip->fw->data);
if (err < 0)
goto out_free;
-#ifndef CONFIG_PM
- release_firmware(chip->fw); /* no longer needed */
- chip->fw = NULL;
-#endif
}
#endif
@@ -2502,8 +2487,10 @@ static const struct pci_device_id azx_ids[] = {
{ PCI_DEVICE_DATA(INTEL, HDA_RPL_M, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
{ PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
{ PCI_DEVICE_DATA(INTEL, HDA_MTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Battlemage */
+ { PCI_DEVICE_DATA(INTEL, HDA_BMG, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
/* Lunarlake-P */
- { PCI_DEVICE_DATA(INTEL, HDA_LNL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_LNL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
/* Arrow Lake-S */
{ PCI_DEVICE_DATA(INTEL, HDA_ARL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
/* Arrow Lake */
@@ -2765,7 +2752,7 @@ static struct pci_driver azx_driver = {
.remove = azx_remove,
.shutdown = azx_shutdown,
.driver = {
- .pm = AZX_PM_OPS,
+ .pm = &azx_pm,
},
};
diff --git a/sound/pci/hda/hda_intel_trace.h b/sound/pci/hda/hda_intel_trace.h
index 73a7adfa192d..2775fa81a500 100644
--- a/sound/pci/hda/hda_intel_trace.h
+++ b/sound/pci/hda/hda_intel_trace.h
@@ -34,7 +34,6 @@ DEFINE_EVENT(hda_pm, azx_resume,
TP_ARGS(chip)
);
-#ifdef CONFIG_PM
DEFINE_EVENT(hda_pm, azx_runtime_suspend,
TP_PROTO(struct azx *chip),
TP_ARGS(chip)
@@ -44,7 +43,6 @@ DEFINE_EVENT(hda_pm, azx_runtime_resume,
TP_PROTO(struct azx *chip),
TP_ARGS(chip)
);
-#endif
#endif /* _TRACE_HDA_INTEL_H */
diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c
index 69ebc37a4d6f..265fd4737893 100644
--- a/sound/pci/hda/hda_sysfs.c
+++ b/sound/pci/hda/hda_sysfs.c
@@ -26,7 +26,6 @@ struct hda_hint {
const char *val; /* contained in the same alloc as key */
};
-#ifdef CONFIG_PM
static ssize_t power_on_acct_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -47,7 +46,6 @@ static ssize_t power_off_acct_show(struct device *dev,
static DEVICE_ATTR_RO(power_on_acct);
static DEVICE_ATTR_RO(power_off_acct);
-#endif /* CONFIG_PM */
#define CODEC_INFO_SHOW(type, field) \
static ssize_t type##_show(struct device *dev, \
@@ -745,10 +743,8 @@ static struct attribute *hda_dev_attrs[] = {
&dev_attr_modelname.attr,
&dev_attr_init_pin_configs.attr,
&dev_attr_driver_pin_configs.attr,
-#ifdef CONFIG_PM
&dev_attr_power_on_acct.attr,
&dev_attr_power_off_acct.attr,
-#endif
#ifdef CONFIG_SND_HDA_RECONFIG
&dev_attr_init_verbs.attr,
&dev_attr_hints.attr,
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 8afe6000f7da..1e9dadcdc51b 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -72,7 +72,6 @@ static int create_beep_ctls(struct hda_codec *codec)
#define create_beep_ctls(codec) 0
#endif
-#ifdef CONFIG_PM
static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
hda_nid_t hp)
{
@@ -118,7 +117,6 @@ static int ad198x_suspend(struct hda_codec *codec)
ad198x_power_eapd(codec);
return 0;
}
-#endif
/* follow EAPD via vmaster hook */
static void ad_vmaster_eapd_hook(void *private_data, int enabled)
@@ -158,10 +156,8 @@ static const struct hda_codec_ops ad198x_auto_patch_ops = {
.init = snd_hda_gen_init,
.free = snd_hda_gen_free,
.unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
.check_power_status = snd_hda_gen_check_power_status,
.suspend = ad198x_suspend,
-#endif
};
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index aa312441604f..e4673a71551a 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -9682,7 +9682,6 @@ static void dbpro_free(struct hda_codec *codec)
kfree(codec->spec);
}
-#ifdef CONFIG_PM
static int ca0132_suspend(struct hda_codec *codec)
{
struct ca0132_spec *spec = codec->spec;
@@ -9690,7 +9689,6 @@ static int ca0132_suspend(struct hda_codec *codec)
cancel_delayed_work_sync(&spec->unsol_hp_work);
return 0;
}
-#endif
static const struct hda_codec_ops ca0132_patch_ops = {
.build_controls = ca0132_build_controls,
@@ -9698,9 +9696,7 @@ static const struct hda_codec_ops ca0132_patch_ops = {
.init = ca0132_init,
.free = ca0132_free,
.unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
.suspend = ca0132_suspend,
-#endif
};
static const struct hda_codec_ops dbpro_patch_ops = {
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 6807b4708a17..654724559355 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -1128,7 +1128,6 @@ static int cs421x_parse_auto_config(struct hda_codec *codec)
return 0;
}
-#ifdef CONFIG_PM
/*
* Manage PDREF, when transitioning to D3hot
* (DAC,ADC) -> D3, PDREF=1, AFG->D3
@@ -1153,7 +1152,6 @@ static int cs421x_suspend(struct hda_codec *codec)
return 0;
}
-#endif
static const struct hda_codec_ops cs421x_patch_ops = {
.build_controls = snd_hda_gen_build_controls,
@@ -1161,9 +1159,7 @@ static const struct hda_codec_ops cs421x_patch_ops = {
.init = cs421x_init,
.free = cs_free,
.unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
.suspend = cs421x_suspend,
-#endif
};
static int patch_cs4210(struct hda_codec *codec)
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index e8209178d87b..17389a3801bd 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -294,13 +294,11 @@ static void cx_jack_unsol_event(struct hda_codec *codec, unsigned int res)
snd_hda_jack_unsol_event(codec, res);
}
-#ifdef CONFIG_PM
static int cx_auto_suspend(struct hda_codec *codec)
{
cx_auto_shutdown(codec);
return 0;
}
-#endif
static const struct hda_codec_ops cx_auto_patch_ops = {
.build_controls = snd_hda_gen_build_controls,
@@ -308,10 +306,8 @@ static const struct hda_codec_ops cx_auto_patch_ops = {
.init = cx_auto_init,
.free = cx_auto_free,
.unsol_event = cx_jack_unsol_event,
-#ifdef CONFIG_PM
.suspend = cx_auto_suspend,
.check_power_status = snd_hda_gen_check_power_status,
-#endif
};
/*
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index e41316e2e983..26f3c31600d7 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -909,7 +909,6 @@ static void cs42l42_resume(struct sub_codec *cs42l42)
cs42l42_enable_jack_detect(cs42l42);
}
-#ifdef CONFIG_PM
static void cs42l42_suspend(struct sub_codec *cs42l42)
{
struct hda_codec *codec = cs42l42->codec;
@@ -948,7 +947,6 @@ static void cs42l42_suspend(struct sub_codec *cs42l42)
spec->gpio_data &= ~cs42l42->reset_gpio;
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
}
-#endif
static void cs8409_free(struct hda_codec *codec)
{
@@ -1003,7 +1001,6 @@ static void cs8409_cs42l42_jack_unsol_event(struct hda_codec *codec, unsigned in
}
}
-#ifdef CONFIG_PM
/* Manage PDREF, when transition to D3hot */
static int cs8409_cs42l42_suspend(struct hda_codec *codec)
{
@@ -1025,7 +1022,6 @@ static int cs8409_cs42l42_suspend(struct hda_codec *codec)
return 0;
}
-#endif
/* Vendor specific HW configuration
* PLL, ASP, I2C, SPI, GPIOs, DMIC etc...
@@ -1080,9 +1076,7 @@ static const struct hda_codec_ops cs8409_cs42l42_patch_ops = {
.init = cs8409_init,
.free = cs8409_free,
.unsol_event = cs8409_cs42l42_jack_unsol_event,
-#ifdef CONFIG_PM
.suspend = cs8409_cs42l42_suspend,
-#endif
};
static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
@@ -1310,9 +1304,7 @@ static const struct hda_codec_ops cs8409_dolphin_patch_ops = {
.init = cs8409_init,
.free = cs8409_free,
.unsol_event = dolphin_jack_unsol_event,
-#ifdef CONFIG_PM
.suspend = cs8409_cs42l42_suspend,
-#endif
};
static int dolphin_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 495d63101186..707d203ba652 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -3,7 +3,7 @@
*
* patch_hdmi.c - routines for HDMI/DisplayPort codecs
*
- * Copyright(c) 2008-2010 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008-2010 Intel Corporation
* Copyright (c) 2006 ATI Technologies Inc.
* Copyright (c) 2008 NVIDIA Corp. All rights reserved.
* Copyright (c) 2008 Wei Ni <wni@nvidia.com>
@@ -2513,7 +2513,6 @@ static void generic_hdmi_free(struct hda_codec *codec)
generic_spec_free(codec);
}
-#ifdef CONFIG_PM
static int generic_hdmi_suspend(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
@@ -2540,7 +2539,6 @@ static int generic_hdmi_resume(struct hda_codec *codec)
}
return 0;
}
-#endif
static const struct hda_codec_ops generic_hdmi_patch_ops = {
.init = generic_hdmi_init,
@@ -2548,10 +2546,8 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = {
.build_pcms = generic_hdmi_build_pcms,
.build_controls = generic_hdmi_build_controls,
.unsol_event = hdmi_unsol_event,
-#ifdef CONFIG_PM
.suspend = generic_hdmi_suspend,
.resume = generic_hdmi_resume,
-#endif
};
static const struct hdmi_ops generic_standard_hdmi_ops = {
@@ -2952,7 +2948,6 @@ static void i915_pin_cvt_fixup(struct hda_codec *codec,
}
}
-#ifdef CONFIG_PM
static int i915_adlp_hdmi_suspend(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
@@ -3032,7 +3027,6 @@ static int i915_adlp_hdmi_resume(struct hda_codec *codec)
return res;
}
-#endif
/* precondition and allocation for Intel codecs */
static int alloc_intel_hdmi(struct hda_codec *codec)
@@ -3167,10 +3161,8 @@ static int patch_i915_adlp_hdmi(struct hda_codec *codec)
if (spec->silent_stream_type) {
spec->silent_stream_type = SILENT_STREAM_KAE;
-#ifdef CONFIG_PM
codec->patch_ops.resume = i915_adlp_hdmi_resume;
codec->patch_ops.suspend = i915_adlp_hdmi_suspend;
-#endif
}
}
@@ -4642,6 +4634,7 @@ HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi),
HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi),
HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_adlp_hdmi),
HDA_CODEC_ENTRY(0x8086281d, "Meteor Lake HDMI", patch_i915_adlp_hdmi),
+HDA_CODEC_ENTRY(0x8086281e, "Battlemage HDMI", patch_i915_adlp_hdmi),
HDA_CODEC_ENTRY(0x8086281f, "Raptor Lake P HDMI", patch_i915_adlp_hdmi),
HDA_CODEC_ENTRY(0x80862820, "Lunar Lake HDMI", patch_i915_adlp_hdmi),
HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 70d80b6af3fe..e3c0b9d5552d 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -109,9 +109,7 @@ struct alc_spec {
/* hooks */
void (*init_hook)(struct hda_codec *codec);
-#ifdef CONFIG_PM
void (*power_hook)(struct hda_codec *codec);
-#endif
void (*shutup)(struct hda_codec *codec);
int init_amp;
@@ -920,6 +918,8 @@ static void alc_pre_init(struct hda_codec *codec)
((codec)->core.dev.power.power_state.event == PM_EVENT_RESUME)
#define is_s4_resume(codec) \
((codec)->core.dev.power.power_state.event == PM_EVENT_RESTORE)
+#define is_s4_suspend(codec) \
+ ((codec)->core.dev.power.power_state.event == PM_EVENT_FREEZE)
static int alc_init(struct hda_codec *codec)
{
@@ -945,7 +945,6 @@ static int alc_init(struct hda_codec *codec)
#define alc_free snd_hda_gen_free
-#ifdef CONFIG_PM
static inline void alc_shutup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -984,7 +983,6 @@ static int alc_resume(struct hda_codec *codec)
hda_call_check_power_status(codec, 0x01);
return 0;
}
-#endif
/*
*/
@@ -994,11 +992,9 @@ static const struct hda_codec_ops alc_patch_ops = {
.init = alc_init,
.free = alc_free,
.unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
.resume = alc_resume,
.suspend = alc_suspend,
.check_power_status = snd_hda_gen_check_power_status,
-#endif
};
@@ -4039,7 +4035,6 @@ static void alc5505_dsp_init(struct hda_codec *codec)
#define alc5505_dsp_resume(codec) alc5505_dsp_back_from_halt(codec)
#endif
-#ifdef CONFIG_PM
static int alc269_suspend(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -4085,7 +4080,6 @@ static int alc269_resume(struct hda_codec *codec)
return 0;
}
-#endif /* CONFIG_PM */
static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
@@ -6533,6 +6527,20 @@ static void alc295_fixup_chromebook(struct hda_codec *codec,
}
}
+static void alc256_fixup_chromebook(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.suppress_auto_mute = 1;
+ spec->gen.suppress_auto_mic = 1;
+ spec->en_3kpull_low = false;
+ break;
+ }
+}
+
static void alc_fixup_disable_mic_vref(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -6791,7 +6799,7 @@ static int comp_bind(struct device *dev)
struct alc_spec *spec = cdc->spec;
int ret;
- ret = hda_component_manager_bind(cdc, spec->comps);
+ ret = hda_component_manager_bind(cdc, spec->comps, ARRAY_SIZE(spec->comps));
if (ret)
return ret;
@@ -7183,6 +7191,44 @@ static void alc245_fixup_hp_spectre_x360_eu0xxx(struct hda_codec *codec,
alc245_fixup_hp_gpio_led(codec, fix, action);
}
+/*
+ * ALC287 PCM hooks
+ */
+static void alc287_alc1318_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ alc_write_coef_idx(codec, 0x10, 0x8806); /* Change MLK to GPIO3 */
+ switch (action) {
+ case HDA_GEN_PCM_ACT_OPEN:
+ alc_write_coefex_idx(codec, 0x5a, 0x00, 0x954f); /* write gpio3 to high */
+ break;
+ case HDA_GEN_PCM_ACT_CLOSE:
+ alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */
+ break;
+ }
+}
+
+static void alc287_s4_power_gpio3_default(struct hda_codec *codec)
+{
+ if (is_s4_suspend(codec)) {
+ alc_write_coef_idx(codec, 0x10, 0x8806); /* Change MLK to GPIO3 */
+ alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */
+ }
+}
+
+static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ spec->power_hook = alc287_s4_power_gpio3_default;
+ spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook;
+}
+
enum {
ALC269_FIXUP_GPIO2,
@@ -7426,6 +7472,7 @@ enum {
ALC287_FIXUP_YOGA7_14ITL_SPEAKERS,
ALC298_FIXUP_LENOVO_C940_DUET7,
ALC287_FIXUP_LENOVO_14IRP8_DUETITL,
+ ALC287_FIXUP_LENOVO_LEGION_7,
ALC287_FIXUP_13S_GEN2_SPEAKERS,
ALC256_FIXUP_SET_COEF_DEFAULTS,
ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE,
@@ -7470,7 +7517,9 @@ enum {
ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC,
ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1,
ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC,
- ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1
+ ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1,
+ ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318,
+ ALC256_FIXUP_CHROME_BOOK,
};
/* A special fixup for Lenovo C940 and Yoga Duet 7;
@@ -7510,6 +7559,23 @@ static void alc287_fixup_lenovo_14irp8_duetitl(struct hda_codec *codec,
__snd_hda_apply_fixup(codec, id, action, 0);
}
+/* Another hilarious PCI SSID conflict with Lenovo Legion Pro 7 16ARX8H (with
+ * TAS2781 codec) and Legion 7i 16IAX7 (with CS35L41 codec);
+ * we apply a corresponding fixup depending on the codec SSID instead
+ */
+static void alc287_fixup_lenovo_legion_7(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ int id;
+
+ if (codec->core.subsystem_id == 0x17aa38a8)
+ id = ALC287_FIXUP_TAS2781_I2C; /* Legion Pro 7 16ARX8H */
+ else
+ id = ALC287_FIXUP_CS35L41_I2C_2; /* Legion 7i 16IAX7 */
+ __snd_hda_apply_fixup(codec, id, action, 0);
+}
+
static const struct hda_fixup alc269_fixups[] = {
[ALC269_FIXUP_GPIO2] = {
.type = HDA_FIXUP_FUNC,
@@ -9404,6 +9470,10 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc287_fixup_lenovo_14irp8_duetitl,
},
+ [ALC287_FIXUP_LENOVO_LEGION_7] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_lenovo_legion_7,
+ },
[ALC287_FIXUP_13S_GEN2_SPEAKERS] = {
.type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
@@ -9726,6 +9796,18 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC285_FIXUP_ASUS_GA403U,
},
+ [ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_lenovo_thinkpad_with_alc1318,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC256_FIXUP_CHROME_BOOK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc256_fixup_chromebook,
+ .chained = true,
+ .chain_id = ALC225_FIXUP_HEADSET_JACK
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -9937,6 +10019,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO),
SND_PCI_QUIRK(0x103c, 0x86e7, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
SND_PCI_QUIRK(0x103c, 0x86e8, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
@@ -10031,7 +10114,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8a2c, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8a2d, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4),
@@ -10076,6 +10158,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8bdd, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8bde, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8bdf, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
@@ -10096,6 +10180,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c48, "HP EliteBook 860 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c49, "HP Elite x360 830 2-in-1 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c4d, "HP Omen", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c4e, "HP Omen", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8c4f, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8c50, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8c51, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
@@ -10108,8 +10194,11 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c89, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c8a, "HP EliteBook 630", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c8c, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c8d, "HP ProBook 440 G11", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c8e, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c90, "HP EliteBook 640", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c91, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
@@ -10210,6 +10299,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1e63, "ASUS H7606W", ALC285_FIXUP_CS35L56_I2C_2),
SND_PCI_QUIRK(0x1043, 0x1e83, "ASUS GA605W", ALC285_FIXUP_CS35L56_I2C_2),
SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401),
SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401),
@@ -10393,6 +10483,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x2318, "Thinkpad Z13 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x2319, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x231a, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x231e, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318),
+ SND_PCI_QUIRK(0x17aa, 0x231f, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318),
SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
@@ -10421,8 +10513,11 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6),
+ SND_PCI_QUIRK(0x17aa, 0x3865, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x3866, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
- SND_PCI_QUIRK(0x17aa, 0x386f, "Legion 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7/7i", ALC287_FIXUP_LENOVO_LEGION_7),
SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C),
SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x17aa, 0x3878, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2),
@@ -10681,6 +10776,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{.id = ALC295_FIXUP_HP_X360, .name = "alc295-hp-x360"},
{.id = ALC225_FIXUP_HEADSET_JACK, .name = "alc-headset-jack"},
{.id = ALC295_FIXUP_CHROME_BOOK, .name = "alc-chrome-book"},
+ {.id = ALC256_FIXUP_CHROME_BOOK, .name = "alc-2024y-chromebook"},
{.id = ALC299_FIXUP_PREDATOR_SPK, .name = "predator-spk"},
{.id = ALC298_FIXUP_HUAWEI_MBX_STEREO, .name = "huawei-mbx-stereo"},
{.id = ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE, .name = "alc256-medion-headset"},
@@ -11213,10 +11309,8 @@ static int patch_alc269(struct hda_codec *codec)
codec->power_save_node = 0;
spec->en_3kpull_low = true;
-#ifdef CONFIG_PM
codec->patch_ops.suspend = alc269_suspend;
codec->patch_ops.resume = alc269_resume;
-#endif
spec->shutup = alc_default_shutup;
spec->init_hook = alc_default_init;
@@ -11514,9 +11608,7 @@ static int patch_alc861(struct hda_codec *codec)
if (has_cdefine_beep(codec))
spec->gen.beep_nid = 0x23;
-#ifdef CONFIG_PM
spec->power_hook = alc_power_eapd;
-#endif
alc_pre_init(codec);
@@ -11939,6 +12031,7 @@ enum {
ALC897_FIXUP_LENOVO_HEADSET_MODE,
ALC897_FIXUP_HEADSET_MIC_PIN2,
ALC897_FIXUP_UNIS_H3C_X500S,
+ ALC897_FIXUP_HEADSET_MIC_PIN3,
};
static const struct hda_fixup alc662_fixups[] = {
@@ -12385,10 +12478,18 @@ static const struct hda_fixup alc662_fixups[] = {
{}
},
},
+ [ALC897_FIXUP_HEADSET_MIC_PIN3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 }, /* use as headset mic */
+ { }
+ },
+ },
};
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1019, 0x9859, "JP-IK LEAP W502", ALC897_FIXUP_HEADSET_MIC_PIN3),
SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x0241, "Packard Bell DOTS", ALC662_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE),
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 61258b0aac8d..ae1a34c68c61 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -2154,10 +2154,8 @@ static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec,
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->mic_mute_led_gpio = 0x08; /* GPIO3 */
-#ifdef CONFIG_PM
/* resetting controller clears GPIO, so we need to keep on */
codec->core.power_caps &= ~AC_PWRST_CLKSTOP;
-#endif
}
}
@@ -4442,7 +4440,6 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer,
#define stac927x_proc_hook NULL
#endif
-#ifdef CONFIG_PM
static int stac_suspend(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -4456,9 +4453,6 @@ static int stac_suspend(struct hda_codec *codec)
return 0;
}
-#else
-#define stac_suspend NULL
-#endif /* CONFIG_PM */
static const struct hda_codec_ops stac_patch_ops = {
.build_controls = snd_hda_gen_build_controls,
@@ -4466,9 +4460,7 @@ static const struct hda_codec_ops stac_patch_ops = {
.init = stac_init,
.free = stac_free,
.unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
.suspend = stac_suspend,
-#endif
};
static int alloc_stac_spec(struct hda_codec *codec)
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 2994f85bc1b9..a8ef4bb70dd0 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -379,7 +379,6 @@ static void via_free(struct hda_codec *codec)
snd_hda_gen_free(codec);
}
-#ifdef CONFIG_PM
static int via_suspend(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
@@ -400,9 +399,7 @@ static int via_resume(struct hda_codec *codec)
snd_hda_regmap_sync(codec);
return 0;
}
-#endif
-#ifdef CONFIG_PM
static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
struct via_spec *spec = codec->spec;
@@ -410,7 +407,6 @@ static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
vt1708_update_hp_work(codec);
return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
}
-#endif
/*
*/
@@ -423,11 +419,9 @@ static const struct hda_codec_ops via_patch_ops = {
.init = via_init,
.free = via_free,
.unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
.suspend = via_suspend,
.resume = via_resume,
.check_power_status = via_check_power_status,
-#endif
};
diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile
index 1196f22a9b45..f406a048374c 100644
--- a/sound/pci/ice1712/Makefile
+++ b/sound/pci/ice1712/Makefile
@@ -4,9 +4,9 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ice17xx-ak4xxx-objs := ak4xxx.o
-snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
-snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o quartet.o psc724.o wm8766.o wm8776.o
+snd-ice17xx-ak4xxx-y := ak4xxx.o
+snd-ice1712-y := ice1712.o delta.o hoontech.o ews.o
+snd-ice1724-y := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o quartet.o psc724.o wm8766.o wm8776.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
diff --git a/sound/pci/korg1212/Makefile b/sound/pci/korg1212/Makefile
index 42eb287c77af..ab0186ffbd58 100644
--- a/sound/pci/korg1212/Makefile
+++ b/sound/pci/korg1212/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-korg1212-objs := korg1212.o
+snd-korg1212-y := korg1212.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_KORG1212) += snd-korg1212.o
diff --git a/sound/pci/lx6464es/Makefile b/sound/pci/lx6464es/Makefile
index c295f68bac68..2b3047c7a388 100644
--- a/sound/pci/lx6464es/Makefile
+++ b/sound/pci/lx6464es/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-lx6464es-objs := lx6464es.o lx_core.o
+snd-lx6464es-y := lx6464es.o lx_core.o
obj-$(CONFIG_SND_LX6464ES) += snd-lx6464es.o
diff --git a/sound/pci/mixart/Makefile b/sound/pci/mixart/Makefile
index 16cfeb78a0b6..b803e5e72791 100644
--- a/sound/pci/mixart/Makefile
+++ b/sound/pci/mixart/Makefile
@@ -4,6 +4,6 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-mixart-objs := mixart.o mixart_core.o mixart_hwdep.o mixart_mixer.o
+snd-mixart-y := mixart.o mixart_core.o mixart_hwdep.o mixart_mixer.o
obj-$(CONFIG_SND_MIXART) += snd-mixart.o
diff --git a/sound/pci/nm256/Makefile b/sound/pci/nm256/Makefile
index 3063766ac56b..7d55fe774d20 100644
--- a/sound/pci/nm256/Makefile
+++ b/sound/pci/nm256/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-nm256-objs := nm256.o
+snd-nm256-y := nm256.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_NM256) += snd-nm256.o
diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile
index 0dfc4f840992..cc0c24694750 100644
--- a/sound/pci/oxygen/Makefile
+++ b/sound/pci/oxygen/Makefile
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
-snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
-snd-oxygen-objs := oxygen.o xonar_dg_mixer.o xonar_dg.o
-snd-se6x-objs := se6x.o
-snd-virtuoso-objs := virtuoso.o xonar_lib.o \
+snd-oxygen-lib-y := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
+snd-oxygen-y := oxygen.o xonar_dg_mixer.o xonar_dg.o
+snd-se6x-y := se6x.o
+snd-virtuoso-y := virtuoso.o xonar_lib.o \
xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o
obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
diff --git a/sound/pci/pcxhr/Makefile b/sound/pci/pcxhr/Makefile
index 5993d86cfb5d..0ea1e5ccb56f 100644
--- a/sound/pci/pcxhr/Makefile
+++ b/sound/pci/pcxhr/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-pcxhr-objs := pcxhr.o pcxhr_hwdep.o pcxhr_mixer.o pcxhr_core.o pcxhr_mix22.o
+snd-pcxhr-y := pcxhr.o pcxhr_hwdep.o pcxhr_mixer.o pcxhr_core.o pcxhr_mix22.o
obj-$(CONFIG_SND_PCXHR) += snd-pcxhr.o
diff --git a/sound/pci/riptide/Makefile b/sound/pci/riptide/Makefile
index 9a505bae243e..9b4e9595859a 100644
--- a/sound/pci/riptide/Makefile
+++ b/sound/pci/riptide/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-riptide-objs := riptide.o
+snd-riptide-y := riptide.o
obj-$(CONFIG_SND_RIPTIDE) += snd-riptide.o
diff --git a/sound/pci/rme9652/Makefile b/sound/pci/rme9652/Makefile
index a3351447ddc0..cc99ae892211 100644
--- a/sound/pci/rme9652/Makefile
+++ b/sound/pci/rme9652/Makefile
@@ -4,9 +4,9 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-rme9652-objs := rme9652.o
-snd-hdsp-objs := hdsp.o
-snd-hdspm-objs := hdspm.o
+snd-rme9652-y := rme9652.o
+snd-hdsp-y := hdsp.o
+snd-hdspm-y := hdspm.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_RME9652) += snd-rme9652.o
diff --git a/sound/pci/trident/Makefile b/sound/pci/trident/Makefile
index e8975bc37fcb..476d16abcfc9 100644
--- a/sound/pci/trident/Makefile
+++ b/sound/pci/trident/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-trident-objs := trident.o trident_main.o trident_memory.o
+snd-trident-y := trident.o trident_main.o trident_memory.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_TRIDENT) += snd-trident.o
diff --git a/sound/pci/vx222/Makefile b/sound/pci/vx222/Makefile
index dda900e45385..6889137eb438 100644
--- a/sound/pci/vx222/Makefile
+++ b/sound/pci/vx222/Makefile
@@ -4,6 +4,6 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-vx222-objs := vx222.o vx222_ops.o
+snd-vx222-y := vx222.o vx222_ops.o
obj-$(CONFIG_SND_VX222) += snd-vx222.o
diff --git a/sound/pci/ymfpci/Makefile b/sound/pci/ymfpci/Makefile
index 40a1d83e1a9e..2d7856403371 100644
--- a/sound/pci/ymfpci/Makefile
+++ b/sound/pci/ymfpci/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ymfpci-objs := ymfpci.o ymfpci_main.o
+snd-ymfpci-y := ymfpci.o ymfpci_main.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_YMFPCI) += snd-ymfpci.o
diff --git a/sound/pcmcia/pdaudiocf/Makefile b/sound/pcmcia/pdaudiocf/Makefile
index ea0d67576df9..34a288c1eebd 100644
--- a/sound/pcmcia/pdaudiocf/Makefile
+++ b/sound/pcmcia/pdaudiocf/Makefile
@@ -4,6 +4,6 @@
# Copyright (c) 2004 by Jaroslav Kysela <perex@perex.cz>
#
-snd-pdaudiocf-objs := pdaudiocf.o pdaudiocf_core.o pdaudiocf_irq.o pdaudiocf_pcm.o
+snd-pdaudiocf-y := pdaudiocf.o pdaudiocf_core.o pdaudiocf_irq.o pdaudiocf_pcm.o
obj-$(CONFIG_SND_PDAUDIOCF) += snd-pdaudiocf.o
diff --git a/sound/pcmcia/vx/Makefile b/sound/pcmcia/vx/Makefile
index b25006e4d25a..abd187544946 100644
--- a/sound/pcmcia/vx/Makefile
+++ b/sound/pcmcia/vx/Makefile
@@ -4,6 +4,6 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-vxpocket-objs := vxpocket.o vxp_ops.o vxp_mixer.o
+snd-vxpocket-y := vxpocket.o vxp_ops.o vxp_mixer.o
obj-$(CONFIG_SND_VXPOCKET) += snd-vxpocket.o
diff --git a/sound/ppc/Makefile b/sound/ppc/Makefile
index 0188ce3e30b8..655bcffba843 100644
--- a/sound/ppc/Makefile
+++ b/sound/ppc/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o
+snd-powermac-y := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o
diff --git a/sound/sh/Makefile b/sound/sh/Makefile
index c0bbc500c17c..6871dece28a5 100644
--- a/sound/sh/Makefile
+++ b/sound/sh/Makefile
@@ -3,8 +3,8 @@
# Makefile for ALSA
#
-snd-aica-objs := aica.o
-snd-sh_dac_audio-objs := sh_dac_audio.o
+snd-aica-y := aica.o
+snd-sh_dac_audio-y := sh_dac_audio.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AICA) += snd-aica.o
diff --git a/sound/soc/amd/acp/acp-legacy-common.c b/sound/soc/amd/acp/acp-legacy-common.c
index b5aff3f230be..3be7c6d55a6f 100644
--- a/sound/soc/amd/acp/acp-legacy-common.c
+++ b/sound/soc/amd/acp/acp-legacy-common.c
@@ -358,11 +358,25 @@ int smn_read(struct pci_dev *dev, u32 smn_addr)
}
EXPORT_SYMBOL_NS_GPL(smn_read, SND_SOC_ACP_COMMON);
-int check_acp_pdm(struct pci_dev *pci, struct acp_chip_info *chip)
+static void check_acp3x_config(struct acp_chip_info *chip)
{
- struct acpi_device *pdm_dev;
- const union acpi_object *obj;
- u32 pdm_addr, val;
+ u32 val;
+
+ val = readl(chip->base + ACP3X_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_4:
+ chip->is_i2s_config = true;
+ chip->is_pdm_config = true;
+ break;
+ default:
+ chip->is_pdm_config = true;
+ break;
+ }
+}
+
+static void check_acp6x_config(struct acp_chip_info *chip)
+{
+ u32 val;
val = readl(chip->base + ACP_PIN_CONFIG);
switch (val) {
@@ -371,42 +385,94 @@ int check_acp_pdm(struct pci_dev *pci, struct acp_chip_info *chip)
case ACP_CONFIG_6:
case ACP_CONFIG_7:
case ACP_CONFIG_8:
- case ACP_CONFIG_10:
case ACP_CONFIG_11:
+ case ACP_CONFIG_14:
+ chip->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_9:
+ chip->is_i2s_config = true;
+ break;
+ case ACP_CONFIG_10:
case ACP_CONFIG_12:
case ACP_CONFIG_13:
+ chip->is_i2s_config = true;
+ chip->is_pdm_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static void check_acp70_config(struct acp_chip_info *chip)
+{
+ u32 val;
+
+ val = readl(chip->base + ACP_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_11:
case ACP_CONFIG_14:
+ case ACP_CONFIG_17:
+ case ACP_CONFIG_18:
+ chip->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_9:
+ chip->is_i2s_config = true;
+ break;
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_13:
+ case ACP_CONFIG_19:
+ case ACP_CONFIG_20:
+ chip->is_i2s_config = true;
+ chip->is_pdm_config = true;
break;
default:
- return -EINVAL;
+ break;
}
+}
+
+void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip)
+{
+ struct acpi_device *pdm_dev;
+ const union acpi_object *obj;
+ u32 pdm_addr;
switch (chip->acp_rev) {
case ACP3X_DEV:
pdm_addr = ACP_RENOIR_PDM_ADDR;
+ check_acp3x_config(chip);
break;
case ACP6X_DEV:
pdm_addr = ACP_REMBRANDT_PDM_ADDR;
+ check_acp6x_config(chip);
break;
case ACP63_DEV:
pdm_addr = ACP63_PDM_ADDR;
+ check_acp6x_config(chip);
break;
case ACP70_DEV:
pdm_addr = ACP70_PDM_ADDR;
+ check_acp70_config(chip);
break;
default:
- return -EINVAL;
+ break;
}
- pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), pdm_addr, 0);
- if (pdm_dev) {
- if (!acpi_dev_get_property(pdm_dev, "acp-audio-device-type",
- ACPI_TYPE_INTEGER, &obj) &&
- obj->integer.value == pdm_addr)
- return 0;
+ if (chip->is_pdm_config) {
+ pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), pdm_addr, 0);
+ if (pdm_dev) {
+ if (!acpi_dev_get_property(pdm_dev, "acp-audio-device-type",
+ ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == pdm_addr)
+ chip->is_pdm_dev = true;
+ }
}
- return -ENODEV;
}
-EXPORT_SYMBOL_NS_GPL(check_acp_pdm, SND_SOC_ACP_COMMON);
+EXPORT_SYMBOL_NS_GPL(check_acp_config, SND_SOC_ACP_COMMON);
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c
index 5f35b90eab8d..ad320b29e87d 100644
--- a/sound/soc/amd/acp/acp-pci.c
+++ b/sound/soc/amd/acp/acp-pci.c
@@ -100,7 +100,6 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id
ret = -EINVAL;
goto release_regions;
}
-
dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
if (IS_ERR(dmic_dev)) {
dev_err(dev, "failed to create DMIC device\n");
@@ -119,6 +118,10 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id
if (ret)
goto unregister_dmic_dev;
+ check_acp_config(pci, chip);
+ if (!chip->is_pdm_dev && !chip->is_i2s_config)
+ goto skip_pdev_creation;
+
res = devm_kcalloc(&pci->dev, num_res, sizeof(struct resource), GFP_KERNEL);
if (!res) {
ret = -ENOMEM;
@@ -136,10 +139,6 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id
}
}
- ret = check_acp_pdm(pci, chip);
- if (ret < 0)
- goto skip_pdev_creation;
-
chip->flag = flag;
memset(&pdevinfo, 0, sizeof(pdevinfo));
diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h
index 5017e868f39b..d75b4eb34de8 100644
--- a/sound/soc/amd/acp/amd.h
+++ b/sound/soc/amd/acp/amd.h
@@ -138,6 +138,9 @@ struct acp_chip_info {
void __iomem *base; /* ACP memory PCI base */
struct platform_device *chip_pdev;
unsigned int flag; /* Distinguish b/w Legacy or Only PDM */
+ bool is_pdm_dev; /* flag set to true when ACP PDM controller exists */
+ bool is_pdm_config; /* flag set to true when PDM configuration is selected from BIOS */
+ bool is_i2s_config; /* flag set to true when I2S configuration is selected from BIOS */
};
struct acp_stream {
@@ -212,6 +215,11 @@ enum acp_config {
ACP_CONFIG_13,
ACP_CONFIG_14,
ACP_CONFIG_15,
+ ACP_CONFIG_16,
+ ACP_CONFIG_17,
+ ACP_CONFIG_18,
+ ACP_CONFIG_19,
+ ACP_CONFIG_20,
};
extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops;
@@ -240,7 +248,7 @@ void restore_acp_pdm_params(struct snd_pcm_substream *substream,
int restore_acp_i2s_params(struct snd_pcm_substream *substream,
struct acp_dev_data *adata, struct acp_stream *stream);
-int check_acp_pdm(struct pci_dev *pci, struct acp_chip_info *chip);
+void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip);
static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int direction)
{
diff --git a/sound/soc/amd/acp/chip_offset_byte.h b/sound/soc/amd/acp/chip_offset_byte.h
index cfd6c4d07594..18da734c0e9e 100644
--- a/sound/soc/amd/acp/chip_offset_byte.h
+++ b/sound/soc/amd/acp/chip_offset_byte.h
@@ -20,6 +20,7 @@
#define ACP_SOFT_RESET 0x1000
#define ACP_CONTROL 0x1004
#define ACP_PIN_CONFIG 0x1440
+#define ACP3X_PIN_CONFIG 0x1400
#define ACP_EXTERNAL_INTR_REG_ADDR(adata, offset, ctrl) \
(adata->acp_base + adata->rsrc->irq_reg_offset + offset + (ctrl * 0x04))
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index dfd703d9e12f..758dfdf9d3ea 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -1361,6 +1361,7 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5
"spk-id-gpios", ACPI_TYPE_PACKAGE, &obj);
if (ret) {
dev_dbg(cs35l56->base.dev, "Could not get spk-id-gpios package: %d\n", ret);
+ fwnode_handle_put(af01_fwnode);
return -ENOENT;
}
@@ -1368,6 +1369,7 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5
if (obj->package.count != 4) {
dev_warn(cs35l56->base.dev, "Unexpected spk-id element count %d\n",
obj->package.count);
+ fwnode_handle_put(af01_fwnode);
return -ENOENT;
}
@@ -1382,6 +1384,7 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5
*/
ret = acpi_dev_add_driver_gpios(adev, cs35l56_af01_spkid_gpios_mapping);
if (ret) {
+ fwnode_handle_put(af01_fwnode);
return dev_err_probe(cs35l56->base.dev, ret,
"Failed to add gpio mapping to AF01\n");
}
@@ -1389,14 +1392,17 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5
ret = devm_add_action_or_reset(cs35l56->base.dev,
cs35l56_acpi_dev_release_driver_gpios,
adev);
- if (ret)
+ if (ret) {
+ fwnode_handle_put(af01_fwnode);
return ret;
+ }
dev_dbg(cs35l56->base.dev, "Added spk-id-gpios mapping to AF01\n");
}
desc = fwnode_gpiod_get_index(af01_fwnode, "spk-id", 0, GPIOD_IN, NULL);
if (IS_ERR(desc)) {
+ fwnode_handle_put(af01_fwnode);
ret = PTR_ERR(desc);
return dev_err_probe(cs35l56->base.dev, ret, "Get GPIO from AF01 failed\n");
}
@@ -1405,9 +1411,12 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5
gpiod_put(desc);
if (ret < 0) {
+ fwnode_handle_put(af01_fwnode);
dev_err_probe(cs35l56->base.dev, ret, "Error reading spk-id GPIO\n");
return ret;
- }
+ }
+
+ fwnode_handle_put(af01_fwnode);
dev_info(cs35l56->base.dev, "Got spk-id from AF01\n");
diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c
index 6bc068cdcbe2..15e5e3eb592b 100644
--- a/sound/soc/codecs/da7219-aad.c
+++ b/sound/soc/codecs/da7219-aad.c
@@ -671,8 +671,10 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev)
return NULL;
aad_pdata = devm_kzalloc(dev, sizeof(*aad_pdata), GFP_KERNEL);
- if (!aad_pdata)
+ if (!aad_pdata) {
+ fwnode_handle_put(aad_np);
return NULL;
+ }
aad_pdata->irq = i2c->irq;
@@ -753,6 +755,8 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev)
else
aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
+ fwnode_handle_put(aad_np);
+
return aad_pdata;
}
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c
index c4d52fadebb6..81e84095107e 100644
--- a/sound/soc/generic/audio-graph-card2.c
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -1197,7 +1197,7 @@ static int graph_count_c2c(struct simple_util_priv *priv,
{
struct device_node *ports = of_get_parent(lnk);
struct device_node *port0 = lnk;
- struct device_node *port1 = of_get_next_child(ports, lnk);
+ struct device_node *port1 = of_get_next_child(ports, of_node_get(lnk));
struct device_node *ep0 = port_to_endpoint(port0);
struct device_node *ep1 = port_to_endpoint(port1);
struct device_node *codec0 = of_graph_get_remote_port(ep0);
diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c
index e19ad8deed5c..3bf37a8fd6e6 100644
--- a/sound/soc/intel/avs/boards/es8336.c
+++ b/sound/soc/intel/avs/boards/es8336.c
@@ -113,7 +113,7 @@ static int avs_es8336_codec_init(struct snd_soc_pcm_runtime *runtime)
if (!pins)
return -ENOMEM;
- ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0,
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0,
&data->jack, pins, num_pins);
if (ret)
return ret;
diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c
index 8dee6801fe51..6ea9058fdb2a 100644
--- a/sound/soc/intel/avs/boards/nau8825.c
+++ b/sound/soc/intel/avs/boards/nau8825.c
@@ -96,7 +96,7 @@ static int avs_nau8825_codec_init(struct snd_soc_pcm_runtime *runtime)
* 4 buttons here map to the google Reference headset.
* The use of these buttons can be decided by the user space.
*/
- ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3,
jack, pins, num_pins);
if (ret)
diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c
index 68d6fcb215bb..9fcce86c6eb4 100644
--- a/sound/soc/intel/avs/boards/rt274.c
+++ b/sound/soc/intel/avs/boards/rt274.c
@@ -102,7 +102,8 @@ static int avs_rt274_codec_init(struct snd_soc_pcm_runtime *runtime)
if (!pins)
return -ENOMEM;
- ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET, jack, pins, num_pins);
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET, jack, pins,
+ num_pins);
if (ret)
return ret;
diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c
index a3d0c5da5dfe..f157f2d19efb 100644
--- a/sound/soc/intel/avs/boards/rt286.c
+++ b/sound/soc/intel/avs/boards/rt286.c
@@ -63,8 +63,8 @@ static int avs_rt286_codec_init(struct snd_soc_pcm_runtime *runtime)
if (!pins)
return -ENOMEM;
- ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0, jack,
- pins, num_pins);
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0,
+ jack, pins, num_pins);
if (ret)
return ret;
diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c
index f819f8dd447b..1e85242c8dd2 100644
--- a/sound/soc/intel/avs/boards/rt298.c
+++ b/sound/soc/intel/avs/boards/rt298.c
@@ -74,8 +74,8 @@ static int avs_rt298_codec_init(struct snd_soc_pcm_runtime *runtime)
if (!pins)
return -ENOMEM;
- ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0, jack,
- pins, num_pins);
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0,
+ jack, pins, num_pins);
if (ret)
return ret;
diff --git a/sound/soc/mediatek/mt8192/mt8192-dai-tdm.c b/sound/soc/mediatek/mt8192/mt8192-dai-tdm.c
index 9ce06821c7d0..49440db370af 100644
--- a/sound/soc/mediatek/mt8192/mt8192-dai-tdm.c
+++ b/sound/soc/mediatek/mt8192/mt8192-dai-tdm.c
@@ -566,10 +566,10 @@ static int mtk_dai_tdm_hw_params(struct snd_pcm_substream *substream,
tdm_con |= 1 << DELAY_DATA_SFT;
tdm_con |= get_tdm_lrck_width(format) << LRCK_TDM_WIDTH_SFT;
} else if (tdm_priv->tdm_out_mode == TDM_OUT_DSP_A) {
- tdm_con |= 0 << DELAY_DATA_SFT;
+ tdm_con |= 1 << DELAY_DATA_SFT;
tdm_con |= 0 << LRCK_TDM_WIDTH_SFT;
} else if (tdm_priv->tdm_out_mode == TDM_OUT_DSP_B) {
- tdm_con |= 1 << DELAY_DATA_SFT;
+ tdm_con |= 0 << DELAY_DATA_SFT;
tdm_con |= 0 << LRCK_TDM_WIDTH_SFT;
}
diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
index b93ea33739f2..6458d5dc4902 100644
--- a/sound/soc/meson/Kconfig
+++ b/sound/soc/meson/Kconfig
@@ -99,6 +99,7 @@ config SND_MESON_AXG_PDM
config SND_MESON_CARD_UTILS
tristate
+ select SND_DYNAMIC_MINORS
config SND_MESON_CODEC_GLUE
tristate
diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c
index 3180aa4d3a15..8c5605c1e34e 100644
--- a/sound/soc/meson/axg-card.c
+++ b/sound/soc/meson/axg-card.c
@@ -318,6 +318,7 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
dai_link->cpus = cpu;
dai_link->num_cpus = 1;
+ dai_link->nonatomic = true;
ret = meson_card_parse_dai(card, np, dai_link->cpus);
if (ret)
diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c
index 65ef25e83f5f..59abe0b3c59f 100644
--- a/sound/soc/meson/axg-fifo.c
+++ b/sound/soc/meson/axg-fifo.c
@@ -204,18 +204,26 @@ static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id)
unsigned int status;
regmap_read(fifo->map, FIFO_STATUS1, &status);
-
status = FIELD_GET(STATUS1_INT_STS, status);
+ axg_fifo_ack_irq(fifo, status);
+
+ /* Use the thread to call period elapsed on nonatomic links */
if (status & FIFO_INT_COUNT_REPEAT)
- snd_pcm_period_elapsed(ss);
- else
- dev_dbg(axg_fifo_dev(ss), "unexpected irq - STS 0x%02x\n",
- status);
+ return IRQ_WAKE_THREAD;
- /* Ack irqs */
- axg_fifo_ack_irq(fifo, status);
+ dev_dbg(axg_fifo_dev(ss), "unexpected irq - STS 0x%02x\n",
+ status);
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t axg_fifo_pcm_irq_block_thread(int irq, void *dev_id)
+{
+ struct snd_pcm_substream *ss = dev_id;
+
+ snd_pcm_period_elapsed(ss);
- return IRQ_RETVAL(status);
+ return IRQ_HANDLED;
}
int axg_fifo_pcm_open(struct snd_soc_component *component,
@@ -243,8 +251,9 @@ int axg_fifo_pcm_open(struct snd_soc_component *component,
if (ret)
return ret;
- ret = request_irq(fifo->irq, axg_fifo_pcm_irq_block, 0,
- dev_name(dev), ss);
+ ret = request_threaded_irq(fifo->irq, axg_fifo_pcm_irq_block,
+ axg_fifo_pcm_irq_block_thread,
+ IRQF_ONESHOT, dev_name(dev), ss);
if (ret)
return ret;
diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c
index 63333a2b0a9c..a6579efd3775 100644
--- a/sound/soc/meson/axg-tdm-formatter.c
+++ b/sound/soc/meson/axg-tdm-formatter.c
@@ -392,6 +392,46 @@ void axg_tdm_stream_free(struct axg_tdm_stream *ts)
}
EXPORT_SYMBOL_GPL(axg_tdm_stream_free);
+int axg_tdm_stream_set_cont_clocks(struct axg_tdm_stream *ts,
+ unsigned int fmt)
+{
+ int ret = 0;
+
+ if (fmt & SND_SOC_DAIFMT_CONT) {
+ /* Clock are already enabled - skipping */
+ if (ts->clk_enabled)
+ return 0;
+
+ ret = clk_prepare_enable(ts->iface->mclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(ts->iface->sclk);
+ if (ret)
+ goto err_sclk;
+
+ ret = clk_prepare_enable(ts->iface->lrclk);
+ if (ret)
+ goto err_lrclk;
+
+ ts->clk_enabled = true;
+ return 0;
+ }
+
+ /* Clocks are already disabled - skipping */
+ if (!ts->clk_enabled)
+ return 0;
+
+ clk_disable_unprepare(ts->iface->lrclk);
+err_lrclk:
+ clk_disable_unprepare(ts->iface->sclk);
+err_sclk:
+ clk_disable_unprepare(ts->iface->mclk);
+ ts->clk_enabled = false;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(axg_tdm_stream_set_cont_clocks);
+
MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c
index bf708717635b..62057c71f742 100644
--- a/sound/soc/meson/axg-tdm-interface.c
+++ b/sound/soc/meson/axg-tdm-interface.c
@@ -309,6 +309,7 @@ static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
+ struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
int ret;
switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -346,7 +347,11 @@ static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- return 0;
+ ret = axg_tdm_stream_set_cont_clocks(ts, iface->fmt);
+ if (ret)
+ dev_err(dai->dev, "failed to apply continuous clock setting\n");
+
+ return ret;
}
static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream,
@@ -354,19 +359,32 @@ static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream,
{
struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
- /* Stop all attached formatters */
- axg_tdm_stream_stop(ts);
-
- return 0;
+ return axg_tdm_stream_set_cont_clocks(ts, 0);
}
-static int axg_tdm_iface_prepare(struct snd_pcm_substream *substream,
+static int axg_tdm_iface_trigger(struct snd_pcm_substream *substream,
+ int cmd,
struct snd_soc_dai *dai)
{
- struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
+ struct axg_tdm_stream *ts =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ axg_tdm_stream_start(ts);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ axg_tdm_stream_stop(ts);
+ break;
+ default:
+ return -EINVAL;
+ }
- /* Force all attached formatters to update */
- return axg_tdm_stream_reset(ts);
+ return 0;
}
static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai)
@@ -412,8 +430,8 @@ static const struct snd_soc_dai_ops axg_tdm_iface_ops = {
.set_fmt = axg_tdm_iface_set_fmt,
.startup = axg_tdm_iface_startup,
.hw_params = axg_tdm_iface_hw_params,
- .prepare = axg_tdm_iface_prepare,
.hw_free = axg_tdm_iface_hw_free,
+ .trigger = axg_tdm_iface_trigger,
};
/* TDM Backend DAIs */
diff --git a/sound/soc/meson/axg-tdm.h b/sound/soc/meson/axg-tdm.h
index 42f7470b9a7f..daaca10fec9e 100644
--- a/sound/soc/meson/axg-tdm.h
+++ b/sound/soc/meson/axg-tdm.h
@@ -58,12 +58,17 @@ struct axg_tdm_stream {
unsigned int physical_width;
u32 *mask;
bool ready;
+
+ /* For continuous clock tracking */
+ bool clk_enabled;
};
struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface);
void axg_tdm_stream_free(struct axg_tdm_stream *ts);
int axg_tdm_stream_start(struct axg_tdm_stream *ts);
void axg_tdm_stream_stop(struct axg_tdm_stream *ts);
+int axg_tdm_stream_set_cont_clocks(struct axg_tdm_stream *ts,
+ unsigned int fmt);
static inline int axg_tdm_stream_reset(struct axg_tdm_stream *ts)
{
diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile
index 380a9769c321..63fe0d55fd0e 100644
--- a/sound/soc/sof/amd/Makefile
+++ b/sound/soc/sof/amd/Makefile
@@ -5,14 +5,14 @@
# Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved.
snd-sof-amd-acp-y := acp.o acp-loader.o acp-ipc.o acp-pcm.o acp-stream.o acp-trace.o acp-common.o
-snd-sof-amd-acp-$(CONFIG_SND_SOC_SOF_ACP_PROBES) = acp-probes.o
+snd-sof-amd-acp-$(CONFIG_SND_SOC_SOF_ACP_PROBES) += acp-probes.o
snd-sof-amd-renoir-y := pci-rn.o renoir.o
snd-sof-amd-rembrandt-y := pci-rmb.o rembrandt.o
snd-sof-amd-vangogh-y := pci-vangogh.o vangogh.o
snd-sof-amd-acp63-y := pci-acp63.o acp63.o
obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o
-obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) +=snd-sof-amd-renoir.o
-obj-$(CONFIG_SND_SOC_SOF_AMD_REMBRANDT) +=snd-sof-amd-rembrandt.o
-obj-$(CONFIG_SND_SOC_SOF_AMD_VANGOGH) +=snd-sof-amd-vangogh.o
-obj-$(CONFIG_SND_SOC_SOF_AMD_ACP63) +=snd-sof-amd-acp63.o
+obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) += snd-sof-amd-renoir.o
+obj-$(CONFIG_SND_SOC_SOF_AMD_REMBRANDT) += snd-sof-amd-rembrandt.o
+obj-$(CONFIG_SND_SOC_SOF_AMD_VANGOGH) += snd-sof-amd-vangogh.o
+obj-$(CONFIG_SND_SOC_SOF_AMD_ACP63) += snd-sof-amd-acp63.o
diff --git a/sound/soc/sof/intel/hda-bus.c b/sound/soc/sof/intel/hda-bus.c
index b43d41b5099c..1989147aa6a4 100644
--- a/sound/soc/sof/intel/hda-bus.c
+++ b/sound/soc/sof/intel/hda-bus.c
@@ -72,7 +72,12 @@ void sof_hda_bus_init(struct snd_sof_dev *sdev, struct device *dev)
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+ const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata);
+
snd_hdac_ext_bus_init(bus, dev, &bus_core_ops, sof_hda_ext_ops);
+
+ if (chip && chip->hw_ip_version == SOF_INTEL_ACE_2_0)
+ bus->use_pio_for_commands = true;
#else
snd_hdac_ext_bus_init(bus, dev, NULL, NULL);
#endif
diff --git a/sound/sparc/Makefile b/sound/sparc/Makefile
index e1f596571d7f..0a03123933c6 100644
--- a/sound/sparc/Makefile
+++ b/sound/sparc/Makefile
@@ -4,9 +4,9 @@
# Copyright (c) 2002 by David S. Miller <davem@redhat.com>
#
-snd-sun-amd7930-objs := amd7930.o
-snd-sun-cs4231-objs := cs4231.o
-snd-sun-dbri-objs := dbri.o
+snd-sun-amd7930-y := amd7930.o
+snd-sun-cs4231-y := cs4231.o
+snd-sun-dbri-y := dbri.o
obj-$(CONFIG_SND_SUN_AMD7930) += snd-sun-amd7930.o
obj-$(CONFIG_SND_SUN_CS4231) += snd-sun-cs4231.o
diff --git a/sound/spi/Makefile b/sound/spi/Makefile
index a3834919b0f6..d6a198a44917 100644
--- a/sound/spi/Makefile
+++ b/sound/spi/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
# Makefile for SPI drivers
-snd-at73c213-objs := at73c213.o
+snd-at73c213-y := at73c213.o
obj-$(CONFIG_SND_AT73C213) += snd-at73c213.o
diff --git a/sound/synth/Makefile b/sound/synth/Makefile
index b9f71d5dbc8c..369f3be2cd17 100644
--- a/sound/synth/Makefile
+++ b/sound/synth/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-util-mem-objs := util_mem.o
+snd-util-mem-y := util_mem.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o
diff --git a/sound/synth/emux/Makefile b/sound/synth/emux/Makefile
index ed28c81ac12e..59357da6d1ef 100644
--- a/sound/synth/emux/Makefile
+++ b/sound/synth/emux/Makefile
@@ -4,7 +4,7 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-emux-synth-objs := emux.o emux_synth.o emux_seq.o emux_nrpn.o \
+snd-emux-synth-y := emux.o emux_synth.o emux_seq.o emux_nrpn.o \
emux_effect.o emux_hwdep.o soundfont.o
snd-emux-synth-$(CONFIG_SND_PROC_FS) += emux_proc.o
ifneq ($(CONFIG_SND_SEQUENCER_OSS),)
diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c
index a82af9374852..01444fc960d0 100644
--- a/sound/synth/emux/emux.c
+++ b/sound/synth/emux/emux.c
@@ -94,10 +94,8 @@ int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, ch
/* create soundfont list */
memset(&sf_cb, 0, sizeof(sf_cb));
sf_cb.private_data = emu;
- if (emu->ops.sample_new)
- sf_cb.sample_new = sf_sample_new;
- if (emu->ops.sample_free)
- sf_cb.sample_free = sf_sample_free;
+ sf_cb.sample_new = sf_sample_new;
+ sf_cb.sample_free = sf_sample_free;
if (emu->ops.sample_reset)
sf_cb.sample_reset = sf_sample_reset;
emu->sflist = snd_sf_new(&sf_cb, emu->memhdr);
diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c
index 81719bfb8ed7..fd8f978cde1c 100644
--- a/sound/synth/emux/emux_hwdep.c
+++ b/sound/synth/emux/emux_hwdep.c
@@ -27,8 +27,7 @@ snd_emux_hwdep_load_patch(struct snd_emux *emu, void __user *arg)
if (patch.key == GUS_PATCH)
return snd_soundfont_load_guspatch(emu->sflist, arg,
- patch.len + sizeof(patch),
- TMP_CLIENT_ID);
+ patch.len + sizeof(patch));
if (patch.type >= SNDRV_SFNT_LOAD_INFO &&
patch.type <= SNDRV_SFNT_PROBE_DATA) {
diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c
index d8d32671f703..04df46b269d3 100644
--- a/sound/synth/emux/emux_oss.c
+++ b/sound/synth/emux/emux_oss.c
@@ -205,8 +205,7 @@ snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
return -ENXIO;
if (format == GUS_PATCH)
- rc = snd_soundfont_load_guspatch(emu->sflist, buf, count,
- SF_CLIENT_NO(p->chset.port));
+ rc = snd_soundfont_load_guspatch(emu->sflist, buf, count);
else if (format == SNDRV_OSS_SOUNDFONT_PATCH) {
struct soundfont_patch_info patch;
if (count < (int)sizeof(patch))
diff --git a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c
index 7993e6a01e54..820351f52551 100644
--- a/sound/synth/emux/emux_proc.c
+++ b/sound/synth/emux/emux_proc.c
@@ -102,6 +102,7 @@ void snd_emux_proc_init(struct snd_emux *emu, struct snd_card *card, int device)
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = emu;
entry->c.text.read = snd_emux_proc_info_read;
+ emu->proc = entry;
}
void snd_emux_proc_free(struct snd_emux *emu)
diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c
index b227c7e0bc2a..1adaa75df2f6 100644
--- a/sound/synth/emux/emux_seq.c
+++ b/sound/synth/emux/emux_seq.c
@@ -65,11 +65,11 @@ snd_emux_init_seq(struct snd_emux *emu, struct snd_card *card, int index)
return -ENODEV;
}
- if (emu->num_ports < 0) {
+ if (emu->num_ports <= 0) {
snd_printk(KERN_WARNING "seqports must be greater than zero\n");
emu->num_ports = 1;
- } else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) {
- snd_printk(KERN_WARNING "too many ports."
+ } else if (emu->num_ports > SNDRV_EMUX_MAX_PORTS) {
+ snd_printk(KERN_WARNING "too many ports. "
"limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS);
emu->num_ports = SNDRV_EMUX_MAX_PORTS;
}
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
index 16f00097cb95..2373ed580bf8 100644
--- a/sound/synth/emux/soundfont.c
+++ b/sound/synth/emux/soundfont.c
@@ -689,6 +689,21 @@ find_sample(struct snd_soundfont *sf, int sample_id)
}
+static int
+validate_sample_info(struct soundfont_sample_info *si)
+{
+ if (si->end < 0 || si->end > si->size)
+ return -EINVAL;
+ if (si->loopstart < 0 || si->loopstart > si->end)
+ return -EINVAL;
+ if (si->loopend < 0 || si->loopend > si->end)
+ return -EINVAL;
+ /* be sure loop points start < end */
+ if (si->loopstart > si->loopend)
+ swap(si->loopstart, si->loopend);
+ return 0;
+}
+
/*
* Load sample information, this can include data to be loaded onto
* the soundcard. It can also just be a pointer into soundcard ROM.
@@ -701,7 +716,6 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count)
struct snd_soundfont *sf;
struct soundfont_sample_info sample_info;
struct snd_sf_sample *sp;
- long off;
/* patch must be opened */
sf = sflist->currsf;
@@ -711,12 +725,16 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count)
if (is_special_type(sf->type))
return -EINVAL;
+ if (count < (long)sizeof(sample_info)) {
+ return -EINVAL;
+ }
if (copy_from_user(&sample_info, data, sizeof(sample_info)))
return -EFAULT;
+ data += sizeof(sample_info);
+ count -= sizeof(sample_info);
- off = sizeof(sample_info);
-
- if (sample_info.size != (count-off)/2)
+ // SoundFont uses S16LE samples.
+ if (sample_info.size * 2 != count)
return -EINVAL;
/* Check for dup */
@@ -727,6 +745,21 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count)
return -EINVAL;
}
+ if (sample_info.size > 0) {
+ if (sample_info.start < 0)
+ return -EINVAL;
+
+ // Here we "rebase out" the start address, because the
+ // real start is the start of the provided sample data.
+ sample_info.end -= sample_info.start;
+ sample_info.loopstart -= sample_info.start;
+ sample_info.loopend -= sample_info.start;
+ sample_info.start = 0;
+
+ if (validate_sample_info(&sample_info) < 0)
+ return -EINVAL;
+ }
+
/* Allocate a new sample structure */
sp = sf_sample_new(sflist, sf);
if (!sp)
@@ -735,7 +768,7 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count)
sp->v = sample_info;
sp->v.sf_id = sf->id;
sp->v.dummy = 0;
- sp->v.truesize = sp->v.size;
+ sp->v.truesize = 0;
/*
* If there is wave data then load it.
@@ -744,7 +777,7 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count)
int rc;
rc = sflist->callback.sample_new
(sflist->callback.private_data, sp, sflist->memhdr,
- data + off, count - off);
+ data, count);
if (rc < 0) {
sf_sample_delete(sflist, sf, sp);
return rc;
@@ -941,8 +974,7 @@ int snd_sf_vol_table[128] = {
/* load GUS patch */
static int
-load_guspatch(struct snd_sf_list *sflist, const char __user *data,
- long count, int client)
+load_guspatch(struct snd_sf_list *sflist, const char __user *data, long count)
{
struct patch_info patch;
struct snd_soundfont *sf;
@@ -957,10 +989,12 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data,
}
if (copy_from_user(&patch, data, sizeof(patch)))
return -EFAULT;
-
count -= sizeof(patch);
data += sizeof(patch);
+ if ((patch.len << (patch.mode & WAVE_16_BITS ? 1 : 0)) != count)
+ return -EINVAL;
+
sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_GUS|SNDRV_SFNT_PAT_SHARED, NULL);
if (sf == NULL)
return -ENOMEM;
@@ -975,6 +1009,11 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data,
smp->v.loopend = patch.loop_end;
smp->v.size = patch.len;
+ if (validate_sample_info(&smp->v) < 0) {
+ sf_sample_delete(sflist, sf, smp);
+ return -EINVAL;
+ }
+
/* set up mode flags */
smp->v.mode_flags = 0;
if (!(patch.mode & WAVE_16_BITS))
@@ -1012,7 +1051,7 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data,
/*
* load wave data
*/
- if (sflist->callback.sample_new) {
+ if (smp->v.size > 0) {
rc = sflist->callback.sample_new
(sflist->callback.private_data, smp, sflist->memhdr,
data, count);
@@ -1122,11 +1161,11 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data,
/* load GUS patch */
int
snd_soundfont_load_guspatch(struct snd_sf_list *sflist, const char __user *data,
- long count, int client)
+ long count)
{
int rc;
lock_preset(sflist);
- rc = load_guspatch(sflist, data, count, client);
+ rc = load_guspatch(sflist, data, count);
unlock_preset(sflist);
return rc;
}
@@ -1377,9 +1416,8 @@ snd_sf_clear(struct snd_sf_list *sflist)
}
for (sp = sf->samples; sp; sp = nextsp) {
nextsp = sp->next;
- if (sflist->callback.sample_free)
- sflist->callback.sample_free(sflist->callback.private_data,
- sp, sflist->memhdr);
+ sflist->callback.sample_free(sflist->callback.private_data,
+ sp, sflist->memhdr);
kfree(sp);
}
kfree(sf);
@@ -1481,9 +1519,8 @@ snd_soundfont_remove_unlocked(struct snd_sf_list *sflist)
nextsp = sp->next;
sf->samples = nextsp;
sflist->mem_used -= sp->v.truesize;
- if (sflist->callback.sample_free)
- sflist->callback.sample_free(sflist->callback.private_data,
- sp, sflist->memhdr);
+ sflist->callback.sample_free(sflist->callback.private_data,
+ sp, sflist->memhdr);
kfree(sp);
}
}
diff --git a/sound/usb/6fire/Makefile b/sound/usb/6fire/Makefile
index 7d353bbf7493..587f25c64e56 100644
--- a/sound/usb/6fire/Makefile
+++ b/sound/usb/6fire/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-usb-6fire-objs += chip.o comm.o midi.o control.o firmware.o pcm.o
+snd-usb-6fire-y += chip.o comm.o midi.o control.o firmware.o pcm.o
obj-$(CONFIG_SND_USB_6FIRE) += snd-usb-6fire.o
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index 8c657c2753c8..0532499dbc6d 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -3,7 +3,7 @@
# Makefile for ALSA
#
-snd-usb-audio-objs := card.o \
+snd-usb-audio-y := card.o \
clock.o \
endpoint.o \
format.o \
@@ -25,7 +25,7 @@ snd-usb-audio-objs := card.o \
snd-usb-audio-$(CONFIG_SND_USB_AUDIO_MIDI_V2) += midi2.o
snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.o
-snd-usbmidi-lib-objs := midi.o
+snd-usbmidi-lib-y := midi.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usbmidi-lib.o
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 1b2edc0fd2e9..bdb04fa37a71 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -425,6 +425,10 @@ static const struct usb_audio_device_name usb_audio_names[] = {
DEVICE_NAME(0x0fd9, 0x0008, "Hauppauge", "HVR-950Q"),
+ /* Dock/Stand for HP Engage Go */
+ PROFILE_NAME(0x103c, 0x830a, "HP", "HP Engage Go Dock",
+ "HP-Engage-Go-Dock"),
+
/* Stanton/N2IT Final Scratch v1 device ('Scratchamp') */
DEVICE_NAME(0x103d, 0x0100, "Stanton", "ScratchAmp"),
DEVICE_NAME(0x103d, 0x0101, "Stanton", "ScratchAmp"),
diff --git a/sound/usb/hiface/Makefile b/sound/usb/hiface/Makefile
index 8f3b24e7d6c2..997c1558d0cb 100644
--- a/sound/usb/hiface/Makefile
+++ b/sound/usb/hiface/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-usb-hiface-objs := chip.o pcm.o
+snd-usb-hiface-y := chip.o pcm.o
obj-$(CONFIG_SND_USB_HIFACE) += snd-usb-hiface.o
diff --git a/sound/usb/misc/Makefile b/sound/usb/misc/Makefile
index 068ecd7bc043..3e9f4adc28de 100644
--- a/sound/usb/misc/Makefile
+++ b/sound/usb/misc/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-ua101-objs := ua101.o
+snd-ua101-y := ua101.o
obj-$(CONFIG_SND_USB_UA101) += snd-ua101.o
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 065a4be0d771..212b5e6443d8 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -3447,6 +3447,8 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
case USB_ID(0x1235, 0x8213): /* Focusrite Scarlett 8i6 3rd Gen */
case USB_ID(0x1235, 0x8214): /* Focusrite Scarlett 18i8 3rd Gen */
case USB_ID(0x1235, 0x8215): /* Focusrite Scarlett 18i20 3rd Gen */
+ case USB_ID(0x1235, 0x8216): /* Focusrite Vocaster One */
+ case USB_ID(0x1235, 0x8217): /* Focusrite Vocaster Two */
case USB_ID(0x1235, 0x8218): /* Focusrite Scarlett Solo 4th Gen */
case USB_ID(0x1235, 0x8219): /* Focusrite Scarlett 2i2 4th Gen */
case USB_ID(0x1235, 0x821a): /* Focusrite Scarlett 4i4 4th Gen */
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index bd114be537d7..1150cf104985 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -85,8 +85,10 @@
* controls
* - disable/enable MSD mode
* - disable/enable standalone mode
- * - input gain, autogain, safe mode
+ * - input mute, gain, autogain, safe mode
* - direct monitor mixes
+ * - compressor and EQ
+ * - Bluetooth volume
*
* <ditaa>
* /--------------\ 18chn 20chn /--------------\
@@ -174,22 +176,24 @@
/* some gui mixers can't handle negative ctl values */
#define SCARLETT2_VOLUME_BIAS 127
-/* maximum preamp input gain and value
- * values are from 0 to 70, preamp gain is from 0 to 69 dB
+/* maximum preamp input gain value
+ * (the corresponding value in dB is per-device)
*/
#define SCARLETT2_MAX_GAIN_VALUE 70
-#define SCARLETT2_MAX_GAIN_DB 69
-/* mixer range from -80dB to +6dB in 0.5dB steps */
+/* maximum Bluetooth volume value */
+#define SCARLETT2_MAX_BLUETOOTH_VOLUME 30
+
+/* mixer range from -80dB to +12dB in 0.5dB steps */
#define SCARLETT2_MIXER_MIN_DB -80
#define SCARLETT2_MIXER_BIAS (-SCARLETT2_MIXER_MIN_DB * 2)
-#define SCARLETT2_MIXER_MAX_DB 6
+#define SCARLETT2_MIXER_MAX_DB 12
#define SCARLETT2_MIXER_MAX_VALUE \
((SCARLETT2_MIXER_MAX_DB - SCARLETT2_MIXER_MIN_DB) * 2)
#define SCARLETT2_MIXER_VALUE_COUNT (SCARLETT2_MIXER_MAX_VALUE + 1)
/* map from (dB + 80) * 2 to mixer value
- * for dB in 0 .. 172: int(8192 * pow(10, ((dB - 160) / 2 / 20)))
+ * for dB in 0 .. 184: int(8192 * pow(10, ((dB - 160) / 2 / 20)))
*/
static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = {
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
@@ -205,7 +209,8 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = {
3078, 3261, 3454, 3659, 3876, 4105, 4349, 4606, 4879, 5168,
5475, 5799, 6143, 6507, 6892, 7301, 7733, 8192, 8677, 9191,
9736, 10313, 10924, 11571, 12257, 12983, 13752, 14567, 15430,
- 16345
+ 16345, 17313, 18339, 19426, 20577, 21796, 23088, 24456, 25905,
+ 27440, 29066, 30788, 32612
};
/* Maximum number of analogue outputs */
@@ -215,6 +220,8 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = {
#define SCARLETT2_LEVEL_SWITCH_MAX 2
#define SCARLETT2_PAD_SWITCH_MAX 8
#define SCARLETT2_AIR_SWITCH_MAX 8
+#define SCARLETT2_DSP_SWITCH_MAX 2
+#define SCARLETT2_INPUT_MUTE_SWITCH_MAX 2
#define SCARLETT2_PHANTOM_SWITCH_MAX 2
#define SCARLETT2_INPUT_GAIN_MAX 2
@@ -245,6 +252,59 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = {
/* Maximum number of meters (sum of output port counts) */
#define SCARLETT2_MAX_METERS 65
+/* Compressor parameter data
+ *
+ * The compressor parameters are 32-bit fixed point values with 24
+ * bits of fraction. Integer values are sufficient for the parameters
+ * except for ratio which we can set in 0.5:1 steps.
+ */
+struct compressor_param {
+ const char *name;
+ snd_ctl_elem_type_t type;
+ s32 min;
+ s32 max;
+ int scale_bits;
+};
+
+/* The available compressor parameters on the Vocaster:
+ * - Enable: Off, On
+ * - Threshold: -40dB to 0dB
+ * - Ratio: 1:1 to 50:1 in 0.5:1 steps
+ * - Knee Width: 0dB to 10dB
+ * - Attack: 30ms to 127ms
+ * - Release: 30ms to 127ms
+ * - Makeup Gain: 0dB to 24dB
+ */
+static const struct compressor_param compressor_params[] = {
+ { "Enable", SNDRV_CTL_ELEM_TYPE_BOOLEAN, 0, 1, 0 },
+ { "Threshold", SNDRV_CTL_ELEM_TYPE_INTEGER, -40, 0, 24 },
+ { "Ratio", SNDRV_CTL_ELEM_TYPE_INTEGER, 2, 100, 23 },
+ { "Knee Width", SNDRV_CTL_ELEM_TYPE_INTEGER, 0, 10, 24 },
+ { "Attack", SNDRV_CTL_ELEM_TYPE_INTEGER, 30, 127, 24 },
+ { "Release", SNDRV_CTL_ELEM_TYPE_INTEGER, 30, 127, 24 },
+ { "Makeup Gain", SNDRV_CTL_ELEM_TYPE_INTEGER, 0, 24, 24 },
+};
+
+#define SCARLETT2_COMPRESSOR_PARAM_COUNT ARRAY_SIZE(compressor_params)
+#define SCARLETT2_COMPRESSOR_CTLS_MAX \
+ (SCARLETT2_COMPRESSOR_PARAM_COUNT * SCARLETT2_DSP_SWITCH_MAX)
+
+/* Maximum number of filter controls */
+#define SCARLETT2_PRECOMP_FLT_CTLS_MAX (2 * SCARLETT2_DSP_SWITCH_MAX)
+#define SCARLETT2_PEQ_FLT_CTLS_MAX (3 * SCARLETT2_DSP_SWITCH_MAX)
+
+/* Number of biquad filter coefficients */
+#define SCARLETT2_BIQUAD_COEFFS 5
+
+/* Maximum number of filter coefficient values */
+#define SCARLETT2_PRECOMP_FLT_VALUES_MAX \
+ (SCARLETT2_PRECOMP_FLT_CTLS_MAX * SCARLETT2_BIQUAD_COEFFS)
+#define SCARLETT2_PEQ_FLT_VALUES_MAX \
+ (SCARLETT2_PEQ_FLT_CTLS_MAX * SCARLETT2_BIQUAD_COEFFS)
+
+/* Maximum number of PEQ filter slots */
+#define SCARLETT2_PEQ_FLT_SLOTS_MAX 4
+
/* Hardware port types:
* - None (no input to mux)
* - Analogue I/O
@@ -277,6 +337,17 @@ enum {
SCARLETT2_DIM_MUTE_COUNT
};
+/* Autogain target values */
+
+#define SCARLETT2_AG_TARGET_MIN (-30)
+
+enum {
+ SCARLETT2_AG_HOT_TARGET,
+ SCARLETT2_AG_MEAN_TARGET,
+ SCARLETT2_AG_PEAK_TARGET,
+ SCARLETT2_AG_TARGET_COUNT
+};
+
/* Flash Write State */
enum {
SCARLETT2_FLASH_WRITE_STATE_IDLE,
@@ -295,7 +366,7 @@ static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = {
* If autogain_switch is set, autogain_status is set to 0 (Running).
* The other status values are from the raw_autogain_status value + 1.
*/
-static const char *const scarlett2_autogain_status_texts[] = {
+static const char *const scarlett2_autogain_status_gen4[] = {
"Running",
"Success",
"SuccessDRover",
@@ -304,7 +375,20 @@ static const char *const scarlett2_autogain_status_texts[] = {
"FailMaxGainLimit",
"FailClipped",
"Cancelled",
- "Invalid"
+ "Invalid",
+ NULL
+};
+
+static const char *const scarlett2_autogain_status_vocaster[] = {
+ "Running",
+ "Success",
+ "FailPG",
+ "FailRange",
+ "WarnMaxCap",
+ "WarnMinCap",
+ "Cancelled",
+ "Invalid",
+ NULL
};
/* Power Status Values */
@@ -321,6 +405,7 @@ struct scarlett2_notification {
void (*func)(struct usb_mixer_interface *mixer);
};
+static void scarlett2_notify_ack(struct usb_mixer_interface *mixer);
static void scarlett2_notify_sync(struct usb_mixer_interface *mixer);
static void scarlett2_notify_dim_mute(struct usb_mixer_interface *mixer);
static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer);
@@ -328,6 +413,8 @@ static void scarlett2_notify_volume(struct usb_mixer_interface *mixer);
static void scarlett2_notify_input_level(struct usb_mixer_interface *mixer);
static void scarlett2_notify_input_pad(struct usb_mixer_interface *mixer);
static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_dsp(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_mute(struct usb_mixer_interface *mixer);
static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer);
static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer);
static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer);
@@ -339,11 +426,12 @@ static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer);
static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer);
static void scarlett2_notify_pcm_input_switch(
struct usb_mixer_interface *mixer);
+static void scarlett2_notify_bluetooth(struct usb_mixer_interface *mixer);
/* Arrays of notification callback functions */
static const struct scarlett2_notification scarlett2_notifications[] = {
- { 0x00000001, NULL }, /* ack, gets ignored */
+ { 0x00000001, scarlett2_notify_ack },
{ 0x00000008, scarlett2_notify_sync },
{ 0x00200000, scarlett2_notify_dim_mute },
{ 0x00400000, scarlett2_notify_monitor },
@@ -353,14 +441,26 @@ static const struct scarlett2_notification scarlett2_notifications[] = {
};
static const struct scarlett2_notification scarlett3a_notifications[] = {
- { 0x00000001, NULL }, /* ack, gets ignored */
+ { 0x00000001, scarlett2_notify_ack },
{ 0x00800000, scarlett2_notify_input_other },
{ 0x01000000, scarlett2_notify_direct_monitor },
{ 0, NULL }
};
+static const struct scarlett2_notification vocaster_notifications[] = {
+ { 0x00000001, scarlett2_notify_ack },
+ { 0x00000008, scarlett2_notify_sync },
+ { 0x00200000, scarlett2_notify_input_mute },
+ { 0x00400000, scarlett2_notify_autogain },
+ { 0x04000000, scarlett2_notify_input_dsp },
+ { 0x08000000, scarlett2_notify_input_gain },
+ { 0x10000000, scarlett2_notify_input_phantom },
+ { 0x20000000, scarlett2_notify_bluetooth },
+ { 0, NULL }
+};
+
static const struct scarlett2_notification scarlett4_solo_notifications[] = {
- { 0x00000001, NULL }, /* ack, gets ignored */
+ { 0x00000001, scarlett2_notify_ack },
{ 0x00000008, scarlett2_notify_sync },
{ 0x00400000, scarlett2_notify_input_air },
{ 0x00800000, scarlett2_notify_direct_monitor },
@@ -371,7 +471,7 @@ static const struct scarlett2_notification scarlett4_solo_notifications[] = {
};
static const struct scarlett2_notification scarlett4_2i2_notifications[] = {
- { 0x00000001, NULL }, /* ack, gets ignored */
+ { 0x00000001, scarlett2_notify_ack },
{ 0x00000008, scarlett2_notify_sync },
{ 0x00200000, scarlett2_notify_input_safe },
{ 0x00400000, scarlett2_notify_autogain },
@@ -387,7 +487,7 @@ static const struct scarlett2_notification scarlett4_2i2_notifications[] = {
};
static const struct scarlett2_notification scarlett4_4i4_notifications[] = {
- { 0x00000001, NULL }, /* ack, gets ignored */
+ { 0x00000001, scarlett2_notify_ack },
{ 0x00000008, scarlett2_notify_sync },
{ 0x00200000, scarlett2_notify_input_safe },
{ 0x00400000, scarlett2_notify_autogain },
@@ -414,6 +514,13 @@ enum {
SCARLETT2_CONFIG_PAD_SWITCH,
SCARLETT2_CONFIG_MSD_SWITCH,
SCARLETT2_CONFIG_AIR_SWITCH,
+ SCARLETT2_CONFIG_DSP_SWITCH,
+ SCARLETT2_CONFIG_COMPRESSOR_PARAMS,
+ SCARLETT2_CONFIG_PRECOMP_FLT_SWITCH,
+ SCARLETT2_CONFIG_PRECOMP_FLT_PARAMS,
+ SCARLETT2_CONFIG_PEQ_FLT_SWITCH,
+ SCARLETT2_CONFIG_PEQ_FLT_PARAMS,
+ SCARLETT2_CONFIG_INPUT_MUTE_SWITCH,
SCARLETT2_CONFIG_STANDALONE_SWITCH,
SCARLETT2_CONFIG_PHANTOM_SWITCH,
SCARLETT2_CONFIG_PHANTOM_PERSISTENCE,
@@ -423,6 +530,9 @@ enum {
SCARLETT2_CONFIG_TALKBACK_MAP,
SCARLETT2_CONFIG_AUTOGAIN_SWITCH,
SCARLETT2_CONFIG_AUTOGAIN_STATUS,
+ SCARLETT2_CONFIG_AG_HOT_TARGET,
+ SCARLETT2_CONFIG_AG_MEAN_TARGET,
+ SCARLETT2_CONFIG_AG_PEAK_TARGET,
SCARLETT2_CONFIG_INPUT_GAIN,
SCARLETT2_CONFIG_SAFE_SWITCH,
SCARLETT2_CONFIG_INPUT_SELECT_SWITCH,
@@ -431,15 +541,30 @@ enum {
SCARLETT2_CONFIG_POWER_LOW,
SCARLETT2_CONFIG_PCM_INPUT_SWITCH,
SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN,
+ SCARLETT2_CONFIG_BLUETOOTH_VOLUME,
+ SCARLETT2_CONFIG_SPDIF_MODE,
SCARLETT2_CONFIG_COUNT
};
+/* Autogain target configuration parameters and names */
+
+static const int scarlett2_ag_target_configs[] = {
+ [SCARLETT2_AG_HOT_TARGET] = SCARLETT2_CONFIG_AG_HOT_TARGET,
+ [SCARLETT2_AG_MEAN_TARGET] = SCARLETT2_CONFIG_AG_MEAN_TARGET,
+ [SCARLETT2_AG_PEAK_TARGET] = SCARLETT2_CONFIG_AG_PEAK_TARGET
+};
+
+static const char *const scarlett2_ag_target_names[] = {
+ "Hot", "Mean", "Peak"
+};
+
/* Location, size, and activation command number for the configuration
- * parameters. Size is in bits and may be 0, 1, 8, or 16.
+ * parameters. Size is in bits and may be 1, 8, 16, or 32.
*
- * A size of 0 indicates that the parameter is a byte-sized Scarlett
- * Gen 4 configuration which is written through the gen4_write_addr
- * location (but still read through the given offset location).
+ * Vocaster and 4th Gen devices have a parameter buffer to set certain
+ * configuration parameters. When pbuf is set, rather than writing to
+ * the given offset, the channel and value are written to the
+ * parameter buffer and the activate command is sent to the device.
*
* Some Gen 4 configuration parameters are written with 0x02 for a
* desired value of 0x01, and 0x03 for 0x00. These are indicated with
@@ -451,15 +576,28 @@ struct scarlett2_config {
u16 offset;
u8 size;
u8 activate;
+ u8 pbuf;
u8 mute;
};
struct scarlett2_config_set {
const struct scarlett2_notification *notifications;
- u16 gen4_write_addr;
+ u16 param_buf_addr;
+ const unsigned int *input_gain_tlv;
+ const char *const *autogain_status_texts;
const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT];
};
+/* Input gain TLV dB ranges */
+
+static const DECLARE_TLV_DB_MINMAX(
+ db_scale_vocaster_gain, 0, 70 * 100
+);
+
+static const DECLARE_TLV_DB_MINMAX(
+ db_scale_gen4_gain, 0, 69 * 100
+);
+
/* Gen 2 devices without SW/HW volume switch: 6i6, 18i8 */
static const struct scarlett2_config_set scarlett2_config_set_gen2a = {
@@ -618,31 +756,87 @@ static const struct scarlett2_config_set scarlett2_config_set_gen3c = {
[SCARLETT2_CONFIG_TALKBACK_MAP] = {
.offset = 0xb0, .size = 16, .activate = 10 },
+
+ [SCARLETT2_CONFIG_SPDIF_MODE] = {
+ .offset = 0x94, .size = 8, .activate = 6 },
+ }
+};
+
+/* Vocaster */
+static const struct scarlett2_config_set scarlett2_config_set_vocaster = {
+ .notifications = vocaster_notifications,
+ .param_buf_addr = 0x1bc,
+ .input_gain_tlv = db_scale_vocaster_gain,
+ .autogain_status_texts = scarlett2_autogain_status_vocaster,
+ .items = {
+ [SCARLETT2_CONFIG_MSD_SWITCH] = {
+ .offset = 0x9d, .size = 8, .activate = 6 },
+
+ [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = {
+ .offset = 0x1c0, .size = 8, .activate = 19, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
+ .offset = 0x1c2, .size = 8, },
+
+ [SCARLETT2_CONFIG_AG_HOT_TARGET] = {
+ .offset = 0xc1, .size = 8, .activate = 29, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_INPUT_GAIN] = {
+ .offset = 0x9f, .size = 8, .activate = 21, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
+ .offset = 0x9c, .size = 1, .activate = 20, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_DSP_SWITCH] = {
+ .offset = 0x1c4, .size = 8, .activate = 22, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_COMPRESSOR_PARAMS] = {
+ .offset = 0x1c8, .size = 32, .activate = 23 },
+
+ [SCARLETT2_CONFIG_PRECOMP_FLT_SWITCH] = {
+ .offset = 0x7c, .size = 32, .activate = 27 },
+
+ [SCARLETT2_CONFIG_PRECOMP_FLT_PARAMS] = {
+ .offset = 0x200, .size = 32, .activate = 27 },
+
+ [SCARLETT2_CONFIG_PEQ_FLT_SWITCH] = {
+ .offset = 0x84, .size = 32, .activate = 27 },
+
+ [SCARLETT2_CONFIG_PEQ_FLT_PARAMS] = {
+ .offset = 0x250, .size = 32, .activate = 27 },
+
+ [SCARLETT2_CONFIG_INPUT_MUTE_SWITCH] = {
+ .offset = 0x1be, .size = 8, .activate = 17, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_BLUETOOTH_VOLUME] = {
+ .offset = 0xbf, .size = 8, .activate = 28 },
}
};
/* Solo Gen 4 */
static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = {
.notifications = scarlett4_solo_notifications,
- .gen4_write_addr = 0xd8,
+ .param_buf_addr = 0xd8,
.items = {
[SCARLETT2_CONFIG_MSD_SWITCH] = {
.offset = 0x47, .size = 8, .activate = 4 },
[SCARLETT2_CONFIG_DIRECT_MONITOR] = {
- .offset = 0x108, .activate = 12 },
+ .offset = 0x108, .size = 8, .activate = 12, .pbuf = 1 },
[SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
- .offset = 0x46, .activate = 9, .mute = 1 },
+ .offset = 0x46, .size = 8, .activate = 9, .pbuf = 1,
+ .mute = 1 },
[SCARLETT2_CONFIG_LEVEL_SWITCH] = {
- .offset = 0x3d, .activate = 10, .mute = 1 },
+ .offset = 0x3d, .size = 8, .activate = 10, .pbuf = 1,
+ .mute = 1 },
[SCARLETT2_CONFIG_AIR_SWITCH] = {
- .offset = 0x3e, .activate = 11 },
+ .offset = 0x3e, .size = 8, .activate = 11, .pbuf = 1 },
[SCARLETT2_CONFIG_PCM_INPUT_SWITCH] = {
- .offset = 0x206, .activate = 25 },
+ .offset = 0x206, .size = 8, .activate = 25, .pbuf = 1 },
[SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = {
.offset = 0x232, .size = 16, .activate = 26 }
@@ -652,40 +846,50 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = {
/* 2i2 Gen 4 */
static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = {
.notifications = scarlett4_2i2_notifications,
- .gen4_write_addr = 0xfc,
+ .param_buf_addr = 0xfc,
+ .input_gain_tlv = db_scale_gen4_gain,
+ .autogain_status_texts = scarlett2_autogain_status_gen4,
.items = {
[SCARLETT2_CONFIG_MSD_SWITCH] = {
- .offset = 0x49, .size = 8, .activate = 4 }, // 0x41 ??
+ .offset = 0x49, .size = 8, .activate = 4 },
[SCARLETT2_CONFIG_DIRECT_MONITOR] = {
- .offset = 0x14a, .activate = 16 },
+ .offset = 0x14a, .size = 8, .activate = 16, .pbuf = 1 },
[SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = {
- .offset = 0x135, .activate = 10 },
+ .offset = 0x135, .size = 8, .activate = 10, .pbuf = 1 },
[SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
- .offset = 0x137 },
+ .offset = 0x137, .size = 8 },
+
+ [SCARLETT2_CONFIG_AG_MEAN_TARGET] = {
+ .offset = 0x131, .size = 8, .activate = 29, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_AG_PEAK_TARGET] = {
+ .offset = 0x132, .size = 8, .activate = 30, .pbuf = 1 },
[SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
- .offset = 0x48, .activate = 11, .mute = 1 },
+ .offset = 0x48, .size = 8, .activate = 11, .pbuf = 1,
+ .mute = 1 },
[SCARLETT2_CONFIG_INPUT_GAIN] = {
- .offset = 0x4b, .activate = 12 },
+ .offset = 0x4b, .size = 8, .activate = 12, .pbuf = 1 },
[SCARLETT2_CONFIG_LEVEL_SWITCH] = {
- .offset = 0x3c, .activate = 13, .mute = 1 },
+ .offset = 0x3c, .size = 8, .activate = 13, .pbuf = 1,
+ .mute = 1 },
[SCARLETT2_CONFIG_SAFE_SWITCH] = {
- .offset = 0x147, .activate = 14 },
+ .offset = 0x147, .size = 8, .activate = 14, .pbuf = 1 },
[SCARLETT2_CONFIG_AIR_SWITCH] = {
- .offset = 0x3e, .activate = 15 },
+ .offset = 0x3e, .size = 8, .activate = 15, .pbuf = 1 },
[SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = {
- .offset = 0x14b, .activate = 17 },
+ .offset = 0x14b, .size = 8, .activate = 17, .pbuf = 1 },
[SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = {
- .offset = 0x14e, .activate = 18 },
+ .offset = 0x14e, .size = 8, .activate = 18, .pbuf = 1 },
[SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = {
.offset = 0x2a0, .size = 16, .activate = 36 }
@@ -695,37 +899,47 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = {
/* 4i4 Gen 4 */
static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = {
.notifications = scarlett4_4i4_notifications,
- .gen4_write_addr = 0x130,
+ .param_buf_addr = 0x130,
+ .input_gain_tlv = db_scale_gen4_gain,
+ .autogain_status_texts = scarlett2_autogain_status_gen4,
.items = {
[SCARLETT2_CONFIG_MSD_SWITCH] = {
.offset = 0x5c, .size = 8, .activate = 4 },
[SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = {
- .offset = 0x13e, .activate = 10 },
+ .offset = 0x13e, .size = 8, .activate = 10, .pbuf = 1 },
[SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
- .offset = 0x140 },
+ .offset = 0x140, .size = 8 },
+
+ [SCARLETT2_CONFIG_AG_MEAN_TARGET] = {
+ .offset = 0x13a, .size = 8, .activate = 23, .pbuf = 1 },
+
+ [SCARLETT2_CONFIG_AG_PEAK_TARGET] = {
+ .offset = 0x13b, .size = 8, .activate = 24, .pbuf = 1 },
[SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
- .offset = 0x5a, .activate = 11, .mute = 1 },
+ .offset = 0x5a, .size = 8, .activate = 11, .pbuf = 1,
+ .mute = 1 },
[SCARLETT2_CONFIG_INPUT_GAIN] = {
- .offset = 0x5e, .activate = 12 },
+ .offset = 0x5e, .size = 8, .activate = 12, .pbuf = 1 },
[SCARLETT2_CONFIG_LEVEL_SWITCH] = {
- .offset = 0x4e, .activate = 13, .mute = 1 },
+ .offset = 0x4e, .size = 8, .activate = 13, .pbuf = 1,
+ .mute = 1 },
[SCARLETT2_CONFIG_SAFE_SWITCH] = {
- .offset = 0x150, .activate = 14 },
+ .offset = 0x150, .size = 8, .activate = 14, .pbuf = 1 },
[SCARLETT2_CONFIG_AIR_SWITCH] = {
- .offset = 0x50, .activate = 15 },
+ .offset = 0x50, .size = 8, .activate = 15, .pbuf = 1 },
[SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = {
- .offset = 0x153, .activate = 16 },
+ .offset = 0x153, .size = 8, .activate = 16, .pbuf = 1 },
[SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = {
- .offset = 0x156, .activate = 17 },
+ .offset = 0x156, .size = 8, .activate = 17, .pbuf = 1 },
[SCARLETT2_CONFIG_MASTER_VOLUME] = {
.offset = 0x32, .size = 16 },
@@ -734,10 +948,10 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = {
.offset = 0x3a, .size = 16 },
[SCARLETT2_CONFIG_POWER_EXT] = {
- .offset = 0x168 },
+ .offset = 0x168, .size = 8 },
[SCARLETT2_CONFIG_POWER_LOW] = {
- .offset = 0x16d }
+ .offset = 0x16d, .size = 8 }
}
};
@@ -768,6 +982,9 @@ static const struct scarlett2_config_set scarlett2_config_set_clarett = {
[SCARLETT2_CONFIG_STANDALONE_SWITCH] = {
.offset = 0x8d, .size = 8, .activate = 6 },
+
+ [SCARLETT2_CONFIG_SPDIF_MODE] = {
+ .offset = 0x9e, .size = 8, .activate = 4 },
}
};
@@ -895,6 +1112,23 @@ struct scarlett2_device_info {
*/
u8 air_option;
+ /* the number of analogue inputs with DSP control */
+ u8 dsp_input_count;
+
+ /* number of pre-compressor filters */
+ u8 precomp_flt_count;
+
+ /* number of parametric EQ filters */
+ u8 peq_flt_count;
+
+ /* number of PEQ filters plus unused slots */
+ u8 peq_flt_total_count;
+
+ /* the number of analogue inputs with a software switchable
+ * mute control
+ */
+ u8 mute_input_count;
+
/* the number of phantom (48V) software switchable controls */
u8 phantom_count;
@@ -907,6 +1141,9 @@ struct scarlett2_device_info {
/* the number of inputs with software-controllable gain */
u8 gain_input_count;
+ /* the number of inputs with safe mode */
+ u8 safe_input_count;
+
/* the number of direct monitor options
* (0 = none, 1 = mono only, 2 = mono/stereo)
*/
@@ -915,6 +1152,14 @@ struct scarlett2_device_info {
/* the number of DSP channels */
u8 dsp_count;
+ /* has a Bluetooth module with volume control */
+ u8 has_bluetooth;
+
+ /* S/PDIF Source/Digital I/O mode control */
+ const char * const spdif_mode_control_name;
+ const u8 *spdif_mode_values;
+ const char * const *spdif_mode_texts;
+
/* remap analogue outputs; 18i8 Gen 3 has "line 3/4" connected
* internally to the analogue 7/8 outputs
*/
@@ -942,7 +1187,9 @@ struct scarlett2_device_info {
struct scarlett2_data {
struct usb_mixer_interface *mixer;
struct mutex usb_mutex; /* prevent sending concurrent USB requests */
+ struct completion cmd_done;
struct mutex data_mutex; /* lock access to this data */
+ u8 running;
u8 hwdep_in_use;
u8 selected_flash_segment_id;
u8 flash_write_state;
@@ -960,6 +1207,7 @@ struct scarlett2_data {
u8 num_mix_out;
u8 num_line_out;
u8 num_monitor_mix_ctls;
+ u8 num_autogain_status_texts;
u32 firmware_version;
u8 flash_segment_nums[SCARLETT2_SEGMENT_ID_COUNT];
u8 flash_segment_blocks[SCARLETT2_SEGMENT_ID_COUNT];
@@ -970,6 +1218,8 @@ struct scarlett2_data {
u8 input_level_updated;
u8 input_pad_updated;
u8 input_air_updated;
+ u8 input_dsp_updated;
+ u8 input_mute_updated;
u8 input_phantom_updated;
u8 input_select_updated;
u8 input_gain_updated;
@@ -982,6 +1232,7 @@ struct scarlett2_data {
u8 mix_updated;
u8 speaker_switching_switched;
u8 power_status_updated;
+ u8 bluetooth_updated;
u8 sync;
u8 master_vol;
u8 headphone_vol;
@@ -992,6 +1243,13 @@ struct scarlett2_data {
u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX];
u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT];
u8 air_switch[SCARLETT2_AIR_SWITCH_MAX];
+ u8 dsp_switch[SCARLETT2_DSP_SWITCH_MAX];
+ s32 compressor_values[SCARLETT2_COMPRESSOR_CTLS_MAX];
+ s32 precomp_flt_values[SCARLETT2_PRECOMP_FLT_VALUES_MAX];
+ s32 peq_flt_values[SCARLETT2_PEQ_FLT_VALUES_MAX];
+ u8 precomp_flt_switch[SCARLETT2_DSP_SWITCH_MAX];
+ u8 peq_flt_switch[SCARLETT2_DSP_SWITCH_MAX];
+ u8 input_mute_switch[SCARLETT2_INPUT_MUTE_SWITCH_MAX];
u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX];
u8 phantom_persistence;
u8 input_select_switch;
@@ -999,6 +1257,7 @@ struct scarlett2_data {
u8 gain[SCARLETT2_INPUT_GAIN_MAX];
u8 autogain_switch[SCARLETT2_INPUT_GAIN_MAX];
u8 autogain_status[SCARLETT2_INPUT_GAIN_MAX];
+ s8 ag_targets[SCARLETT2_AG_TARGET_COUNT];
u8 safe_switch[SCARLETT2_INPUT_GAIN_MAX];
u8 pcm_input_switch;
u8 direct_monitor_switch;
@@ -1008,6 +1267,8 @@ struct scarlett2_data {
u8 msd_switch;
u8 standalone_switch;
u8 power_status;
+ u8 bluetooth_volume;
+ u8 spdif_mode;
u8 meter_level_map[SCARLETT2_MAX_METERS];
struct snd_kcontrol *sync_ctl;
struct snd_kcontrol *master_vol_ctl;
@@ -1019,20 +1280,29 @@ struct scarlett2_data {
struct snd_kcontrol *level_ctls[SCARLETT2_LEVEL_SWITCH_MAX];
struct snd_kcontrol *pad_ctls[SCARLETT2_PAD_SWITCH_MAX];
struct snd_kcontrol *air_ctls[SCARLETT2_AIR_SWITCH_MAX];
+ struct snd_kcontrol *dsp_ctls[SCARLETT2_DSP_SWITCH_MAX];
+ struct snd_kcontrol *input_mute_ctls[SCARLETT2_INPUT_MUTE_SWITCH_MAX];
struct snd_kcontrol *phantom_ctls[SCARLETT2_PHANTOM_SWITCH_MAX];
struct snd_kcontrol *input_select_ctl;
struct snd_kcontrol *input_link_ctls[SCARLETT2_INPUT_GAIN_MAX / 2];
struct snd_kcontrol *input_gain_ctls[SCARLETT2_INPUT_GAIN_MAX];
struct snd_kcontrol *autogain_ctls[SCARLETT2_INPUT_GAIN_MAX];
struct snd_kcontrol *autogain_status_ctls[SCARLETT2_INPUT_GAIN_MAX];
+ struct snd_kcontrol *ag_target_ctls[SCARLETT2_AG_TARGET_COUNT];
struct snd_kcontrol *safe_ctls[SCARLETT2_INPUT_GAIN_MAX];
struct snd_kcontrol *pcm_input_switch_ctl;
struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX];
struct snd_kcontrol *mix_ctls[SCARLETT2_MIX_MAX];
+ struct snd_kcontrol *compressor_ctls[SCARLETT2_COMPRESSOR_CTLS_MAX];
+ struct snd_kcontrol *precomp_flt_ctls[SCARLETT2_PRECOMP_FLT_CTLS_MAX];
+ struct snd_kcontrol *peq_flt_ctls[SCARLETT2_PEQ_FLT_CTLS_MAX];
+ struct snd_kcontrol *precomp_flt_switch_ctls[SCARLETT2_DSP_SWITCH_MAX];
+ struct snd_kcontrol *peq_flt_switch_ctls[SCARLETT2_DSP_SWITCH_MAX];
struct snd_kcontrol *direct_monitor_ctl;
struct snd_kcontrol *speaker_switching_ctl;
struct snd_kcontrol *talkback_ctl;
struct snd_kcontrol *power_status_ctl;
+ struct snd_kcontrol *bluetooth_volume_ctl;
u8 mux[SCARLETT2_MUX_MAX];
u8 mix[SCARLETT2_MIX_MAX];
u8 monitor_mix[SCARLETT2_MONITOR_MIX_MAX];
@@ -1326,6 +1596,14 @@ static const struct scarlett2_device_info s8i6_gen3_info = {
}
};
+static const u8 scarlett2_spdif_s18i8_gen3_values[] = { 0, 2, 0xff };
+
+static const char * const scarlett2_spdif_s18i8_gen3_texts[] = {
+ "RCA",
+ "Optical",
+ NULL
+};
+
static const struct scarlett2_device_info s18i8_gen3_info = {
.config_set = &scarlett2_config_set_gen3c,
.has_speaker_switching = 1,
@@ -1335,6 +1613,10 @@ static const struct scarlett2_device_info s18i8_gen3_info = {
.phantom_count = 2,
.inputs_per_phantom = 2,
+ .spdif_mode_control_name = "S/PDIF Mode Capture Enum",
+ .spdif_mode_values = scarlett2_spdif_s18i8_gen3_values,
+ .spdif_mode_texts = scarlett2_spdif_s18i8_gen3_texts,
+
.line_out_remap_enable = 1,
.line_out_remap = { 0, 1, 6, 7, 2, 3, 4, 5 },
.line_out_unmap = { 0, 1, 4, 5, 6, 7, 2, 3 },
@@ -1405,6 +1687,15 @@ static const struct scarlett2_device_info s18i8_gen3_info = {
}
};
+static const u8 scarlett2_spdif_s18i20_gen3_values[] = { 0, 6, 1, 0xff };
+
+static const char * const scarlett2_spdif_s18i20_gen3_texts[] = {
+ "S/PDIF RCA",
+ "S/PDIF Optical",
+ "Dual ADAT",
+ NULL
+};
+
static const struct scarlett2_device_info s18i20_gen3_info = {
.config_set = &scarlett2_config_set_gen3c,
.has_speaker_switching = 1,
@@ -1415,6 +1706,10 @@ static const struct scarlett2_device_info s18i20_gen3_info = {
.phantom_count = 2,
.inputs_per_phantom = 4,
+ .spdif_mode_control_name = "Digital I/O Mode Capture Enum",
+ .spdif_mode_values = scarlett2_spdif_s18i20_gen3_values,
+ .spdif_mode_texts = scarlett2_spdif_s18i20_gen3_texts,
+
.line_out_descrs = {
"Monitor 1 L",
"Monitor 1 R",
@@ -1475,6 +1770,91 @@ static const struct scarlett2_device_info s18i20_gen3_info = {
}
};
+static const struct scarlett2_device_info vocaster_one_info = {
+ .config_set = &scarlett2_config_set_vocaster,
+ .min_firmware_version = 1769,
+
+ .phantom_count = 1,
+ .inputs_per_phantom = 1,
+ .dsp_count = 1,
+ .dsp_input_count = 1,
+ .precomp_flt_count = 2,
+ .peq_flt_count = 3,
+ .peq_flt_total_count = 4,
+ .mute_input_count = 1,
+ .gain_input_count = 1,
+
+ .port_count = {
+ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 },
+ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 2, 4 },
+ [SCARLETT2_PORT_TYPE_MIX] = { 9, 9 },
+ [SCARLETT2_PORT_TYPE_PCM] = { 4, 10 },
+ },
+
+ .mux_assignment = { {
+ { SCARLETT2_PORT_TYPE_MIX, 8, 1 },
+ { SCARLETT2_PORT_TYPE_PCM, 5, 5 },
+ { SCARLETT2_PORT_TYPE_MIX, 6, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 5 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 6 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 },
+ { 0, 0, 0 },
+ } },
+
+ .meter_map = {
+ { 12, 1 },
+ { 18, 5 },
+ { 10, 2 },
+ { 13, 5 },
+ { 4, 6 },
+ { 0, 4 },
+ { 0, 0 }
+ }
+};
+
+static const struct scarlett2_device_info vocaster_two_info = {
+ .config_set = &scarlett2_config_set_vocaster,
+ .min_firmware_version = 1769,
+
+ .phantom_count = 2,
+ .inputs_per_phantom = 1,
+ .dsp_count = 2,
+ .dsp_input_count = 2,
+ .precomp_flt_count = 2,
+ .peq_flt_count = 3,
+ .peq_flt_total_count = 4,
+ .mute_input_count = 2,
+ .gain_input_count = 2,
+ .has_bluetooth = 1,
+
+ .port_count = {
+ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 },
+ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 6, 6 },
+ [SCARLETT2_PORT_TYPE_MIX] = { 12, 14 },
+ [SCARLETT2_PORT_TYPE_PCM] = { 4, 14 },
+ },
+
+ .mux_assignment = { {
+ { SCARLETT2_PORT_TYPE_MIX, 12, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 6, 8 },
+ { SCARLETT2_PORT_TYPE_MIX, 10, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 6 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 10 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 },
+ { 0, 0, 0 },
+ } },
+
+ .meter_map = {
+ { 18, 2 },
+ { 26, 8 },
+ { 16, 2 },
+ { 20, 6 },
+ { 6, 10 },
+ { 0, 6 },
+ { 0, 0 }
+ }
+};
+
static const struct scarlett2_device_info solo_gen4_info = {
.config_set = &scarlett2_config_set_gen4_solo,
.min_firmware_version = 2115,
@@ -1539,6 +1919,7 @@ static const struct scarlett2_device_info s2i2_gen4_info = {
.phantom_count = 1,
.inputs_per_phantom = 2,
.gain_input_count = 2,
+ .safe_input_count = 2,
.direct_monitor = 2,
.dsp_count = 2,
@@ -1592,6 +1973,7 @@ static const struct scarlett2_device_info s4i4_gen4_info = {
.phantom_count = 2,
.inputs_per_phantom = 1,
.gain_input_count = 2,
+ .safe_input_count = 2,
.dsp_count = 2,
.port_count = {
@@ -1676,11 +2058,24 @@ static const struct scarlett2_device_info clarett_2pre_info = {
}
};
+static const u8 scarlett2_spdif_clarett_values[] = { 0, 1, 2, 0xff };
+
+static const char * const scarlett2_spdif_clarett_texts[] = {
+ "None",
+ "Optical",
+ "RCA",
+ NULL
+};
+
static const struct scarlett2_device_info clarett_4pre_info = {
.config_set = &scarlett2_config_set_clarett,
.level_input_count = 2,
.air_input_count = 4,
+ .spdif_mode_control_name = "S/PDIF Source Capture Enum",
+ .spdif_mode_values = scarlett2_spdif_clarett_values,
+ .spdif_mode_texts = scarlett2_spdif_clarett_texts,
+
.line_out_descrs = {
"Monitor L",
"Monitor R",
@@ -1733,6 +2128,10 @@ static const struct scarlett2_device_info clarett_8pre_info = {
.level_input_count = 2,
.air_input_count = 8,
+ .spdif_mode_control_name = "S/PDIF Source Capture Enum",
+ .spdif_mode_values = scarlett2_spdif_clarett_values,
+ .spdif_mode_texts = scarlett2_spdif_clarett_texts,
+
.line_out_descrs = {
"Monitor L",
"Monitor R",
@@ -1806,6 +2205,10 @@ static const struct scarlett2_device_entry scarlett2_devices[] = {
{ USB_ID(0x1235, 0x8214), &s18i8_gen3_info, "Scarlett Gen 3" },
{ USB_ID(0x1235, 0x8215), &s18i20_gen3_info, "Scarlett Gen 3" },
+ /* Supported Vocaster devices */
+ { USB_ID(0x1235, 0x8216), &vocaster_one_info, "Vocaster" },
+ { USB_ID(0x1235, 0x8217), &vocaster_two_info, "Vocaster" },
+
/* Supported Gen 4 devices */
{ USB_ID(0x1235, 0x8218), &solo_gen4_info, "Scarlett Gen 4" },
{ USB_ID(0x1235, 0x8219), &s2i2_gen4_info, "Scarlett Gen 4" },
@@ -1856,6 +2259,7 @@ static int scarlett2_get_port_start_num(
#define SCARLETT2_USB_ERASE_SEGMENT 0x00004002
#define SCARLETT2_USB_GET_ERASE 0x00004003
#define SCARLETT2_USB_WRITE_SEGMENT 0x00004004
+#define SCARLETT2_USB_READ_SEGMENT 0x00004005
#define SCARLETT2_USB_GET_SYNC 0x00006004
#define SCARLETT2_USB_GET_DATA 0x00800000
#define SCARLETT2_USB_SET_DATA 0x00800001
@@ -1866,7 +2270,7 @@ static int scarlett2_get_port_start_num(
#define SCARLETT2_USB_METER_LEVELS_GET_MAGIC 1
#define SCARLETT2_FLASH_BLOCK_SIZE 4096
-#define SCARLETT2_FLASH_WRITE_MAX 1024
+#define SCARLETT2_FLASH_RW_MAX 1024
#define SCARLETT2_SEGMENT_NUM_MIN 1
#define SCARLETT2_SEGMENT_NUM_MAX 4
@@ -1960,6 +2364,17 @@ static int scarlett2_usb(
goto unlock;
}
+ if (!wait_for_completion_timeout(&private->cmd_done,
+ msecs_to_jiffies(1000))) {
+ usb_audio_err(
+ mixer->chip,
+ "%s USB request timed out, cmd %x\n",
+ private->series_name, cmd);
+
+ err = -ETIMEDOUT;
+ goto unlock;
+ }
+
/* send a second message to get the response */
err = scarlett2_usb_rx(dev, private->bInterfaceNumber,
@@ -2065,7 +2480,7 @@ static int scarlett2_usb_get_config(
if (!config_item->offset)
return -EFAULT;
- /* Gen 4 style parameters are always 1 byte */
+ /* Writes to the parameter buffer are always 1 byte */
size = config_item->size ? config_item->size : 8;
/* For byte-sized parameters, retrieve directly into buf */
@@ -2079,6 +2494,11 @@ static int scarlett2_usb_get_config(
for (i = 0; i < count; i++, buf_16++)
*buf_16 = le16_to_cpu(*(__le16 *)buf_16);
+ } else if (size == 4) {
+ u32 *buf_32 = buf;
+
+ for (i = 0; i < count; i++, buf_32++)
+ *buf_32 = le32_to_cpu(*(__le32 *)buf_32);
}
return 0;
}
@@ -2118,6 +2538,54 @@ static int scarlett2_usb_set_data(
&req, sizeof(u32) * 2 + size, NULL, 0);
}
+/* Send a SCARLETT2_USB_SET_DATA command with multiple values.
+ * offset: location in the device's data space
+ * size: size in bytes of each value (1, 2, 4)
+ * count: number of values
+ */
+static int scarlett2_usb_set_data_buf(
+ struct usb_mixer_interface *mixer,
+ int offset, int size, int count, void *buf)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int bytes = size * count;
+ struct {
+ __le32 offset;
+ __le32 size;
+ u8 data[];
+ } __packed *req;
+ int err;
+ int buf_size = struct_size(req, data, bytes);
+
+ req = kmalloc(buf_size, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ req->offset = cpu_to_le32(offset);
+ req->size = cpu_to_le32(bytes);
+ if (size == 1) {
+ memcpy(req->data, buf, count);
+ } else if (size == 2) {
+ u16 *buf_16 = buf;
+ int i;
+
+ for (i = 0; i < count; i++)
+ ((__le16 *)req->data)[i] = cpu_to_le16(buf_16[i]);
+ } else {
+ u32 *buf_32 = buf;
+ int i;
+
+ for (i = 0; i < count; i++)
+ ((__le32 *)req->data)[i] = cpu_to_le32(buf_32[i]);
+ }
+
+ err = scarlett2_usb(private->mixer, SCARLETT2_USB_SET_DATA,
+ req, buf_size, NULL, 0);
+
+ kfree(req);
+ return err;
+}
+
/* Send a SCARLETT2_USB_DATA_CMD command.
* Configuration changes require activation with this after they have
* been uploaded by a previous SCARLETT2_USB_SET_DATA.
@@ -2152,34 +2620,30 @@ static int scarlett2_usb_set_config(
if (!config_item->offset)
return -EFAULT;
- /* Gen 4 style writes are selected with size = 0;
- * these are only byte-sized values written through a shared
- * location, different to the read address
- */
- if (!config_item->size) {
- if (!config_set->gen4_write_addr)
+ /* Write via the parameter buffer? */
+ if (config_item->pbuf) {
+ if (!config_set->param_buf_addr)
return -EFAULT;
- /* Place index in gen4_write_addr + 1 */
+ /* Place index in param_buf_addr + 1 */
err = scarlett2_usb_set_data(
- mixer, config_set->gen4_write_addr + 1, 1, index);
+ mixer, config_set->param_buf_addr + 1, 1, index);
if (err < 0)
return err;
- /* Place value in gen4_write_addr */
+ /* Place value in param_buf_addr */
err = scarlett2_usb_set_data(
- mixer, config_set->gen4_write_addr, 1, value);
+ mixer, config_set->param_buf_addr, 1, value);
if (err < 0)
return err;
- /* Request the interface do the write */
+ /* Activate the write through the parameter buffer */
return scarlett2_usb_activate_config(
mixer, config_item->activate);
}
- /* Not-Gen 4 style needs NVRAM save, supports
- * bit-modification, and writing is done to the same place
- * that the value can be read from
+ /* Direct writes (not via the parameter buffer) need NVRAM
+ * save and support bit-modification
*/
/* Cancel any pending NVRAM save */
@@ -2213,7 +2677,7 @@ static int scarlett2_usb_set_config(
value = tmp;
}
- /* Send the configuration parameter data */
+ /* Write the new value */
err = scarlett2_usb_set_data(mixer, offset, size, value);
if (err < 0)
return err;
@@ -2223,8 +2687,10 @@ static int scarlett2_usb_set_config(
if (err < 0)
return err;
- /* Gen 2 style writes to Gen 4 devices don't need saving */
- if (config_set->gen4_write_addr)
+ /* Interfaces with parameter buffer writes don't need a
+ * separate save step
+ */
+ if (config_set->param_buf_addr)
return 0;
/* Schedule the change to be written to NVRAM */
@@ -2234,6 +2700,47 @@ static int scarlett2_usb_set_config(
return 0;
}
+/* Send USB messages to set a SCARLETT2_CONFIG_* parameter with
+ * multiple values
+ */
+static int scarlett2_usb_set_config_buf(
+ struct usb_mixer_interface *mixer,
+ int config_item_num, int index, int count, void *buf)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_config_set *config_set = private->config_set;
+ const struct scarlett2_config *config_item =
+ &config_set->items[config_item_num];
+ int offset, size;
+ int err;
+
+ /* Check that the configuration item is present in the
+ * configuration set used by this device
+ */
+ if (!config_item->offset)
+ return -EFAULT;
+
+ /* Convert config_item->size in bits to size in bytes and
+ * calculate offset
+ */
+ if (config_item->size >= 8) {
+ size = config_item->size / 8;
+ offset = config_item->offset + index * size;
+
+ /* Bit updates not supported */
+ } else {
+ return -EFAULT;
+ }
+
+ /* Write the new values */
+ err = scarlett2_usb_set_data_buf(mixer, offset, size, count, buf);
+ if (err < 0)
+ return err;
+
+ /* Activate the change */
+ return scarlett2_usb_activate_config(mixer, config_item->activate);
+}
+
/* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */
static void scarlett2_config_save(struct usb_mixer_interface *mixer)
{
@@ -2862,6 +3369,7 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer)
const struct scarlett2_device_info *info = private->info;
int err, i;
u8 raw_autogain_status[SCARLETT2_INPUT_GAIN_MAX];
+ s8 ag_target_values[SCARLETT2_AG_TARGET_COUNT];
private->autogain_updated = 0;
@@ -2893,12 +3401,27 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer)
if (private->autogain_switch[i])
private->autogain_status[i] = 0;
else if (raw_autogain_status[i] <
- ARRAY_SIZE(scarlett2_autogain_status_texts) - 1)
+ private->num_autogain_status_texts - 1)
private->autogain_status[i] =
raw_autogain_status[i] + 1;
else
private->autogain_status[i] =
- ARRAY_SIZE(scarlett2_autogain_status_texts) - 1;
+ private->num_autogain_status_texts - 1;
+
+
+ for (int i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
+ if (scarlett2_has_config_item(private,
+ scarlett2_ag_target_configs[i])) {
+ err = scarlett2_usb_get_config(
+ mixer, scarlett2_ag_target_configs[i],
+ 1, &ag_target_values[i]);
+ if (err < 0)
+ return err;
+ }
+
+ /* convert from negative dBFS as used by the device */
+ for (int i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
+ private->ag_targets[i] = -ag_target_values[i];
return 0;
}
@@ -2911,19 +3434,34 @@ static void scarlett2_autogain_update_access(struct usb_mixer_interface *mixer)
int val = !scarlett2_autogain_is_running(private);
int i;
- scarlett2_set_ctl_access(private->input_select_ctl, val);
- for (i = 0; i < info->gain_input_count / 2; i++)
- scarlett2_set_ctl_access(private->input_link_ctls[i], val);
- for (i = 0; i < info->gain_input_count; i++) {
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_INPUT_SELECT_SWITCH))
+ scarlett2_set_ctl_access(private->input_select_ctl, val);
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_INPUT_LINK_SWITCH))
+ for (i = 0; i < info->gain_input_count / 2; i++)
+ scarlett2_set_ctl_access(private->input_link_ctls[i],
+ val);
+ for (i = 0; i < info->gain_input_count; i++)
scarlett2_set_ctl_access(private->input_gain_ctls[i], val);
+ for (i = 0; i < info->safe_input_count; i++)
scarlett2_set_ctl_access(private->safe_ctls[i], val);
- }
for (i = 0; i < info->level_input_count; i++)
scarlett2_set_ctl_access(private->level_ctls[i], val);
for (i = 0; i < info->air_input_count; i++)
scarlett2_set_ctl_access(private->air_ctls[i], val);
+ for (i = 0; i < info->mute_input_count; i++)
+ scarlett2_set_ctl_access(private->input_mute_ctls[i], val);
for (i = 0; i < info->phantom_count; i++)
scarlett2_set_ctl_access(private->phantom_ctls[i], val);
+ for (i = 0; i < info->dsp_input_count; i++)
+ scarlett2_set_ctl_access(private->dsp_ctls[i], val);
+
+ for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
+ if (scarlett2_has_config_item(private,
+ scarlett2_ag_target_configs[i]))
+ scarlett2_set_ctl_access(
+ private->ag_target_ctls[i], val);
}
/* Notify of access mode change for all controls read-only while
@@ -2936,26 +3474,42 @@ static void scarlett2_autogain_notify_access(struct usb_mixer_interface *mixer)
const struct scarlett2_device_info *info = private->info;
int i;
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
- &private->input_select_ctl->id);
- for (i = 0; i < info->gain_input_count / 2; i++)
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_INPUT_SELECT_SWITCH))
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
- &private->input_link_ctls[i]->id);
- for (i = 0; i < info->gain_input_count; i++) {
+ &private->input_select_ctl->id);
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_INPUT_LINK_SWITCH))
+ for (i = 0; i < info->gain_input_count / 2; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->input_link_ctls[i]->id);
+ for (i = 0; i < info->gain_input_count; i++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
&private->input_gain_ctls[i]->id);
+ for (i = 0; i < info->safe_input_count; i++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
&private->safe_ctls[i]->id);
- }
for (i = 0; i < info->level_input_count; i++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
&private->level_ctls[i]->id);
for (i = 0; i < info->air_input_count; i++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
&private->air_ctls[i]->id);
+ for (i = 0; i < info->dsp_input_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->dsp_ctls[i]->id);
+ for (i = 0; i < info->mute_input_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->input_mute_ctls[i]->id);
for (i = 0; i < info->phantom_count; i++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
&private->phantom_ctls[i]->id);
+
+ for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
+ if (scarlett2_has_config_item(private,
+ scarlett2_ag_target_configs[i]))
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->ag_target_ctls[i]->id);
}
/* Call scarlett2_update_autogain() and
@@ -3124,10 +3678,13 @@ unlock:
static int scarlett2_autogain_status_ctl_info(
struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_data *private = elem->head.mixer->private_data;
+
return snd_ctl_enum_info(
uinfo, 1,
- ARRAY_SIZE(scarlett2_autogain_status_texts),
- scarlett2_autogain_status_texts);
+ private->num_autogain_status_texts,
+ private->config_set->autogain_status_texts);
}
static const struct snd_kcontrol_new scarlett2_autogain_switch_ctl = {
@@ -3146,6 +3703,122 @@ static const struct snd_kcontrol_new scarlett2_autogain_status_ctl = {
.get = scarlett2_autogain_status_ctl_get,
};
+/*** Autogain Target Controls ***/
+
+static int scarlett2_ag_target_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_autogain_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = SCARLETT2_AG_TARGET_MIN;
+ uinfo->value.integer.max = 0;
+ uinfo->value.integer.step = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_ag_target_ctl_get(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->autogain_updated) {
+ err = scarlett2_update_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+
+ ucontrol->value.integer.value[0] = private->ag_targets[elem->control];
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_ag_target_ctl_put(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
+ oval = private->ag_targets[index];
+ val = clamp(ucontrol->value.integer.value[0],
+ (long)SCARLETT2_AG_TARGET_MIN, 0L);
+
+ if (oval == val)
+ goto unlock;
+
+ private->ag_targets[index] = val;
+
+ /* Send new value to the device */
+ err = scarlett2_usb_set_config(
+ mixer, scarlett2_ag_target_configs[index], 1, -val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const DECLARE_TLV_DB_MINMAX(
+ db_scale_ag_target, SCARLETT2_AG_TARGET_MIN * 100, 0
+);
+
+static const struct snd_kcontrol_new scarlett2_ag_target_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "",
+ .info = scarlett2_ag_target_ctl_info,
+ .get = scarlett2_ag_target_ctl_get,
+ .put = scarlett2_ag_target_ctl_put,
+ .tlv = { .p = db_scale_ag_target }
+};
+
/*** Input Select Control ***/
static int scarlett2_update_input_select(struct usb_mixer_interface *mixer)
@@ -3157,7 +3830,9 @@ static int scarlett2_update_input_select(struct usb_mixer_interface *mixer)
private->input_select_updated = 0;
- if (!link_count)
+ if (!scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_INPUT_SELECT_SWITCH) ||
+ !link_count)
return 0;
err = scarlett2_usb_get_config(
@@ -3545,10 +4220,6 @@ unlock:
return err;
}
-static const DECLARE_TLV_DB_MINMAX(
- db_scale_scarlett2_gain, 0, SCARLETT2_MAX_GAIN_DB * 100
-);
-
static const struct snd_kcontrol_new scarlett2_input_gain_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -3558,7 +4229,6 @@ static const struct snd_kcontrol_new scarlett2_input_gain_ctl = {
.get = scarlett2_input_gain_ctl_get,
.put = scarlett2_input_gain_ctl_put,
.private_value = 0, /* max value */
- .tlv = { .p = db_scale_scarlett2_gain }
};
/*** Safe Controls ***/
@@ -3570,12 +4240,12 @@ static int scarlett2_update_input_safe(struct usb_mixer_interface *mixer)
private->input_safe_updated = 0;
- if (!info->gain_input_count)
+ if (!info->safe_input_count)
return 0;
return scarlett2_usb_get_config(
mixer, SCARLETT2_CONFIG_SAFE_SWITCH,
- info->gain_input_count, private->safe_switch);
+ info->safe_input_count, private->safe_switch);
}
static int scarlett2_safe_ctl_get(struct snd_kcontrol *kctl,
@@ -4567,6 +5237,670 @@ static const struct snd_kcontrol_new scarlett2_air_ctl[2] = {
}
};
+/*** DSP Switch Control ***/
+
+static int scarlett2_update_input_dsp(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+
+ private->input_dsp_updated = 0;
+
+ if (!info->dsp_input_count)
+ return 0;
+
+ return scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_DSP_SWITCH,
+ info->dsp_input_count, private->dsp_switch);
+}
+
+static int scarlett2_dsp_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->input_dsp_updated) {
+ err = scarlett2_update_input_dsp(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.integer.value[0] = private->dsp_switch[elem->control];
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_dsp_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
+ oval = private->dsp_switch[index];
+ val = ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->dsp_switch[index] = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_DSP_SWITCH,
+ index, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_dsp_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_autogain_disables_ctl_info,
+ .get = scarlett2_dsp_ctl_get,
+ .put = scarlett2_dsp_ctl_put,
+};
+
+/*** DSP Compressor Parameter Controls ***/
+
+static int scarlett2_update_compressor_values(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int err, i, j;
+
+ if (!info->dsp_input_count)
+ return 0;
+
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_COMPRESSOR_PARAMS,
+ SCARLETT2_COMPRESSOR_PARAM_COUNT * info->dsp_input_count,
+ private->compressor_values);
+
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < SCARLETT2_COMPRESSOR_PARAM_COUNT; i++) {
+ const struct compressor_param *param = &compressor_params[i];
+
+ for (j = 0; j < info->dsp_input_count; j++) {
+ int idx = i + j * SCARLETT2_COMPRESSOR_PARAM_COUNT;
+ int val = private->compressor_values[idx];
+
+ val >>= param->scale_bits;
+ val = clamp(val, param->min, param->max);
+ private->compressor_values[idx] = val;
+ }
+ }
+
+ return 0;
+}
+
+static int scarlett2_compressor_ctl_get(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.integer.value[0] =
+ private->compressor_values[elem->control];
+ return 0;
+}
+
+static int scarlett2_compressor_ctl_put(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int channel = index / SCARLETT2_COMPRESSOR_PARAM_COUNT;
+ int param_index = index % SCARLETT2_COMPRESSOR_PARAM_COUNT;
+ int oval, val, err;
+ s32 scaled_val;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
+ oval = private->compressor_values[index];
+ val = ucontrol->value.integer.value[0];
+ if (oval == val)
+ goto unlock;
+
+ private->compressor_values[index] = val;
+
+ const struct compressor_param *param = &compressor_params[param_index];
+
+ scaled_val = val << param->scale_bits;
+
+ /* Send change to the device */
+
+ /* The channel needs to be put in the parameter buffer index
+ * field (param_buf_addr + 1); the value field isn't used in
+ * this case.
+ */
+ err = scarlett2_usb_set_data(
+ mixer, private->config_set->param_buf_addr + 1, 1, channel);
+ if (err < 0)
+ goto unlock;
+
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_COMPRESSOR_PARAMS, index, scaled_val);
+ if (err < 0)
+ goto unlock;
+
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_compressor_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ int control = elem->control % SCARLETT2_COMPRESSOR_PARAM_COUNT;
+
+ uinfo->type = compressor_params[control].type;
+ uinfo->count = 1;
+ uinfo->value.integer.min = compressor_params[control].min;
+ uinfo->value.integer.max = compressor_params[control].max;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static const struct snd_kcontrol_new scarlett2_compressor_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "",
+ .info = scarlett2_compressor_ctl_info,
+ .get = scarlett2_compressor_ctl_get,
+ .put = scarlett2_compressor_ctl_put,
+};
+
+/*** DSP Pre-Compressor and PEQ Filter Controls ***/
+
+static int scarlett2_precomp_flt_switch_ctl_get(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.integer.value[0] = private->precomp_flt_switch[elem->control];
+
+ return 0;
+}
+
+static int scarlett2_peq_flt_switch_ctl_get(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.integer.value[0] =
+ private->peq_flt_switch[elem->control];
+
+ return 0;
+}
+
+static int scarlett2_precomp_flt_switch_ctl_put(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int oval, val, err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ oval = private->precomp_flt_switch[elem->control];
+ val = ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->precomp_flt_switch[elem->control] = val;
+
+ /* Send change to the device */
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_PRECOMP_FLT_SWITCH,
+ elem->control, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_peq_flt_switch_ctl_put(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int oval, val, err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ oval = private->peq_flt_switch[elem->control];
+ val = ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->peq_flt_switch[elem->control] = val;
+
+ /* Send change to the device */
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_PEQ_FLT_SWITCH,
+ elem->control, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_precomp_flt_switch_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "",
+ .info = snd_ctl_boolean_mono_info,
+ .get = scarlett2_precomp_flt_switch_ctl_get,
+ .put = scarlett2_precomp_flt_switch_ctl_put,
+};
+
+static const struct snd_kcontrol_new scarlett2_peq_flt_switch_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "",
+ .info = snd_ctl_boolean_mono_info,
+ .get = scarlett2_peq_flt_switch_ctl_get,
+ .put = scarlett2_peq_flt_switch_ctl_put,
+};
+
+static int scarlett2_update_filter_values(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int err, i, j, k, src_idx, dst_idx;
+ s32 peq_flt_values[SCARLETT2_DSP_SWITCH_MAX *
+ SCARLETT2_PEQ_FLT_SLOTS_MAX *
+ SCARLETT2_BIQUAD_COEFFS];
+
+ if (!info->dsp_input_count)
+ return 0;
+
+ /* Get filter switch values */
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_PRECOMP_FLT_SWITCH,
+ info->dsp_input_count, private->precomp_flt_switch);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_PEQ_FLT_SWITCH,
+ info->dsp_input_count * info->peq_flt_count,
+ private->peq_flt_switch);
+ if (err < 0)
+ return err;
+
+ /* Get pre-compressor filter values directly */
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_PRECOMP_FLT_PARAMS,
+ info->dsp_input_count *
+ info->precomp_flt_count *
+ SCARLETT2_BIQUAD_COEFFS,
+ private->precomp_flt_values);
+
+ if (err < 0)
+ return err;
+
+ /* PEQ filter values need to be copied via buffer because of
+ * padding after peq_flt_count up to peq_flt_total_count
+ */
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_PEQ_FLT_PARAMS,
+ info->dsp_input_count *
+ info->peq_flt_total_count *
+ SCARLETT2_BIQUAD_COEFFS,
+ peq_flt_values);
+
+ for (i = 0, dst_idx = 0; i < info->dsp_input_count; i++) {
+ src_idx = i *
+ info->peq_flt_total_count *
+ SCARLETT2_BIQUAD_COEFFS;
+ for (j = 0; j < info->peq_flt_count; j++)
+ for (k = 0;
+ k < SCARLETT2_BIQUAD_COEFFS;
+ k++, src_idx++, dst_idx++)
+ private->peq_flt_values[dst_idx] =
+ peq_flt_values[src_idx];
+ }
+
+ return 0;
+}
+
+static int scarlett2_precomp_flt_ctl_get(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_data *private = elem->head.mixer->private_data;
+ int i, idx;
+
+ for (i = 0, idx = elem->control * SCARLETT2_BIQUAD_COEFFS;
+ i < SCARLETT2_BIQUAD_COEFFS;
+ i++, idx++)
+ ucontrol->value.integer.value[i] =
+ private->precomp_flt_values[idx];
+
+ return 0;
+}
+
+static int scarlett2_peq_flt_ctl_get(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_data *private = elem->head.mixer->private_data;
+ int i, idx;
+
+ for (i = 0, idx = elem->control * SCARLETT2_BIQUAD_COEFFS;
+ i < SCARLETT2_BIQUAD_COEFFS;
+ i++, idx++)
+ ucontrol->value.integer.value[i] =
+ private->peq_flt_values[idx];
+
+ return 0;
+}
+
+static int scarlett2_precomp_flt_ctl_put(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+
+ int index = elem->control * SCARLETT2_BIQUAD_COEFFS;
+ int i, oval, val, err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
+ /* Check if any of the values have changed; if not, return */
+ for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++) {
+ oval = private->precomp_flt_values[index + i];
+ val = ucontrol->value.integer.value[i];
+ if (oval != val)
+ break;
+ }
+
+ if (i == SCARLETT2_BIQUAD_COEFFS)
+ goto unlock;
+
+ /* Update the values */
+ for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++)
+ private->precomp_flt_values[index + i] =
+ ucontrol->value.integer.value[i];
+
+ /* Send change to the device */
+ err = scarlett2_usb_set_data(
+ mixer, private->config_set->param_buf_addr, 1, index);
+ if (err < 0)
+ goto unlock;
+
+ err = scarlett2_usb_set_config_buf(
+ mixer, SCARLETT2_CONFIG_PRECOMP_FLT_PARAMS,
+ index, SCARLETT2_BIQUAD_COEFFS,
+ &private->precomp_flt_values[index]);
+
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_peq_flt_ctl_put(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+
+ int src_index = elem->control * SCARLETT2_BIQUAD_COEFFS;
+ int dst_index = (
+ elem->control /
+ info->peq_flt_count *
+ info->peq_flt_total_count +
+ elem->control % info->peq_flt_count
+ ) * SCARLETT2_BIQUAD_COEFFS;
+ int i, oval, val, err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
+ /* Check if any of the values have changed; if not, return */
+ for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++) {
+ oval = private->peq_flt_values[src_index + i];
+ val = ucontrol->value.integer.value[i];
+ if (oval != val)
+ break;
+ }
+
+ if (i == SCARLETT2_BIQUAD_COEFFS)
+ goto unlock;
+
+ /* Update the values */
+ for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++)
+ private->peq_flt_values[src_index + i] =
+ ucontrol->value.integer.value[i];
+
+ /* Send change to the device */
+ err = scarlett2_usb_set_data(
+ mixer, private->config_set->param_buf_addr, 1, dst_index);
+ if (err < 0)
+ goto unlock;
+
+ err = scarlett2_usb_set_config_buf(
+ mixer, SCARLETT2_CONFIG_PEQ_FLT_PARAMS,
+ dst_index, SCARLETT2_BIQUAD_COEFFS,
+ &private->peq_flt_values[src_index]);
+
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_flt_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = SCARLETT2_BIQUAD_COEFFS;
+ uinfo->value.integer.min = INT_MIN;
+ uinfo->value.integer.max = INT_MAX;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static const struct snd_kcontrol_new scarlett2_precomp_flt_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "",
+ .info = scarlett2_flt_ctl_info,
+ .get = scarlett2_precomp_flt_ctl_get,
+ .put = scarlett2_precomp_flt_ctl_put,
+};
+
+static const struct snd_kcontrol_new scarlett2_peq_flt_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "",
+ .info = scarlett2_flt_ctl_info,
+ .get = scarlett2_peq_flt_ctl_get,
+ .put = scarlett2_peq_flt_ctl_put,
+};
+
+/*** Input Mute Switch Controls ***/
+
+static int scarlett2_update_input_mute(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+
+ private->input_mute_updated = 0;
+
+ if (!info->mute_input_count)
+ return 0;
+
+ return scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_INPUT_MUTE_SWITCH,
+ info->mute_input_count, private->input_mute_switch);
+}
+
+static int scarlett2_input_mute_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->input_mute_updated) {
+ err = scarlett2_update_input_mute(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.integer.value[0] =
+ private->input_mute_switch[elem->control];
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_input_mute_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
+ oval = private->input_mute_switch[index];
+ val = ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->input_mute_switch[index] = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_INPUT_MUTE_SWITCH,
+ index, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_input_mute_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_autogain_disables_ctl_info,
+ .get = scarlett2_input_mute_ctl_get,
+ .put = scarlett2_input_mute_ctl_put,
+};
+
/*** Phantom Switch Controls ***/
static int scarlett2_update_input_phantom(struct usb_mixer_interface *mixer)
@@ -5406,6 +6740,69 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
/*** Create the analogue input controls ***/
+static int scarlett2_add_dsp_ctls(struct usb_mixer_interface *mixer, int i)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int j, err;
+ char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ const char *compr_fmt = "Line In %d Compressor %s";
+ const char *flt_switch_fmt = "Line In %d %s Filter Enable";
+ const char *flt_fmt = "Line In %d %s Coefficients %d";
+
+ /* Add compressor controls */
+ for (j = 0; j < SCARLETT2_COMPRESSOR_PARAM_COUNT; j++) {
+ const struct compressor_param *param = &compressor_params[j];
+ int idx = i * SCARLETT2_COMPRESSOR_PARAM_COUNT + j;
+
+ scnprintf(s, sizeof(s), compr_fmt, i + 1, param->name);
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_compressor_ctl,
+ i * SCARLETT2_COMPRESSOR_PARAM_COUNT + j,
+ 1, s, &private->compressor_ctls[idx]);
+ if (err < 0)
+ return err;
+ }
+
+ /* Add filter enable controls */
+ scnprintf(s, sizeof(s), flt_switch_fmt, i + 1, "Pre-Comp");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_precomp_flt_switch_ctl,
+ i, 1, s, &private->precomp_flt_switch_ctls[i]);
+ if (err < 0)
+ return err;
+
+ scnprintf(s, sizeof(s), flt_switch_fmt, i + 1, "PEQ");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_peq_flt_switch_ctl,
+ i, 1, s, &private->peq_flt_switch_ctls[i]);
+ if (err < 0)
+ return err;
+
+ /* Add filter coefficient controls */
+ for (j = 0; j < info->precomp_flt_count; j++) {
+ scnprintf(s, sizeof(s), flt_fmt, i + 1, "Pre-Comp", j + 1);
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_precomp_flt_ctl,
+ i * info->precomp_flt_count + j,
+ 1, s, &private->precomp_flt_switch_ctls[j]);
+ if (err < 0)
+ return err;
+ }
+
+ for (j = 0; j < info->peq_flt_count; j++) {
+ scnprintf(s, sizeof(s), flt_fmt, i + 1, "PEQ", j + 1);
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_peq_flt_ctl,
+ i * info->peq_flt_count + j,
+ 1, s, &private->peq_flt_switch_ctls[j]);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
@@ -5445,6 +6842,29 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
return err;
}
+ /* Add input DSP controls */
+ for (i = 0; i < info->dsp_input_count; i++) {
+ scnprintf(s, sizeof(s), fmt, i + 1, "DSP", "Switch");
+ err = scarlett2_add_new_ctl(mixer, &scarlett2_dsp_ctl,
+ i, 1, s, &private->dsp_ctls[i]);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_add_dsp_ctls(mixer, i);
+ if (err < 0)
+ return err;
+ }
+
+ /* Add input mute controls */
+ for (i = 0; i < info->mute_input_count; i++) {
+ scnprintf(s, sizeof(s), fmt, i + 1, "Mute", "Switch");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_input_mute_ctl,
+ i, 1, s, &private->input_mute_ctls[i]);
+ if (err < 0)
+ return err;
+ }
+
/* Add input phantom controls */
if (info->inputs_per_phantom == 1) {
for (i = 0; i < info->phantom_count; i++) {
@@ -5481,58 +6901,81 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
return err;
}
- /* Add software-controllable input gain controls */
- if (info->gain_input_count) {
+ /* Add input select/link controls */
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_INPUT_SELECT_SWITCH)) {
err = scarlett2_add_new_ctl(
mixer, &scarlett2_input_select_ctl, 0, 1,
"Input Select Capture Enum",
&private->input_select_ctl);
if (err < 0)
return err;
+ }
- for (i = 0; i < info->gain_input_count; i++) {
- if (i % 2) {
- scnprintf(s, sizeof(s),
- "Line In %d-%d Link Capture Switch",
- i, i + 1);
- err = scarlett2_add_new_ctl(
- mixer, &scarlett2_input_link_ctl,
- i / 2, 1, s,
- &private->input_link_ctls[i / 2]);
- if (err < 0)
- return err;
- }
-
- scnprintf(s, sizeof(s), fmt, i + 1,
- "Gain", "Volume");
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_INPUT_LINK_SWITCH)) {
+ for (i = 0; i < info->gain_input_count / 2; i++) {
+ scnprintf(s, sizeof(s),
+ "Line In %d-%d Link Capture Switch",
+ (i * 2) + 1, (i * 2) + 2);
err = scarlett2_add_new_ctl(
- mixer, &scarlett2_input_gain_ctl,
- i, 1, s, &private->input_gain_ctls[i]);
+ mixer, &scarlett2_input_link_ctl,
+ i, 1, s, &private->input_link_ctls[i]);
if (err < 0)
return err;
+ }
+ }
- scnprintf(s, sizeof(s), fmt, i + 1,
- "Autogain", "Switch");
- err = scarlett2_add_new_ctl(
- mixer, &scarlett2_autogain_switch_ctl,
- i, 1, s, &private->autogain_ctls[i]);
- if (err < 0)
- return err;
+ /* Add software-controllable input gain controls */
+ for (i = 0; i < info->gain_input_count; i++) {
+ scnprintf(s, sizeof(s), fmt, i + 1,
+ "Gain", "Volume");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_input_gain_ctl,
+ i, 1, s, &private->input_gain_ctls[i]);
+ if (err < 0)
+ return err;
+ private->input_gain_ctls[i]->tlv.p =
+ private->config_set->input_gain_tlv;
- scnprintf(s, sizeof(s), fmt, i + 1,
- "Autogain Status", "Enum");
- err = scarlett2_add_new_ctl(
- mixer, &scarlett2_autogain_status_ctl,
- i, 1, s, &private->autogain_status_ctls[i]);
+ scnprintf(s, sizeof(s), fmt, i + 1,
+ "Autogain", "Switch");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_autogain_switch_ctl,
+ i, 1, s, &private->autogain_ctls[i]);
+ if (err < 0)
+ return err;
+
+ scnprintf(s, sizeof(s), fmt, i + 1,
+ "Autogain Status", "Enum");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_autogain_status_ctl,
+ i, 1, s, &private->autogain_status_ctls[i]);
+ }
+
+ /* Add autogain target controls */
+ for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
+ if (scarlett2_has_config_item(private,
+ scarlett2_ag_target_configs[i])) {
- scnprintf(s, sizeof(s), fmt, i + 1,
- "Safe", "Switch");
+ scnprintf(s, sizeof(s), "Autogain %s Target",
+ scarlett2_ag_target_names[i]);
err = scarlett2_add_new_ctl(
- mixer, &scarlett2_safe_ctl,
- i, 1, s, &private->safe_ctls[i]);
+ mixer, &scarlett2_ag_target_ctl,
+ i, 1, s, &private->ag_target_ctls[i]);
if (err < 0)
return err;
}
+
+ /* Add safe-mode input switch controls */
+ for (i = 0; i < info->safe_input_count; i++) {
+ scnprintf(s, sizeof(s), fmt, i + 1,
+ "Safe", "Switch");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_safe_ctl,
+ i, 1, s, &private->safe_ctls[i]);
+ if (err < 0)
+ return err;
}
/* Add PCM Input Switch control */
@@ -6383,413 +7826,238 @@ static int scarlett2_add_power_status_ctl(struct usb_mixer_interface *mixer)
&private->power_status_ctl);
}
-/*** Cleanup/Suspend Callbacks ***/
+/*** Bluetooth Volume ***/
-static void scarlett2_private_free(struct usb_mixer_interface *mixer)
-{
- struct scarlett2_data *private = mixer->private_data;
-
- cancel_delayed_work_sync(&private->work);
- kfree(private);
- mixer->private_data = NULL;
-}
-
-static void scarlett2_private_suspend(struct usb_mixer_interface *mixer)
+static int scarlett2_update_bluetooth_volume(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
+ int err;
- if (cancel_delayed_work_sync(&private->work))
- scarlett2_config_save(private->mixer);
-}
-
-/*** Initialisation ***/
-
-static void scarlett2_count_io(struct scarlett2_data *private)
-{
- const struct scarlett2_device_info *info = private->info;
- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
- int port_type, srcs = 0, dsts = 0;
-
- /* Count the number of mux sources and destinations */
- for (port_type = 0;
- port_type < SCARLETT2_PORT_TYPE_COUNT;
- port_type++) {
- srcs += port_count[port_type][SCARLETT2_PORT_IN];
- dsts += port_count[port_type][SCARLETT2_PORT_OUT];
- }
-
- private->num_mux_srcs = srcs;
- private->num_mux_dsts = dsts;
-
- /* Mixer inputs are mux outputs and vice versa.
- * Scarlett Gen 4 DSP I/O uses SCARLETT2_PORT_TYPE_MIX but
- * doesn't have mixer controls.
- */
- private->num_mix_in =
- port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT] -
- info->dsp_count;
+ private->bluetooth_updated = 0;
- private->num_mix_out =
- port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN] -
- info->dsp_count;
+ if (!private->info->has_bluetooth)
+ return 0;
- /* Number of analogue line outputs */
- private->num_line_out =
- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
+ err = scarlett2_usb_get_config(mixer,
+ SCARLETT2_CONFIG_BLUETOOTH_VOLUME,
+ 1, &private->bluetooth_volume);
+ if (err < 0)
+ return err;
- /* Number of monitor mix controls */
- private->num_monitor_mix_ctls =
- info->direct_monitor * 2 * private->num_mix_in;
+ return 0;
}
-/* Look through the interface descriptors for the Focusrite Control
- * interface (bInterfaceClass = 255 Vendor Specific Class) and set
- * bInterfaceNumber, bEndpointAddress, wMaxPacketSize, and bInterval
- * in private
- */
-static int scarlett2_find_fc_interface(struct usb_device *dev,
- struct scarlett2_data *private)
+static int scarlett2_bluetooth_volume_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct usb_host_config *config = dev->actconfig;
- int i;
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
- for (i = 0; i < config->desc.bNumInterfaces; i++) {
- struct usb_interface *intf = config->interface[i];
- struct usb_interface_descriptor *desc =
- &intf->altsetting[0].desc;
- struct usb_endpoint_descriptor *epd;
+ mutex_lock(&private->data_mutex);
- if (desc->bInterfaceClass != 255)
- continue;
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
- epd = get_endpoint(intf->altsetting, 0);
- private->bInterfaceNumber = desc->bInterfaceNumber;
- private->bEndpointAddress = epd->bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
- private->wMaxPacketSize = le16_to_cpu(epd->wMaxPacketSize);
- private->bInterval = epd->bInterval;
- return 0;
+ if (private->bluetooth_updated) {
+ err = scarlett2_update_bluetooth_volume(mixer);
+ if (err < 0)
+ goto unlock;
}
+ ucontrol->value.integer.value[0] = private->bluetooth_volume;
- return -EINVAL;
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
-/* Initialise private data */
-static int scarlett2_init_private(struct usb_mixer_interface *mixer,
- const struct scarlett2_device_entry *entry)
+static int scarlett2_bluetooth_volume_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct scarlett2_data *private =
- kzalloc(sizeof(struct scarlett2_data), GFP_KERNEL);
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int oval, val, err = 0;
- if (!private)
- return -ENOMEM;
+ mutex_lock(&private->data_mutex);
- mutex_init(&private->usb_mutex);
- mutex_init(&private->data_mutex);
- INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
- mixer->private_data = private;
- mixer->private_free = scarlett2_private_free;
- mixer->private_suspend = scarlett2_private_suspend;
+ oval = private->bluetooth_volume;
+ val = clamp(ucontrol->value.integer.value[0],
+ 0L, (long)SCARLETT2_MAX_BLUETOOTH_VOLUME);
- private->info = entry->info;
- private->config_set = entry->info->config_set;
- private->series_name = entry->series_name;
- scarlett2_count_io(private);
- private->scarlett2_seq = 0;
- private->mixer = mixer;
+ if (oval == val)
+ goto unlock;
- return scarlett2_find_fc_interface(mixer->chip->dev, private);
+ private->bluetooth_volume = val;
+ err = scarlett2_usb_set_config(mixer,
+ SCARLETT2_CONFIG_BLUETOOTH_VOLUME,
+ 0, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
-/* Cargo cult proprietary initialisation sequence */
-static int scarlett2_usb_init(struct usb_mixer_interface *mixer)
+static int scarlett2_bluetooth_volume_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
{
- struct usb_device *dev = mixer->chip->dev;
- struct scarlett2_data *private = mixer->private_data;
- u8 step0_buf[24];
- u8 step2_buf[84];
- int err;
-
- if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0)))
- return -EINVAL;
-
- /* step 0 */
- err = scarlett2_usb_rx(dev, private->bInterfaceNumber,
- SCARLETT2_USB_CMD_INIT,
- step0_buf, sizeof(step0_buf));
- if (err < 0)
- return err;
-
- /* step 1 */
- private->scarlett2_seq = 1;
- err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_1, NULL, 0, NULL, 0);
- if (err < 0)
- return err;
-
- /* step 2 */
- private->scarlett2_seq = 1;
- err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_2,
- NULL, 0,
- step2_buf, sizeof(step2_buf));
- if (err < 0)
- return err;
-
- /* extract 4-byte firmware version from step2_buf[8] */
- private->firmware_version = le32_to_cpu(*(__le32 *)(step2_buf + 8));
- usb_audio_info(mixer->chip,
- "Firmware version %d\n",
- private->firmware_version);
-
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SCARLETT2_MAX_BLUETOOTH_VOLUME;
+ uinfo->value.integer.step = 1;
return 0;
}
-/* Get the flash segment numbers for the App_Settings and App_Upgrade
- * segments and put them in the private data
- */
-static int scarlett2_get_flash_segment_nums(struct usb_mixer_interface *mixer)
+static const struct snd_kcontrol_new scarlett2_bluetooth_volume_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_bluetooth_volume_ctl_info,
+ .get = scarlett2_bluetooth_volume_ctl_get,
+ .put = scarlett2_bluetooth_volume_ctl_put,
+};
+
+static int scarlett2_add_bluetooth_volume_ctl(
+ struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
- int err, count, i;
-
- struct {
- __le32 size;
- __le32 count;
- u8 unknown[8];
- } __packed flash_info;
-
- struct {
- __le32 size;
- __le32 flags;
- char name[16];
- } __packed segment_info;
- err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_FLASH,
- NULL, 0,
- &flash_info, sizeof(flash_info));
- if (err < 0)
- return err;
-
- count = le32_to_cpu(flash_info.count);
-
- /* sanity check count */
- if (count < SCARLETT2_SEGMENT_NUM_MIN ||
- count > SCARLETT2_SEGMENT_NUM_MAX + 1) {
- usb_audio_err(mixer->chip,
- "invalid flash segment count: %d\n", count);
- return -EINVAL;
- }
-
- for (i = 0; i < count; i++) {
- __le32 segment_num_req = cpu_to_le32(i);
- int flash_segment_id;
-
- err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_SEGMENT,
- &segment_num_req, sizeof(segment_num_req),
- &segment_info, sizeof(segment_info));
- if (err < 0) {
- usb_audio_err(mixer->chip,
- "failed to get flash segment info %d: %d\n",
- i, err);
- return err;
- }
-
- if (!strncmp(segment_info.name,
- SCARLETT2_SEGMENT_SETTINGS_NAME, 16))
- flash_segment_id = SCARLETT2_SEGMENT_ID_SETTINGS;
- else if (!strncmp(segment_info.name,
- SCARLETT2_SEGMENT_FIRMWARE_NAME, 16))
- flash_segment_id = SCARLETT2_SEGMENT_ID_FIRMWARE;
- else
- continue;
-
- private->flash_segment_nums[flash_segment_id] = i;
- private->flash_segment_blocks[flash_segment_id] =
- le32_to_cpu(segment_info.size) /
- SCARLETT2_FLASH_BLOCK_SIZE;
- }
-
- /* segment 0 is App_Gold and we never want to touch that, so
- * use 0 as the "not-found" value
- */
- if (!private->flash_segment_nums[SCARLETT2_SEGMENT_ID_SETTINGS]) {
- usb_audio_err(mixer->chip,
- "failed to find flash segment %s\n",
- SCARLETT2_SEGMENT_SETTINGS_NAME);
- return -EINVAL;
- }
- if (!private->flash_segment_nums[SCARLETT2_SEGMENT_ID_FIRMWARE]) {
- usb_audio_err(mixer->chip,
- "failed to find flash segment %s\n",
- SCARLETT2_SEGMENT_FIRMWARE_NAME);
- return -EINVAL;
- }
+ if (!private->info->has_bluetooth)
+ return 0;
- return 0;
+ /* Add Bluetooth volume control */
+ return scarlett2_add_new_ctl(mixer, &scarlett2_bluetooth_volume_ctl,
+ 0, 1, "Bluetooth Capture Volume",
+ &private->bluetooth_volume_ctl);
}
-/* Read configuration from the interface on start */
-static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
+/*** S/PDIF Mode Controls ***/
+
+static int scarlett2_update_spdif_mode(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
- const struct scarlett2_device_info *info = private->info;
int err, i;
+ u8 mode;
+ const u8 *mode_values = private->info->spdif_mode_values;
- if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_MSD_SWITCH)) {
- err = scarlett2_usb_get_config(
- mixer, SCARLETT2_CONFIG_MSD_SWITCH,
- 1, &private->msd_switch);
- if (err < 0)
- return err;
- }
-
- if (private->firmware_version < info->min_firmware_version) {
- usb_audio_err(mixer->chip,
- "Focusrite %s firmware version %d is too old; "
- "need %d",
- private->series_name,
- private->firmware_version,
- info->min_firmware_version);
- return 0;
- }
-
- /* no other controls are created if MSD mode is on */
- if (private->msd_switch)
+ if (!private->info->spdif_mode_control_name)
return 0;
- err = scarlett2_update_input_level(mixer);
- if (err < 0)
- return err;
-
- err = scarlett2_update_input_pad(mixer);
+ err = scarlett2_usb_get_config(mixer, SCARLETT2_CONFIG_SPDIF_MODE,
+ 1, &mode);
if (err < 0)
return err;
- err = scarlett2_update_input_air(mixer);
- if (err < 0)
- return err;
+ private->spdif_mode = 0;
- err = scarlett2_update_input_phantom(mixer);
- if (err < 0)
- return err;
-
- err = scarlett2_update_direct_monitor(mixer);
- if (err < 0)
- return err;
-
- /* the rest of the configuration is for devices with a mixer */
- if (!scarlett2_has_mixer(private))
- return 0;
-
- err = scarlett2_update_monitor_mix(mixer);
- if (err < 0)
- return err;
-
- err = scarlett2_update_monitor_other(mixer);
- if (err < 0)
- return err;
+ for (i = 0; *mode_values != 0xff; i++, mode_values++)
+ if (*mode_values == mode) {
+ private->spdif_mode = i;
+ break;
+ }
- if (scarlett2_has_config_item(private,
- SCARLETT2_CONFIG_STANDALONE_SWITCH)) {
- err = scarlett2_usb_get_config(
- mixer, SCARLETT2_CONFIG_STANDALONE_SWITCH,
- 1, &private->standalone_switch);
- if (err < 0)
- return err;
- }
+ return 0;
+}
- if (scarlett2_has_config_item(private,
- SCARLETT2_CONFIG_POWER_EXT)) {
- err = scarlett2_update_power_status(mixer);
- if (err < 0)
- return err;
- }
+static int scarlett2_spdif_mode_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_data *private = elem->head.mixer->private_data;
+ const char * const *mode_texts = private->info->spdif_mode_texts;
+ int count = 0;
- err = scarlett2_update_sync(mixer);
- if (err < 0)
- return err;
+ while (*mode_texts++)
+ count++;
- if (scarlett2_has_config_item(private,
- SCARLETT2_CONFIG_LINE_OUT_VOLUME)) {
- s16 sw_vol[SCARLETT2_ANALOGUE_MAX];
+ return snd_ctl_enum_info(uinfo, 1, count,
+ private->info->spdif_mode_texts);
+}
- /* read SW line out volume */
- err = scarlett2_usb_get_config(
- mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME,
- private->num_line_out, &sw_vol);
- if (err < 0)
- return err;
+static int scarlett2_spdif_mode_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_data *private = elem->head.mixer->private_data;
- for (i = 0; i < private->num_line_out; i++)
- private->vol[i] = clamp(
- sw_vol[i] + SCARLETT2_VOLUME_BIAS,
- 0, SCARLETT2_VOLUME_BIAS);
+ ucontrol->value.enumerated.item[0] = private->spdif_mode;
+ return 0;
+}
- /* read SW mute */
- err = scarlett2_usb_get_config(
- mixer, SCARLETT2_CONFIG_MUTE_SWITCH,
- private->num_line_out, &private->mute_switch);
- if (err < 0)
- return err;
+static int scarlett2_spdif_mode_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int oval, val, err = 0;
+ int i;
- for (i = 0; i < private->num_line_out; i++)
- private->mute_switch[i] =
- !!private->mute_switch[i];
+ mutex_lock(&private->data_mutex);
- /* read SW/HW switches */
- if (scarlett2_has_config_item(private,
- SCARLETT2_CONFIG_SW_HW_SWITCH)) {
- err = scarlett2_usb_get_config(
- mixer, SCARLETT2_CONFIG_SW_HW_SWITCH,
- private->num_line_out,
- &private->vol_sw_hw_switch);
- if (err < 0)
- return err;
+ oval = private->spdif_mode;
+ val = ucontrol->value.enumerated.item[0];
- for (i = 0; i < private->num_line_out; i++)
- private->vol_sw_hw_switch[i] =
- !!private->vol_sw_hw_switch[i];
- }
+ if (val < 0) {
+ err = -EINVAL;
+ goto unlock;
}
- err = scarlett2_update_volumes(mixer);
- if (err < 0)
- return err;
+ for (i = 0; i <= val; i++)
+ if (private->info->spdif_mode_values[i] == 0xff) {
+ err = -EINVAL;
+ goto unlock;
+ }
- err = scarlett2_update_dim_mute(mixer);
- if (err < 0)
- return err;
+ if (oval == val)
+ goto unlock;
- err = scarlett2_update_input_select(mixer);
- if (err < 0)
- return err;
+ private->spdif_mode = val;
- err = scarlett2_update_input_gain(mixer);
- if (err < 0)
- return err;
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_SPDIF_MODE, 0,
+ private->info->spdif_mode_values[val]);
+ if (!err)
+ err = 1;
- err = scarlett2_update_autogain(mixer);
- if (err < 0)
- return err;
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
- err = scarlett2_update_input_safe(mixer);
- if (err < 0)
- return err;
+static const struct snd_kcontrol_new scarlett2_spdif_mode_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_spdif_mode_ctl_info,
+ .get = scarlett2_spdif_mode_ctl_get,
+ .put = scarlett2_spdif_mode_ctl_put,
+};
- if (scarlett2_has_config_item(private,
- SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) {
- err = scarlett2_update_pcm_input_switch(mixer);
- if (err < 0)
- return err;
- }
+static int scarlett2_add_spdif_mode_ctl(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
- err = scarlett2_update_mix(mixer);
- if (err < 0)
- return err;
+ if (!private->info->spdif_mode_control_name)
+ return 0;
- return scarlett2_usb_get_mux(mixer);
+ return scarlett2_add_new_ctl(mixer, &scarlett2_spdif_mode_ctl,
+ 0, 1,
+ private->info->spdif_mode_control_name,
+ NULL);
}
+/*** Notification Handlers ***/
+
/* Notify on sync change */
static void scarlett2_notify_sync(struct usb_mixer_interface *mixer)
{
@@ -6902,6 +8170,36 @@ static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer)
&private->air_ctls[i]->id);
}
+/* Notify on input DSP switch change */
+static void scarlett2_notify_input_dsp(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int i;
+
+ private->input_dsp_updated = 1;
+
+ for (i = 0; i < info->dsp_input_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->dsp_ctls[i]->id);
+}
+
+/* Notify on input mute switch change */
+static void scarlett2_notify_input_mute(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int i;
+
+ private->input_mute_updated = 1;
+
+ for (i = 0; i < info->mute_input_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->input_mute_ctls[i]->id);
+}
+
/* Notify on input phantom switch change */
static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer)
{
@@ -6936,7 +8234,8 @@ static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer)
const struct scarlett2_device_info *info = private->info;
int i;
- if (!info->gain_input_count)
+ if (!scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_INPUT_SELECT_SWITCH))
return;
private->input_select_updated = 1;
@@ -6988,6 +8287,12 @@ static void scarlett2_notify_autogain(struct usb_mixer_interface *mixer)
&private->autogain_status_ctls[i]->id);
}
+ for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
+ if (scarlett2_has_config_item(private,
+ scarlett2_ag_target_configs[i]))
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->ag_target_ctls[i]->id);
+
scarlett2_autogain_notify_access(mixer);
}
@@ -6999,12 +8304,12 @@ static void scarlett2_notify_input_safe(struct usb_mixer_interface *mixer)
const struct scarlett2_device_info *info = private->info;
int i;
- if (!info->gain_input_count)
+ if (!info->safe_input_count)
return;
private->input_safe_updated = 1;
- for (i = 0; i < info->gain_input_count; i++)
+ for (i = 0; i < info->safe_input_count; i++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
&private->safe_ctls[i]->id);
}
@@ -7107,6 +8412,33 @@ static void scarlett2_notify_pcm_input_switch(struct usb_mixer_interface *mixer)
scarlett2_notify_mux(mixer);
}
+/* Notify on Bluetooth change */
+static void scarlett2_notify_bluetooth(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+
+ if (!private->info->has_bluetooth)
+ return;
+
+ private->bluetooth_updated = 1;
+
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->bluetooth_volume_ctl->id);
+}
+
+/* Handle acknowledgement that a command was received; let
+ * scarlett2_usb() know that it can proceed
+ */
+static void scarlett2_notify_ack(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+
+ /* if running == 0, ignore ACKs */
+ if (private->running)
+ complete(&private->cmd_done);
+}
+
/* Interrupt callback */
static void scarlett2_notify(struct urb *urb)
{
@@ -7123,6 +8455,12 @@ static void scarlett2_notify(struct urb *urb)
data = le32_to_cpu(*(__le32 *)urb->transfer_buffer);
+ /* Ignore notifications except ACK during initialisation.
+ * ACK is 0x00000001 on every device.
+ */
+ if (private->running < 2)
+ data &= 1;
+
while (data && notifications->mask) {
if (data & notifications->mask) {
data &= ~notifications->mask;
@@ -7143,9 +8481,141 @@ requeue:
ustatus != -ESHUTDOWN) {
urb->dev = mixer->chip->dev;
usb_submit_urb(urb, GFP_ATOMIC);
+ } else {
+ complete(&private->cmd_done);
}
}
+/*** Cleanup/Suspend Callbacks ***/
+
+static void scarlett2_private_free(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+
+ cancel_delayed_work_sync(&private->work);
+ kfree(private);
+ mixer->private_data = NULL;
+}
+
+static void scarlett2_private_suspend(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+
+ if (cancel_delayed_work_sync(&private->work))
+ scarlett2_config_save(private->mixer);
+}
+
+/*** Initialisation ***/
+
+static void scarlett2_count_io(struct scarlett2_data *private)
+{
+ const struct scarlett2_device_info *info = private->info;
+ const struct scarlett2_config_set *config_set = info->config_set;
+ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
+ int port_type, srcs = 0, dsts = 0, i;
+
+ /* Count the number of mux sources and destinations */
+ for (port_type = 0;
+ port_type < SCARLETT2_PORT_TYPE_COUNT;
+ port_type++) {
+ srcs += port_count[port_type][SCARLETT2_PORT_IN];
+ dsts += port_count[port_type][SCARLETT2_PORT_OUT];
+ }
+
+ private->num_mux_srcs = srcs;
+ private->num_mux_dsts = dsts;
+
+ /* Mixer inputs are mux outputs and vice versa.
+ * Scarlett Gen 4 DSP I/O uses SCARLETT2_PORT_TYPE_MIX but
+ * doesn't have mixer controls.
+ */
+ private->num_mix_in =
+ port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT] -
+ info->dsp_count;
+
+ private->num_mix_out =
+ port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN] -
+ info->dsp_count;
+
+ /* Number of analogue line outputs */
+ private->num_line_out =
+ port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
+
+ /* Number of monitor mix controls */
+ private->num_monitor_mix_ctls =
+ info->direct_monitor * 2 * private->num_mix_in;
+
+ /* Number of autogain status texts */
+ if (config_set->autogain_status_texts) {
+ const char * const *texts = config_set->autogain_status_texts;
+
+ for (i = 0; texts[i]; i++)
+ ;
+ private->num_autogain_status_texts = i;
+ }
+}
+
+/* Look through the interface descriptors for the Focusrite Control
+ * interface (bInterfaceClass = 255 Vendor Specific Class) and set
+ * bInterfaceNumber, bEndpointAddress, wMaxPacketSize, and bInterval
+ * in private
+ */
+static int scarlett2_find_fc_interface(struct usb_device *dev,
+ struct scarlett2_data *private)
+{
+ struct usb_host_config *config = dev->actconfig;
+ int i;
+
+ for (i = 0; i < config->desc.bNumInterfaces; i++) {
+ struct usb_interface *intf = config->interface[i];
+ struct usb_interface_descriptor *desc =
+ &intf->altsetting[0].desc;
+ struct usb_endpoint_descriptor *epd;
+
+ if (desc->bInterfaceClass != 255)
+ continue;
+
+ epd = get_endpoint(intf->altsetting, 0);
+ private->bInterfaceNumber = desc->bInterfaceNumber;
+ private->bEndpointAddress = epd->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ private->wMaxPacketSize = le16_to_cpu(epd->wMaxPacketSize);
+ private->bInterval = epd->bInterval;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/* Initialise private data */
+static int scarlett2_init_private(struct usb_mixer_interface *mixer,
+ const struct scarlett2_device_entry *entry)
+{
+ struct scarlett2_data *private =
+ kzalloc(sizeof(struct scarlett2_data), GFP_KERNEL);
+
+ if (!private)
+ return -ENOMEM;
+
+ mutex_init(&private->usb_mutex);
+ mutex_init(&private->data_mutex);
+ INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work);
+
+ mixer->private_data = private;
+ mixer->private_free = scarlett2_private_free;
+ mixer->private_suspend = scarlett2_private_suspend;
+
+ private->info = entry->info;
+ private->config_set = entry->info->config_set;
+ private->series_name = entry->series_name;
+ scarlett2_count_io(private);
+ private->scarlett2_seq = 0;
+ private->mixer = mixer;
+
+ return scarlett2_find_fc_interface(mixer->chip->dev, private);
+}
+
+/* Submit a URB to receive notifications from the device */
static int scarlett2_init_notify(struct usb_mixer_interface *mixer)
{
struct usb_device *dev = mixer->chip->dev;
@@ -7174,9 +8644,341 @@ static int scarlett2_init_notify(struct usb_mixer_interface *mixer)
transfer_buffer, private->wMaxPacketSize,
scarlett2_notify, mixer, private->bInterval);
+ init_completion(&private->cmd_done);
+
return usb_submit_urb(mixer->urb, GFP_KERNEL);
}
+/* Cargo cult proprietary initialisation sequence */
+static int scarlett2_usb_init(struct usb_mixer_interface *mixer)
+{
+ struct usb_device *dev = mixer->chip->dev;
+ struct scarlett2_data *private = mixer->private_data;
+ u8 step0_buf[24];
+ u8 step2_buf[84];
+ int err;
+
+ if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0)))
+ return -EINVAL;
+
+ /* step 0 */
+ err = scarlett2_usb_rx(dev, private->bInterfaceNumber,
+ SCARLETT2_USB_CMD_INIT,
+ step0_buf, sizeof(step0_buf));
+ if (err < 0)
+ return err;
+
+ /* Set up the interrupt polling for notifications.
+ * When running is:
+ * 0: all notifications are ignored
+ * 1: only ACKs are handled
+ * 2: all notifications are handled
+ */
+ err = scarlett2_init_notify(mixer);
+ if (err < 0)
+ return err;
+
+ /* sleep for a moment in case of an outstanding ACK */
+ msleep(20);
+
+ /* start handling ACKs, but no other notifications until the
+ * ALSA controls have been created
+ */
+ private->running = 1;
+
+ /* step 1 */
+ private->scarlett2_seq = 1;
+ err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_1, NULL, 0, NULL, 0);
+ if (err < 0)
+ return err;
+
+ /* step 2 */
+ private->scarlett2_seq = 1;
+ err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_2,
+ NULL, 0,
+ step2_buf, sizeof(step2_buf));
+ if (err < 0)
+ return err;
+
+ /* extract 4-byte firmware version from step2_buf[8] */
+ private->firmware_version = le32_to_cpu(*(__le32 *)(step2_buf + 8));
+ usb_audio_info(mixer->chip,
+ "Firmware version %d\n",
+ private->firmware_version);
+
+ return 0;
+}
+
+/* Get the flash segment numbers for the App_Settings and App_Upgrade
+ * segments and put them in the private data
+ */
+static int scarlett2_get_flash_segment_nums(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int err, count, i;
+
+ struct {
+ __le32 size;
+ __le32 count;
+ u8 unknown[8];
+ } __packed flash_info;
+
+ struct {
+ __le32 size;
+ __le32 flags;
+ char name[16];
+ } __packed segment_info;
+
+ err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_FLASH,
+ NULL, 0,
+ &flash_info, sizeof(flash_info));
+ if (err < 0)
+ return err;
+
+ count = le32_to_cpu(flash_info.count);
+
+ /* sanity check count */
+ if (count < SCARLETT2_SEGMENT_NUM_MIN ||
+ count > SCARLETT2_SEGMENT_NUM_MAX + 1) {
+ usb_audio_err(mixer->chip,
+ "invalid flash segment count: %d\n", count);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < count; i++) {
+ __le32 segment_num_req = cpu_to_le32(i);
+ int flash_segment_id;
+
+ err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_SEGMENT,
+ &segment_num_req, sizeof(segment_num_req),
+ &segment_info, sizeof(segment_info));
+ if (err < 0) {
+ usb_audio_err(mixer->chip,
+ "failed to get flash segment info %d: %d\n",
+ i, err);
+ return err;
+ }
+
+ if (!strncmp(segment_info.name,
+ SCARLETT2_SEGMENT_SETTINGS_NAME, 16))
+ flash_segment_id = SCARLETT2_SEGMENT_ID_SETTINGS;
+ else if (!strncmp(segment_info.name,
+ SCARLETT2_SEGMENT_FIRMWARE_NAME, 16))
+ flash_segment_id = SCARLETT2_SEGMENT_ID_FIRMWARE;
+ else
+ continue;
+
+ private->flash_segment_nums[flash_segment_id] = i;
+ private->flash_segment_blocks[flash_segment_id] =
+ le32_to_cpu(segment_info.size) /
+ SCARLETT2_FLASH_BLOCK_SIZE;
+ }
+
+ /* segment 0 is App_Gold and we never want to touch that, so
+ * use 0 as the "not-found" value
+ */
+ if (!private->flash_segment_nums[SCARLETT2_SEGMENT_ID_SETTINGS]) {
+ usb_audio_err(mixer->chip,
+ "failed to find flash segment %s\n",
+ SCARLETT2_SEGMENT_SETTINGS_NAME);
+ return -EINVAL;
+ }
+ if (!private->flash_segment_nums[SCARLETT2_SEGMENT_ID_FIRMWARE]) {
+ usb_audio_err(mixer->chip,
+ "failed to find flash segment %s\n",
+ SCARLETT2_SEGMENT_FIRMWARE_NAME);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Read configuration from the interface on start */
+static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int err, i;
+
+ if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_MSD_SWITCH)) {
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_MSD_SWITCH,
+ 1, &private->msd_switch);
+ if (err < 0)
+ return err;
+ }
+
+ if (private->firmware_version < info->min_firmware_version) {
+ usb_audio_err(mixer->chip,
+ "Focusrite %s firmware version %d is too old; "
+ "need %d",
+ private->series_name,
+ private->firmware_version,
+ info->min_firmware_version);
+ return 0;
+ }
+
+ /* no other controls are created if MSD mode is on */
+ if (private->msd_switch)
+ return 0;
+
+ err = scarlett2_update_input_level(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_pad(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_air(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_dsp(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_compressor_values(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_filter_values(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_mute(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_phantom(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_direct_monitor(mixer);
+ if (err < 0)
+ return err;
+
+ /* the rest of the configuration is for devices with a mixer */
+ if (!scarlett2_has_mixer(private))
+ return 0;
+
+ err = scarlett2_update_monitor_mix(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_monitor_other(mixer);
+ if (err < 0)
+ return err;
+
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_STANDALONE_SWITCH)) {
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_STANDALONE_SWITCH,
+ 1, &private->standalone_switch);
+ if (err < 0)
+ return err;
+ }
+
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_POWER_EXT)) {
+ err = scarlett2_update_power_status(mixer);
+ if (err < 0)
+ return err;
+ }
+
+ err = scarlett2_update_sync(mixer);
+ if (err < 0)
+ return err;
+
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_LINE_OUT_VOLUME)) {
+ s16 sw_vol[SCARLETT2_ANALOGUE_MAX];
+
+ /* read SW line out volume */
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME,
+ private->num_line_out, &sw_vol);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < private->num_line_out; i++)
+ private->vol[i] = clamp(
+ sw_vol[i] + SCARLETT2_VOLUME_BIAS,
+ 0, SCARLETT2_VOLUME_BIAS);
+
+ /* read SW mute */
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_MUTE_SWITCH,
+ private->num_line_out, &private->mute_switch);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < private->num_line_out; i++)
+ private->mute_switch[i] =
+ !!private->mute_switch[i];
+
+ /* read SW/HW switches */
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_SW_HW_SWITCH)) {
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_SW_HW_SWITCH,
+ private->num_line_out,
+ &private->vol_sw_hw_switch);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < private->num_line_out; i++)
+ private->vol_sw_hw_switch[i] =
+ !!private->vol_sw_hw_switch[i];
+ }
+ }
+
+ err = scarlett2_update_volumes(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_dim_mute(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_select(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_gain(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_autogain(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_safe(mixer);
+ if (err < 0)
+ return err;
+
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) {
+ err = scarlett2_update_pcm_input_switch(mixer);
+ if (err < 0)
+ return err;
+ }
+
+ err = scarlett2_update_bluetooth_volume(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_spdif_mode(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_mix(mixer);
+ if (err < 0)
+ return err;
+
+ return scarlett2_usb_get_mux(mixer);
+}
+
static const struct scarlett2_device_entry *get_scarlett2_device_entry(
struct usb_mixer_interface *mixer)
{
@@ -7297,6 +9099,16 @@ static int snd_scarlett2_controls_create(
if (err < 0)
return err;
+ /* Create the Bluetooth volume control */
+ err = scarlett2_add_bluetooth_volume_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ /* Create the S/PDIF mode control */
+ err = scarlett2_add_spdif_mode_ctl(mixer);
+ if (err < 0)
+ return err;
+
/* Set the access mode of controls disabled during
* autogain/phantom power switching.
*/
@@ -7305,10 +9117,8 @@ static int snd_scarlett2_controls_create(
scarlett2_phantom_update_access(mixer);
}
- /* Set up the interrupt polling */
- err = scarlett2_init_notify(mixer);
- if (err < 0)
- return err;
+ /* Start handling all notifications */
+ private->running = 2;
return 0;
}
@@ -7397,7 +9207,7 @@ static int scarlett2_reboot(struct usb_mixer_interface *mixer)
return scarlett2_usb(mixer, SCARLETT2_USB_REBOOT, NULL, 0, NULL, 0);
}
-/* Select a flash segment for erasing (and possibly writing to) */
+/* Select a flash segment for reading/erasing/writing */
static int scarlett2_ioctl_select_flash_segment(
struct usb_mixer_interface *mixer,
unsigned long arg)
@@ -7578,6 +9388,84 @@ static int scarlett2_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
}
}
+static long scarlett2_hwdep_read(struct snd_hwdep *hw,
+ char __user *buf,
+ long count, loff_t *offset)
+{
+ struct usb_mixer_interface *mixer = hw->private_data;
+ struct scarlett2_data *private = mixer->private_data;
+ int segment_id, segment_num, err;
+ int flash_size;
+
+ /* SCARLETT2_USB_READ_SEGMENT request data */
+ struct {
+ __le32 segment_num;
+ __le32 offset;
+ __le32 len;
+ } __packed req;
+
+ u8 *resp;
+
+ /* Flash segment must first be selected */
+ if (private->flash_write_state != SCARLETT2_FLASH_WRITE_STATE_SELECTED)
+ return -EINVAL;
+
+ /* Get the selected flash segment number */
+ segment_id = private->selected_flash_segment_id;
+ if (segment_id < 0 || segment_id >= SCARLETT2_SEGMENT_ID_COUNT)
+ return -EINVAL;
+
+ segment_num = private->flash_segment_nums[segment_id];
+ if (segment_num < 0 ||
+ segment_num > SCARLETT2_SEGMENT_NUM_MAX)
+ return -EFAULT;
+
+ /* Validate the offset and count */
+ if (count < 0 || *offset < 0)
+ return -EINVAL;
+
+ /* Reached EOF? */
+ flash_size = private->flash_segment_blocks[segment_id] *
+ SCARLETT2_FLASH_BLOCK_SIZE;
+ if (!count || *offset >= flash_size)
+ return 0;
+
+ /* Limit the numbers of bytes read to SCARLETT2_FLASH_RW_MAX */
+ if (count > SCARLETT2_FLASH_RW_MAX)
+ count = SCARLETT2_FLASH_RW_MAX;
+
+ /* Limit read to EOF */
+ if (*offset + count >= flash_size)
+ count = flash_size - *offset;
+
+ /* Create and send the request */
+ req.segment_num = cpu_to_le32(segment_num);
+ req.offset = cpu_to_le32(*offset);
+ req.len = cpu_to_le32(count);
+
+ resp = kzalloc(count, GFP_KERNEL);
+ if (!resp)
+ return -ENOMEM;
+
+ err = scarlett2_usb(mixer, SCARLETT2_USB_READ_SEGMENT,
+ &req, sizeof(req), resp, count);
+ if (err < 0)
+ goto error;
+
+ /* Copy the response to userspace */
+ if (copy_to_user(buf, resp, count)) {
+ err = -EFAULT;
+ goto error;
+ }
+
+ *offset += count;
+ err = count;
+
+error:
+ kfree(resp);
+ return err;
+}
+
static long scarlett2_hwdep_write(struct snd_hwdep *hw,
const char __user *buf,
long count, loff_t *offset)
@@ -7596,7 +9484,7 @@ static long scarlett2_hwdep_write(struct snd_hwdep *hw,
} __packed *req;
/* Calculate the maximum permitted in data[] */
- const size_t max_data_size = SCARLETT2_FLASH_WRITE_MAX -
+ const size_t max_data_size = SCARLETT2_FLASH_RW_MAX -
offsetof(typeof(*req), data);
/* If erasing, wait for it to complete */
@@ -7633,7 +9521,7 @@ static long scarlett2_hwdep_write(struct snd_hwdep *hw,
if (!count)
return 0;
- /* Limit the *req size to SCARLETT2_FLASH_WRITE_MAX */
+ /* Limit the *req size to SCARLETT2_FLASH_RW_MAX */
if (count > max_data_size)
count = max_data_size;
@@ -7694,6 +9582,7 @@ static int scarlett2_hwdep_init(struct usb_mixer_interface *mixer)
hw->exclusive = 1;
hw->ops.open = scarlett2_hwdep_open;
hw->ops.ioctl = scarlett2_hwdep_ioctl;
+ hw->ops.read = scarlett2_hwdep_read;
hw->ops.write = scarlett2_hwdep_write;
hw->ops.release = scarlett2_hwdep_release;
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 5d72dc8441cb..73abc38a5400 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -3013,21 +3013,28 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.type = QUIRK_AUDIO_FIXED_ENDPOINT,
.data = &(const struct audioformat) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .fmt_bits = 24,
.channels = 4,
.iface = 2,
.altsetting = 1,
.altset_idx = 1,
.attributes = 0x00,
- .endpoint = 0x01,
+ .endpoint = USB_RECIP_INTERFACE | USB_DIR_OUT,
.ep_attr = USB_ENDPOINT_XFER_ISOC |
USB_ENDPOINT_SYNC_ASYNC,
- .rates = SNDRV_PCM_RATE_48000,
- .rate_min = 48000,
- .rate_max = 48000,
- .nr_rates = 1,
+ .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
.rate_table = (unsigned int[]) {
- 48000
- }
+ 44100, 48000, 88200, 96000
+ },
+ .sync_ep = USB_RECIP_INTERFACE | USB_DIR_IN,
+ .sync_iface = 3,
+ .sync_altsetting = 1,
+ .sync_ep_idx = 1,
+ .implicit_fb = 1,
}
},
{
@@ -3035,22 +3042,25 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.type = QUIRK_AUDIO_FIXED_ENDPOINT,
.data = &(const struct audioformat) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .fmt_bits = 24,
.channels = 4,
.iface = 3,
.altsetting = 1,
.altset_idx = 1,
- .endpoint = 0x81,
.attributes = 0x00,
+ .endpoint = USB_RECIP_INTERFACE | USB_DIR_IN,
.ep_attr = USB_ENDPOINT_XFER_ISOC |
USB_ENDPOINT_SYNC_ASYNC,
.maxpacksize = 0x009c,
- .rates = SNDRV_PCM_RATE_48000,
- .rate_min = 48000,
- .rate_max = 48000,
- .nr_rates = 1,
+ .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
.rate_table = (unsigned int[]) {
- 48000
- }
+ 44100, 48000, 88200, 96000
+ },
+ .implicit_fb = 0,
}
},
{
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 09712e61c606..58156fbca02c 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -984,21 +984,13 @@ static int snd_usb_axefx3_boot_quirk(struct usb_device *dev)
return 0;
}
-static void mbox3_setup_48_24_magic(struct usb_device *dev)
+static void mbox3_setup_defaults(struct usb_device *dev)
{
/* The Mbox 3 is "little endian" */
/* max volume is: 0x0000. */
/* min volume is: 0x0080 (shown in little endian form) */
-
- /* Load 48000Hz rate into buffer */
- u8 com_buff[4] = {0x80, 0xbb, 0x00, 0x00};
-
- /* Set 48000Hz sample rate */
- snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
- 0x01, 0x21, 0x0100, 0x0001, &com_buff, 4); //Is this really needed?
- snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
- 0x01, 0x21, 0x0100, 0x8101, &com_buff, 4);
+ u8 com_buff[2];
/* Deactivate Tuner */
/* on = 0x01*/
@@ -1008,6 +1000,8 @@ static void mbox3_setup_48_24_magic(struct usb_device *dev)
0x01, 0x21, 0x0003, 0x2001, &com_buff, 1);
/* Set clock source to Internal (as opposed to S/PDIF) */
+ /* Internal = 0x01*/
+ /* S/PDIF = 0x02*/
com_buff[0] = 0x01;
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
1, 0x21, 0x0100, 0x8001, &com_buff, 1);
@@ -1113,9 +1107,11 @@ static void mbox3_setup_48_24_magic(struct usb_device *dev)
1, 0x21, 0x0107, 0x4201, &com_buff, 2);
/* Toggle allowing host control */
+ /* Not needed
com_buff[0] = 0x02;
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
3, 0x21, 0x0000, 0x2001, &com_buff, 1);
+ */
/* Do not dim fx returns */
com_buff[0] = 0x00;
@@ -1259,26 +1255,27 @@ static int snd_usb_mbox3_boot_quirk(struct usb_device *dev)
descriptor_size = le16_to_cpu(get_cfg_desc(config)->wTotalLength);
if (descriptor_size != MBOX3_DESCRIPTOR_SIZE) {
- dev_err(&dev->dev, "Invalid descriptor size=%d.\n", descriptor_size);
+ dev_err(&dev->dev, "MBOX3: Invalid descriptor size=%d.\n", descriptor_size);
return -ENODEV;
}
- dev_dbg(&dev->dev, "device initialised!\n");
+ dev_dbg(&dev->dev, "MBOX3: device initialised!\n");
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
&dev->descriptor, sizeof(dev->descriptor));
config = dev->actconfig;
if (err < 0)
- dev_dbg(&dev->dev, "error usb_get_descriptor: %d\n", err);
+ dev_dbg(&dev->dev, "MBOX3: error usb_get_descriptor: %d\n", err);
err = usb_reset_configuration(dev);
if (err < 0)
- dev_dbg(&dev->dev, "error usb_reset_configuration: %d\n", err);
- dev_dbg(&dev->dev, "mbox3_boot: new boot length = %d\n",
+ dev_dbg(&dev->dev, "MBOX3: error usb_reset_configuration: %d\n", err);
+
+ dev_dbg(&dev->dev, "MBOX3: new boot length = %d\n",
le16_to_cpu(get_cfg_desc(config)->wTotalLength));
- mbox3_setup_48_24_magic(dev);
- dev_info(&dev->dev, "Digidesign Mbox 3: 24bit 48kHz");
+ mbox3_setup_defaults(dev);
+ dev_info(&dev->dev, "MBOX3: Initialized.");
return 0; /* Successful boot */
}
@@ -1734,6 +1731,46 @@ static int pioneer_djm_set_format_quirk(struct snd_usb_substream *subs,
return 0;
}
+static void mbox3_set_format_quirk(struct snd_usb_substream *subs,
+ const struct audioformat *fmt)
+{
+ __le32 buff4 = 0;
+ u8 buff1 = 0x01;
+ u32 new_rate = subs->data_endpoint->cur_rate;
+ u32 current_rate;
+
+ // Get current rate from card and check if changing it is needed
+ snd_usb_ctl_msg(subs->dev, usb_rcvctrlpipe(subs->dev, 0),
+ 0x01, 0x21 | USB_DIR_IN, 0x0100, 0x8101, &buff4, 4);
+ current_rate = le32_to_cpu(buff4);
+ dev_dbg(&subs->dev->dev,
+ "MBOX3: Current configured sample rate: %d", current_rate);
+ if (current_rate == new_rate) {
+ dev_dbg(&subs->dev->dev,
+ "MBOX3: No change needed (current rate:%d == new rate:%d)",
+ current_rate, new_rate);
+ return;
+ }
+
+ // Set new rate
+ dev_info(&subs->dev->dev,
+ "MBOX3: Changing sample rate to: %d", new_rate);
+ buff4 = cpu_to_le32(new_rate);
+ snd_usb_ctl_msg(subs->dev, usb_sndctrlpipe(subs->dev, 0),
+ 0x01, 0x21, 0x0100, 0x8101, &buff4, 4);
+
+ // Set clock source to Internal
+ snd_usb_ctl_msg(subs->dev, usb_sndctrlpipe(subs->dev, 0),
+ 0x01, 0x21, 0x0100, 0x8001, &buff1, 1);
+
+ // Check whether the change was successful
+ buff4 = 0;
+ snd_usb_ctl_msg(subs->dev, usb_rcvctrlpipe(subs->dev, 0),
+ 0x01, 0x21 | USB_DIR_IN, 0x0100, 0x8101, &buff4, 4);
+ if (new_rate != le32_to_cpu(buff4))
+ dev_warn(&subs->dev->dev, "MBOX3: Couldn't set the sample rate");
+}
+
void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
const struct audioformat *fmt)
{
@@ -1755,6 +1792,9 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
case USB_ID(0x08e4, 0x0163): /* Pioneer DJM-850 */
pioneer_djm_set_format_quirk(subs, 0x0086);
break;
+ case USB_ID(0x0dba, 0x5000):
+ mbox3_set_format_quirk(subs, fmt); /* Digidesign Mbox 3 */
+ break;
}
}
diff --git a/sound/usb/usx2y/Makefile b/sound/usb/usx2y/Makefile
index cc4c2f1efab2..fc033aba03a4 100644
--- a/sound/usb/usx2y/Makefile
+++ b/sound/usb/usx2y/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
-snd-usb-usx2y-objs := usbusx2y.o usX2Yhwdep.o usx2yhwdeppcm.o
-snd-usb-us122l-objs := us122l.o
+snd-usb-usx2y-y := usbusx2y.o usX2Yhwdep.o usx2yhwdeppcm.o
+snd-usb-us122l-y := us122l.o
obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-usx2y.o
obj-$(CONFIG_SND_USB_US122L) += snd-usb-us122l.o
diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile
index a839f8c8b5e6..3ca41b3c8b95 100644
--- a/sound/virtio/Makefile
+++ b/sound/virtio/Makefile
@@ -2,7 +2,7 @@
obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o
-virtio_snd-objs := \
+virtio_snd-y := \
virtio_card.o \
virtio_chmap.o \
virtio_ctl_msg.o \
diff --git a/sound/x86/Makefile b/sound/x86/Makefile
index 6b5ffb329d47..44d2a339615d 100644
--- a/sound/x86/Makefile
+++ b/sound/x86/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-hdmi-lpe-audio-objs += \
+snd-hdmi-lpe-audio-y += \
intel_hdmi_audio.o
obj-$(CONFIG_HDMI_LPE_AUDIO) += snd-hdmi-lpe-audio.o
diff --git a/sound/xen/Makefile b/sound/xen/Makefile
index 24031775b715..5dab1616eac0 100644
--- a/sound/xen/Makefile
+++ b/sound/xen/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0 OR MIT
-snd_xen_front-objs := xen_snd_front.o \
+snd_xen_front-y := xen_snd_front.o \
xen_snd_front_cfg.o \
xen_snd_front_evtchnl.o \
xen_snd_front_alsa.o