Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aea3bfbc authored by Clemens Ladisch's avatar Clemens Ladisch
Browse files

[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: default avatarClemens Ladisch <clemens@ladisch.de>
Tested-by: default avatarPavel Hofman <pavel.hofman@insite.cz>
parent 33e5b222
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -692,7 +692,7 @@ config SND_ICE1712
config SND_ICE1724
	tristate "ICE/VT1724/1720 (Envy24HT/PT)"
	depends on SND
	select SND_MPU401_UART
	select SND_RAWMIDI
	select SND_AC97_CODEC
	select SND_VMASTER
	help
+7 −3
Original line number Diff line number Diff line
@@ -93,9 +93,13 @@ enum {
#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*/

//are these 2 the wrong way around? they don't seem to be used yet anyway
#define VT1724_REG_MPU_CTRL		0x0c	/* byte */
#define VT1724_REG_MPU_DATA		0x0d	/* byte */
#define VT1724_REG_MPU_DATA		0x0c	/* byte */
#define VT1724_REG_MPU_CTRL		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_MPU_RX_FIFO	0x20	//1=rx fifo watermark 0=tx fifo watermark
+2 −0
Original line number Diff line number Diff line
@@ -333,6 +333,8 @@ struct snd_ice1712 {
	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_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_adcs;	/* total ADCs */
	unsigned int cur_rate;		/* current rate */
+167 −46
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/mpu401.h>
#include <sound/rawmidi.h>
#include <sound/initval.h>

#include <sound/asoundef.h>
@@ -223,29 +223,152 @@ 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)
{
	unsigned int count;

	for (count = inb(ICEREG1724(ice, MPU_RXFIFO)); count > 0; --count)
		inb(ICEREG1724(ice, MPU_DATA));
}

static inline struct snd_rawmidi_substream *
get_rawmidi_substream(struct snd_ice1712 *ice, unsigned int stream)
{
	return list_first_entry(&ice->rmidi[0]->streams[stream].substreams,
				struct snd_rawmidi_substream, list);
}

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)
{
	/* fix status bits to the standard position */
	/* only RX_EMPTY and TX_FULL are checked */
	if (addr == MPU401C(mpu))
		return (inb(addr) & 0x0c) << 4;
	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
		return inb(addr);
		mask |= flag;
	outb(mask, ICEREG1724(ice, IRQMASK));
	spin_unlock_irq(&ice->reg_lock);
}

static void snd_vt1724_mpu401_write(struct snd_mpu401 *mpu,
				    unsigned char data, unsigned long addr)
static int vt1724_midi_output_open(struct snd_rawmidi_substream *s)
{
	if (addr == MPU401C(mpu)) {
		if (data == MPU401_ENTER_UART)
			outb(0x01, addr);
		/* what else? */
	} else
		outb(data, addr);
	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,
};


/*
@@ -278,13 +401,10 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
#endif
		handled = 1;		
		if (status & VT1724_IRQ_MPU_TX) {
			if (ice->rmidi[0])
				snd_mpu401_uart_interrupt_tx(irq,
					ice->rmidi[0]->private_data);
			else /* disable TX to be sure */
				outb(inb(ICEREG1724(ice, IRQMASK)) |
				     VT1724_IRQ_MPU_TX,
				     ICEREG1724(ice, IRQMASK));
			spin_lock(&ice->reg_lock);
			if (ice->midi_output)
				vt1724_midi_write(ice);
			spin_unlock(&ice->reg_lock);
			/* Due to mysterical reasons, MPU_TX is always
			 * generated (and can't be cleared) when a PCM
			 * 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;
		}
		if (status & VT1724_IRQ_MPU_RX) {
			if (ice->rmidi[0])
				snd_mpu401_uart_interrupt(irq,
					ice->rmidi[0]->private_data);
			else /* disable RX to be sure */
				outb(inb(ICEREG1724(ice, IRQMASK)) |
				     VT1724_IRQ_MPU_RX,
				     ICEREG1724(ice, IRQMASK));
			spin_lock(&ice->reg_lock);
			if (ice->midi_input)
				vt1724_midi_read(ice);
			else
				vt1724_midi_clear_rx(ice);
			spin_unlock(&ice->reg_lock);
		}
		/* ack MPU irq */
		outb(status, ICEREG1724(ice, IRQSTAT));
@@ -2425,28 +2544,30 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,

	if (! c->no_mpu401) {
		if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
			struct snd_mpu401 *mpu;
			if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
						       ICEREG1724(ice, MPU_CTRL),
						       (MPU401_INFO_INTEGRATED |
							MPU401_INFO_NO_ACK |
							MPU401_INFO_TX_IRQ),
						       ice->irq, 0,
						       &ice->rmidi[0])) < 0) {
			struct snd_rawmidi *rmidi;

			err = snd_rawmidi_new(card, "MIDI", 0, 1, 1, &rmidi);
			if (err < 0) {
				snd_card_free(card);
				return err;
			}
			mpu = ice->rmidi[0]->private_data;
			mpu->read = snd_vt1724_mpu401_read;
			mpu->write = snd_vt1724_mpu401_write;
			/* unmask MPU RX/TX irqs */
			outb(inb(ICEREG1724(ice, IRQMASK)) &
			     ~(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX),
			     ICEREG1724(ice, IRQMASK));
			ice->rmidi[0] = rmidi;
			rmidi->private_data = ice;
			strcpy(rmidi->name, "ICE1724 MIDI");
			rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
					    SNDRV_RAWMIDI_INFO_INPUT |
					    SNDRV_RAWMIDI_INFO_DUPLEX;
			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 */
			outb(VT1724_MPU_RX_FIFO | 0x1,
			     ICEREG1724(ice, MPU_FIFO_WM));
			outb(0x1, ICEREG1724(ice, MPU_FIFO_WM));
			/* set UART mode */
			outb(VT1724_MPU_UART, ICEREG1724(ice, MPU_CTRL));
		}
	}