aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2024-05-02 11:52:16 +1000
committerStephen Rothwell <sfr@canb.auug.org.au>2024-05-02 11:52:16 +1000
commit6531c5b470dd1e735cbcb488c95190856c117795 (patch)
tree2232f6dc3742d8bbd6921f6365ec4a5a4bff552c
parent9ffe750e3138cff82e21c066cf1439222324e5af (diff)
parente7aeb238026c2226e0636e8159f9e71c0c91a84f (diff)
downloadlinux-next-history-6531c5b470dd1e735cbcb488c95190856c117795.tar.gz
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
Notice: this object is not reachable from any branch.
Notice: this object is not reachable from any branch.
-rw-r--r--include/sound/cs35l41.h5
-rw-r--r--include/sound/emu10k1.h33
-rw-r--r--include/sound/hdaudio.h1
-rw-r--r--include/sound/pcm.h5
-rw-r--r--include/sound/soundfont.h2
-rw-r--r--sound/aoa/soundbus/i2sbus/pcm.c10
-rw-r--r--sound/core/control_led.c15
-rw-r--r--sound/core/pcm_native.c2
-rw-r--r--sound/core/seq/seq_dummy.c24
-rw-r--r--sound/core/sound_kunit.c10
-rw-r--r--sound/drivers/aloop.c9
-rw-r--r--sound/firewire/amdtp-stream.c10
-rw-r--r--sound/hda/hdac_controller.c127
-rw-r--r--sound/isa/sb/emu8000_patch.c13
-rw-r--r--sound/pci/emu10k1/emu10k1_callback.c13
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c75
-rw-r--r--sound/pci/emu10k1/emu10k1_patch.c209
-rw-r--r--sound/pci/emu10k1/io.c53
-rw-r--r--sound/pci/emu10k1/memory.c55
-rw-r--r--sound/pci/hda/Kconfig2
-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.c22
-rw-r--r--sound/pci/hda/hda_controller.c7
-rw-r--r--sound/pci/hda/hda_controller.h1
-rw-r--r--sound/pci/hda/hda_intel.c5
-rw-r--r--sound/pci/hda/patch_realtek.c6
-rw-r--r--sound/soc/sof/intel/hda-bus.c5
-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/mixer_quirks.c2
-rw-r--r--sound/usb/mixer_scarlett2.c2711
-rw-r--r--sound/usb/quirks-table.h38
-rw-r--r--sound/usb/quirks.c74
38 files changed, 3087 insertions, 971 deletions
diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h
index 68e053fe73403d..bb70782d15d06e 100644
--- a/include/sound/cs35l41.h
+++ b/include/sound/cs35l41.h
@@ -554,6 +554,11 @@
#define CS35L41_LRCLK_FRC_SHIFT 1
#define CS35L41_AMP_GAIN_PCM_MASK 0x3E0
+#define CS35L41_AMP_GAIN_PCM_SHIFT 5
+#define CS35L41_AMP_GAIN_PDM_MASK 0x1F
+#define CS35L41_AMP_GAIN_PDM_SHIFT 0
+#define CS35L41_AMP_GAIN_PCM_MAX 20
+#define CS35L41_AMP_GAIN_PDM_MAX 20
#define CS35L41_AMP_GAIN_ZC_MASK 0x0400
#define CS35L41_AMP_GAIN_ZC_SHIFT 10
diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h
index 234b5baea69c8e..38db50b280eba5 100644
--- a/include/sound/emu10k1.h
+++ b/include/sound/emu10k1.h
@@ -598,17 +598,25 @@ SUB_REG(PEFE, FILTERAMOUNT, 0x000000ff) /* Filter envlope amount */
// In stereo mode, the two channels' caches are concatenated into one,
// and hold the interleaved frames.
// The cache holds 64 frames, so the upper half is not used in 8-bit mode.
-// All registers mentioned below count in frames.
-// The cache is a ring buffer; CCR_READADDRESS operates modulo 64.
-// The cache is filled from (CCCA_CURRADDR - CCR_CACHEINVALIDSIZE)
-// into (CCR_READADDRESS - CCR_CACHEINVALIDSIZE).
+// All registers mentioned below count in frames. Shortcuts:
+// CA = CCCA_CURRADDR, CRA = CCR_READADDRESS,
+// CLA = CCR_CACHELOOPADDRHI:CLP_CACHELOOPADDR,
+// CIS = CCR_CACHEINVALIDSIZE, LIS = CCR_LOOPINVALSIZE,
+// CLF = CCR_CACHELOOPFLAG, LF = CCR_LOOPFLAG
+// The cache is a ring buffer; CRA operates modulo 64.
+// The cache is filled from (CA - CIS) into (CRA - CIS).
// The engine has a fetch threshold of 32 bytes, so it tries to keep
-// CCR_CACHEINVALIDSIZE below 8 (16-bit stereo), 16 (16-bit mono,
-// 8-bit stereo), or 32 (8-bit mono). The actual transfers are pretty
-// unpredictable, especially if several voices are running.
-// Frames are consumed at CCR_READADDRESS, which is incremented afterwards,
-// along with CCCA_CURRADDR and CCR_CACHEINVALIDSIZE. This implies that the
-// actual playback position always lags CCCA_CURRADDR by exactly 64 frames.
+// CIS below 8 (16-bit stereo), 16 (16-bit mono, 8-bit stereo), or
+// 32 (8-bit mono). The actual transfers are pretty unpredictable,
+// especially if several voices are running.
+// Frames are consumed at CRA, which is incremented afterwards,
+// along with CA and CIS. This implies that the actual playback
+// position always lags CA by exactly 64 frames.
+// When CA reaches DSL_LOOPENDADDR, LF is set for one frame's time.
+// LF's rising edge causes the current values of CA and CIS to be
+// copied into CLA and LIS, resp., and CLF to be set.
+// If CLF is set, the first LIS of the CIS frames are instead
+// filled from (CLA - LIS), and CLF is subsequently reset.
#define CD0 0x20 /* Cache data registers 0 .. 0x1f */
#define PTB 0x40 /* Page table base register */
@@ -1843,6 +1851,7 @@ void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 s
u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst);
int snd_emu1010_get_raw_rate(struct snd_emu10k1 *emu, u8 src);
void snd_emu1010_update_clock(struct snd_emu10k1 *emu);
+void snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, int dock, const struct firmware *fw_entry);
unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc);
void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb);
void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb);
@@ -1885,8 +1894,8 @@ int snd_emu10k1_alloc_pages_maybe_wider(struct snd_emu10k1 *emu, size_t size,
struct snd_dma_buffer *dmab);
struct snd_util_memblk *snd_emu10k1_synth_alloc(struct snd_emu10k1 *emu, unsigned int size);
int snd_emu10k1_synth_free(struct snd_emu10k1 *emu, struct snd_util_memblk *blk);
-int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, int offset, int size);
-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 snd_emu10k1_synth_memset(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, int offset, int size, u8 value);
+int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, int offset, const char __user *data, int size, u32 xor);
int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk);
/* voice allocation */
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h
index a73d7f34f4e545..85d2c3d00a2719 100644
--- a/include/sound/hdaudio.h
+++ b/include/sound/hdaudio.h
@@ -351,6 +351,7 @@ struct hdac_bus {
bool needs_damn_long_delay:1;
bool not_use_interrupts:1; /* prohibiting the RIRB IRQ */
bool access_sdnctl_in_dword:1; /* accessing the sdnctl register by dword */
+ bool use_pio_for_commands:1; /* Use PIO instead of CORB for commands */
int poll_count;
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 210096f124eed5..61c6054618c8ea 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -120,6 +120,8 @@ struct snd_pcm_ops {
#define SNDRV_PCM_RATE_192000 (1U<<12) /* 192000Hz */
#define SNDRV_PCM_RATE_352800 (1U<<13) /* 352800Hz */
#define SNDRV_PCM_RATE_384000 (1U<<14) /* 384000Hz */
+#define SNDRV_PCM_RATE_705600 (1U<<15) /* 705600Hz */
+#define SNDRV_PCM_RATE_768000 (1U<<16) /* 768000Hz */
#define SNDRV_PCM_RATE_CONTINUOUS (1U<<30) /* continuous range */
#define SNDRV_PCM_RATE_KNOT (1U<<31) /* supports more non-continuos rates */
@@ -135,6 +137,9 @@ struct snd_pcm_ops {
#define SNDRV_PCM_RATE_8000_384000 (SNDRV_PCM_RATE_8000_192000|\
SNDRV_PCM_RATE_352800|\
SNDRV_PCM_RATE_384000)
+#define SNDRV_PCM_RATE_8000_768000 (SNDRV_PCM_RATE_8000_384000|\
+ SNDRV_PCM_RATE_705600|\
+ SNDRV_PCM_RATE_768000)
#define _SNDRV_PCM_FMTBIT(fmt) (1ULL << (__force int)SNDRV_PCM_FORMAT_##fmt)
#define SNDRV_PCM_FMTBIT_S8 _SNDRV_PCM_FMTBIT(S8)
#define SNDRV_PCM_FMTBIT_U8 _SNDRV_PCM_FMTBIT(U8)
diff --git a/include/sound/soundfont.h b/include/sound/soundfont.h
index e445688a4f4f9b..98ed98d89d6de0 100644
--- a/include/sound/soundfont.h
+++ b/include/sound/soundfont.h
@@ -89,7 +89,7 @@ struct snd_sf_list {
int snd_soundfont_load(struct snd_sf_list *sflist, const void __user *data,
long count, int client);
int snd_soundfont_load_guspatch(struct snd_sf_list *sflist, const char __user *data,
- long count, int client);
+ long count);
int snd_soundfont_close_check(struct snd_sf_list *sflist, int client);
struct snd_sf_list *snd_sf_new(struct snd_sf_callback *callback,
diff --git a/sound/aoa/soundbus/i2sbus/pcm.c b/sound/aoa/soundbus/i2sbus/pcm.c
index 07df5cc0f2d7c0..98b812ffbde68e 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/core/control_led.c b/sound/core/control_led.c
index 3d37e9fa7b9c20..ac19d14bd57456 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/pcm_native.c b/sound/core/pcm_native.c
index 0b76e76823d288..521ba56392a07f 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/seq_dummy.c b/sound/core/seq/seq_dummy.c
index 9308194b2d9adf..783fc72c2ef673 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 eb90f62228c028..165bd0f5338afc 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 */
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index 892c4e29c0a348..d6dd4b8c750ad1 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/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index c9f153f85ae6b6..d35d0a420ee088 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/hda/hdac_controller.c b/sound/hda/hdac_controller.c
index 7f3a000fab0ce0..b5c833b9f8b9ca 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/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c
index 8c1e7f2bfc34a1..ab4f988f080dc7 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/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
index 941bfbf812ed30..ef26e4d3e2a3bc 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 8ccc0178360ce8..5b8a5ba825bdb2 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,7 +683,8 @@ 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 snd_emu1010_load_dock_firmware(struct snd_emu10k1 *emu)
@@ -744,9 +699,6 @@ static void snd_emu1010_load_dock_firmware(struct snd_emu10k1 *emu)
msleep(200);
dev_info(emu->card->dev, "emu1010: Loading Audio Dock Firmware\n");
- /* Return to Audio Dock programming mode */
- 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;
@@ -864,28 +816,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
snd_emu1010_fpga_lock(emu);
- /* 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);
-
+ 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");
diff --git a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c
index 89890f24509f03..dbfa89435ac2a4 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/io.c b/sound/pci/emu10k1/io.c
index f4a1c2d4b07875..b60ab5671e00dc 100644
--- a/sound/pci/emu10k1/io.c
+++ b/sound/pci/emu10k1/io.c
@@ -422,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 20b07117574b4b..d29711777161c3 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 f806636242ee90..0da625533afc27 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/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c
index 990b5bd717a1cb..6c49e5c6cd2088 100644
--- a/sound/pci/hda/cs35l41_hda.c
+++ b/sound/pci/hda/cs35l41_hda.c
@@ -23,7 +23,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"
@@ -38,6 +37,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);
@@ -76,7 +111,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
@@ -85,7 +120,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[] = {
@@ -93,11 +128,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
@@ -118,9 +148,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;
@@ -128,23 +179,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);
@@ -185,13 +236,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)
@@ -202,12 +251,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)
@@ -218,18 +266,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)
@@ -240,18 +287,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)
@@ -278,13 +324,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);
@@ -313,12 +359,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)
@@ -329,18 +374,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)
@@ -362,95 +405,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)
{
@@ -471,27 +581,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);
@@ -504,6 +622,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");
@@ -554,6 +673,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);
@@ -571,6 +694,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);
@@ -582,8 +706,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));
@@ -1057,6 +1186,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:
@@ -1462,13 +1594,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;
@@ -1511,26 +1686,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);
}
@@ -1809,6 +1966,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);
@@ -1889,6 +2050,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 43d55292b327ab..b0bebb77846230 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 8fb688e4141485..ad9b6abdf269fe 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,16 @@ 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 },
{ "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 +466,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 +497,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 +511,13 @@ 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", "17AA386F", generic_dsd_config },
{ "CSC3551", "17AA3877", generic_dsd_config },
{ "CSC3551", "17AA3878", generic_dsd_config },
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index 206306a0eb827a..8af5ee1b0ea8dd 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -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);
@@ -1188,6 +1188,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 8556031bcd68e4..c2d0109866e62e 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_intel.c b/sound/pci/hda/hda_intel.c
index 1b550c42db0927..6a3022a87245aa 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -289,6 +289,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 |\
@@ -2503,7 +2506,7 @@ static const struct pci_device_id azx_ids[] = {
{ 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) },
/* 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 */
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 723b35842347a2..bd1b824baae14a 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -10148,6 +10148,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),
@@ -10168,6 +10170,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),
@@ -10495,6 +10499,8 @@ 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 Pro 7/7i", ALC287_FIXUP_LENOVO_LEGION_7),
SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C),
diff --git a/sound/soc/sof/intel/hda-bus.c b/sound/soc/sof/intel/hda-bus.c
index fc63085d2d743f..f78d6b998be0ff 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/synth/emux/emux.c b/sound/synth/emux/emux.c
index a82af937485263..01444fc960d086 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 81719bfb8ed738..fd8f978cde1c6c 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 d8d32671f7038e..04df46b269d375 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 7993e6a01e548f..820351f52551ec 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 b227c7e0bc2ac9..1adaa75df2f6a4 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 16f00097cb95a8..2373ed580bf8ce 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/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 065a4be0d77148..212b5e6443d88f 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 bd114be537d7ae..b251d25f2a858a 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,11 +176,13 @@
/* 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
+
+/* maximum Bluetooth volume value */
+#define SCARLETT2_MAX_BLUETOOTH_VOLUME 30
/* mixer range from -80dB to +6dB in 0.5dB steps */
#define SCARLETT2_MIXER_MIN_DB -80
@@ -215,6 +219,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 +251,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 +336,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 +365,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 +374,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 +404,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 +412,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 +425,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 +440,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 +470,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 +486,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 +513,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 +529,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 +540,29 @@ enum {
SCARLETT2_CONFIG_POWER_LOW,
SCARLETT2_CONFIG_PCM_INPUT_SWITCH,
SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN,
+ SCARLETT2_CONFIG_BLUETOOTH_VOLUME,
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 +574,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 = {
@@ -621,28 +757,81 @@ static const struct scarlett2_config_set scarlett2_config_set_gen3c = {
}
};
+/* 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 +841,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 +894,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 +943,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 }
}
};
@@ -895,6 +1104,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 +1133,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 +1144,9 @@ struct scarlett2_device_info {
/* the number of DSP channels */
u8 dsp_count;
+ /* has a Bluetooth module with volume control */
+ u8 has_bluetooth;
+
/* remap analogue outputs; 18i8 Gen 3 has "line 3/4" connected
* internally to the analogue 7/8 outputs
*/
@@ -942,7 +1174,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 +1194,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 +1205,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 +1219,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 +1230,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 +1244,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 +1254,7 @@ struct scarlett2_data {
u8 msd_switch;
u8 standalone_switch;
u8 power_status;
+ u8 bluetooth_volume;
u8 meter_level_map[SCARLETT2_MAX_METERS];
struct snd_kcontrol *sync_ctl;
struct snd_kcontrol *master_vol_ctl;
@@ -1019,20 +1266,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];
@@ -1475,6 +1731,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 +1880,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 +1934,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 = {
@@ -1806,6 +2149,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 +2203,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 +2214,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 +2308,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 +2424,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 +2438,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 +2482,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 +2564,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 +2621,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 +2631,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 +2644,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 +3313,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 +3345,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 +3378,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 +3418,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 +3622,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 +3647,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 +3774,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 +4164,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 +4173,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 +4184,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 +5181,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 +6684,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 +6786,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 +6845,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]);
+ }
- scnprintf(s, sizeof(s), fmt, i + 1,
- "Safe", "Switch");
+ /* 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), "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 +7770,123 @@ static int scarlett2_add_power_status_ctl(struct usb_mixer_interface *mixer)
&private->power_status_ctl);
}
-/*** 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;
-}
+/*** Bluetooth Volume ***/
-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;
-
- 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->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;
-}
-
-/* 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);
-}
-
-/* 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;
+ private->bluetooth_updated = 0;
- /* step 1 */
- private->scarlett2_seq = 1;
- err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_1, NULL, 0, NULL, 0);
- if (err < 0)
- return err;
+ if (!private->info->has_bluetooth)
+ return 0;
- /* step 2 */
- private->scarlett2_seq = 1;
- err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_2,
- NULL, 0,
- step2_buf, sizeof(step2_buf));
+ err = scarlett2_usb_get_config(mixer,
+ SCARLETT2_CONFIG_BLUETOOTH_VOLUME,
+ 1, &private->bluetooth_volume);
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)
+static int scarlett2_bluetooth_volume_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, 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;
- }
+ int err = 0;
- 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;
+ mutex_lock(&private->data_mutex);
- 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;
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
}
- /* 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->bluetooth_updated) {
+ err = scarlett2_update_bluetooth_volume(mixer);
+ if (err < 0)
+ goto unlock;
}
+ ucontrol->value.integer.value[0] = private->bluetooth_volume;
- return 0;
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
-/* Read configuration from the interface on start */
-static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
+static int scarlett2_bluetooth_volume_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 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_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];
+ int oval, val, err = 0;
- /* 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;
+ mutex_lock(&private->data_mutex);
- for (i = 0; i < private->num_line_out; i++)
- private->vol_sw_hw_switch[i] =
- !!private->vol_sw_hw_switch[i];
- }
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
}
- err = scarlett2_update_volumes(mixer);
- if (err < 0)
- return err;
+ oval = private->bluetooth_volume;
+ val = clamp(ucontrol->value.integer.value[0],
+ 0L, (long)SCARLETT2_MAX_BLUETOOTH_VOLUME);
- 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->bluetooth_volume = val;
+ err = scarlett2_usb_set_config(mixer,
+ SCARLETT2_CONFIG_BLUETOOTH_VOLUME,
+ 0, val);
+ if (err == 0)
+ err = 1;
- err = scarlett2_update_input_gain(mixer);
- if (err < 0)
- return err;
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
- err = scarlett2_update_autogain(mixer);
- if (err < 0)
- return err;
+static int scarlett2_bluetooth_volume_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ 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;
+}
- err = scarlett2_update_input_safe(mixer);
- if (err < 0)
- return err;
+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,
+};
- 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_bluetooth_volume_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->has_bluetooth)
+ return 0;
- return scarlett2_usb_get_mux(mixer);
+ /* Add Bluetooth volume control */
+ return scarlett2_add_new_ctl(mixer, &scarlett2_bluetooth_volume_ctl,
+ 0, 1, "Bluetooth Capture Volume",
+ &private->bluetooth_volume_ctl);
}
+/*** Notification Handlers ***/
+
/* Notify on sync change */
static void scarlett2_notify_sync(struct usb_mixer_interface *mixer)
{
@@ -6902,6 +7999,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 +8063,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 +8116,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 +8133,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 +8241,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 +8284,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 +8310,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 +8473,337 @@ 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_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 +8924,11 @@ 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;
+
/* Set the access mode of controls disabled during
* autogain/phantom power switching.
*/
@@ -7305,10 +8937,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 +9027,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 +9208,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 +9304,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 +9341,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 +9402,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 5d72dc8441cbb4..73abc38a540068 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 09712e61c606ef..2f961f0e937827 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_sndctrlpipe(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_sndctrlpipe(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;
}
}