11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 41da177e4SLinus Torvalds * Routines for the GF1 MIDI interface - like UART 6850 51da177e4SLinus Torvalds */ 61da177e4SLinus Torvalds 71da177e4SLinus Torvalds #include <linux/delay.h> 81da177e4SLinus Torvalds #include <linux/interrupt.h> 91da177e4SLinus Torvalds #include <linux/time.h> 101da177e4SLinus Torvalds #include <sound/core.h> 111da177e4SLinus Torvalds #include <sound/gus.h> 121da177e4SLinus Torvalds 135e2da206STakashi Iwai static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus) 141da177e4SLinus Torvalds { 151da177e4SLinus Torvalds int count; 166a4f2b69SPierre-Louis Bossart unsigned char stat, byte; 176a4f2b69SPierre-Louis Bossart __always_unused unsigned char data; 181da177e4SLinus Torvalds unsigned long flags; 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds count = 10; 211da177e4SLinus Torvalds while (count) { 221da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 231da177e4SLinus Torvalds stat = snd_gf1_uart_stat(gus); 241da177e4SLinus Torvalds if (!(stat & 0x01)) { /* data in Rx FIFO? */ 251da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 261da177e4SLinus Torvalds count--; 271da177e4SLinus Torvalds continue; 281da177e4SLinus Torvalds } 291da177e4SLinus Torvalds count = 100; /* arm counter to new value */ 301da177e4SLinus Torvalds data = snd_gf1_uart_get(gus); 311da177e4SLinus Torvalds if (!(gus->gf1.uart_cmd & 0x80)) { 321da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 331da177e4SLinus Torvalds continue; 341da177e4SLinus Torvalds } 351da177e4SLinus Torvalds if (stat & 0x10) { /* framing error */ 361da177e4SLinus Torvalds gus->gf1.uart_framing++; 371da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 381da177e4SLinus Torvalds continue; 391da177e4SLinus Torvalds } 401da177e4SLinus Torvalds byte = snd_gf1_uart_get(gus); 411da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 421da177e4SLinus Torvalds snd_rawmidi_receive(gus->midi_substream_input, &byte, 1); 431da177e4SLinus Torvalds if (stat & 0x20) { 441da177e4SLinus Torvalds gus->gf1.uart_overrun++; 451da177e4SLinus Torvalds } 461da177e4SLinus Torvalds } 471da177e4SLinus Torvalds } 481da177e4SLinus Torvalds 495e2da206STakashi Iwai static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus) 501da177e4SLinus Torvalds { 511da177e4SLinus Torvalds char byte; 521da177e4SLinus Torvalds unsigned long flags; 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds /* try unlock output */ 551da177e4SLinus Torvalds if (snd_gf1_uart_stat(gus) & 0x01) 561da177e4SLinus Torvalds snd_gf1_interrupt_midi_in(gus); 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 591da177e4SLinus Torvalds if (snd_gf1_uart_stat(gus) & 0x02) { /* Tx FIFO free? */ 601da177e4SLinus Torvalds if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) { /* no other bytes or error */ 611da177e4SLinus Torvalds snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */ 621da177e4SLinus Torvalds } else { 631da177e4SLinus Torvalds snd_gf1_uart_put(gus, byte); 641da177e4SLinus Torvalds } 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 671da177e4SLinus Torvalds } 681da177e4SLinus Torvalds 695e2da206STakashi Iwai static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close) 701da177e4SLinus Torvalds { 711da177e4SLinus Torvalds snd_gf1_uart_cmd(gus, 0x03); /* reset */ 721da177e4SLinus Torvalds if (!close && gus->uart_enable) { 731da177e4SLinus Torvalds udelay(160); 741da177e4SLinus Torvalds snd_gf1_uart_cmd(gus, 0x00); /* normal operations */ 751da177e4SLinus Torvalds } 761da177e4SLinus Torvalds } 771da177e4SLinus Torvalds 785e2da206STakashi Iwai static int snd_gf1_uart_output_open(struct snd_rawmidi_substream *substream) 791da177e4SLinus Torvalds { 801da177e4SLinus Torvalds unsigned long flags; 815e2da206STakashi Iwai struct snd_gus_card *gus; 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds gus = substream->rmidi->private_data; 841da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 851da177e4SLinus Torvalds if (!(gus->gf1.uart_cmd & 0x80)) { /* input active? */ 861da177e4SLinus Torvalds snd_gf1_uart_reset(gus, 0); 871da177e4SLinus Torvalds } 881da177e4SLinus Torvalds gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out; 891da177e4SLinus Torvalds gus->midi_substream_output = substream; 901da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 911da177e4SLinus Torvalds #if 0 9299b359baSTakashi Iwai snd_printk(KERN_DEBUG "write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); 931da177e4SLinus Torvalds #endif 941da177e4SLinus Torvalds return 0; 951da177e4SLinus Torvalds } 961da177e4SLinus Torvalds 975e2da206STakashi Iwai static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream) 981da177e4SLinus Torvalds { 991da177e4SLinus Torvalds unsigned long flags; 1005e2da206STakashi Iwai struct snd_gus_card *gus; 1011da177e4SLinus Torvalds int i; 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds gus = substream->rmidi->private_data; 1041da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 1051da177e4SLinus Torvalds if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) { 1061da177e4SLinus Torvalds snd_gf1_uart_reset(gus, 0); 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in; 1091da177e4SLinus Torvalds gus->midi_substream_input = substream; 1101da177e4SLinus Torvalds if (gus->uart_enable) { 1111da177e4SLinus Torvalds for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++) 1121da177e4SLinus Torvalds snd_gf1_uart_get(gus); /* clean Rx */ 1131da177e4SLinus Torvalds if (i >= 1000) 11499b359baSTakashi Iwai snd_printk(KERN_ERR "gus midi uart init read - cleanup error\n"); 1151da177e4SLinus Torvalds } 1161da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 1171da177e4SLinus Torvalds #if 0 11891f05060STakashi Iwai snd_printk(KERN_DEBUG 11991f05060STakashi Iwai "read init - enable = %i, cmd = 0x%x, stat = 0x%x\n", 12091f05060STakashi Iwai gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); 12191f05060STakashi Iwai snd_printk(KERN_DEBUG 12291f05060STakashi Iwai "[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x " 12391f05060STakashi Iwai "(page = 0x%x)\n", 12491f05060STakashi Iwai gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100), 12591f05060STakashi Iwai inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102)); 1261da177e4SLinus Torvalds #endif 1271da177e4SLinus Torvalds return 0; 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds 1305e2da206STakashi Iwai static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream) 1311da177e4SLinus Torvalds { 1321da177e4SLinus Torvalds unsigned long flags; 1335e2da206STakashi Iwai struct snd_gus_card *gus; 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds gus = substream->rmidi->private_data; 1361da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 1371da177e4SLinus Torvalds if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in) 1381da177e4SLinus Torvalds snd_gf1_uart_reset(gus, 1); 1391da177e4SLinus Torvalds snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT); 1401da177e4SLinus Torvalds gus->midi_substream_output = NULL; 1411da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 1421da177e4SLinus Torvalds return 0; 1431da177e4SLinus Torvalds } 1441da177e4SLinus Torvalds 1455e2da206STakashi Iwai static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream) 1461da177e4SLinus Torvalds { 1471da177e4SLinus Torvalds unsigned long flags; 1485e2da206STakashi Iwai struct snd_gus_card *gus; 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds gus = substream->rmidi->private_data; 1511da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 1521da177e4SLinus Torvalds if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) 1531da177e4SLinus Torvalds snd_gf1_uart_reset(gus, 1); 1541da177e4SLinus Torvalds snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN); 1551da177e4SLinus Torvalds gus->midi_substream_input = NULL; 1561da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 1571da177e4SLinus Torvalds return 0; 1581da177e4SLinus Torvalds } 1591da177e4SLinus Torvalds 1605e2da206STakashi Iwai static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up) 1611da177e4SLinus Torvalds { 1625e2da206STakashi Iwai struct snd_gus_card *gus; 1631da177e4SLinus Torvalds unsigned long flags; 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds gus = substream->rmidi->private_data; 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 1681da177e4SLinus Torvalds if (up) { 1691da177e4SLinus Torvalds if ((gus->gf1.uart_cmd & 0x80) == 0) 1701da177e4SLinus Torvalds snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */ 1711da177e4SLinus Torvalds } else { 1721da177e4SLinus Torvalds if (gus->gf1.uart_cmd & 0x80) 1731da177e4SLinus Torvalds snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */ 1741da177e4SLinus Torvalds } 1751da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 1761da177e4SLinus Torvalds } 1771da177e4SLinus Torvalds 1785e2da206STakashi Iwai static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up) 1791da177e4SLinus Torvalds { 1801da177e4SLinus Torvalds unsigned long flags; 1815e2da206STakashi Iwai struct snd_gus_card *gus; 1821da177e4SLinus Torvalds char byte; 1831da177e4SLinus Torvalds int timeout; 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds gus = substream->rmidi->private_data; 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 1881da177e4SLinus Torvalds if (up) { 1891da177e4SLinus Torvalds if ((gus->gf1.uart_cmd & 0x20) == 0) { 1901da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 1911da177e4SLinus Torvalds /* wait for empty Rx - Tx is probably unlocked */ 1921da177e4SLinus Torvalds timeout = 10000; 1931da177e4SLinus Torvalds while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01); 1941da177e4SLinus Torvalds /* Tx FIFO free? */ 1951da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 1961da177e4SLinus Torvalds if (gus->gf1.uart_cmd & 0x20) { 1971da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 1981da177e4SLinus Torvalds return; 1991da177e4SLinus Torvalds } 2001da177e4SLinus Torvalds if (snd_gf1_uart_stat(gus) & 0x02) { 2011da177e4SLinus Torvalds if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { 2021da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 2031da177e4SLinus Torvalds return; 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds snd_gf1_uart_put(gus, byte); 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20); /* enable Tx interrupt */ 2081da177e4SLinus Torvalds } 2091da177e4SLinus Torvalds } else { 2101da177e4SLinus Torvalds if (gus->gf1.uart_cmd & 0x20) 2111da177e4SLinus Torvalds snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); 2121da177e4SLinus Torvalds } 2131da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds 2169021b2b8STakashi Iwai static const struct snd_rawmidi_ops snd_gf1_uart_output = 2171da177e4SLinus Torvalds { 2181da177e4SLinus Torvalds .open = snd_gf1_uart_output_open, 2191da177e4SLinus Torvalds .close = snd_gf1_uart_output_close, 2201da177e4SLinus Torvalds .trigger = snd_gf1_uart_output_trigger, 2211da177e4SLinus Torvalds }; 2221da177e4SLinus Torvalds 2239021b2b8STakashi Iwai static const struct snd_rawmidi_ops snd_gf1_uart_input = 2241da177e4SLinus Torvalds { 2251da177e4SLinus Torvalds .open = snd_gf1_uart_input_open, 2261da177e4SLinus Torvalds .close = snd_gf1_uart_input_close, 2271da177e4SLinus Torvalds .trigger = snd_gf1_uart_input_trigger, 2281da177e4SLinus Torvalds }; 2291da177e4SLinus Torvalds 230db5abb3cSLars-Peter Clausen int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device) 2311da177e4SLinus Torvalds { 2325e2da206STakashi Iwai struct snd_rawmidi *rmidi; 2331da177e4SLinus Torvalds int err; 2341da177e4SLinus Torvalds 235*310efd3aSTakashi Iwai err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi); 236*310efd3aSTakashi Iwai if (err < 0) 2371da177e4SLinus Torvalds return err; 2381da177e4SLinus Torvalds strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); 2391da177e4SLinus Torvalds snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output); 2401da177e4SLinus Torvalds snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input); 2411da177e4SLinus Torvalds rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; 2421da177e4SLinus Torvalds rmidi->private_data = gus; 2431da177e4SLinus Torvalds gus->midi_uart = rmidi; 2441da177e4SLinus Torvalds return err; 2451da177e4SLinus Torvalds } 246