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 92*a6676811STakashi Iwai dev_dbg(gus->card->dev, 93*a6676811STakashi Iwai "write init - cmd = 0x%x, stat = 0x%x\n", 94*a6676811STakashi Iwai gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); 951da177e4SLinus Torvalds #endif 961da177e4SLinus Torvalds return 0; 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds 995e2da206STakashi Iwai static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream) 1001da177e4SLinus Torvalds { 1011da177e4SLinus Torvalds unsigned long flags; 1025e2da206STakashi Iwai struct snd_gus_card *gus; 1031da177e4SLinus Torvalds int i; 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds gus = substream->rmidi->private_data; 1061da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 1071da177e4SLinus Torvalds if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) { 1081da177e4SLinus Torvalds snd_gf1_uart_reset(gus, 0); 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in; 1111da177e4SLinus Torvalds gus->midi_substream_input = substream; 1121da177e4SLinus Torvalds if (gus->uart_enable) { 1131da177e4SLinus Torvalds for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++) 1141da177e4SLinus Torvalds snd_gf1_uart_get(gus); /* clean Rx */ 1151da177e4SLinus Torvalds if (i >= 1000) 116*a6676811STakashi Iwai dev_err(gus->card->dev, "gus midi uart init read - cleanup error\n"); 1171da177e4SLinus Torvalds } 1181da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 1191da177e4SLinus Torvalds #if 0 120*a6676811STakashi Iwai dev_dbg(gus->card->dev, 12191f05060STakashi Iwai "read init - enable = %i, cmd = 0x%x, stat = 0x%x\n", 12291f05060STakashi Iwai gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); 123*a6676811STakashi Iwai dev_dbg(gus->card->dev, 124*a6676811STakashi Iwai "[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n", 12591f05060STakashi Iwai gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100), 12691f05060STakashi Iwai inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102)); 1271da177e4SLinus Torvalds #endif 1281da177e4SLinus Torvalds return 0; 1291da177e4SLinus Torvalds } 1301da177e4SLinus Torvalds 1315e2da206STakashi Iwai static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream) 1321da177e4SLinus Torvalds { 1331da177e4SLinus Torvalds unsigned long flags; 1345e2da206STakashi Iwai struct snd_gus_card *gus; 1351da177e4SLinus Torvalds 1361da177e4SLinus Torvalds gus = substream->rmidi->private_data; 1371da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 1381da177e4SLinus Torvalds if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in) 1391da177e4SLinus Torvalds snd_gf1_uart_reset(gus, 1); 1401da177e4SLinus Torvalds snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT); 1411da177e4SLinus Torvalds gus->midi_substream_output = NULL; 1421da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 1431da177e4SLinus Torvalds return 0; 1441da177e4SLinus Torvalds } 1451da177e4SLinus Torvalds 1465e2da206STakashi Iwai static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream) 1471da177e4SLinus Torvalds { 1481da177e4SLinus Torvalds unsigned long flags; 1495e2da206STakashi Iwai struct snd_gus_card *gus; 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds gus = substream->rmidi->private_data; 1521da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 1531da177e4SLinus Torvalds if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) 1541da177e4SLinus Torvalds snd_gf1_uart_reset(gus, 1); 1551da177e4SLinus Torvalds snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN); 1561da177e4SLinus Torvalds gus->midi_substream_input = NULL; 1571da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 1581da177e4SLinus Torvalds return 0; 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 1615e2da206STakashi Iwai static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up) 1621da177e4SLinus Torvalds { 1635e2da206STakashi Iwai struct snd_gus_card *gus; 1641da177e4SLinus Torvalds unsigned long flags; 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds gus = substream->rmidi->private_data; 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 1691da177e4SLinus Torvalds if (up) { 1701da177e4SLinus Torvalds if ((gus->gf1.uart_cmd & 0x80) == 0) 1711da177e4SLinus Torvalds snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */ 1721da177e4SLinus Torvalds } else { 1731da177e4SLinus Torvalds if (gus->gf1.uart_cmd & 0x80) 1741da177e4SLinus Torvalds snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */ 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 1771da177e4SLinus Torvalds } 1781da177e4SLinus Torvalds 1795e2da206STakashi Iwai static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up) 1801da177e4SLinus Torvalds { 1811da177e4SLinus Torvalds unsigned long flags; 1825e2da206STakashi Iwai struct snd_gus_card *gus; 1831da177e4SLinus Torvalds char byte; 1841da177e4SLinus Torvalds int timeout; 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds gus = substream->rmidi->private_data; 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 1891da177e4SLinus Torvalds if (up) { 1901da177e4SLinus Torvalds if ((gus->gf1.uart_cmd & 0x20) == 0) { 1911da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 1921da177e4SLinus Torvalds /* wait for empty Rx - Tx is probably unlocked */ 1931da177e4SLinus Torvalds timeout = 10000; 1941da177e4SLinus Torvalds while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01); 1951da177e4SLinus Torvalds /* Tx FIFO free? */ 1961da177e4SLinus Torvalds spin_lock_irqsave(&gus->uart_cmd_lock, flags); 1971da177e4SLinus Torvalds if (gus->gf1.uart_cmd & 0x20) { 1981da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 1991da177e4SLinus Torvalds return; 2001da177e4SLinus Torvalds } 2011da177e4SLinus Torvalds if (snd_gf1_uart_stat(gus) & 0x02) { 2021da177e4SLinus Torvalds if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { 2031da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 2041da177e4SLinus Torvalds return; 2051da177e4SLinus Torvalds } 2061da177e4SLinus Torvalds snd_gf1_uart_put(gus, byte); 2071da177e4SLinus Torvalds } 2081da177e4SLinus Torvalds snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20); /* enable Tx interrupt */ 2091da177e4SLinus Torvalds } 2101da177e4SLinus Torvalds } else { 2111da177e4SLinus Torvalds if (gus->gf1.uart_cmd & 0x20) 2121da177e4SLinus Torvalds snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 2151da177e4SLinus Torvalds } 2161da177e4SLinus Torvalds 2179021b2b8STakashi Iwai static const struct snd_rawmidi_ops snd_gf1_uart_output = 2181da177e4SLinus Torvalds { 2191da177e4SLinus Torvalds .open = snd_gf1_uart_output_open, 2201da177e4SLinus Torvalds .close = snd_gf1_uart_output_close, 2211da177e4SLinus Torvalds .trigger = snd_gf1_uart_output_trigger, 2221da177e4SLinus Torvalds }; 2231da177e4SLinus Torvalds 2249021b2b8STakashi Iwai static const struct snd_rawmidi_ops snd_gf1_uart_input = 2251da177e4SLinus Torvalds { 2261da177e4SLinus Torvalds .open = snd_gf1_uart_input_open, 2271da177e4SLinus Torvalds .close = snd_gf1_uart_input_close, 2281da177e4SLinus Torvalds .trigger = snd_gf1_uart_input_trigger, 2291da177e4SLinus Torvalds }; 2301da177e4SLinus Torvalds 231db5abb3cSLars-Peter Clausen int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device) 2321da177e4SLinus Torvalds { 2335e2da206STakashi Iwai struct snd_rawmidi *rmidi; 2341da177e4SLinus Torvalds int err; 2351da177e4SLinus Torvalds 236310efd3aSTakashi Iwai err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi); 237310efd3aSTakashi Iwai if (err < 0) 2381da177e4SLinus Torvalds return err; 2391da177e4SLinus Torvalds strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); 2401da177e4SLinus Torvalds snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output); 2411da177e4SLinus Torvalds snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input); 2421da177e4SLinus Torvalds rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; 2431da177e4SLinus Torvalds rmidi->private_data = gus; 2441da177e4SLinus Torvalds gus->midi_uart = rmidi; 2451da177e4SLinus Torvalds return err; 2461da177e4SLinus Torvalds } 247