brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · 0827650 Raw
247 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>4 *  Routines for the GF1 MIDI interface - like UART 68505 */6 7#include <linux/delay.h>8#include <linux/interrupt.h>9#include <linux/time.h>10#include <sound/core.h>11#include <sound/gus.h>12 13static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus)14{15	int count;16	unsigned char stat, byte;17	__always_unused unsigned char data;18	unsigned long flags;19 20	count = 10;21	while (count) {22		spin_lock_irqsave(&gus->uart_cmd_lock, flags);23		stat = snd_gf1_uart_stat(gus);24		if (!(stat & 0x01)) {	/* data in Rx FIFO? */25			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);26			count--;27			continue;28		}29		count = 100;	/* arm counter to new value */30		data = snd_gf1_uart_get(gus);31		if (!(gus->gf1.uart_cmd & 0x80)) {32			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);33			continue;34		}			35		if (stat & 0x10) {	/* framing error */36			gus->gf1.uart_framing++;37			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);38			continue;39		}40		byte = snd_gf1_uart_get(gus);41		spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);42		snd_rawmidi_receive(gus->midi_substream_input, &byte, 1);43		if (stat & 0x20) {44			gus->gf1.uart_overrun++;45		}46	}47}48 49static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus)50{51	char byte;52	unsigned long flags;53 54	/* try unlock output */55	if (snd_gf1_uart_stat(gus) & 0x01)56		snd_gf1_interrupt_midi_in(gus);57 58	spin_lock_irqsave(&gus->uart_cmd_lock, flags);59	if (snd_gf1_uart_stat(gus) & 0x02) {	/* Tx FIFO free? */60		if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) {	/* no other bytes or error */61			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */62		} else {63			snd_gf1_uart_put(gus, byte);64		}65	}66	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);67}68 69static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close)70{71	snd_gf1_uart_cmd(gus, 0x03);	/* reset */72	if (!close && gus->uart_enable) {73		udelay(160);74		snd_gf1_uart_cmd(gus, 0x00);	/* normal operations */75	}76}77 78static int snd_gf1_uart_output_open(struct snd_rawmidi_substream *substream)79{80	unsigned long flags;81	struct snd_gus_card *gus;82 83	gus = substream->rmidi->private_data;84	spin_lock_irqsave(&gus->uart_cmd_lock, flags);85	if (!(gus->gf1.uart_cmd & 0x80)) {	/* input active? */86		snd_gf1_uart_reset(gus, 0);87	}88	gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;89	gus->midi_substream_output = substream;90	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);91#if 092	dev_dbg(gus->card->dev,93		"write init - cmd = 0x%x, stat = 0x%x\n",94		gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));95#endif96	return 0;97}98 99static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream)100{101	unsigned long flags;102	struct snd_gus_card *gus;103	int i;104 105	gus = substream->rmidi->private_data;106	spin_lock_irqsave(&gus->uart_cmd_lock, flags);107	if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) {108		snd_gf1_uart_reset(gus, 0);109	}110	gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in;111	gus->midi_substream_input = substream;112	if (gus->uart_enable) {113		for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)114			snd_gf1_uart_get(gus);	/* clean Rx */115		if (i >= 1000)116			dev_err(gus->card->dev, "gus midi uart init read - cleanup error\n");117	}118	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);119#if 0120	dev_dbg(gus->card->dev,121		"read init - enable = %i, cmd = 0x%x, stat = 0x%x\n",122		gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));123	dev_dbg(gus->card->dev,124		"[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n",125		gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100),126		inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));127#endif128	return 0;129}130 131static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream)132{133	unsigned long flags;134	struct snd_gus_card *gus;135 136	gus = substream->rmidi->private_data;137	spin_lock_irqsave(&gus->uart_cmd_lock, flags);138	if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in)139		snd_gf1_uart_reset(gus, 1);140	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT);141	gus->midi_substream_output = NULL;142	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);143	return 0;144}145 146static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream)147{148	unsigned long flags;149	struct snd_gus_card *gus;150 151	gus = substream->rmidi->private_data;152	spin_lock_irqsave(&gus->uart_cmd_lock, flags);153	if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out)154		snd_gf1_uart_reset(gus, 1);155	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN);156	gus->midi_substream_input = NULL;157	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);158	return 0;159}160 161static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)162{163	struct snd_gus_card *gus;164	unsigned long flags;165 166	gus = substream->rmidi->private_data;167 168	spin_lock_irqsave(&gus->uart_cmd_lock, flags);169	if (up) {170		if ((gus->gf1.uart_cmd & 0x80) == 0)171			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */172	} else {173		if (gus->gf1.uart_cmd & 0x80)174			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */175	}176	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);177}178 179static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)180{181	unsigned long flags;182	struct snd_gus_card *gus;183	char byte;184	int timeout;185 186	gus = substream->rmidi->private_data;187 188	spin_lock_irqsave(&gus->uart_cmd_lock, flags);189	if (up) {190		if ((gus->gf1.uart_cmd & 0x20) == 0) {191			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);192			/* wait for empty Rx - Tx is probably unlocked */193			timeout = 10000;194			while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01);195			/* Tx FIFO free? */196			spin_lock_irqsave(&gus->uart_cmd_lock, flags);197			if (gus->gf1.uart_cmd & 0x20) {198				spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);199				return;200			}201			if (snd_gf1_uart_stat(gus) & 0x02) {202				if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {203					spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);204					return;205				}206				snd_gf1_uart_put(gus, byte);207			}208			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20);	/* enable Tx interrupt */209		}210	} else {211		if (gus->gf1.uart_cmd & 0x20)212			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20);213	}214	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);215}216 217static const struct snd_rawmidi_ops snd_gf1_uart_output =218{219	.open =		snd_gf1_uart_output_open,220	.close =	snd_gf1_uart_output_close,221	.trigger =	snd_gf1_uart_output_trigger,222};223 224static const struct snd_rawmidi_ops snd_gf1_uart_input =225{226	.open =		snd_gf1_uart_input_open,227	.close =	snd_gf1_uart_input_close,228	.trigger =	snd_gf1_uart_input_trigger,229};230 231int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device)232{233	struct snd_rawmidi *rmidi;234	int err;235 236	err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi);237	if (err < 0)238		return err;239	strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");240	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);241	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input);242	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;243	rmidi->private_data = gus;244	gus->midi_uart = rmidi;245	return err;246}247