[ALSA] ice1724: fix MIDI

The VT1724 MIDI port is not MPU-401 compatible; remove the hacks that
try to make the MPU-401 library work with it, and just use some simple
device-specific code.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Tested-by: Pavel Hofman <pavel.hofman@insite.cz>
This commit is contained in:
Clemens Ladisch 2008-05-20 14:22:44 +02:00
parent 33e5b22285
commit aea3bfbcfb
4 changed files with 178 additions and 51 deletions

View File

@ -692,7 +692,7 @@ config SND_ICE1712
config SND_ICE1724 config SND_ICE1724
tristate "ICE/VT1724/1720 (Envy24HT/PT)" tristate "ICE/VT1724/1720 (Envy24HT/PT)"
depends on SND depends on SND
select SND_MPU401_UART select SND_RAWMIDI
select SND_AC97_CODEC select SND_AC97_CODEC
select SND_VMASTER select SND_VMASTER
help help

View File

@ -93,9 +93,13 @@ enum {
#define VT1724_REG_MPU_TXFIFO 0x0a /*byte ro. number of bytes in TX fifo*/ #define VT1724_REG_MPU_TXFIFO 0x0a /*byte ro. number of bytes in TX fifo*/
#define VT1724_REG_MPU_RXFIFO 0x0b /*byte ro. number of bytes in RX fifo*/ #define VT1724_REG_MPU_RXFIFO 0x0b /*byte ro. number of bytes in RX fifo*/
//are these 2 the wrong way around? they don't seem to be used yet anyway #define VT1724_REG_MPU_DATA 0x0c /* byte */
#define VT1724_REG_MPU_CTRL 0x0c /* byte */ #define VT1724_REG_MPU_CTRL 0x0d /* byte */
#define VT1724_REG_MPU_DATA 0x0d /* byte */ #define VT1724_MPU_UART 0x01
#define VT1724_MPU_TX_EMPTY 0x02
#define VT1724_MPU_TX_FULL 0x04
#define VT1724_MPU_RX_EMPTY 0x08
#define VT1724_MPU_RX_FULL 0x10
#define VT1724_REG_MPU_FIFO_WM 0x0e /*byte set the high/low watermarks for RX/TX fifos*/ #define VT1724_REG_MPU_FIFO_WM 0x0e /*byte set the high/low watermarks for RX/TX fifos*/
#define VT1724_MPU_RX_FIFO 0x20 //1=rx fifo watermark 0=tx fifo watermark #define VT1724_MPU_RX_FIFO 0x20 //1=rx fifo watermark 0=tx fifo watermark

View File

@ -333,6 +333,8 @@ struct snd_ice1712 {
unsigned int has_spdif: 1; /* VT1720/4 - has SPDIF I/O */ unsigned int has_spdif: 1; /* VT1720/4 - has SPDIF I/O */
unsigned int force_pdma4: 1; /* VT1720/4 - PDMA4 as non-spdif */ unsigned int force_pdma4: 1; /* VT1720/4 - PDMA4 as non-spdif */
unsigned int force_rdma1: 1; /* VT1720/4 - RDMA1 as non-spdif */ unsigned int force_rdma1: 1; /* VT1720/4 - RDMA1 as non-spdif */
unsigned int midi_output: 1; /* VT1720/4: MIDI output triggered */
unsigned int midi_input: 1; /* VT1720/4: MIDI input triggered */
unsigned int num_total_dacs; /* total DACs */ unsigned int num_total_dacs; /* total DACs */
unsigned int num_total_adcs; /* total ADCs */ unsigned int num_total_adcs; /* total ADCs */
unsigned int cur_rate; /* current rate */ unsigned int cur_rate; /* current rate */

View File

@ -32,7 +32,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/info.h> #include <sound/info.h>
#include <sound/mpu401.h> #include <sound/rawmidi.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/asoundef.h> #include <sound/asoundef.h>
@ -223,30 +223,153 @@ static unsigned int snd_vt1724_get_gpio_data(struct snd_ice1712 *ice)
} }
/* /*
* MPU401 accessor * MIDI
*/ */
static unsigned char snd_vt1724_mpu401_read(struct snd_mpu401 *mpu,
unsigned long addr) static void vt1724_midi_clear_rx(struct snd_ice1712 *ice)
{ {
/* fix status bits to the standard position */ unsigned int count;
/* only RX_EMPTY and TX_FULL are checked */
if (addr == MPU401C(mpu)) for (count = inb(ICEREG1724(ice, MPU_RXFIFO)); count > 0; --count)
return (inb(addr) & 0x0c) << 4; inb(ICEREG1724(ice, MPU_DATA));
else
return inb(addr);
} }
static void snd_vt1724_mpu401_write(struct snd_mpu401 *mpu, static inline struct snd_rawmidi_substream *
unsigned char data, unsigned long addr) get_rawmidi_substream(struct snd_ice1712 *ice, unsigned int stream)
{ {
if (addr == MPU401C(mpu)) { return list_first_entry(&ice->rmidi[0]->streams[stream].substreams,
if (data == MPU401_ENTER_UART) struct snd_rawmidi_substream, list);
outb(0x01, addr);
/* what else? */
} else
outb(data, addr);
} }
static void vt1724_midi_write(struct snd_ice1712 *ice)
{
struct snd_rawmidi_substream *s;
int count, i;
u8 buffer[32];
s = get_rawmidi_substream(ice, SNDRV_RAWMIDI_STREAM_OUTPUT);
count = 31 - inb(ICEREG1724(ice, MPU_TXFIFO));
if (count > 0) {
count = snd_rawmidi_transmit(s, buffer, count);
for (i = 0; i < count; ++i)
outb(buffer[i], ICEREG1724(ice, MPU_DATA));
}
}
static void vt1724_midi_read(struct snd_ice1712 *ice)
{
struct snd_rawmidi_substream *s;
int count, i;
u8 buffer[32];
s = get_rawmidi_substream(ice, SNDRV_RAWMIDI_STREAM_INPUT);
count = inb(ICEREG1724(ice, MPU_RXFIFO));
if (count > 0) {
count = min(count, 32);
for (i = 0; i < count; ++i)
buffer[i] = inb(ICEREG1724(ice, MPU_DATA));
snd_rawmidi_receive(s, buffer, count);
}
}
static void vt1724_enable_midi_irq(struct snd_rawmidi_substream *substream,
u8 flag, int enable)
{
struct snd_ice1712 *ice = substream->rmidi->private_data;
u8 mask;
spin_lock_irq(&ice->reg_lock);
mask = inb(ICEREG1724(ice, IRQMASK));
if (enable)
mask &= ~flag;
else
mask |= flag;
outb(mask, ICEREG1724(ice, IRQMASK));
spin_unlock_irq(&ice->reg_lock);
}
static int vt1724_midi_output_open(struct snd_rawmidi_substream *s)
{
vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_TX, 1);
return 0;
}
static int vt1724_midi_output_close(struct snd_rawmidi_substream *s)
{
vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_TX, 0);
return 0;
}
static void vt1724_midi_output_trigger(struct snd_rawmidi_substream *s, int up)
{
struct snd_ice1712 *ice = s->rmidi->private_data;
unsigned long flags;
spin_lock_irqsave(&ice->reg_lock, flags);
if (up) {
ice->midi_output = 1;
vt1724_midi_write(ice);
} else {
ice->midi_output = 0;
}
spin_unlock_irqrestore(&ice->reg_lock, flags);
}
static void vt1724_midi_output_drain(struct snd_rawmidi_substream *s)
{
struct snd_ice1712 *ice = s->rmidi->private_data;
unsigned long timeout;
/* 32 bytes should be transmitted in less than about 12 ms */
timeout = jiffies + msecs_to_jiffies(15);
do {
if (inb(ICEREG1724(ice, MPU_CTRL)) & VT1724_MPU_TX_EMPTY)
break;
schedule_timeout_uninterruptible(1);
} while (time_after(timeout, jiffies));
}
static struct snd_rawmidi_ops vt1724_midi_output_ops = {
.open = vt1724_midi_output_open,
.close = vt1724_midi_output_close,
.trigger = vt1724_midi_output_trigger,
.drain = vt1724_midi_output_drain,
};
static int vt1724_midi_input_open(struct snd_rawmidi_substream *s)
{
vt1724_midi_clear_rx(s->rmidi->private_data);
vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_RX, 1);
return 0;
}
static int vt1724_midi_input_close(struct snd_rawmidi_substream *s)
{
vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_RX, 0);
return 0;
}
static void vt1724_midi_input_trigger(struct snd_rawmidi_substream *s, int up)
{
struct snd_ice1712 *ice = s->rmidi->private_data;
unsigned long flags;
spin_lock_irqsave(&ice->reg_lock, flags);
if (up) {
ice->midi_input = 1;
vt1724_midi_read(ice);
} else {
ice->midi_input = 0;
}
spin_unlock_irqrestore(&ice->reg_lock, flags);
}
static struct snd_rawmidi_ops vt1724_midi_input_ops = {
.open = vt1724_midi_input_open,
.close = vt1724_midi_input_close,
.trigger = vt1724_midi_input_trigger,
};
/* /*
* Interrupt handler * Interrupt handler
@ -278,13 +401,10 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
#endif #endif
handled = 1; handled = 1;
if (status & VT1724_IRQ_MPU_TX) { if (status & VT1724_IRQ_MPU_TX) {
if (ice->rmidi[0]) spin_lock(&ice->reg_lock);
snd_mpu401_uart_interrupt_tx(irq, if (ice->midi_output)
ice->rmidi[0]->private_data); vt1724_midi_write(ice);
else /* disable TX to be sure */ spin_unlock(&ice->reg_lock);
outb(inb(ICEREG1724(ice, IRQMASK)) |
VT1724_IRQ_MPU_TX,
ICEREG1724(ice, IRQMASK));
/* Due to mysterical reasons, MPU_TX is always /* Due to mysterical reasons, MPU_TX is always
* generated (and can't be cleared) when a PCM * generated (and can't be cleared) when a PCM
* playback is going. So let's ignore at the * playback is going. So let's ignore at the
@ -293,13 +413,12 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
status_mask &= ~VT1724_IRQ_MPU_TX; status_mask &= ~VT1724_IRQ_MPU_TX;
} }
if (status & VT1724_IRQ_MPU_RX) { if (status & VT1724_IRQ_MPU_RX) {
if (ice->rmidi[0]) spin_lock(&ice->reg_lock);
snd_mpu401_uart_interrupt(irq, if (ice->midi_input)
ice->rmidi[0]->private_data); vt1724_midi_read(ice);
else /* disable RX to be sure */ else
outb(inb(ICEREG1724(ice, IRQMASK)) | vt1724_midi_clear_rx(ice);
VT1724_IRQ_MPU_RX, spin_unlock(&ice->reg_lock);
ICEREG1724(ice, IRQMASK));
} }
/* ack MPU irq */ /* ack MPU irq */
outb(status, ICEREG1724(ice, IRQSTAT)); outb(status, ICEREG1724(ice, IRQSTAT));
@ -2425,28 +2544,30 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
if (! c->no_mpu401) { if (! c->no_mpu401) {
if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) { if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
struct snd_mpu401 *mpu; struct snd_rawmidi *rmidi;
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
ICEREG1724(ice, MPU_CTRL), err = snd_rawmidi_new(card, "MIDI", 0, 1, 1, &rmidi);
(MPU401_INFO_INTEGRATED | if (err < 0) {
MPU401_INFO_NO_ACK |
MPU401_INFO_TX_IRQ),
ice->irq, 0,
&ice->rmidi[0])) < 0) {
snd_card_free(card); snd_card_free(card);
return err; return err;
} }
mpu = ice->rmidi[0]->private_data; ice->rmidi[0] = rmidi;
mpu->read = snd_vt1724_mpu401_read; rmidi->private_data = ice;
mpu->write = snd_vt1724_mpu401_write; strcpy(rmidi->name, "ICE1724 MIDI");
/* unmask MPU RX/TX irqs */ rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
outb(inb(ICEREG1724(ice, IRQMASK)) & SNDRV_RAWMIDI_INFO_INPUT |
~(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX), SNDRV_RAWMIDI_INFO_DUPLEX;
ICEREG1724(ice, IRQMASK)); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
&vt1724_midi_output_ops);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
&vt1724_midi_input_ops);
/* set watermarks */ /* set watermarks */
outb(VT1724_MPU_RX_FIFO | 0x1, outb(VT1724_MPU_RX_FIFO | 0x1,
ICEREG1724(ice, MPU_FIFO_WM)); ICEREG1724(ice, MPU_FIFO_WM));
outb(0x1, ICEREG1724(ice, MPU_FIFO_WM)); outb(0x1, ICEREG1724(ice, MPU_FIFO_WM));
/* set UART mode */
outb(VT1724_MPU_UART, ICEREG1724(ice, MPU_CTRL));
} }
} }