diff options
Diffstat (limited to 'sound')
34 files changed, 3057 insertions, 958 deletions
diff --git a/sound/aoa/soundbus/i2sbus/pcm.c b/sound/aoa/soundbus/i2sbus/pcm.c index 07df5cc0f2d7..98b812ffbde6 100644 --- a/sound/aoa/soundbus/i2sbus/pcm.c +++ b/sound/aoa/soundbus/i2sbus/pcm.c @@ -255,24 +255,24 @@ static void i2sbus_wait_for_stop(struct i2sbus_dev *i2sdev, { unsigned long flags; DECLARE_COMPLETION_ONSTACK(done); - long timeout; + unsigned long time_left; spin_lock_irqsave(&i2sdev->low_lock, flags); if (pi->dbdma_ring.stopping) { pi->stop_completion = &done; spin_unlock_irqrestore(&i2sdev->low_lock, flags); - timeout = wait_for_completion_timeout(&done, HZ); + time_left = wait_for_completion_timeout(&done, HZ); spin_lock_irqsave(&i2sdev->low_lock, flags); pi->stop_completion = NULL; - if (timeout == 0) { + if (time_left == 0) { /* timeout expired, stop dbdma forcefully */ printk(KERN_ERR "i2sbus_wait_for_stop: timed out\n"); /* make sure RUN, PAUSE and S0 bits are cleared */ out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16); pi->dbdma_ring.stopping = 0; - timeout = 10; + time_left = 10; while (in_le32(&pi->dbdma->status) & ACTIVE) { - if (--timeout <= 0) + if (--time_left <= 0) break; udelay(1); } diff --git a/sound/core/control_led.c b/sound/core/control_led.c index 3d37e9fa7b9c..ac19d14bd574 100644 --- a/sound/core/control_led.c +++ b/sound/core/control_led.c @@ -285,25 +285,22 @@ static void snd_ctl_led_ctl_destroy(struct snd_ctl_led_ctl *lctl) static void snd_ctl_led_clean(struct snd_card *card) { unsigned int group; + struct snd_ctl_led_ctl *lctl, *_lctl; struct snd_ctl_led *led; - struct snd_ctl_led_ctl *lctl; for (group = 0; group < MAX_LED; group++) { led = &snd_ctl_leds[group]; -repeat: - list_for_each_entry(lctl, &led->controls, list) - if (!card || lctl->card == card) { + list_for_each_entry_safe(lctl, _lctl, &led->controls, list) + if (!card || lctl->card == card) snd_ctl_led_ctl_destroy(lctl); - goto repeat; - } } } static int snd_ctl_led_reset(int card_number, unsigned int group) { struct snd_card *card __free(snd_card_unref) = NULL; + struct snd_ctl_led_ctl *lctl, *_lctl; struct snd_ctl_led *led; - struct snd_ctl_led_ctl *lctl; struct snd_kcontrol_volatile *vd; bool change = false; @@ -315,14 +312,12 @@ static int snd_ctl_led_reset(int card_number, unsigned int group) if (!snd_ctl_led_card_valid[card_number]) return -ENXIO; led = &snd_ctl_leds[group]; -repeat: - list_for_each_entry(lctl, &led->controls, list) + list_for_each_entry_safe(lctl, _lctl, &led->controls, list) if (lctl->card == card) { vd = &lctl->kctl->vd[lctl->index_offset]; vd->access &= ~group_to_access(group); snd_ctl_led_ctl_destroy(lctl); change = true; - goto repeat; } } if (change) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 0b76e76823d2..521ba56392a0 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2416,7 +2416,7 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params, static const unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, - 48000, 64000, 88200, 96000, 176400, 192000, 352800, 384000 + 48000, 64000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, 768000 }; const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c index 9308194b2d9a..783fc72c2ef6 100644 --- a/sound/core/seq/seq_dummy.c +++ b/sound/core/seq/seq_dummy.c @@ -58,6 +58,12 @@ MODULE_PARM_DESC(ports, "number of ports to be created"); module_param(duplex, bool, 0444); MODULE_PARM_DESC(duplex, "create DUPLEX ports"); +#if IS_ENABLED(CONFIG_SND_SEQ_UMP) +static int ump; +module_param(ump, int, 0444); +MODULE_PARM_DESC(ump, "UMP conversion (0: no convert, 1: MIDI 1.0, 2: MIDI 2.0)"); +#endif + struct snd_seq_dummy_port { int client; int port; @@ -152,7 +158,9 @@ static int __init register_client(void) { struct snd_seq_dummy_port *rec1, *rec2; +#if IS_ENABLED(CONFIG_SND_SEQ_UMP) struct snd_seq_client *client; +#endif int i; if (ports < 1) { @@ -166,12 +174,24 @@ register_client(void) if (my_client < 0) return my_client; - /* don't convert events but just pass-through */ +#if IS_ENABLED(CONFIG_SND_SEQ_UMP) client = snd_seq_kernel_client_get(my_client); if (!client) return -EINVAL; - client->filter = SNDRV_SEQ_FILTER_NO_CONVERT; + switch (ump) { + case 1: + client->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_1_0; + break; + case 2: + client->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_2_0; + break; + default: + /* don't convert events but just pass-through */ + client->filter = SNDRV_SEQ_FILTER_NO_CONVERT; + break; + } snd_seq_kernel_client_put(client); +#endif /* create ports */ for (i = 0; i < ports; i++) { diff --git a/sound/core/sound_kunit.c b/sound/core/sound_kunit.c index eb90f62228c0..165bd0f5338a 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 892c4e29c0a3..d6dd4b8c750a 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -927,10 +927,13 @@ static const struct snd_pcm_hardware loopback_pcm_hardware = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | - SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE), - .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000, + SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE | + SNDRV_PCM_FMTBIT_DSD_U8 | + SNDRV_PCM_FMTBIT_DSD_U16_LE | SNDRV_PCM_FMTBIT_DSD_U16_BE | + SNDRV_PCM_FMTBIT_DSD_U32_LE | SNDRV_PCM_FMTBIT_DSD_U32_BE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_768000, .rate_min = 8000, - .rate_max = 192000, + .rate_max = 768000, .channels_min = 1, .channels_max = 32, .buffer_bytes_max = 2 * 1024 * 1024, diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index c9f153f85ae6..d35d0a420ee0 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -1180,13 +1180,11 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ (void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time); for (i = 0; i < packets; ++i) { - struct { - struct fw_iso_packet params; - __be32 header[CIP_HEADER_QUADLETS]; - } template = { {0}, {0} }; + DEFINE_FLEX(struct fw_iso_packet, template, header, + header_length, CIP_HEADER_QUADLETS); bool sched_irq = false; - build_it_pkt_header(s, desc->cycle, &template.params, pkt_header_length, + build_it_pkt_header(s, desc->cycle, template, pkt_header_length, desc->data_blocks, desc->data_block_counter, desc->syt, i, curr_cycle_time); @@ -1198,7 +1196,7 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ } } - if (queue_out_packet(s, &template.params, sched_irq) < 0) { + if (queue_out_packet(s, template, sched_irq) < 0) { cancel_stream(s); return; } diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 7f3a000fab0c..b5c833b9f8b9 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -62,7 +62,8 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) azx_clear_corbrp(bus); /* enable corb dma */ - snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN); + if (!bus->use_pio_for_commands) + snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN); /* RIRB set up */ bus->rirb.addr = bus->rb.addr + 2048; @@ -135,14 +136,94 @@ static unsigned int azx_command_addr(u32 cmd) return addr; } +/* receive an Immediate Response with PIO */ +static int snd_hdac_bus_wait_for_pio_response(struct hdac_bus *bus, + unsigned int addr) +{ + int timeout = 50; + + while (timeout--) { + /* check IRV bit */ + if (snd_hdac_chip_readw(bus, IRS) & AZX_IRS_VALID) { + /* reuse rirb.res as the response return value */ + bus->rirb.res[addr] = snd_hdac_chip_readl(bus, IR); + return 0; + } + udelay(1); + } + + dev_dbg_ratelimited(bus->dev, "get_response_pio timeout: IRS=%#x\n", + snd_hdac_chip_readw(bus, IRS)); + + bus->rirb.res[addr] = -1; + + return -EIO; +} + /** - * snd_hdac_bus_send_cmd - send a command verb via CORB + * snd_hdac_bus_send_cmd_pio - send a command verb via Immediate Command * @bus: HD-audio core bus * @val: encoded verb value to send * * Returns zero for success or a negative error code. */ -int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val) +static int snd_hdac_bus_send_cmd_pio(struct hdac_bus *bus, unsigned int val) +{ + unsigned int addr = azx_command_addr(val); + int timeout = 50; + int ret = -EIO; + + spin_lock_irq(&bus->reg_lock); + + while (timeout--) { + /* check ICB bit */ + if (!((snd_hdac_chip_readw(bus, IRS) & AZX_IRS_BUSY))) { + /* Clear IRV bit */ + snd_hdac_chip_updatew(bus, IRS, AZX_IRS_VALID, AZX_IRS_VALID); + snd_hdac_chip_writel(bus, IC, val); + /* Set ICB bit */ + snd_hdac_chip_updatew(bus, IRS, AZX_IRS_BUSY, AZX_IRS_BUSY); + + ret = snd_hdac_bus_wait_for_pio_response(bus, addr); + goto out; + } + udelay(1); + } + + dev_dbg_ratelimited(bus->dev, "send_cmd_pio timeout: IRS=%#x, val=%#x\n", + snd_hdac_chip_readw(bus, IRS), val); + +out: + spin_unlock_irq(&bus->reg_lock); + + return ret; +} + +/** + * snd_hdac_bus_get_response_pio - receive a response via Immediate Response + * @bus: HD-audio core bus + * @addr: codec address + * @res: pointer to store the value, NULL when not needed + * + * Returns zero if a value is read, or a negative error code. + */ +static int snd_hdac_bus_get_response_pio(struct hdac_bus *bus, + unsigned int addr, unsigned int *res) +{ + if (res) + *res = bus->rirb.res[addr]; + + return 0; +} + +/** + * snd_hdac_bus_send_cmd_corb - send a command verb via CORB + * @bus: HD-audio core bus + * @val: encoded verb value to send + * + * Returns zero for success or a negative error code. + */ +static int snd_hdac_bus_send_cmd_corb(struct hdac_bus *bus, unsigned int val) { unsigned int addr = azx_command_addr(val); unsigned int wp, rp; @@ -176,7 +257,6 @@ int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val) return 0; } -EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd); #define AZX_RIRB_EX_UNSOL_EV (1<<4) @@ -234,15 +314,15 @@ void snd_hdac_bus_update_rirb(struct hdac_bus *bus) EXPORT_SYMBOL_GPL(snd_hdac_bus_update_rirb); /** - * snd_hdac_bus_get_response - receive a response via RIRB + * snd_hdac_bus_get_response_rirb - receive a response via RIRB * @bus: HD-audio core bus * @addr: codec address * @res: pointer to store the value, NULL when not needed * * Returns zero if a value is read, or a negative error code. */ -int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, - unsigned int *res) +static int snd_hdac_bus_get_response_rirb(struct hdac_bus *bus, + unsigned int addr, unsigned int *res) { unsigned long timeout; unsigned long loopcounter; @@ -293,6 +373,39 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, return -EIO; } + +/** + * snd_hdac_bus_send_cmd - send a command verb via CORB or PIO + * @bus: HD-audio core bus + * @val: encoded verb value to send + * + * Returns zero for success or a negative error code. + */ +int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val) +{ + if (bus->use_pio_for_commands) + return snd_hdac_bus_send_cmd_pio(bus, val); + + return snd_hdac_bus_send_cmd_corb(bus, val); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd); + +/** + * snd_hdac_bus_get_response - receive a response via RIRB or PIO + * @bus: HD-audio core bus + * @addr: codec address + * @res: pointer to store the value, NULL when not needed + * + * Returns zero if a value is read, or a negative error code. + */ +int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, + unsigned int *res) +{ + if (bus->use_pio_for_commands) + return snd_hdac_bus_get_response_pio(bus, addr, res); + + return snd_hdac_bus_get_response_rirb(bus, addr, res); +} EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response); #define HDAC_MAX_CAPS 10 diff --git a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c index 8c1e7f2bfc34..ab4f988f080d 100644 --- a/sound/isa/sb/emu8000_patch.c +++ b/sound/isa/sb/emu8000_patch.c @@ -148,13 +148,6 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, if (snd_BUG_ON(!sp)) return -EINVAL; - if (sp->v.size == 0) - return 0; - - /* be sure loop points start < end */ - if (sp->v.loopstart > sp->v.loopend) - swap(sp->v.loopstart, sp->v.loopend); - /* compute true data size to be loaded */ truesize = sp->v.size; if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) @@ -177,12 +170,6 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, return -EFAULT; } - /* recalculate address offset */ - sp->v.end -= sp->v.start; - sp->v.loopstart -= sp->v.start; - sp->v.loopend -= sp->v.start; - sp->v.start = 0; - /* dram position (in word) -- mem_offset is byte */ dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1); dram_start = dram_offset; diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c index 941bfbf812ed..ef26e4d3e2a3 100644 --- a/sound/pci/emu10k1/emu10k1_callback.c +++ b/sound/pci/emu10k1/emu10k1_callback.c @@ -255,7 +255,7 @@ lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw, /* check if sample is finished playing (non-looping only) */ if (bp != best + V_OFF && bp != best + V_FREE && (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { - val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch); + val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch) - 64 + 3; if (val >= vp->reg.loopstart) bp = best + V_OFF; } @@ -310,6 +310,7 @@ start_voice(struct snd_emux_voice *vp) { unsigned int temp; int ch; + bool w_16; u32 psst, dsl, map, ccca, vtarget; unsigned int addr, mapped_offset; struct snd_midi_channel *chan; @@ -321,6 +322,7 @@ start_voice(struct snd_emux_voice *vp) if (snd_BUG_ON(ch < 0)) return -EINVAL; chan = vp->chan; + w_16 = !(vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS); emem = (struct snd_emu10k1_memblk *)vp->block; if (emem == NULL) @@ -330,7 +332,7 @@ start_voice(struct snd_emux_voice *vp) /* dev_err(hw->card->devK, "emu: cannot map!\n"); */ return -ENOMEM; } - mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1; + mapped_offset = snd_emu10k1_memblk_offset(emem) >> w_16; vp->reg.start += mapped_offset; vp->reg.end += mapped_offset; vp->reg.loopstart += mapped_offset; @@ -362,7 +364,7 @@ start_voice(struct snd_emux_voice *vp) map = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0); - addr = vp->reg.start; + addr = vp->reg.start + 64 - 3; temp = vp->reg.parm.filterQ; ccca = (temp << 28) | addr; if (vp->apitch < 0xe400) @@ -371,7 +373,7 @@ start_voice(struct snd_emux_voice *vp) unsigned int shift = (vp->apitch - 0xe000) >> 10; ccca |= shift << 25; } - if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) + if (!w_16) ccca |= CCCA_8BITSELECT; vtarget = (unsigned int)vp->vtarget << 16; @@ -430,6 +432,9 @@ start_voice(struct snd_emux_voice *vp) /* Q & current address (Q 4bit value, MSB) */ CCCA, ccca, + /* cache */ + CCR, REG_VAL_PUT(CCR_CACHEINVALIDSIZE, 64), + /* reset volume */ VTFT, vtarget | vp->ftarget, CVCF, vtarget | CVCF_CURRENTFILTER_MASK, diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 8ccc0178360c..5b8a5ba825bd 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -652,52 +652,6 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 *emu) return 0; } -static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, - const struct firmware *fw_entry) -{ - int n, i; - u16 reg; - u8 value; - __always_unused u16 write_post; - - if (!fw_entry) - return -EIO; - - /* The FPGA is a Xilinx Spartan IIE XC2S50E */ - /* On E-MU 0404b it is a Xilinx Spartan III XC3S50 */ - /* GPIO7 -> FPGA PGMN - * GPIO6 -> FPGA CCLK - * GPIO5 -> FPGA DIN - * FPGA CONFIG OFF -> FPGA PGMN - */ - spin_lock_irq(&emu->emu_lock); - outw(0x00, emu->port + A_GPIO); /* Set PGMN low for 100uS. */ - write_post = inw(emu->port + A_GPIO); - udelay(100); - outw(0x80, emu->port + A_GPIO); /* Leave bit 7 set during netlist setup. */ - write_post = inw(emu->port + A_GPIO); - udelay(100); /* Allow FPGA memory to clean */ - for (n = 0; n < fw_entry->size; n++) { - value = fw_entry->data[n]; - for (i = 0; i < 8; i++) { - reg = 0x80; - if (value & 0x1) - reg = reg | 0x20; - value = value >> 1; - outw(reg, emu->port + A_GPIO); - write_post = inw(emu->port + A_GPIO); - outw(reg | 0x40, emu->port + A_GPIO); - write_post = inw(emu->port + A_GPIO); - } - } - /* After programming, set GPIO bit 4 high again. */ - outw(0x10, emu->port + A_GPIO); - write_post = inw(emu->port + A_GPIO); - spin_unlock_irq(&emu->emu_lock); - - return 0; -} - /* firmware file names, per model, init-fw and dock-fw (optional) */ static const char * const firmware_names[5][2] = { [EMU_MODEL_EMU1010] = { @@ -729,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, ®); - 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, ®); - 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 89890f24509f..dbfa89435ac2 100644 --- a/sound/pci/emu10k1/emu10k1_patch.c +++ b/sound/pci/emu10k1/emu10k1_patch.c @@ -16,7 +16,7 @@ #define BLANK_LOOP_START 4 #define BLANK_LOOP_END 8 #define BLANK_LOOP_SIZE 12 -#define BLANK_HEAD_SIZE 32 +#define BLANK_HEAD_SIZE 3 /* * allocate a sample block and copy data from userspace @@ -26,56 +26,76 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, struct snd_util_memhdr *hdr, const void __user *data, long count) { + u8 fill; + u32 xor; + int shift; int offset; int truesize, size, blocksize; - __maybe_unused int loopsize; - int loopend, sampleend; - unsigned int start_addr; + int loop_start, loop_end, loop_size, data_end, unroll; struct snd_emu10k1 *emu; emu = rec->hw; if (snd_BUG_ON(!sp || !hdr)) return -EINVAL; - if (sp->v.size == 0) { - dev_dbg(emu->card->dev, - "emu: rom font for sample %d\n", sp->v.sample); - return 0; + if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP | SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) { + /* should instead return -ENOTSUPP; but compatibility */ + printk(KERN_WARNING "Emu10k1 wavetable patch %d with unsupported loop feature\n", + sp->v.sample); } - /* recalculate address offset */ - sp->v.end -= sp->v.start; - sp->v.loopstart -= sp->v.start; - sp->v.loopend -= sp->v.start; - sp->v.start = 0; - - /* some samples have invalid data. the addresses are corrected in voice info */ - sampleend = sp->v.end; - if (sampleend > sp->v.size) - sampleend = sp->v.size; - loopend = sp->v.loopend; - if (loopend > sampleend) - loopend = sampleend; - - /* be sure loop points start < end */ - if (sp->v.loopstart >= sp->v.loopend) - swap(sp->v.loopstart, sp->v.loopend); + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) { + shift = 0; + fill = 0x80; + xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0 : 0x80808080; + } else { + shift = 1; + fill = 0; + xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0x80008000 : 0; + } /* compute true data size to be loaded */ truesize = sp->v.size + BLANK_HEAD_SIZE; - loopsize = 0; -#if 0 /* not supported */ - if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) - loopsize = sp->v.loopend - sp->v.loopstart; - truesize += loopsize; -#endif - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { truesize += BLANK_LOOP_SIZE; + /* if no blank loop is attached in the sample, add it */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { + sp->v.loopstart = sp->v.end + BLANK_LOOP_START; + sp->v.loopend = sp->v.end + BLANK_LOOP_END; + } + } + + loop_start = sp->v.loopstart; + loop_end = sp->v.loopend; + loop_size = loop_end - loop_start; + if (!loop_size) + return -EINVAL; + data_end = sp->v.end; + + /* recalculate offset */ + sp->v.start += BLANK_HEAD_SIZE; + sp->v.end += BLANK_HEAD_SIZE; + sp->v.loopstart += BLANK_HEAD_SIZE; + sp->v.loopend += BLANK_HEAD_SIZE; + + // Automatic pre-filling of the cache does not work in the presence + // of loops (*), and we don't want to fill it manually, as that is + // fiddly and slow. So we unroll the loop until the loop end is + // beyond the cache size. + // (*) Strictly speaking, a single iteration is supported (that's + // how it works when the playback engine runs), but handling this + // special case is not worth it. + unroll = 0; + while (sp->v.loopend < 64) { + truesize += loop_size; + sp->v.loopstart += loop_size; + sp->v.loopend += loop_size; + sp->v.end += loop_size; + unroll++; + } /* try to allocate a memory block */ - blocksize = truesize; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - blocksize *= 2; + blocksize = truesize << shift; sp->block = snd_emu10k1_synth_alloc(emu, blocksize); if (sp->block == NULL) { dev_dbg(emu->card->dev, @@ -88,110 +108,43 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, /* write blank samples at head */ offset = 0; - size = BLANK_HEAD_SIZE; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - size *= 2; - if (offset + size > blocksize) - return -EINVAL; - snd_emu10k1_synth_bzero(emu, sp->block, offset, size); - offset += size; - - /* copy start->loopend */ - size = loopend; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - size *= 2; - if (offset + size > blocksize) - return -EINVAL; - if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { - snd_emu10k1_synth_free(emu, sp->block); - sp->block = NULL; - return -EFAULT; - } + size = BLANK_HEAD_SIZE << shift; + snd_emu10k1_synth_memset(emu, sp->block, offset, size, fill); offset += size; - data += size; - -#if 0 /* not supported yet */ - /* handle reverse (or bidirectional) loop */ - if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) { - /* copy loop in reverse */ - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { - int woffset; - unsigned short *wblock = (unsigned short*)block; - woffset = offset / 2; - if (offset + loopsize * 2 > blocksize) - return -EINVAL; - for (i = 0; i < loopsize; i++) - wblock[woffset + i] = wblock[woffset - i -1]; - offset += loopsize * 2; - } else { - if (offset + loopsize > blocksize) - return -EINVAL; - for (i = 0; i < loopsize; i++) - block[offset + i] = block[offset - i -1]; - offset += loopsize; - } - /* modify loop pointers */ - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) { - sp->v.loopend += loopsize; - } else { - sp->v.loopstart += loopsize; - sp->v.loopend += loopsize; + /* copy provided samples */ + if (unroll && loop_end <= data_end) { + size = loop_end << shift; + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor)) + goto faulty; + offset += size; + + data += loop_start << shift; + while (--unroll > 0) { + size = loop_size << shift; + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor)) + goto faulty; + offset += size; } - /* add sample pointer */ - sp->v.end += loopsize; - } -#endif - /* loopend -> sample end */ - size = sp->v.size - loopend; - if (size < 0) - return -EINVAL; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - size *= 2; - if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { - snd_emu10k1_synth_free(emu, sp->block); - sp->block = NULL; - return -EFAULT; + size = (data_end - loop_start) << shift; + } else { + size = data_end << shift; } + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor)) + goto faulty; offset += size; /* clear rest of samples (if any) */ if (offset < blocksize) - snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset); - - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { - /* if no blank loop is attached in the sample, add it */ - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { - sp->v.loopstart = sp->v.end + BLANK_LOOP_START; - sp->v.loopend = sp->v.end + BLANK_LOOP_END; - } - } - -#if 0 /* not supported yet */ - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) { - /* unsigned -> signed */ - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { - unsigned short *wblock = (unsigned short*)block; - for (i = 0; i < truesize; i++) - wblock[i] ^= 0x8000; - } else { - for (i = 0; i < truesize; i++) - block[i] ^= 0x80; - } - } -#endif - - /* recalculate offset */ - start_addr = BLANK_HEAD_SIZE * 2; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - start_addr >>= 1; - sp->v.start += start_addr; - sp->v.end += start_addr; - sp->v.loopstart += start_addr; - sp->v.loopend += start_addr; + snd_emu10k1_synth_memset(emu, sp->block, offset, blocksize - offset, fill); return 0; + +faulty: + snd_emu10k1_synth_free(emu, sp->block); + sp->block = NULL; + return -EFAULT; } /* diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index f4a1c2d4b078..b60ab5671e00 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 20b07117574b..d29711777161 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -565,15 +565,18 @@ static inline void *offset_ptr(struct snd_emu10k1 *emu, int page, int offset) } /* - * bzero(blk + offset, size) + * memset(blk + offset, value, size) */ -int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, - int offset, int size) +int snd_emu10k1_synth_memset(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, + int offset, int size, u8 value) { int page, nextofs, end_offset, temp, temp1; void *ptr; struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk; + if (snd_BUG_ON(offset + size > p->mem.size)) + return -EFAULT; + offset += blk->offset & (PAGE_SIZE - 1); end_offset = offset + size; page = get_aligned_page(offset); @@ -585,25 +588,55 @@ int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk temp = temp1; ptr = offset_ptr(emu, page + p->first_page, offset); if (ptr) - memset(ptr, 0, temp); + memset(ptr, value, temp); offset = nextofs; page++; } while (offset < end_offset); return 0; } -EXPORT_SYMBOL(snd_emu10k1_synth_bzero); +EXPORT_SYMBOL(snd_emu10k1_synth_memset); + +// Note that the value is assumed to be suitably repetitive. +static void xor_range(void *ptr, int size, u32 value) +{ + if ((long)ptr & 1) { + *(u8 *)ptr ^= (u8)value; + ptr++; + size--; + } + if (size > 1 && ((long)ptr & 2)) { + *(u16 *)ptr ^= (u16)value; + ptr += 2; + size -= 2; + } + while (size > 3) { + *(u32 *)ptr ^= value; + ptr += 4; + size -= 4; + } + if (size > 1) { + *(u16 *)ptr ^= (u16)value; + ptr += 2; + size -= 2; + } + if (size > 0) + *(u8 *)ptr ^= (u8)value; +} /* - * copy_from_user(blk + offset, data, size) + * copy_from_user(blk + offset, data, size) ^ xor */ int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, - int offset, const char __user *data, int size) + int offset, const char __user *data, int size, u32 xor) { int page, nextofs, end_offset, temp, temp1; void *ptr; struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk; + if (snd_BUG_ON(offset + size > p->mem.size)) + return -EFAULT; + offset += blk->offset & (PAGE_SIZE - 1); end_offset = offset + size; page = get_aligned_page(offset); @@ -614,8 +647,12 @@ int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_me if (temp1 < temp) temp = temp1; ptr = offset_ptr(emu, page + p->first_page, offset); - if (ptr && copy_from_user(ptr, data, temp)) - return -EFAULT; + if (ptr) { + if (copy_from_user(ptr, data, temp)) + return -EFAULT; + if (xor) + xor_range(ptr, temp, xor); + } offset = nextofs; data += temp; page++; diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index f806636242ee..0da625533afc 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -128,6 +128,7 @@ config SND_HDA_SCODEC_CS35L41_I2C select SND_SOC_CS35L41_LIB select SND_HDA_SCODEC_CS35L41 select SND_HDA_CS_DSP_CONTROLS + select SND_SOC_CS_AMP_LIB help Say Y or M here to include CS35L41 I2C HD-audio side codec support in snd-hda-intel driver, such as ALC287. @@ -144,6 +145,7 @@ config SND_HDA_SCODEC_CS35L41_SPI select SND_SOC_CS35L41_LIB select SND_HDA_SCODEC_CS35L41 select SND_HDA_CS_DSP_CONTROLS + select SND_SOC_CS_AMP_LIB help Say Y or M here to include CS35L41 SPI HD-audio side codec support in snd-hda-intel driver, such as ALC287. diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c index d3fa6e136744..2b0404f6e271 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c @@ -22,7 +22,6 @@ #include "hda_cs_dsp_ctl.h" #include "cs35l41_hda_property.h" -#define CS35L41_FIRMWARE_ROOT "cirrus/" #define CS35L41_PART "cs35l41" #define HALO_STATE_DSP_CTL_NAME "HALO_STATE" @@ -37,6 +36,42 @@ #define CS35L41_UUID "50d90cdc-3de4-4f18-b528-c7fe3b71f40d" #define CS35L41_DSM_GET_MUTE 5 #define CS35L41_NOTIFY_EVENT 0x91 +#define CS35L41_TUNING_SIG 0x109A4A35 + +enum cs35l41_tuning_param_types { + TUNING_PARAM_GAIN, +}; + +struct cs35l41_tuning_param_hdr { + __le32 tuning_index; + __le32 type; + __le32 size; +} __packed; + +struct cs35l41_tuning_param { + struct cs35l41_tuning_param_hdr hdr; + union { + __le32 gain; + }; +} __packed; + +struct cs35l41_tuning_params { + __le32 signature; + __le32 version; + __le32 size; + __le32 num_entries; + u8 data[]; +} __packed; + +/* Firmware calibration controls */ +static const struct cirrus_amp_cal_controls cs35l41_calibration_controls = { + .alg_id = CAL_DSP_CTL_ALG, + .mem_region = CAL_DSP_CTL_TYPE, + .ambient = CAL_AMBIENT_DSP_CTL_NAME, + .calr = CAL_R_DSP_CTL_NAME, + .status = CAL_STATUS_DSP_CTL_NAME, + .checksum = CAL_CHECKSUM_DSP_CTL_NAME, +}; static bool firmware_autostart = 1; module_param(firmware_autostart, bool, 0444); @@ -75,7 +110,7 @@ static const struct reg_sequence cs35l41_hda_config_dsp[] = { { CS35L41_SP_HIZ_CTRL, 0x00000003 }, // Hi-Z unused/disabled { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot - { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = ERR_VOL + { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = DSP1TX1 { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON { CS35L41_ASP_TX3_SRC, 0x00000028 }, // ASPTX3 SRC = VPMON @@ -84,7 +119,7 @@ static const struct reg_sequence cs35l41_hda_config_dsp[] = { { CS35L41_DSP1_RX2_SRC, 0x00000008 }, // DSP1RX2 SRC = ASPRX1 { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON - { CS35L41_DSP1_RX5_SRC, 0x00000029 }, // DSP1RX5 SRC = VBSTMON + { CS35L41_DSP1_RX6_SRC, 0x00000029 }, // DSP1RX6 SRC = VBSTMON }; static const struct reg_sequence cs35l41_hda_unmute[] = { @@ -92,11 +127,6 @@ static const struct reg_sequence cs35l41_hda_unmute[] = { { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB }; -static const struct reg_sequence cs35l41_hda_unmute_dsp[] = { - { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB - { CS35L41_AMP_GAIN_CTRL, 0x00000233 }, // AMP_GAIN_PCM = 17.5dB AMP_GAIN_PDM = 19.5dB -}; - static const struct reg_sequence cs35l41_hda_mute[] = { { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, // AMP_GAIN_PCM 0.5 dB { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM Mute @@ -117,9 +147,30 @@ static const struct cs_dsp_client_ops client_ops = { .control_remove = hda_cs_dsp_control_remove, }; +static int cs35l41_request_tuning_param_file(struct cs35l41_hda *cs35l41, char *tuning_filename, + const struct firmware **firmware, char **filename, + const char *ssid) +{ + int ret = 0; + + /* Filename is the same as the tuning file with "cfg" suffix */ + *filename = kasprintf(GFP_KERNEL, "%scfg", tuning_filename); + if (*filename == NULL) + return -ENOMEM; + + ret = firmware_request_nowarn(firmware, *filename, cs35l41->dev); + if (ret != 0) { + dev_dbg(cs35l41->dev, "Failed to request '%s'\n", *filename); + kfree(*filename); + *filename = NULL; + } + + return ret; +} + static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41, const struct firmware **firmware, char **filename, - const char *dir, const char *ssid, const char *amp_name, + const char *ssid, const char *amp_name, int spkid, const char *filetype) { const char * const dsp_name = cs35l41->cs_dsp.name; @@ -127,23 +178,23 @@ static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41, int ret = 0; if (spkid > -1 && ssid && amp_name) - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d-%s.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d-%s.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], ssid, spkid, amp_name, filetype); else if (spkid > -1 && ssid) - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], ssid, spkid, filetype); else if (ssid && amp_name) - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-%s.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], ssid, amp_name, filetype); else if (ssid) - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], ssid, filetype); else - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], filetype); @@ -184,13 +235,11 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) @@ -201,12 +250,11 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, cs35l41->amp_name, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) @@ -217,18 +265,17 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, NULL, cs35l41->speaker_id, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, - coeff_filename, CS35L41_FIRMWARE_ROOT, + coeff_filename, cs35l41->acpi_subsystem_id, NULL, cs35l41->speaker_id, "bin"); if (ret) @@ -239,18 +286,17 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, NULL, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, - coeff_filename, CS35L41_FIRMWARE_ROOT, + coeff_filename, cs35l41->acpi_subsystem_id, NULL, cs35l41->speaker_id, "bin"); if (ret) @@ -277,13 +323,13 @@ static int cs35l41_fallback_firmware_file(struct cs35l41_hda *cs35l41, /* fallback try cirrus/part-dspN-fwtype.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw"); + NULL, NULL, -1, "wmfw"); if (ret) goto err; /* fallback try cirrus/part-dspN-fwtype.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin"); + NULL, NULL, -1, "bin"); if (ret) { release_firmware(*wmfw_firmware); kfree(*wmfw_filename); @@ -312,12 +358,11 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, cs35l41->amp_name, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, -1, "bin"); if (ret) @@ -328,18 +373,16 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, NULL, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, -1, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, NULL, -1, "bin"); if (ret) @@ -361,95 +404,162 @@ fallback: coeff_firmware, coeff_filename); } -#if IS_ENABLED(CONFIG_EFI) -static int cs35l41_apply_calibration(struct cs35l41_hda *cs35l41, __be32 ambient, __be32 r0, - __be32 status, __be32 checksum) + +static void cs35l41_hda_apply_calibration(struct cs35l41_hda *cs35l41) +{ + int ret; + + if (!cs35l41->cal_data_valid) + return; + + ret = cs_amp_write_cal_coeffs(&cs35l41->cs_dsp, &cs35l41_calibration_controls, + &cs35l41->cal_data); + if (ret < 0) + dev_warn(cs35l41->dev, "Failed to apply calibration: %d\n", ret); + else + dev_info(cs35l41->dev, "Calibration applied: R0=%d\n", cs35l41->cal_data.calR); +} + +static int cs35l41_read_silicon_uid(struct cs35l41_hda *cs35l41, u64 *uid) { + u32 tmp; int ret; - ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_AMBIENT_DSP_CTL_NAME, CAL_DSP_CTL_TYPE, - CAL_DSP_CTL_ALG, &ambient, 4); + ret = regmap_read(cs35l41->regmap, CS35L41_DIE_STS2, &tmp); if (ret) { - dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_AMBIENT_DSP_CTL_NAME, - ret); + dev_err(cs35l41->dev, "Cannot obtain CS35L41_DIE_STS2: %d\n", ret); return ret; } - ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_R_DSP_CTL_NAME, CAL_DSP_CTL_TYPE, - CAL_DSP_CTL_ALG, &r0, 4); + + *uid = tmp; + *uid <<= 32; + + ret = regmap_read(cs35l41->regmap, CS35L41_DIE_STS1, &tmp); if (ret) { - dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_R_DSP_CTL_NAME, ret); + dev_err(cs35l41->dev, "Cannot obtain CS35L41_DIE_STS1: %d\n", ret); return ret; } - ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_STATUS_DSP_CTL_NAME, CAL_DSP_CTL_TYPE, - CAL_DSP_CTL_ALG, &status, 4); - if (ret) { - dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_STATUS_DSP_CTL_NAME, - ret); + + *uid |= tmp; + + dev_dbg(cs35l41->dev, "UniqueID = %#llx\n", *uid); + + return 0; +} + +static int cs35l41_get_calibration(struct cs35l41_hda *cs35l41) +{ + u64 silicon_uid; + int ret; + + ret = cs35l41_read_silicon_uid(cs35l41, &silicon_uid); + if (ret < 0) return ret; - } - ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_CHECKSUM_DSP_CTL_NAME, CAL_DSP_CTL_TYPE, - CAL_DSP_CTL_ALG, &checksum, 4); - if (ret) { - dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_CHECKSUM_DSP_CTL_NAME, - ret); + + ret = cs_amp_get_efi_calibration_data(cs35l41->dev, silicon_uid, + cs35l41->index, + &cs35l41->cal_data); + + /* Only return an error status if probe should be aborted */ + if ((ret == -ENOENT) || (ret == -EOVERFLOW)) + return 0; + + if (ret < 0) return ret; - } + + cs35l41->cal_data_valid = true; return 0; } -static int cs35l41_save_calibration(struct cs35l41_hda *cs35l41) + +static void cs35l41_set_default_tuning_params(struct cs35l41_hda *cs35l41) { - static efi_guid_t efi_guid = EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, - 0x5a, 0xa3, 0x5d, 0xb3); - static efi_char16_t efi_name[] = L"CirrusSmartAmpCalibrationData"; - const struct cs35l41_amp_efi_data *efi_data; - const struct cs35l41_amp_cal_data *cl; - unsigned long data_size = 0; - efi_status_t status; - int ret = 0; - u8 *data = NULL; - u32 attr; + cs35l41->tuning_gain = DEFAULT_AMP_GAIN_PCM; +} - /* Get real size of UEFI variable */ - status = efi.get_variable(efi_name, &efi_guid, &attr, &data_size, data); - if (status == EFI_BUFFER_TOO_SMALL) { - ret = -ENODEV; - /* Allocate data buffer of data_size bytes */ - data = vmalloc(data_size); - if (!data) - return -ENOMEM; - /* Get variable contents into buffer */ - status = efi.get_variable(efi_name, &efi_guid, &attr, &data_size, data); - if (status == EFI_SUCCESS) { - efi_data = (struct cs35l41_amp_efi_data *)data; - dev_dbg(cs35l41->dev, "Calibration: Size=%d, Amp Count=%d\n", - efi_data->size, efi_data->count); - if (efi_data->count > cs35l41->index) { - cl = &efi_data->data[cs35l41->index]; - dev_dbg(cs35l41->dev, - "Calibration: Ambient=%02x, Status=%02x, R0=%d\n", - cl->calAmbient, cl->calStatus, cl->calR); - - /* Calibration can only be applied whilst the DSP is not running */ - ret = cs35l41_apply_calibration(cs35l41, - cpu_to_be32(cl->calAmbient), - cpu_to_be32(cl->calR), - cpu_to_be32(cl->calStatus), - cpu_to_be32(cl->calR + 1)); - } +static int cs35l41_read_tuning_params(struct cs35l41_hda *cs35l41, const struct firmware *firmware) +{ + struct cs35l41_tuning_params *params; + unsigned int offset = 0; + unsigned int end; + int i; + + params = (void *)&firmware->data[0]; + + if (le32_to_cpu(params->size) != firmware->size) { + dev_err(cs35l41->dev, "Wrong Size for Tuning Param file. Expected %d got %zu\n", + le32_to_cpu(params->size), firmware->size); + return -EINVAL; + } + + if (le32_to_cpu(params->version) != 1) { + dev_err(cs35l41->dev, "Unsupported Tuning Param Version: %d\n", + le32_to_cpu(params->version)); + return -EINVAL; + } + + if (le32_to_cpu(params->signature) != CS35L41_TUNING_SIG) { + dev_err(cs35l41->dev, + "Mismatched Signature for Tuning Param file. Expected %#x got %#x\n", + CS35L41_TUNING_SIG, le32_to_cpu(params->signature)); + return -EINVAL; + } + + end = firmware->size - sizeof(struct cs35l41_tuning_params); + + for (i = 0; i < le32_to_cpu(params->num_entries); i++) { + struct cs35l41_tuning_param *param; + + if ((offset >= end) || ((offset + sizeof(struct cs35l41_tuning_param_hdr)) >= end)) + return -EFAULT; + + param = (void *)¶ms->data[offset]; + offset += le32_to_cpu(param->hdr.size); + + if (offset > end) + return -EFAULT; + + switch (le32_to_cpu(param->hdr.type)) { + case TUNING_PARAM_GAIN: + cs35l41->tuning_gain = le32_to_cpu(param->gain); + dev_dbg(cs35l41->dev, "Applying Gain: %d\n", cs35l41->tuning_gain); + break; + default: + break; } - vfree(data); } - return ret; + + return 0; } -#else -static int cs35l41_save_calibration(struct cs35l41_hda *cs35l41) + +static int cs35l41_load_tuning_params(struct cs35l41_hda *cs35l41, char *tuning_filename) { - dev_warn(cs35l41->dev, "Calibration not supported without EFI support.\n"); - return 0; + const struct firmware *tuning_param_file = NULL; + char *tuning_param_filename = NULL; + int ret; + + ret = cs35l41_request_tuning_param_file(cs35l41, tuning_filename, &tuning_param_file, + &tuning_param_filename, cs35l41->acpi_subsystem_id); + if (ret) { + dev_dbg(cs35l41->dev, "Missing Tuning Param for file: %s: %d\n", tuning_filename, + ret); + return 0; + } + + ret = cs35l41_read_tuning_params(cs35l41, tuning_param_file); + if (ret) { + dev_err(cs35l41->dev, "Error reading Tuning Params from file: %s: %d\n", + tuning_param_filename, ret); + /* Reset to default Tuning Parameters */ + cs35l41_set_default_tuning_params(cs35l41); + } + + release_firmware(tuning_param_file); + kfree(tuning_param_filename); + + return ret; } -#endif static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41) { @@ -470,27 +580,35 @@ static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41) cs35l41->halo_initialized = true; } + cs35l41_set_default_tuning_params(cs35l41); + ret = cs35l41_request_firmware_files(cs35l41, &wmfw_firmware, &wmfw_filename, &coeff_firmware, &coeff_filename); if (ret < 0) return ret; dev_dbg(cs35l41->dev, "Loading WMFW Firmware: %s\n", wmfw_filename); - if (coeff_filename) + if (coeff_filename) { dev_dbg(cs35l41->dev, "Loading Coefficient File: %s\n", coeff_filename); - else + ret = cs35l41_load_tuning_params(cs35l41, coeff_filename); + if (ret) + dev_warn(cs35l41->dev, "Unable to load Tuning Parameters: %d\n", ret); + } else { dev_warn(cs35l41->dev, "No Coefficient File available.\n"); + } ret = cs_dsp_power_up(dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename, hda_cs_dsp_fw_ids[cs35l41->firmware_type]); if (ret) - goto err_release; + goto err; cs35l41_add_controls(cs35l41); - ret = cs35l41_save_calibration(cs35l41); + cs35l41_hda_apply_calibration(cs35l41); -err_release: +err: + if (ret) + cs35l41_set_default_tuning_params(cs35l41); release_firmware(wmfw_firmware); release_firmware(coeff_firmware); kfree(wmfw_filename); @@ -503,6 +621,7 @@ static void cs35l41_shutdown_dsp(struct cs35l41_hda *cs35l41) { struct cs_dsp *dsp = &cs35l41->cs_dsp; + cs35l41_set_default_tuning_params(cs35l41); cs_dsp_stop(dsp); cs_dsp_power_down(dsp); dev_dbg(cs35l41->dev, "Unloaded Firmware\n"); @@ -553,6 +672,10 @@ static void cs35l41_hda_play_start(struct device *dev) if (cs35l41->cs_dsp.running) { regmap_multi_reg_write(reg, cs35l41_hda_config_dsp, ARRAY_SIZE(cs35l41_hda_config_dsp)); + if (cs35l41->hw_cfg.bst_type == CS35L41_INT_BOOST) + regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VPMON); + else + regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VBSTMON); regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK, 1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT); @@ -570,6 +693,7 @@ static void cs35l41_mute(struct device *dev, bool mute) { struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); struct regmap *reg = cs35l41->regmap; + unsigned int amp_gain; dev_dbg(dev, "Mute(%d:%d) Playback Started: %d\n", mute, cs35l41->mute_override, cs35l41->playback_started); @@ -581,8 +705,13 @@ static void cs35l41_mute(struct device *dev, bool mute) } else { dev_dbg(dev, "Unmuting\n"); if (cs35l41->cs_dsp.running) { - regmap_multi_reg_write(reg, cs35l41_hda_unmute_dsp, - ARRAY_SIZE(cs35l41_hda_unmute_dsp)); + dev_dbg(dev, "Using Tuned Gain: %d\n", cs35l41->tuning_gain); + amp_gain = (cs35l41->tuning_gain << CS35L41_AMP_GAIN_PCM_SHIFT) | + (DEFAULT_AMP_GAIN_PDM << CS35L41_AMP_GAIN_PDM_SHIFT); + + /* AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB */ + regmap_write(reg, CS35L41_AMP_DIG_VOL_CTRL, 0x00008000); + regmap_write(reg, CS35L41_AMP_GAIN_CTRL, amp_gain); } else { regmap_multi_reg_write(reg, cs35l41_hda_unmute, ARRAY_SIZE(cs35l41_hda_unmute)); @@ -1056,6 +1185,9 @@ static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41) goto clean_dsp; } + dev_info(cs35l41->dev, "Firmware Loaded - Type: %s, Gain: %d\n", + hda_cs_dsp_fw_ids[cs35l41->firmware_type], cs35l41->tuning_gain); + return 0; clean_dsp: @@ -1461,13 +1593,56 @@ static struct regmap_irq_chip cs35l41_regmap_irq_chip = { .runtime_pm = true, }; +static void cs35l41_configure_interrupt(struct cs35l41_hda *cs35l41, int irq_pol) +{ + int irq; + int ret; + int i; + + if (!cs35l41->irq) { + dev_warn(cs35l41->dev, "No Interrupt Found"); + goto err; + } + + ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq, + IRQF_ONESHOT | IRQF_SHARED | irq_pol, + 0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data); + if (ret) { + dev_dbg(cs35l41->dev, "Unable to add IRQ Chip: %d.", ret); + goto err; + } + + for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) { + irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq); + if (irq < 0) { + ret = irq; + dev_dbg(cs35l41->dev, "Unable to map IRQ %s: %d.", cs35l41_irqs[i].name, + ret); + goto err; + } + + ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL, + cs35l41_irqs[i].handler, + IRQF_ONESHOT | IRQF_SHARED | irq_pol, + cs35l41_irqs[i].name, cs35l41); + if (ret) { + dev_dbg(cs35l41->dev, "Unable to allocate IRQ %s:: %d.", + cs35l41_irqs[i].name, ret); + goto err; + } + } + return; +err: + dev_warn(cs35l41->dev, + "IRQ Config Failed. Amp errors may not be recoverable without reboot."); +} + static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41) { struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; bool using_irq = false; - int irq, irq_pol; + int irq_pol; int ret; - int i; if (!cs35l41->hw_cfg.valid) return -EINVAL; @@ -1510,26 +1685,8 @@ static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41) irq_pol = cs35l41_gpio_config(cs35l41->regmap, hw_cfg); - if (cs35l41->irq && using_irq) { - ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq, - IRQF_ONESHOT | IRQF_SHARED | irq_pol, - 0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data); - if (ret) - return ret; - - for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) { - irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq); - if (irq < 0) - return irq; - - ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL, - cs35l41_irqs[i].handler, - IRQF_ONESHOT | IRQF_SHARED | irq_pol, - cs35l41_irqs[i].name, cs35l41); - if (ret) - return ret; - } - } + if (using_irq) + cs35l41_configure_interrupt(cs35l41, irq_pol); return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos); } @@ -1808,6 +1965,10 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i if (ret) goto err; + ret = cs35l41_get_calibration(cs35l41); + if (ret && ret != -ENOENT) + goto err; + cs35l41_mute(cs35l41->dev, true); INIT_WORK(&cs35l41->fw_load_work, cs35l41_fw_load_work); @@ -1888,6 +2049,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, SND_HDA_SCODEC_CS35L41); MODULE_DESCRIPTION("CS35L41 HDA Driver"); MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS); +MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB); MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(FW_CS_DSP); diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h index 43d55292b327..b0bebb778462 100644 --- a/sound/pci/hda/cs35l41_hda.h +++ b/sound/pci/hda/cs35l41_hda.h @@ -16,11 +16,14 @@ #include <linux/gpio/consumer.h> #include <linux/device.h> #include <sound/cs35l41.h> +#include <sound/cs-amp-lib.h> #include <linux/firmware/cirrus/cs_dsp.h> #include <linux/firmware/cirrus/wmfw.h> #define CS35L41_MAX_ACCEPTABLE_SPI_SPEED_HZ 1000000 +#define DEFAULT_AMP_GAIN_PCM 17 /* 17.5dB Gain */ +#define DEFAULT_AMP_GAIN_PDM 19 /* 19.5dB Gain */ struct cs35l41_amp_cal_data { u32 calTarget[2]; @@ -83,6 +86,9 @@ struct cs35l41_hda { bool mute_override; enum control_bus control_bus; bool bypass_fw; + unsigned int tuning_gain; + struct cirrus_amp_cal_data cal_data; + bool cal_data_valid; }; diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c index 8fb688e41414..ad9b6abdf269 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 c7142eee9f35..1030a8bfb3cd 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); @@ -1186,6 +1186,9 @@ int azx_bus_init(struct azx *chip, const char *model) if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) bus->core.align_bdle_4k = true; + if (chip->driver_caps & AZX_DCAPS_PIO_COMMANDS) + bus->core.use_pio_for_commands = true; + /* enable sync_write flag for stable communication as default */ bus->core.sync_write = 1; diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index 8556031bcd68..c2d0109866e6 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -45,6 +45,7 @@ #define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ #define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */ #define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */ +#define AZX_DCAPS_PIO_COMMANDS (1 << 31) /* Use PIO instead of CORB for commands */ enum { AZX_SNOOP_TYPE_NONE, diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 31b0c954b0c8..2bd652dcb5b3 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -291,6 +291,9 @@ enum { #define AZX_DCAPS_INTEL_BROXTON AZX_DCAPS_INTEL_SKYLAKE +#define AZX_DCAPS_INTEL_LNL \ + (AZX_DCAPS_INTEL_SKYLAKE | AZX_DCAPS_PIO_COMMANDS) + /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_POSFIX_LPIB |\ @@ -2484,8 +2487,10 @@ static const struct pci_device_id azx_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, { PCI_DEVICE_DATA(INTEL, HDA_MTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + /* Battlemage */ + { PCI_DEVICE_DATA(INTEL, HDA_BMG, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Lunarlake-P */ - { PCI_DEVICE_DATA(INTEL, HDA_LNL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_LNL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, /* Arrow Lake-S */ { PCI_DEVICE_DATA(INTEL, HDA_ARL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Arrow Lake */ diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index b2c42cd54351..db24c8fc7cd0 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -4634,6 +4634,7 @@ HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_adlp_hdmi), HDA_CODEC_ENTRY(0x8086281d, "Meteor Lake HDMI", patch_i915_adlp_hdmi), +HDA_CODEC_ENTRY(0x8086281e, "Battlemage HDMI", patch_i915_adlp_hdmi), HDA_CODEC_ENTRY(0x8086281f, "Raptor Lake P HDMI", patch_i915_adlp_hdmi), HDA_CODEC_ENTRY(0x80862820, "Lunar Lake HDMI", patch_i915_adlp_hdmi), HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 944f36f94a5a..2eb419c814f6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10138,6 +10138,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), @@ -10158,6 +10160,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), @@ -10485,6 +10489,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 fc63085d2d74..f78d6b998be0 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 a82af9374852..01444fc960d0 100644 --- a/sound/synth/emux/emux.c +++ b/sound/synth/emux/emux.c @@ -94,10 +94,8 @@ int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, ch /* create soundfont list */ memset(&sf_cb, 0, sizeof(sf_cb)); sf_cb.private_data = emu; - if (emu->ops.sample_new) - sf_cb.sample_new = sf_sample_new; - if (emu->ops.sample_free) - sf_cb.sample_free = sf_sample_free; + sf_cb.sample_new = sf_sample_new; + sf_cb.sample_free = sf_sample_free; if (emu->ops.sample_reset) sf_cb.sample_reset = sf_sample_reset; emu->sflist = snd_sf_new(&sf_cb, emu->memhdr); diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c index 81719bfb8ed7..fd8f978cde1c 100644 --- a/sound/synth/emux/emux_hwdep.c +++ b/sound/synth/emux/emux_hwdep.c @@ -27,8 +27,7 @@ snd_emux_hwdep_load_patch(struct snd_emux *emu, void __user *arg) if (patch.key == GUS_PATCH) return snd_soundfont_load_guspatch(emu->sflist, arg, - patch.len + sizeof(patch), - TMP_CLIENT_ID); + patch.len + sizeof(patch)); if (patch.type >= SNDRV_SFNT_LOAD_INFO && patch.type <= SNDRV_SFNT_PROBE_DATA) { diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c index d8d32671f703..04df46b269d3 100644 --- a/sound/synth/emux/emux_oss.c +++ b/sound/synth/emux/emux_oss.c @@ -205,8 +205,7 @@ snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format, return -ENXIO; if (format == GUS_PATCH) - rc = snd_soundfont_load_guspatch(emu->sflist, buf, count, - SF_CLIENT_NO(p->chset.port)); + rc = snd_soundfont_load_guspatch(emu->sflist, buf, count); else if (format == SNDRV_OSS_SOUNDFONT_PATCH) { struct soundfont_patch_info patch; if (count < (int)sizeof(patch)) diff --git a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c index 7993e6a01e54..820351f52551 100644 --- a/sound/synth/emux/emux_proc.c +++ b/sound/synth/emux/emux_proc.c @@ -102,6 +102,7 @@ void snd_emux_proc_init(struct snd_emux *emu, struct snd_card *card, int device) entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = emu; entry->c.text.read = snd_emux_proc_info_read; + emu->proc = entry; } void snd_emux_proc_free(struct snd_emux *emu) diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c index b227c7e0bc2a..1adaa75df2f6 100644 --- a/sound/synth/emux/emux_seq.c +++ b/sound/synth/emux/emux_seq.c @@ -65,11 +65,11 @@ snd_emux_init_seq(struct snd_emux *emu, struct snd_card *card, int index) return -ENODEV; } - if (emu->num_ports < 0) { + if (emu->num_ports <= 0) { snd_printk(KERN_WARNING "seqports must be greater than zero\n"); emu->num_ports = 1; - } else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) { - snd_printk(KERN_WARNING "too many ports." + } else if (emu->num_ports > SNDRV_EMUX_MAX_PORTS) { + snd_printk(KERN_WARNING "too many ports. " "limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS); emu->num_ports = SNDRV_EMUX_MAX_PORTS; } diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c index 16f00097cb95..2373ed580bf8 100644 --- a/sound/synth/emux/soundfont.c +++ b/sound/synth/emux/soundfont.c @@ -689,6 +689,21 @@ find_sample(struct snd_soundfont *sf, int sample_id) } +static int +validate_sample_info(struct soundfont_sample_info *si) +{ + if (si->end < 0 || si->end > si->size) + return -EINVAL; + if (si->loopstart < 0 || si->loopstart > si->end) + return -EINVAL; + if (si->loopend < 0 || si->loopend > si->end) + return -EINVAL; + /* be sure loop points start < end */ + if (si->loopstart > si->loopend) + swap(si->loopstart, si->loopend); + return 0; +} + /* * Load sample information, this can include data to be loaded onto * the soundcard. It can also just be a pointer into soundcard ROM. @@ -701,7 +716,6 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count) struct snd_soundfont *sf; struct soundfont_sample_info sample_info; struct snd_sf_sample *sp; - long off; /* patch must be opened */ sf = sflist->currsf; @@ -711,12 +725,16 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count) if (is_special_type(sf->type)) return -EINVAL; + if (count < (long)sizeof(sample_info)) { + return -EINVAL; + } if (copy_from_user(&sample_info, data, sizeof(sample_info))) return -EFAULT; + data += sizeof(sample_info); + count -= sizeof(sample_info); - off = sizeof(sample_info); - - if (sample_info.size != (count-off)/2) + // SoundFont uses S16LE samples. + if (sample_info.size * 2 != count) return -EINVAL; /* Check for dup */ @@ -727,6 +745,21 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count) return -EINVAL; } + if (sample_info.size > 0) { + if (sample_info.start < 0) + return -EINVAL; + + // Here we "rebase out" the start address, because the + // real start is the start of the provided sample data. + sample_info.end -= sample_info.start; + sample_info.loopstart -= sample_info.start; + sample_info.loopend -= sample_info.start; + sample_info.start = 0; + + if (validate_sample_info(&sample_info) < 0) + return -EINVAL; + } + /* Allocate a new sample structure */ sp = sf_sample_new(sflist, sf); if (!sp) @@ -735,7 +768,7 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count) sp->v = sample_info; sp->v.sf_id = sf->id; sp->v.dummy = 0; - sp->v.truesize = sp->v.size; + sp->v.truesize = 0; /* * If there is wave data then load it. @@ -744,7 +777,7 @@ load_data(struct snd_sf_list *sflist, const void __user *data, long count) int rc; rc = sflist->callback.sample_new (sflist->callback.private_data, sp, sflist->memhdr, - data + off, count - off); + data, count); if (rc < 0) { sf_sample_delete(sflist, sf, sp); return rc; @@ -941,8 +974,7 @@ int snd_sf_vol_table[128] = { /* load GUS patch */ static int -load_guspatch(struct snd_sf_list *sflist, const char __user *data, - long count, int client) +load_guspatch(struct snd_sf_list *sflist, const char __user *data, long count) { struct patch_info patch; struct snd_soundfont *sf; @@ -957,10 +989,12 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data, } if (copy_from_user(&patch, data, sizeof(patch))) return -EFAULT; - count -= sizeof(patch); data += sizeof(patch); + if ((patch.len << (patch.mode & WAVE_16_BITS ? 1 : 0)) != count) + return -EINVAL; + sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_GUS|SNDRV_SFNT_PAT_SHARED, NULL); if (sf == NULL) return -ENOMEM; @@ -975,6 +1009,11 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data, smp->v.loopend = patch.loop_end; smp->v.size = patch.len; + if (validate_sample_info(&smp->v) < 0) { + sf_sample_delete(sflist, sf, smp); + return -EINVAL; + } + /* set up mode flags */ smp->v.mode_flags = 0; if (!(patch.mode & WAVE_16_BITS)) @@ -1012,7 +1051,7 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data, /* * load wave data */ - if (sflist->callback.sample_new) { + if (smp->v.size > 0) { rc = sflist->callback.sample_new (sflist->callback.private_data, smp, sflist->memhdr, data, count); @@ -1122,11 +1161,11 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data, /* load GUS patch */ int snd_soundfont_load_guspatch(struct snd_sf_list *sflist, const char __user *data, - long count, int client) + long count) { int rc; lock_preset(sflist); - rc = load_guspatch(sflist, data, count, client); + rc = load_guspatch(sflist, data, count); unlock_preset(sflist); return rc; } @@ -1377,9 +1416,8 @@ snd_sf_clear(struct snd_sf_list *sflist) } for (sp = sf->samples; sp; sp = nextsp) { nextsp = sp->next; - if (sflist->callback.sample_free) - sflist->callback.sample_free(sflist->callback.private_data, - sp, sflist->memhdr); + sflist->callback.sample_free(sflist->callback.private_data, + sp, sflist->memhdr); kfree(sp); } kfree(sf); @@ -1481,9 +1519,8 @@ snd_soundfont_remove_unlocked(struct snd_sf_list *sflist) nextsp = sp->next; sf->samples = nextsp; sflist->mem_used -= sp->v.truesize; - if (sflist->callback.sample_free) - sflist->callback.sample_free(sflist->callback.private_data, - sp, sflist->memhdr); + sflist->callback.sample_free(sflist->callback.private_data, + sp, sflist->memhdr); kfree(sp); } } diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 065a4be0d771..212b5e6443d8 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3447,6 +3447,8 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x1235, 0x8213): /* Focusrite Scarlett 8i6 3rd Gen */ case USB_ID(0x1235, 0x8214): /* Focusrite Scarlett 18i8 3rd Gen */ case USB_ID(0x1235, 0x8215): /* Focusrite Scarlett 18i20 3rd Gen */ + case USB_ID(0x1235, 0x8216): /* Focusrite Vocaster One */ + case USB_ID(0x1235, 0x8217): /* Focusrite Vocaster Two */ case USB_ID(0x1235, 0x8218): /* Focusrite Scarlett Solo 4th Gen */ case USB_ID(0x1235, 0x8219): /* Focusrite Scarlett 2i2 4th Gen */ case USB_ID(0x1235, 0x821a): /* Focusrite Scarlett 4i4 4th Gen */ diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index bd114be537d7..b251d25f2a85 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 5d72dc8441cb..73abc38a5400 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3013,21 +3013,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), .type = QUIRK_AUDIO_FIXED_ENDPOINT, .data = &(const struct audioformat) { .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .fmt_bits = 24, .channels = 4, .iface = 2, .altsetting = 1, .altset_idx = 1, .attributes = 0x00, - .endpoint = 0x01, + .endpoint = USB_RECIP_INTERFACE | USB_DIR_OUT, .ep_attr = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - .nr_rates = 1, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .nr_rates = 4, .rate_table = (unsigned int[]) { - 48000 - } + 44100, 48000, 88200, 96000 + }, + .sync_ep = USB_RECIP_INTERFACE | USB_DIR_IN, + .sync_iface = 3, + .sync_altsetting = 1, + .sync_ep_idx = 1, + .implicit_fb = 1, } }, { @@ -3035,22 +3042,25 @@ YAMAHA_DEVICE(0x7010, "UB99"), .type = QUIRK_AUDIO_FIXED_ENDPOINT, .data = &(const struct audioformat) { .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .fmt_bits = 24, .channels = 4, .iface = 3, .altsetting = 1, .altset_idx = 1, - .endpoint = 0x81, .attributes = 0x00, + .endpoint = USB_RECIP_INTERFACE | USB_DIR_IN, .ep_attr = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, .maxpacksize = 0x009c, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - .nr_rates = 1, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .nr_rates = 4, .rate_table = (unsigned int[]) { - 48000 - } + 44100, 48000, 88200, 96000 + }, + .implicit_fb = 0, } }, { diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 09712e61c606..2f961f0e9378 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; } } |