diff options
| author | Thierry Reding <treding@nvidia.com> | 2026-04-29 12:30:30 +0200 |
|---|---|---|
| committer | Thierry Reding <treding@nvidia.com> | 2026-04-29 12:30:30 +0200 |
| commit | 1a69830701cb8de4cb43af5e1afba7afe6ef35ef (patch) | |
| tree | e244f8742a102905624a91f93ed794f4ba6b28bb | |
| parent | 2c2967676d24a27a1d86a728f4e2b8d7901f53dc (diff) | |
| parent | 6e934725ec3abb63247a0cb4cd455fff3c009ef5 (diff) | |
| download | linux-next-1a69830701cb8de4cb43af5e1afba7afe6ef35ef.tar.gz linux-next-1a69830701cb8de4cb43af5e1afba7afe6ef35ef.zip | |
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
| -rw-r--r-- | include/sound/snd_wavefront.h | 7 | ||||
| -rw-r--r-- | sound/isa/opti9xx/miro.c | 268 | ||||
| -rw-r--r-- | sound/isa/wavefront/wavefront.c | 61 | ||||
| -rw-r--r-- | sound/isa/wavefront/wavefront_midi.c | 47 | ||||
| -rw-r--r-- | sound/isa/wavefront/wavefront_synth.c | 96 | ||||
| -rw-r--r-- | sound/oss/dmasound/dmasound_core.c | 10 | ||||
| -rw-r--r-- | sound/pci/ctxfi/ctmixer.c | 27 | ||||
| -rw-r--r-- | sound/pci/ctxfi/ctmixer.h | 4 | ||||
| -rw-r--r-- | sound/usb/mixer.c | 17 | ||||
| -rw-r--r-- | sound/usb/mixer_scarlett.c | 4 | ||||
| -rw-r--r-- | sound/usb/mixer_scarlett2.c | 515 | ||||
| -rw-r--r-- | sound/usb/mixer_us16x08.c | 127 |
12 files changed, 1049 insertions, 134 deletions
diff --git a/include/sound/snd_wavefront.h b/include/sound/snd_wavefront.h index 30f508a56766..ac749bb2b836 100644 --- a/include/sound/snd_wavefront.h +++ b/include/sound/snd_wavefront.h @@ -12,6 +12,7 @@ struct _snd_wavefront_midi; struct _snd_wavefront_card; struct _snd_wavefront; +struct snd_wss; typedef struct _snd_wavefront_midi snd_wavefront_midi_t; typedef struct _snd_wavefront_card snd_wavefront_card_t; @@ -46,6 +47,8 @@ extern void snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *); extern void snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *); extern void snd_wavefront_midi_interrupt (snd_wavefront_card_t *); extern int snd_wavefront_midi_start (snd_wavefront_card_t *); +void snd_wavefront_midi_suspend(snd_wavefront_card_t *card); +void snd_wavefront_midi_resume(snd_wavefront_card_t *card); struct _snd_wavefront { unsigned long irq; /* "you were one, one of the few ..." */ @@ -93,6 +96,7 @@ struct _snd_wavefront { int samples_used; /* how many */ char interrupts_are_midi; /* h/w MPU interrupts enabled ? */ char rom_samples_rdonly; /* can we write on ROM samples */ + char midi_in_to_synth; /* route external MIDI to synth */ spinlock_t irq_lock; wait_queue_head_t interrupt_sleeper; snd_wavefront_midi_t midi; /* ICS2115 MIDI interface */ @@ -101,6 +105,7 @@ struct _snd_wavefront { struct _snd_wavefront_card { snd_wavefront_t wavefront; + struct snd_wss *chip; #ifdef CONFIG_PNP struct pnp_dev *wss; struct pnp_dev *ctrl; @@ -110,8 +115,10 @@ struct _snd_wavefront_card { }; extern void snd_wavefront_internal_interrupt (snd_wavefront_card_t *card); +void snd_wavefront_cache_firmware(snd_wavefront_t *dev); extern int snd_wavefront_start (snd_wavefront_t *dev); extern int snd_wavefront_detect (snd_wavefront_card_t *card); +int snd_wavefront_resume_synth(snd_wavefront_card_t *card); extern int snd_wavefront_cmd (snd_wavefront_t *, int, unsigned char *, unsigned char *); diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c index c320af3e9a05..e5870e5e07db 100644 --- a/sound/isa/opti9xx/miro.c +++ b/sound/isa/opti9xx/miro.c @@ -88,6 +88,25 @@ MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard."); #define OPTi9XX_MC_REG(n) n +enum { + MIRO_ACI_MASTER, + MIRO_ACI_MIC, + MIRO_ACI_LINE, + MIRO_ACI_CD, + MIRO_ACI_SYNTH, + MIRO_ACI_PCM, + MIRO_ACI_LINE1, + MIRO_ACI_LINE2, + MIRO_ACI_EQ1, + MIRO_ACI_EQ2, + MIRO_ACI_EQ3, + MIRO_ACI_EQ4, + MIRO_ACI_EQ5, + MIRO_ACI_EQ6, + MIRO_ACI_EQ7, + MIRO_ACI_COUNT, +}; + struct snd_miro { unsigned short hardware; unsigned char password; @@ -102,6 +121,7 @@ struct snd_miro { spinlock_t lock; struct snd_pcm *pcm; + struct snd_wss *codec; long wss_base; int irq; @@ -113,6 +133,12 @@ struct snd_miro { struct snd_card *card; struct snd_miro_aci *aci; +#ifdef CONFIG_PM + unsigned char aci_saved[MIRO_ACI_COUNT][2]; + unsigned char aci_saved_amp; + unsigned char aci_saved_preamp; + unsigned char aci_saved_solomode; +#endif }; static struct snd_miro_aci aci_device; @@ -664,6 +690,44 @@ static const unsigned char aci_init_values[][2] = { { ACI_SET_MASTER + 1, 0x20 }, }; +#ifdef CONFIG_PM +static const unsigned char snd_miro_saved_get_regs[MIRO_ACI_COUNT] = { + [MIRO_ACI_MASTER] = ACI_GET_MASTER, + [MIRO_ACI_MIC] = ACI_GET_MIC, + [MIRO_ACI_LINE] = ACI_GET_LINE, + [MIRO_ACI_CD] = ACI_GET_CD, + [MIRO_ACI_SYNTH] = ACI_GET_SYNTH, + [MIRO_ACI_PCM] = ACI_GET_PCM, + [MIRO_ACI_LINE1] = ACI_GET_LINE1, + [MIRO_ACI_LINE2] = ACI_GET_LINE2, + [MIRO_ACI_EQ1] = ACI_GET_EQ1, + [MIRO_ACI_EQ2] = ACI_GET_EQ2, + [MIRO_ACI_EQ3] = ACI_GET_EQ3, + [MIRO_ACI_EQ4] = ACI_GET_EQ4, + [MIRO_ACI_EQ5] = ACI_GET_EQ5, + [MIRO_ACI_EQ6] = ACI_GET_EQ6, + [MIRO_ACI_EQ7] = ACI_GET_EQ7, +}; + +static const unsigned char snd_miro_saved_set_regs[MIRO_ACI_COUNT] = { + [MIRO_ACI_MASTER] = ACI_SET_MASTER, + [MIRO_ACI_MIC] = ACI_SET_MIC, + [MIRO_ACI_LINE] = ACI_SET_LINE, + [MIRO_ACI_CD] = ACI_SET_CD, + [MIRO_ACI_SYNTH] = ACI_SET_SYNTH, + [MIRO_ACI_PCM] = ACI_SET_PCM, + [MIRO_ACI_LINE1] = ACI_SET_LINE1, + [MIRO_ACI_LINE2] = ACI_SET_LINE2, + [MIRO_ACI_EQ1] = ACI_SET_EQ1, + [MIRO_ACI_EQ2] = ACI_SET_EQ2, + [MIRO_ACI_EQ3] = ACI_SET_EQ3, + [MIRO_ACI_EQ4] = ACI_SET_EQ4, + [MIRO_ACI_EQ5] = ACI_SET_EQ5, + [MIRO_ACI_EQ6] = ACI_SET_EQ6, + [MIRO_ACI_EQ7] = ACI_SET_EQ7, +}; +#endif + static int snd_set_aci_init_values(struct snd_miro *miro) { int idx, error; @@ -702,11 +766,116 @@ static int snd_set_aci_init_values(struct snd_miro *miro) } aci->aci_amp = 0; aci->aci_preamp = 0; - aci->aci_solomode = 1; + aci->aci_solomode = 0; + + return 0; +} + +static int snd_miro_aci_force_known_state(struct snd_miro_aci *aci) +{ + int i, err; + + for (i = 0; i < 3; i++) { + err = snd_aci_cmd(aci, ACI_ERROR_OP, -1, -1); + if (err < 0) + return err; + } return 0; } +static int snd_miro_aci_initialize(struct snd_miro_aci *aci) +{ + int err; + + err = snd_aci_cmd(aci, ACI_INIT, -1, -1); + if (err < 0) + return err; + err = snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP); + if (err < 0) + return err; + + return snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP); +} + +#ifdef CONFIG_PM +static int snd_miro_save_aci_state(struct snd_miro *miro) +{ + struct snd_miro_aci *aci = miro->aci; + int i, limit, value; + + limit = aci->aci_product == 'C' ? MIRO_ACI_COUNT : MIRO_ACI_LINE2 + 1; + for (i = 0; i < limit; i++) { + value = aci_getvalue(aci, snd_miro_saved_get_regs[i]); + if (value < 0) + return value; + miro->aci_saved[i][1] = value; + + value = aci_getvalue(aci, snd_miro_saved_get_regs[i] + 1); + if (value < 0) + return value; + miro->aci_saved[i][0] = value; + } + + miro->aci_saved_amp = aci->aci_amp; + if (aci->aci_version <= 176) { + miro->aci_saved_preamp = aci->aci_preamp; + } else { + value = aci_getvalue(aci, ACI_GET_PREAMP); + if (value < 0) + return value; + miro->aci_saved_preamp = value; + } + + value = aci_getvalue(aci, ACI_S_GENERAL); + if (value < 0) + return value; + miro->aci_saved_solomode = !(value & 0x20); + + return 0; +} + +static int snd_miro_restore_aci_state(struct snd_miro *miro) +{ + struct snd_miro_aci *aci = miro->aci; + int i, limit, err, left_reg; + + err = snd_set_aci_init_values(miro); + if (err < 0) + return err; + + limit = aci->aci_product == 'C' ? MIRO_ACI_COUNT : MIRO_ACI_LINE2 + 1; + for (i = 0; i < limit; i++) { + left_reg = snd_miro_saved_set_regs[i] == ACI_SET_MASTER ? + snd_miro_saved_set_regs[i] + 1 : + snd_miro_saved_set_regs[i] + 8; + err = aci_setvalue(aci, left_reg, miro->aci_saved[i][0]); + if (err < 0) + return err; + err = aci_setvalue(aci, snd_miro_saved_set_regs[i], + miro->aci_saved[i][1]); + if (err < 0) + return err; + } + + err = aci_setvalue(aci, ACI_SET_POWERAMP, miro->aci_saved_amp); + if (err < 0) + return err; + err = aci_setvalue(aci, ACI_SET_PREAMP, miro->aci_saved_preamp); + if (err < 0) + return err; + err = aci_setvalue(aci, ACI_SET_SOLOMODE, miro->aci_saved_solomode); + if (err < 0) + return err; + + aci->aci_amp = miro->aci_saved_amp; + aci->aci_preamp = miro->aci_saved_preamp; + aci->aci_solomode = miro->aci_saved_solomode; + + return 0; +} +#endif + static int snd_miro_mixer(struct snd_card *card, struct snd_miro *miro) { @@ -1203,7 +1372,7 @@ static int snd_card_miro_aci_detect(struct snd_card *card, struct snd_miro *miro) { unsigned char regval; - int i; + int err; struct snd_miro_aci *aci = &aci_device; miro->aci = aci; @@ -1224,12 +1393,12 @@ static int snd_card_miro_aci_detect(struct snd_card *card, return -ENOMEM; } - /* force ACI into a known state */ - for (i = 0; i < 3; i++) - if (snd_aci_cmd(aci, ACI_ERROR_OP, -1, -1) < 0) { - dev_err(card->dev, "can't force aci into known state.\n"); - return -ENXIO; - } + /* force ACI into a known state */ + err = snd_miro_aci_force_known_state(aci); + if (err < 0) { + dev_err(card->dev, "can't force aci into known state.\n"); + return -ENXIO; + } aci->aci_vendor = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1); aci->aci_product = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1); @@ -1246,9 +1415,8 @@ static int snd_card_miro_aci_detect(struct snd_card *card, return -ENXIO; } - if (snd_aci_cmd(aci, ACI_INIT, -1, -1) < 0 || - snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 || - snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) { + err = snd_miro_aci_initialize(aci); + if (err < 0) { dev_err(card->dev, "can't initialize aci.\n"); return -ENXIO; } @@ -1299,6 +1467,7 @@ static int snd_miro_probe(struct snd_card *card) WSS_HW_DETECT, 0, &codec); if (error < 0) return error; + miro->codec = codec; error = snd_wss_pcm(codec, 0); if (error < 0) @@ -1408,6 +1577,7 @@ static int snd_miro_isa_probe(struct device *devptr, unsigned int n) return error; miro = card->private_data; + miro->card = card; error = snd_card_miro_detect(card, miro); if (error < 0) { @@ -1470,12 +1640,69 @@ static int snd_miro_isa_probe(struct device *devptr, unsigned int n) return 0; } +#ifdef CONFIG_PM +static int snd_miro_suspend(struct snd_card *card) +{ + struct snd_miro *miro = card->private_data; + int error; + + error = snd_miro_save_aci_state(miro); + if (error < 0) + return error; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + miro->codec->suspend(miro->codec); + return 0; +} + +static int snd_miro_resume(struct snd_card *card) +{ + struct snd_miro *miro = card->private_data; + int error; + + error = snd_miro_configure(miro); + if (error < 0) + return error; + error = snd_miro_aci_force_known_state(miro->aci); + if (error < 0) { + dev_err(card->dev, "can't force aci into known state\n"); + return error; + } + error = snd_miro_aci_initialize(miro->aci); + if (error < 0) { + dev_err(card->dev, "can't initialize aci\n"); + return error; + } + error = snd_miro_restore_aci_state(miro); + if (error < 0) + return error; + + miro->codec->resume(miro->codec); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} + +static int snd_miro_isa_suspend(struct device *dev, unsigned int n, + pm_message_t state) +{ + return snd_miro_suspend(dev_get_drvdata(dev)); +} + +static int snd_miro_isa_resume(struct device *dev, unsigned int n) +{ + return snd_miro_resume(dev_get_drvdata(dev)); +} +#endif + #define DEV_NAME "miro" static struct isa_driver snd_miro_driver = { .match = snd_miro_isa_match, .probe = snd_miro_isa_probe, - /* FIXME: suspend/resume */ +#ifdef CONFIG_PM + .suspend = snd_miro_isa_suspend, + .resume = snd_miro_isa_resume, +#endif .driver = { .name = DEV_NAME }, @@ -1591,12 +1818,29 @@ static void snd_miro_pnp_remove(struct pnp_card_link *pcard) snd_miro_pnp_is_probed = 0; } +#ifdef CONFIG_PM +static int snd_miro_pnp_suspend(struct pnp_card_link *pcard, + pm_message_t state) +{ + return snd_miro_suspend(pnp_get_card_drvdata(pcard)); +} + +static int snd_miro_pnp_resume(struct pnp_card_link *pcard) +{ + return snd_miro_resume(pnp_get_card_drvdata(pcard)); +} +#endif + static struct pnp_card_driver miro_pnpc_driver = { .flags = PNP_DRIVER_RES_DISABLE, .name = "miro", .id_table = snd_miro_pnpids, .probe = snd_miro_pnp_probe, .remove = snd_miro_pnp_remove, +#ifdef CONFIG_PM + .suspend = snd_miro_pnp_suspend, + .resume = snd_miro_pnp_resume, +#endif }; #endif diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c index 07c68568091d..f4a0c21c905e 100644 --- a/sound/isa/wavefront/wavefront.c +++ b/sound/isa/wavefront/wavefront.c @@ -353,6 +353,7 @@ snd_wavefront_probe (struct snd_card *card, int dev) dev_err(card->dev, "can't allocate WSS device\n"); return err; } + acard->chip = chip; err = snd_wss_pcm(chip, 0); if (err < 0) @@ -400,6 +401,7 @@ snd_wavefront_probe (struct snd_card *card, int dev) acard->wavefront.irq = ics2115_irq[dev]; card->sync_irq = acard->wavefront.irq; acard->wavefront.base = ics2115_port[dev]; + snd_wavefront_cache_firmware(&acard->wavefront); wavefront_synth = snd_wavefront_new_synth(card, hw_dev, acard); if (wavefront_synth == NULL) { @@ -553,12 +555,51 @@ static int snd_wavefront_isa_probe(struct device *pdev, return 0; } +#ifdef CONFIG_PM +static int snd_wavefront_suspend(struct snd_card *card) +{ + snd_wavefront_card_t *acard = card->private_data; + + snd_wavefront_midi_suspend(acard); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + acard->chip->suspend(acard->chip); + return 0; +} + +static int snd_wavefront_resume(struct snd_card *card) +{ + snd_wavefront_card_t *acard = card->private_data; + int err; + + acard->chip->resume(acard->chip); + err = snd_wavefront_resume_synth(acard); + if (err < 0) + return err; + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} + +static int snd_wavefront_isa_suspend(struct device *dev, unsigned int id, + pm_message_t state) +{ + return snd_wavefront_suspend(dev_get_drvdata(dev)); +} + +static int snd_wavefront_isa_resume(struct device *dev, unsigned int id) +{ + return snd_wavefront_resume(dev_get_drvdata(dev)); +} +#endif + #define DEV_NAME "wavefront" static struct isa_driver snd_wavefront_driver = { .match = snd_wavefront_isa_match, .probe = snd_wavefront_isa_probe, - /* FIXME: suspend, resume */ +#ifdef CONFIG_PM + .suspend = snd_wavefront_isa_suspend, + .resume = snd_wavefront_isa_resume, +#endif .driver = { .name = DEV_NAME }, @@ -600,12 +641,28 @@ static int snd_wavefront_pnp_detect(struct pnp_card_link *pcard, return 0; } +#ifdef CONFIG_PM +static int snd_wavefront_pnpc_suspend(struct pnp_card_link *pcard, + pm_message_t state) +{ + return snd_wavefront_suspend(pnp_get_card_drvdata(pcard)); +} + +static int snd_wavefront_pnpc_resume(struct pnp_card_link *pcard) +{ + return snd_wavefront_resume(pnp_get_card_drvdata(pcard)); +} +#endif + static struct pnp_card_driver wavefront_pnpc_driver = { .flags = PNP_DRIVER_RES_DISABLE, .name = "wavefront", .id_table = snd_wavefront_pnpids, .probe = snd_wavefront_pnp_detect, - /* FIXME: suspend,resume */ +#ifdef CONFIG_PM + .suspend = snd_wavefront_pnpc_suspend, + .resume = snd_wavefront_pnpc_resume, +#endif }; #endif /* CONFIG_PNP */ diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c index 69d87c4cafae..fb184d9ef284 100644 --- a/sound/isa/wavefront/wavefront_midi.c +++ b/sound/isa/wavefront/wavefront_midi.c @@ -455,6 +455,49 @@ snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card) card->wavefront.midi.isvirtual = 0; } +void +snd_wavefront_midi_suspend(snd_wavefront_card_t *card) + +{ + snd_wavefront_midi_t *midi = &card->wavefront.midi; + + if (!midi->istimer) + return; + + timer_delete_sync(&midi->timer); + + guard(spinlock_irqsave)(&midi->virtual); + midi->istimer = 0; +} + +void +snd_wavefront_midi_resume(snd_wavefront_card_t *card) + +{ + snd_wavefront_midi_t *midi = &card->wavefront.midi; + int istimer = 0; + bool pending_output = false; + + midi->timer_card = card; + + scoped_guard(spinlock_irqsave, &midi->virtual) { + if (midi->mode[internal_mpu] & MPU401_MODE_OUTPUT_TRIGGER) + istimer++; + if (midi->mode[external_mpu] & MPU401_MODE_OUTPUT_TRIGGER) + istimer++; + if (!istimer) + return; + + midi->istimer = istimer; + timer_setup(&midi->timer, snd_wavefront_midi_output_timer, 0); + mod_timer(&midi->timer, 1 + jiffies); + pending_output = true; + } + + if (pending_output) + snd_wavefront_midi_output_write(card); +} + int snd_wavefront_midi_start (snd_wavefront_card_t *card) @@ -466,6 +509,7 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card) dev = &card->wavefront; midi = &dev->midi; + midi->timer_card = card; /* The ICS2115 MPU-401 interface doesn't do anything until its set into UART mode. @@ -511,6 +555,8 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card) dev_warn(card->wavefront.card->dev, "can't enable MIDI-IN-2-synth routing.\n"); /* XXX error ? */ + } else { + dev->midi_in_to_synth = 1; } /* Turn on Virtual MIDI, but first *always* turn it off, @@ -553,4 +599,3 @@ const struct snd_rawmidi_ops snd_wavefront_midi_input = .close = snd_wavefront_midi_input_close, .trigger = snd_wavefront_midi_input_trigger, }; - diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c index 33b563707a58..2f57a6795d22 100644 --- a/sound/isa/wavefront/wavefront_synth.c +++ b/sound/isa/wavefront/wavefront_synth.c @@ -1626,6 +1626,14 @@ wavefront_synth_control (snd_wavefront_card_t *acard, "support for sample aliases still being considered.\n"); break; + case WFC_MISYNTH_OFF: + dev->midi_in_to_synth = 0; + break; + + case WFC_MISYNTH_ON: + dev->midi_in_to_synth = 1; + break; + case WFC_VMIDI_OFF: snd_wavefront_midi_disable_virtual (acard); break; @@ -1639,6 +1647,83 @@ wavefront_synth_control (snd_wavefront_card_t *acard, return 0; } +static int +wavefront_restore_midi_state(snd_wavefront_card_t *acard, char isvirtual, + char midi_in_to_synth) +{ + snd_wavefront_t *dev = &acard->wavefront; + unsigned char rbuf[4], wbuf[4]; + + if (dev->midi_in_to_synth != midi_in_to_synth) { + if (snd_wavefront_cmd(dev, midi_in_to_synth ? + WFC_MISYNTH_ON : WFC_MISYNTH_OFF, + rbuf, wbuf)) { + dev_err(dev->card->dev, + "cannot restore MIDI-IN routing after resume\n"); + return -EIO; + } + dev->midi_in_to_synth = midi_in_to_synth; + } + + if (dev->midi.isvirtual != isvirtual) { + if (snd_wavefront_cmd(dev, isvirtual ? + WFC_VMIDI_ON : WFC_VMIDI_OFF, + rbuf, wbuf)) { + dev_err(dev->card->dev, + "cannot restore virtual MIDI mode after resume\n"); + return -EIO; + } + if (isvirtual) + snd_wavefront_midi_enable_virtual(acard); + else + snd_wavefront_midi_disable_virtual(acard); + } + + return 0; +} + +int snd_wavefront_resume_synth(snd_wavefront_card_t *acard) +{ + snd_wavefront_t *dev = &acard->wavefront; + char was_virtual = dev->midi.isvirtual; + char midi_in_to_synth = dev->midi_in_to_synth; + char rom_samples_rdonly = dev->rom_samples_rdonly; + int err; + + err = snd_wavefront_detect(acard); + if (err < 0) + dev->israw = 1; + + if (dev->israw) { + dev->fx_initialized = 0; + err = snd_wavefront_start(dev); + if (err < 0) + return err; + } else { + dev->has_fx = (snd_wavefront_fx_detect(dev) == 0); + wavefront_get_sample_status(dev, 0); + wavefront_get_program_status(dev); + wavefront_get_patch_status(dev); + outb(0x80 | 0x40 | 0x20, dev->control_port); + } + + dev->rom_samples_rdonly = rom_samples_rdonly; + dev->midi.base = dev->base; + + err = snd_wavefront_midi_start(acard); + if (err < 0) + return err; + + err = wavefront_restore_midi_state(acard, was_virtual, + midi_in_to_synth); + if (err < 0) + return err; + + snd_wavefront_midi_resume(acard); + + return 0; +} + int snd_wavefront_synth_open (struct snd_hwdep *hw, struct file *file) @@ -2032,6 +2117,17 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path) return 1; } +void snd_wavefront_cache_firmware(snd_wavefront_t *dev) +{ + int err; + + err = firmware_request_cache(dev->card->dev, ospath); + if (err < 0) + dev_warn(dev->card->dev, + "unable to cache firmware %s for resume: %d\n", + ospath, err); +} + static int wavefront_do_reset (snd_wavefront_t *dev) diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c index a718b75bb6a0..e80f730d0803 100644 --- a/sound/oss/dmasound/dmasound_core.c +++ b/sound/oss/dmasound/dmasound_core.c @@ -574,11 +574,6 @@ static ssize_t sq_write(struct file *file, const char __user *src, size_t uLeft, uWritten = 0 ; } -/* FIXME: I think that this may be the wrong behaviour when we get strapped - for time and the cpu is close to being (or actually) behind in sending data. - - because we've lost the time that the N samples, already in the buffer, - would have given us to get here with the next lot from the user. -*/ /* The interrupt doesn't start to play the last, incomplete frame. * Thus we can append to it without disabling the interrupts! (Note * also that write_sq.rear isn't affected by the interrupt.) @@ -598,6 +593,11 @@ static ssize_t sq_write(struct file *file, const char __user *src, size_t uLeft, write_sq.syncing &= ~2 ; /* take out POST status */ spin_unlock_irqrestore(&dmasound.lock, flags); + /* Start any already-complete fragments before we spend + * more time extending the incomplete tail fragment. + */ + sq_play(); + if (write_sq.count > 0 && (bLeft = write_sq.block_size-write_sq.rear_size) > 0) { dest = write_sq.buffers[write_sq.rear]; diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index e3ee76bd8482..50ab69aca2fa 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -965,35 +965,20 @@ error1: static int ct_mixer_get_mem(struct ct_mixer **rmixer) { struct ct_mixer *mixer; - int err; + size_t alloc_size; *rmixer = NULL; /* Allocate mem for mixer obj */ - mixer = kzalloc_obj(*mixer); + alloc_size = struct_size(mixer, amixers, NUM_CT_AMIXERS * CHN_NUM); + alloc_size += sizeof(*mixer->sums) * NUM_CT_SUMS * CHN_NUM; + mixer = kzalloc(alloc_size, GFP_KERNEL); if (!mixer) return -ENOMEM; - mixer->amixers = kcalloc(NUM_CT_AMIXERS * CHN_NUM, sizeof(void *), - GFP_KERNEL); - if (!mixer->amixers) { - err = -ENOMEM; - goto error1; - } - mixer->sums = kcalloc(NUM_CT_SUMS * CHN_NUM, sizeof(void *), - GFP_KERNEL); - if (!mixer->sums) { - err = -ENOMEM; - goto error2; - } + mixer->sums = (struct sum **)(mixer->amixers + (NUM_CT_AMIXERS * CHN_NUM)); *rmixer = mixer; return 0; - -error2: - kfree(mixer->amixers); -error1: - kfree(mixer); - return err; } static int ct_mixer_topology_build(struct ct_mixer *mixer) @@ -1228,8 +1213,6 @@ int ct_mixer_destroy(struct ct_mixer *mixer) } /* Release mem assigned to mixer object */ - kfree(mixer->sums); - kfree(mixer->amixers); kfree(mixer); return 0; diff --git a/sound/pci/ctxfi/ctmixer.h b/sound/pci/ctxfi/ctmixer.h index e812f6c93b41..dd23d227aeb5 100644 --- a/sound/pci/ctxfi/ctmixer.h +++ b/sound/pci/ctxfi/ctmixer.h @@ -41,8 +41,7 @@ enum MIXER_PORT_T { struct ct_mixer { struct ct_atc *atc; - void **amixers; /* amixer resources for volume control */ - void **sums; /* sum resources for signal collection */ + struct sum **sums; /* sum resources for signal collection */ unsigned int switch_state; /* A bit-map to indicate state of switches */ int (*get_output_ports)(struct ct_mixer *mixer, enum MIXER_PORT_T type, @@ -55,6 +54,7 @@ struct ct_mixer { #ifdef CONFIG_PM_SLEEP int (*resume)(struct ct_mixer *mixer); #endif + struct amixer *amixers[]; /* amixer resources for volume control */ }; int ct_alsa_mix_create(struct ct_atc *atc, diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 5fba456eb4a9..4bd2e93f1dd4 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1536,7 +1536,10 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, return -EINVAL; val = get_abs_value(cval, val); if (oval != val) { - snd_usb_set_cur_mix_value(cval, c + 1, cnt, val); + err = snd_usb_set_cur_mix_value(cval, c + 1, + cnt, val); + if (err < 0) + return filter_error(cval, err); changed = 1; } cnt++; @@ -1551,7 +1554,9 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, return -EINVAL; val = get_abs_value(cval, val); if (val != oval) { - snd_usb_set_cur_mix_value(cval, 0, 0, val); + err = snd_usb_set_cur_mix_value(cval, 0, 0, val); + if (err < 0) + return filter_error(cval, err); changed = 1; } } @@ -2476,7 +2481,9 @@ static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol, return -EINVAL; val = get_abs_value(cval, val); if (val != oval) { - set_cur_ctl_value(cval, cval->control << 8, val); + err = set_cur_ctl_value(cval, cval->control << 8, val); + if (err < 0) + return filter_error(cval, err); return 1; } return 0; @@ -2842,7 +2849,9 @@ static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, return -EINVAL; val = get_abs_value(cval, val); if (val != oval) { - set_cur_ctl_value(cval, cval->control << 8, val); + err = set_cur_ctl_value(cval, cval->control << 8, val); + if (err < 0) + return filter_error(cval, err); return 1; } return 0; diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index 1bb01e827654..673eb8d8724d 100644 --- a/sound/usb/mixer_scarlett.c +++ b/sound/usb/mixer_scarlett.c @@ -680,7 +680,9 @@ static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl, val = ucontrol->value.integer.value[0]; val = val + opt->start; if (val != oval) { - snd_usb_set_cur_mix_value(elem, 0, 0, val); + err = snd_usb_set_cur_mix_value(elem, 0, 0, val); + if (err < 0) + return err; return 1; } return 0; diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 8eaa96222759..6f66149baea6 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -192,6 +192,9 @@ /* maximum Bluetooth volume value */ #define SCARLETT2_MAX_BLUETOOTH_VOLUME 30 +/* maximum front-panel sleep time in seconds (24 hours) */ +#define SCARLETT2_MAX_FP_SLEEP_TIME 86400 + /* mixer range from -80dB to +12dB in 0.5dB steps */ #define SCARLETT2_MIXER_MIN_DB -80 #define SCARLETT2_MIXER_BIAS (-SCARLETT2_MIXER_MIN_DB * 2) @@ -398,6 +401,7 @@ static const char *const scarlett2_autogain_status_gen4[] = { "FailMaxGainLimit", "FailClipped", "Cancelled", + "Root", "Invalid", NULL }; @@ -567,6 +571,8 @@ enum { SCARLETT2_CONFIG_BLUETOOTH_VOLUME, SCARLETT2_CONFIG_SPDIF_MODE, SCARLETT2_CONFIG_SP_HP_MUTE, + SCARLETT2_CONFIG_FP_BRIGHTNESS, + SCARLETT2_CONFIG_FP_SLEEP_TIME, SCARLETT2_CONFIG_COUNT }; @@ -612,6 +618,20 @@ struct scarlett2_config_set { const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT]; }; +/* Map firmware versions to config sets per-device. + * + * Each device lists one or more entries, sorted in ascending order of + * from_firmware_version. At probe time the running firmware version + * is looked up against this list and the last entry whose + * from_firmware_version is <= the running version is selected. + * + * The list is terminated by a sentinel entry with config_set == NULL. + */ +struct scarlett2_config_set_entry { + u16 from_firmware_version; + const struct scarlett2_config_set *config_set; +}; + /* Input gain TLV dB ranges */ static const DECLARE_TLV_DB_MINMAX( @@ -870,6 +890,42 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = { } }; +/* Solo Gen 4, firmware version 2417 and above */ +static const struct scarlett2_config_set scarlett2_config_set_gen4_solo_2417 = { + .notifications = scarlett4_solo_notifications, + .param_buf_addr = 0xd8, + .items = { + [SCARLETT2_CONFIG_MSD_SWITCH] = { + .offset = 0x47, .size = 8, .activate = 4 }, + + [SCARLETT2_CONFIG_DIRECT_MONITOR] = { + .offset = 0x108, .size = 8, .activate = 12, .pbuf = 1 }, + + [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { + .offset = 0x46, .size = 8, .activate = 9, .pbuf = 1, + .mute = 1 }, + + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { + .offset = 0x3d, .size = 8, .activate = 10, .pbuf = 1, + .mute = 1 }, + + [SCARLETT2_CONFIG_AIR_SWITCH] = { + .offset = 0x3e, .size = 8, .activate = 11, .pbuf = 1 }, + + [SCARLETT2_CONFIG_PCM_INPUT_SWITCH] = { + .offset = 0x206, .size = 8, .activate = 25, .pbuf = 1 }, + + [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = { + .offset = 0x232, .size = 16, .activate = 26 }, + + [SCARLETT2_CONFIG_FP_BRIGHTNESS] = { + .offset = 0x243, .size = 8, .activate = 27, .pbuf = 1 }, + + [SCARLETT2_CONFIG_FP_SLEEP_TIME] = { + .offset = 0x248, .size = 32, .activate = 29 } + } +}; + /* 2i2 Gen 4 */ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = { .notifications = scarlett4_2i2_notifications, @@ -923,6 +979,70 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = { } }; +/* 2i2 Gen 4, firmware version 2417 and above + * + * Firmware 2417 shifted DIRECT_MONITOR_GAIN by 4 bytes and added + * front-panel brightness and sleep controls; all other offsets are + * unchanged from scarlett2_config_set_gen4_2i2. + */ +static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2_2417 = { + .notifications = scarlett4_2i2_notifications, + .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 }, + + [SCARLETT2_CONFIG_DIRECT_MONITOR] = { + .offset = 0x14a, .size = 8, .activate = 16, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = { + .offset = 0x135, .size = 8, .activate = 10, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = { + .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, .size = 8, .activate = 11, .pbuf = 1, + .mute = 1 }, + + [SCARLETT2_CONFIG_INPUT_GAIN] = { + .offset = 0x4b, .size = 8, .activate = 12, .pbuf = 1 }, + + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { + .offset = 0x3c, .size = 8, .activate = 13, .pbuf = 1, + .mute = 1 }, + + [SCARLETT2_CONFIG_SAFE_SWITCH] = { + .offset = 0x147, .size = 8, .activate = 14, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AIR_SWITCH] = { + .offset = 0x3e, .size = 8, .activate = 15, .pbuf = 1 }, + + [SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = { + .offset = 0x14b, .size = 8, .activate = 17, .pbuf = 1 }, + + [SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = { + .offset = 0x14e, .size = 8, .activate = 18, .pbuf = 1 }, + + [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = { + .offset = 0x2a4, .size = 16, .activate = 36 }, + + [SCARLETT2_CONFIG_FP_BRIGHTNESS] = { + .offset = 0x2c7, .size = 8, .activate = 37, .pbuf = 1 }, + + [SCARLETT2_CONFIG_FP_SLEEP_TIME] = { + .offset = 0x2cc, .size = 32, .activate = 39 } + } +}; + /* 4i4 Gen 4 */ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = { .notifications = scarlett4_4i4_notifications, @@ -982,6 +1102,71 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = { } }; +/* 4i4 Gen 4, firmware version 2417 and above */ +static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4_2417 = { + .notifications = scarlett4_4i4_notifications, + .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, .size = 8, .activate = 10, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = { + .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, .size = 8, .activate = 11, .pbuf = 1, + .mute = 1 }, + + [SCARLETT2_CONFIG_INPUT_GAIN] = { + .offset = 0x5e, .size = 8, .activate = 12, .pbuf = 1 }, + + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { + .offset = 0x4e, .size = 8, .activate = 13, .pbuf = 1, + .mute = 1 }, + + [SCARLETT2_CONFIG_SAFE_SWITCH] = { + .offset = 0x150, .size = 8, .activate = 14, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AIR_SWITCH] = { + .offset = 0x50, .size = 8, .activate = 15, .pbuf = 1 }, + + [SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = { + .offset = 0x153, .size = 8, .activate = 16, .pbuf = 1 }, + + [SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = { + .offset = 0x156, .size = 8, .activate = 17, .pbuf = 1 }, + + [SCARLETT2_CONFIG_MASTER_VOLUME] = { + .offset = 0x32, .size = 16 }, + + [SCARLETT2_CONFIG_HEADPHONE_VOLUME] = { + .offset = 0x3a, .size = 16 }, + + [SCARLETT2_CONFIG_POWER_EXT] = { + .offset = 0x168, .size = 8 }, + + [SCARLETT2_CONFIG_POWER_LOW] = { + .offset = 0x16d, .size = 8 }, + + [SCARLETT2_CONFIG_FP_BRIGHTNESS] = { + .offset = 0x3a9, .size = 8, .activate = 36, .pbuf = 1 }, + + [SCARLETT2_CONFIG_FP_SLEEP_TIME] = { + .offset = 0x3ac, .size = 32, .activate = 38 } + } +}; + /* Clarett USB and Clarett+ devices: 2Pre, 4Pre, 8Pre */ static const struct scarlett2_config_set scarlett2_config_set_clarett = { .notifications = scarlett2_notifications, @@ -1100,11 +1285,8 @@ struct scarlett2_meter_entry { }; struct scarlett2_device_info { - /* which set of configuration parameters the device uses */ - const struct scarlett2_config_set *config_set; - - /* minimum firmware version required */ - u16 min_firmware_version; + /* which sets of configuration parameters the device uses */ + const struct scarlett2_config_set_entry *config_sets; /* has a downloadable device map */ u8 has_devmap; @@ -1335,6 +1517,8 @@ struct scarlett2_data { struct snd_kcontrol *talkback_ctl; struct snd_kcontrol *power_status_ctl; struct snd_kcontrol *bluetooth_volume_ctl; + u8 fp_brightness; + u32 fp_sleep_time; u8 mux[SCARLETT2_MUX_MAX]; u8 mix[SCARLETT2_MIX_MAX]; u8 monitor_mix[SCARLETT2_MONITOR_MIX_MAX]; @@ -1343,7 +1527,10 @@ struct scarlett2_data { /*** Model-specific data ***/ static const struct scarlett2_device_info s6i6_gen2_info = { - .config_set = &scarlett2_config_set_gen2a, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen2a }, + { } + }, .level_input_count = 2, .pad_input_count = 2, @@ -1393,7 +1580,10 @@ static const struct scarlett2_device_info s6i6_gen2_info = { }; static const struct scarlett2_device_info s18i8_gen2_info = { - .config_set = &scarlett2_config_set_gen2a, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen2a }, + { } + }, .level_input_count = 2, .pad_input_count = 4, @@ -1446,7 +1636,10 @@ static const struct scarlett2_device_info s18i8_gen2_info = { }; static const struct scarlett2_device_info s18i20_gen2_info = { - .config_set = &scarlett2_config_set_gen2b, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen2b }, + { } + }, .line_out_descrs = { "Monitor L", @@ -1503,7 +1696,10 @@ static const struct scarlett2_device_info s18i20_gen2_info = { }; static const struct scarlett2_device_info solo_gen3_info = { - .config_set = &scarlett2_config_set_gen3a, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3a }, + { } + }, .level_input_count = 1, .level_input_first = 1, .air_input_count = 1, @@ -1513,7 +1709,10 @@ static const struct scarlett2_device_info solo_gen3_info = { }; static const struct scarlett2_device_info s2i2_gen3_info = { - .config_set = &scarlett2_config_set_gen3a, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3a }, + { } + }, .level_input_count = 2, .air_input_count = 2, .phantom_count = 1, @@ -1522,7 +1721,10 @@ static const struct scarlett2_device_info s2i2_gen3_info = { }; static const struct scarlett2_device_info s4i4_gen3_info = { - .config_set = &scarlett2_config_set_gen3b, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3b }, + { } + }, .level_input_count = 2, .pad_input_count = 2, .air_input_count = 2, @@ -1571,7 +1773,10 @@ static const struct scarlett2_device_info s4i4_gen3_info = { }; static const struct scarlett2_device_info s8i6_gen3_info = { - .config_set = &scarlett2_config_set_gen3b, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3b }, + { } + }, .level_input_count = 2, .pad_input_count = 2, .air_input_count = 2, @@ -1637,7 +1842,10 @@ static const char * const scarlett2_spdif_s18i8_gen3_texts[] = { }; static const struct scarlett2_device_info s18i8_gen3_info = { - .config_set = &scarlett2_config_set_gen3c, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3c }, + { } + }, .has_speaker_switching = 1, .level_input_count = 2, .pad_input_count = 4, @@ -1729,7 +1937,10 @@ static const char * const scarlett2_spdif_s18i20_gen3_texts[] = { }; static const struct scarlett2_device_info s18i20_gen3_info = { - .config_set = &scarlett2_config_set_gen3c, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3c }, + { } + }, .has_speaker_switching = 1, .has_talkback = 1, .level_input_count = 2, @@ -1803,8 +2014,10 @@ 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, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 1769, &scarlett2_config_set_vocaster }, + { } + }, .has_devmap = 1, .phantom_count = 1, @@ -1847,8 +2060,10 @@ static const struct scarlett2_device_info vocaster_one_info = { }; static const struct scarlett2_device_info vocaster_two_info = { - .config_set = &scarlett2_config_set_vocaster, - .min_firmware_version = 1769, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 1769, &scarlett2_config_set_vocaster }, + { } + }, .has_devmap = 1, .phantom_count = 2, @@ -1892,8 +2107,11 @@ static const struct scarlett2_device_info vocaster_two_info = { }; static const struct scarlett2_device_info solo_gen4_info = { - .config_set = &scarlett2_config_set_gen4_solo, - .min_firmware_version = 2115, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 2115, &scarlett2_config_set_gen4_solo }, + { 2417, &scarlett2_config_set_gen4_solo_2417 }, + { } + }, .has_devmap = 1, .level_input_count = 1, @@ -1947,8 +2165,11 @@ static const struct scarlett2_device_info solo_gen4_info = { }; static const struct scarlett2_device_info s2i2_gen4_info = { - .config_set = &scarlett2_config_set_gen4_2i2, - .min_firmware_version = 2115, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 2115, &scarlett2_config_set_gen4_2i2 }, + { 2417, &scarlett2_config_set_gen4_2i2_2417 }, + { } + }, .has_devmap = 1, .level_input_count = 2, @@ -2002,8 +2223,11 @@ static const struct scarlett2_device_info s2i2_gen4_info = { }; static const struct scarlett2_device_info s4i4_gen4_info = { - .config_set = &scarlett2_config_set_gen4_4i4, - .min_firmware_version = 2089, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 2089, &scarlett2_config_set_gen4_4i4 }, + { 2417, &scarlett2_config_set_gen4_4i4_2417 }, + { } + }, .has_devmap = 1, .level_input_count = 2, @@ -2051,7 +2275,10 @@ static const struct scarlett2_device_info s4i4_gen4_info = { }; static const struct scarlett2_device_info clarett_2pre_info = { - .config_set = &scarlett2_config_set_clarett, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_clarett }, + { } + }, .level_input_count = 2, .air_input_count = 2, @@ -2107,7 +2334,10 @@ static const char * const scarlett2_spdif_clarett_texts[] = { }; static const struct scarlett2_device_info clarett_4pre_info = { - .config_set = &scarlett2_config_set_clarett, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_clarett }, + { } + }, .level_input_count = 2, .air_input_count = 4, @@ -2163,7 +2393,10 @@ static const struct scarlett2_device_info clarett_4pre_info = { }; static const struct scarlett2_device_info clarett_8pre_info = { - .config_set = &scarlett2_config_set_clarett, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_clarett }, + { } + }, .level_input_count = 2, .air_input_count = 8, @@ -3276,7 +3509,8 @@ static int scarlett2_min_firmware_version_ctl_get( struct usb_mixer_elem_info *elem = kctl->private_data; struct scarlett2_data *private = elem->head.mixer->private_data; - ucontrol->value.integer.value[0] = private->info->min_firmware_version; + ucontrol->value.integer.value[0] = + private->info->config_sets[0].from_firmware_version; return 0; } @@ -7626,6 +7860,172 @@ static int scarlett2_add_bluetooth_volume_ctl( &private->bluetooth_volume_ctl); } +/*** Front Panel Brightness/Sleep Controls ***/ + +static int scarlett2_update_fp(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + int err; + + if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_FP_BRIGHTNESS)) { + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_FP_BRIGHTNESS, + 1, &private->fp_brightness); + if (err < 0) + return err; + } + + if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_FP_SLEEP_TIME)) { + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_FP_SLEEP_TIME, + 1, &private->fp_sleep_time); + if (err < 0) + return err; + } + + return 0; +} + +static const char * const scarlett2_fp_brightness_texts[] = { + "High", "Medium", "Low" +}; + +static int scarlett2_fp_brightness_ctl_info( + struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + return snd_ctl_enum_info(uinfo, 1, + ARRAY_SIZE(scarlett2_fp_brightness_texts), + scarlett2_fp_brightness_texts); +} + +static int scarlett2_fp_brightness_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.enumerated.item[0] = private->fp_brightness; + return 0; +} + +static int scarlett2_fp_brightness_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; + + guard(mutex)(&private->data_mutex); + + if (private->hwdep_in_use) + return -EBUSY; + + oval = private->fp_brightness; + val = min(ucontrol->value.enumerated.item[0], + ARRAY_SIZE(scarlett2_fp_brightness_texts) - 1); + + if (oval == val) + return 0; + + private->fp_brightness = val; + + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_FP_BRIGHTNESS, 0, val); + + return err < 0 ? err : 1; +} + +static const struct snd_kcontrol_new scarlett2_fp_brightness_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "", + .info = scarlett2_fp_brightness_ctl_info, + .get = scarlett2_fp_brightness_ctl_get, + .put = scarlett2_fp_brightness_ctl_put, +}; + +static int scarlett2_fp_sleep_time_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_FP_SLEEP_TIME; + uinfo->value.integer.step = 1; + return 0; +} + +static int scarlett2_fp_sleep_time_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->fp_sleep_time; + return 0; +} + +static int scarlett2_fp_sleep_time_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; + u32 oval, val; + int err; + + guard(mutex)(&private->data_mutex); + + if (private->hwdep_in_use) + return -EBUSY; + + oval = private->fp_sleep_time; + val = clamp(ucontrol->value.integer.value[0], + 0L, (long)SCARLETT2_MAX_FP_SLEEP_TIME); + + if (oval == val) + return 0; + + private->fp_sleep_time = val; + + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_FP_SLEEP_TIME, 0, val); + + return err < 0 ? err : 1; +} + +static const struct snd_kcontrol_new scarlett2_fp_sleep_time_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "", + .info = scarlett2_fp_sleep_time_ctl_info, + .get = scarlett2_fp_sleep_time_ctl_get, + .put = scarlett2_fp_sleep_time_ctl_put, +}; + +static int scarlett2_add_fp_ctls(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + int err; + + if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_FP_BRIGHTNESS)) { + err = scarlett2_add_new_ctl( + mixer, &scarlett2_fp_brightness_ctl, 0, 1, + "Front Panel Brightness", NULL); + if (err < 0) + return err; + } + + if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_FP_SLEEP_TIME)) { + err = scarlett2_add_new_ctl( + mixer, &scarlett2_fp_sleep_time_ctl, 0, 1, + "Front Panel Sleep Time", NULL); + if (err < 0) + return err; + } + + return 0; +} + /*** S/PDIF Mode Controls ***/ static int scarlett2_update_spdif_mode(struct usb_mixer_interface *mixer) @@ -8184,10 +8584,32 @@ static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) /*** Initialisation ***/ +/* Select the config_set matching the running firmware version. + * + * The device info's config_sets array is ordered by ascending + * from_firmware_version; pick the last entry whose version is <= the + * running firmware version. If the running firmware is older than the + * first entry's from_firmware_version (i.e. older than the driver's + * minimum supported version for this device), the first entry's + * config_set is selected anyway so firmware updates can still be done + * (requires only the ACK handler), but the usual mixer controls + * aren't created. + */ +static void scarlett2_resolve_config_set(struct scarlett2_data *private) +{ + const struct scarlett2_config_set_entry *entry = + private->info->config_sets; + + private->config_set = entry->config_set; + for (entry++; entry->config_set; entry++) + if (entry->from_firmware_version <= private->firmware_version) + private->config_set = entry->config_set; +} + 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 struct scarlett2_config_set *config_set = private->config_set; const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; int port_type, srcs = 0, dsts = 0, i; @@ -8284,9 +8706,14 @@ static int scarlett2_init_private(struct usb_mixer_interface *mixer, mixer->private_suspend = scarlett2_private_suspend; private->info = entry->info; - private->config_set = entry->info->config_set; + + /* Set config_set to the first entry's config_set so the + * notify handler has a valid pointer while USB init runs; it + * is re-resolved once the firmware version has been read. + */ + private->config_set = entry->info->config_sets[0].config_set; + private->series_name = entry->series_name; - scarlett2_count_io(private); private->scarlett2_seq = 0; private->mixer = mixer; @@ -8476,6 +8903,7 @@ 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; + u16 min_firmware_version = info->config_sets[0].from_firmware_version; int err, i; if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_MSD_SWITCH)) { @@ -8486,13 +8914,13 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) return err; } - if (private->firmware_version < info->min_firmware_version) { + if (private->firmware_version < 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); + min_firmware_version); return 0; } @@ -8646,6 +9074,10 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) if (err < 0) return err; + err = scarlett2_update_fp(mixer); + if (err < 0) + return err; + err = scarlett2_update_spdif_mode(mixer); if (err < 0) return err; @@ -8676,6 +9108,7 @@ static int snd_scarlett2_controls_create( const struct scarlett2_device_entry *entry) { struct scarlett2_data *private; + u16 min_firmware_version; int err; /* Initialise private data */ @@ -8684,12 +9117,21 @@ static int snd_scarlett2_controls_create( return err; private = mixer->private_data; + min_firmware_version = + private->info->config_sets[0].from_firmware_version; /* Send proprietary USB initialisation sequence */ err = scarlett2_usb_init(mixer); if (err < 0) return err; + /* Now that the firmware version is known, pick the matching + * config_set + */ + scarlett2_resolve_config_set(private); + + scarlett2_count_io(private); + /* Get the upgrade & settings flash segment numbers */ err = scarlett2_get_flash_segment_nums(mixer); if (err < 0) @@ -8719,7 +9161,7 @@ static int snd_scarlett2_controls_create( * old, don't create any other controls */ if (private->msd_switch || - private->firmware_version < private->info->min_firmware_version) + private->firmware_version < min_firmware_version) return 0; /* Create the analogue output controls */ @@ -8782,6 +9224,11 @@ static int snd_scarlett2_controls_create( if (err < 0) return err; + /* Create the front-panel brightness/sleep controls */ + err = scarlett2_add_fp_ctls(mixer); + if (err < 0) + return err; + /* Create the S/PDIF mode control */ err = scarlett2_add_spdif_mode_ctl(mixer); if (err < 0) diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c index 8a02964e5d7b..ebff185cbd2c 100644 --- a/sound/usb/mixer_us16x08.c +++ b/sound/usb/mixer_us16x08.c @@ -224,14 +224,14 @@ static int snd_us16x08_route_put(struct snd_kcontrol *kcontrol, err = snd_us16x08_send_urb(chip, buf, sizeof(route_msg)); - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set routing, err:%d\n", err); + return err; } - return err > 0 ? 1 : 0; + elem->cached |= 1 << index; + elem->cache_val[index] = val; + return 1; } static int snd_us16x08_master_info(struct snd_kcontrol *kcontrol, @@ -283,14 +283,14 @@ static int snd_us16x08_master_put(struct snd_kcontrol *kcontrol, buf[5] = index + 1; err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_out)); - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set master, err:%d\n", err); + return err; } - return err > 0 ? 1 : 0; + elem->cached |= 1 << index; + elem->cache_val[index] = val; + return 1; } static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol, @@ -324,14 +324,14 @@ static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol, break; } - if (err > 0) { - elem->cached |= 1; - elem->cache_val[0] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set bus parameter, err:%d\n", err); + return err; } - return err > 0 ? 1 : 0; + elem->cached |= 1; + elem->cache_val[0] = val; + return 1; } static int snd_us16x08_bus_get(struct snd_kcontrol *kcontrol, @@ -392,14 +392,14 @@ static int snd_us16x08_channel_put(struct snd_kcontrol *kcontrol, err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_in)); - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set channel, err:%d\n", err); + return err; } - return err > 0 ? 1 : 0; + elem->cached |= 1 << index; + elem->cache_val[index] = val; + return 1; } static int snd_us16x08_mix_info(struct snd_kcontrol *kcontrol, @@ -435,6 +435,7 @@ static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol, int index = ucontrol->id.index; char buf[sizeof(comp_msg)]; int val_idx, val; + int threshold, ratio, attack, release, gain, switch_on; int err; val = ucontrol->value.integer.value[0]; @@ -447,36 +448,61 @@ static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol, /* new control value incl. bias*/ val_idx = elem->head.id - SND_US16X08_ID_COMP_BASE; - store->val[val_idx][index] = ucontrol->value.integer.value[0]; + threshold = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)] + [index]; + ratio = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][index]; + attack = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][index]; + release = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)] + [index]; + gain = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][index]; + switch_on = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)] + [index]; + + switch (val_idx) { + case COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD): + threshold = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO): + ratio = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK): + attack = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE): + release = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN): + gain = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH): + switch_on = val; + break; + } /* prepare compressor URB message from template */ memcpy(buf, comp_msg, sizeof(comp_msg)); /* place comp values in message buffer watch bias! */ - buf[8] = store->val[ - COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)][index] - - SND_US16X08_COMP_THRESHOLD_BIAS; - buf[11] = ratio_map[store->val[ - COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][index]]; - buf[14] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][index] - + SND_US16X08_COMP_ATTACK_BIAS; - buf[17] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)][index] - + SND_US16X08_COMP_RELEASE_BIAS; - buf[20] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][index]; - buf[26] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)][index]; + buf[8] = threshold - SND_US16X08_COMP_THRESHOLD_BIAS; + buf[11] = ratio_map[ratio]; + buf[14] = attack + SND_US16X08_COMP_ATTACK_BIAS; + buf[17] = release + SND_US16X08_COMP_RELEASE_BIAS; + buf[20] = gain; + buf[26] = switch_on; /* place channel selector in message buffer */ buf[5] = index + 1; err = snd_us16x08_send_urb(chip, buf, sizeof(comp_msg)); - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set compressor, err:%d\n", err); + return err; } + store->val[val_idx][index] = val; + elem->cached |= 1 << index; + elem->cache_val[index] = val; return 1; } @@ -529,13 +555,13 @@ static int snd_us16x08_eqswitch_put(struct snd_kcontrol *kcontrol, msleep(15); } - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set eq switch, err:%d\n", err); + return err; } + elem->cached |= 1 << index; + elem->cache_val[index] = val; return 1; } @@ -578,11 +604,10 @@ static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol, /* copy URB buffer from EQ template */ memcpy(buf, eqs_msq, sizeof(eqs_msq)); - store->val[b_idx][p_idx][index] = val; - buf[20] = store->val[b_idx][3][index]; - buf[17] = store->val[b_idx][2][index]; - buf[14] = store->val[b_idx][1][index]; - buf[11] = store->val[b_idx][0][index]; + buf[20] = p_idx == 3 ? val : store->val[b_idx][3][index]; + buf[17] = p_idx == 2 ? val : store->val[b_idx][2][index]; + buf[14] = p_idx == 1 ? val : store->val[b_idx][1][index]; + buf[11] = p_idx == 0 ? val : store->val[b_idx][0][index]; /* place channel index in URB buffer */ buf[5] = index + 1; @@ -592,14 +617,15 @@ static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol, err = snd_us16x08_send_urb(chip, buf, sizeof(eqs_msq)); - if (err > 0) { - /* store new value in EQ band cache */ - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set eq param, err:%d\n", err); + return err; } + store->val[b_idx][p_idx][index] = val; + /* store new value in EQ band cache */ + elem->cached |= 1 << index; + elem->cache_val[index] = val; return 1; } @@ -1418,4 +1444,3 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer) return 0; } - |
