linux/sound/pci/emu10k1/emumpu401.c
Oswald Buddenhagen 67192cc0f0 ALSA: emu10k1: remove superfluous IRQ enable state saving
The mixer, PCM prepare, MIDI, synth driver, and procfs callbacks are all
always invoked with IRQs enabled, so there is no point in saving the
state.

snd_emu1010_load_firmware_entry() is called from emu1010_firmware_work()
and snd_emu10k1_emu1010_init(); the latter from snd_emu10k1_create() and
snd_emu10k1_resume(), all of which have IRQs enabled.

The voice and memory functions are called from mixed contexts, so they
keep the state saving.

The low-level functions all keep the state saving, because it's not
feasible to keep track of what is called where.

Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Link: https://lore.kernel.org/r/20230712145750.125086-2-oswald.buddenhagen@gmx.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-07-13 10:30:06 +02:00

383 lines
10 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of EMU10K1 MPU-401 in UART mode
*/
#include <linux/time.h>
#include <linux/init.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
#define EMU10K1_MIDI_MODE_INPUT (1<<0)
#define EMU10K1_MIDI_MODE_OUTPUT (1<<1)
static inline unsigned char mpu401_read(struct snd_emu10k1 *emu,
struct snd_emu10k1_midi *mpu, int idx)
{
if (emu->audigy)
return (unsigned char)snd_emu10k1_ptr_read(emu, mpu->port + idx, 0);
else
return inb(emu->port + mpu->port + idx);
}
static inline void mpu401_write(struct snd_emu10k1 *emu,
struct snd_emu10k1_midi *mpu, int data, int idx)
{
if (emu->audigy)
snd_emu10k1_ptr_write(emu, mpu->port + idx, 0, data);
else
outb(data, emu->port + mpu->port + idx);
}
#define mpu401_write_data(emu, mpu, data) mpu401_write(emu, mpu, data, 0)
#define mpu401_write_cmd(emu, mpu, data) mpu401_write(emu, mpu, data, 1)
#define mpu401_read_data(emu, mpu) mpu401_read(emu, mpu, 0)
#define mpu401_read_stat(emu, mpu) mpu401_read(emu, mpu, 1)
#define mpu401_input_avail(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x80))
#define mpu401_output_ready(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x40))
#define MPU401_RESET 0xff
#define MPU401_ENTER_UART 0x3f
#define MPU401_ACK 0xfe
static void mpu401_clear_rx(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *mpu)
{
int timeout = 100000;
for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--)
mpu401_read_data(emu, mpu);
#ifdef CONFIG_SND_DEBUG
if (timeout <= 0)
dev_err(emu->card->dev,
"cmd: clear rx timeout (status = 0x%x)\n",
mpu401_read_stat(emu, mpu));
#endif
}
/*
*/
static void do_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, unsigned int status)
{
unsigned char byte;
if (midi->rmidi == NULL) {
snd_emu10k1_intr_disable(emu, midi->tx_enable | midi->rx_enable);
return;
}
spin_lock(&midi->input_lock);
if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
mpu401_clear_rx(emu, midi);
} else {
byte = mpu401_read_data(emu, midi);
if (midi->substream_input)
snd_rawmidi_receive(midi->substream_input, &byte, 1);
}
}
spin_unlock(&midi->input_lock);
spin_lock(&midi->output_lock);
if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
if (midi->substream_output &&
snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
mpu401_write_data(emu, midi, byte);
} else {
snd_emu10k1_intr_disable(emu, midi->tx_enable);
}
}
spin_unlock(&midi->output_lock);
}
static void snd_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, unsigned int status)
{
do_emu10k1_midi_interrupt(emu, &emu->midi, status);
}
static void snd_emu10k1_midi_interrupt2(struct snd_emu10k1 *emu, unsigned int status)
{
do_emu10k1_midi_interrupt(emu, &emu->midi2, status);
}
static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack)
{
int timeout, ok;
spin_lock_irq(&midi->input_lock);
mpu401_write_data(emu, midi, 0x00);
/* mpu401_clear_rx(emu, midi); */
mpu401_write_cmd(emu, midi, cmd);
if (ack) {
ok = 0;
timeout = 10000;
while (!ok && timeout-- > 0) {
if (mpu401_input_avail(emu, midi)) {
if (mpu401_read_data(emu, midi) == MPU401_ACK)
ok = 1;
}
}
if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
ok = 1;
} else {
ok = 1;
}
spin_unlock_irq(&midi->input_lock);
if (!ok) {
dev_err(emu->card->dev,
"midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
cmd, emu->port,
mpu401_read_stat(emu, midi),
mpu401_read_data(emu, midi));
return 1;
}
return 0;
}
static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
spin_lock_irq(&midi->open_lock);
midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
midi->substream_input = substream;
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
spin_unlock_irq(&midi->open_lock);
if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
goto error_out;
if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
goto error_out;
} else {
spin_unlock_irq(&midi->open_lock);
}
return 0;
error_out:
return -EIO;
}
static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
spin_lock_irq(&midi->open_lock);
midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
midi->substream_output = substream;
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
spin_unlock_irq(&midi->open_lock);
if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
goto error_out;
if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
goto error_out;
} else {
spin_unlock_irq(&midi->open_lock);
}
return 0;
error_out:
return -EIO;
}
static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
int err = 0;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
spin_lock_irq(&midi->open_lock);
snd_emu10k1_intr_disable(emu, midi->rx_enable);
midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
midi->substream_input = NULL;
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
spin_unlock_irq(&midi->open_lock);
err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
} else {
spin_unlock_irq(&midi->open_lock);
}
return err;
}
static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
int err = 0;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
spin_lock_irq(&midi->open_lock);
snd_emu10k1_intr_disable(emu, midi->tx_enable);
midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
midi->substream_output = NULL;
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
spin_unlock_irq(&midi->open_lock);
err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
} else {
spin_unlock_irq(&midi->open_lock);
}
return err;
}
static void snd_emu10k1_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return;
if (up)
snd_emu10k1_intr_enable(emu, midi->rx_enable);
else
snd_emu10k1_intr_disable(emu, midi->rx_enable);
}
static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return;
if (up) {
int max = 4;
unsigned char byte;
/* try to send some amount of bytes here before interrupts */
spin_lock_irq(&midi->output_lock);
while (max > 0) {
if (mpu401_output_ready(emu, midi)) {
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
snd_rawmidi_transmit(substream, &byte, 1) != 1) {
/* no more data */
spin_unlock_irq(&midi->output_lock);
return;
}
mpu401_write_data(emu, midi, byte);
max--;
} else {
break;
}
}
spin_unlock_irq(&midi->output_lock);
snd_emu10k1_intr_enable(emu, midi->tx_enable);
} else {
snd_emu10k1_intr_disable(emu, midi->tx_enable);
}
}
/*
*/
static const struct snd_rawmidi_ops snd_emu10k1_midi_output =
{
.open = snd_emu10k1_midi_output_open,
.close = snd_emu10k1_midi_output_close,
.trigger = snd_emu10k1_midi_output_trigger,
};
static const struct snd_rawmidi_ops snd_emu10k1_midi_input =
{
.open = snd_emu10k1_midi_input_open,
.close = snd_emu10k1_midi_input_close,
.trigger = snd_emu10k1_midi_input_trigger,
};
static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi)
{
struct snd_emu10k1_midi *midi = rmidi->private_data;
midi->interrupt = NULL;
midi->rmidi = NULL;
}
static int emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, int device, char *name)
{
struct snd_rawmidi *rmidi;
int err;
err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi);
if (err < 0)
return err;
midi->emu = emu;
spin_lock_init(&midi->open_lock);
spin_lock_init(&midi->input_lock);
spin_lock_init(&midi->output_lock);
strcpy(rmidi->name, name);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = midi;
rmidi->private_free = snd_emu10k1_midi_free;
midi->rmidi = rmidi;
return 0;
}
int snd_emu10k1_midi(struct snd_emu10k1 *emu)
{
struct snd_emu10k1_midi *midi = &emu->midi;
int err;
err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)");
if (err < 0)
return err;
midi->tx_enable = INTE_MIDITXENABLE;
midi->rx_enable = INTE_MIDIRXENABLE;
midi->port = MUDATA;
midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
midi->interrupt = snd_emu10k1_midi_interrupt;
return 0;
}
int snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
{
struct snd_emu10k1_midi *midi;
int err;
midi = &emu->midi;
err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)");
if (err < 0)
return err;
midi->tx_enable = INTE_MIDITXENABLE;
midi->rx_enable = INTE_MIDIRXENABLE;
midi->port = A_MUDATA1;
midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
midi->interrupt = snd_emu10k1_midi_interrupt;
midi = &emu->midi2;
err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2");
if (err < 0)
return err;
midi->tx_enable = INTE_A_MIDITXENABLE2;
midi->rx_enable = INTE_A_MIDIRXENABLE2;
midi->port = A_MUDATA2;
midi->ipr_tx = IPR_A_MIDITRANSBUFEMPTY2;
midi->ipr_rx = IPR_A_MIDIRECVBUFEMPTY2;
midi->interrupt = snd_emu10k1_midi_interrupt2;
return 0;
}