1 /* 2 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 3 * Copyright (c) 2009 by Krzysztof Helt 4 * Routines for control of MPU-401 in UART mode 5 * 6 * MPU-401 supports UART mode which is not capable generate transmit 7 * interrupts thus output is done via polling. Also, if irq < 0, then 8 * input is done also via polling. Do not expect good performance. 9 * 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 2 of the License, or 14 * (at your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program; if not, write to the Free Software 23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 24 * 25 */ 26 27 #include <linux/io.h> 28 #include <linux/delay.h> 29 #include <linux/ioport.h> 30 #include <linux/errno.h> 31 #include <sound/core.h> 32 #include <sound/rawmidi.h> 33 34 #include "msnd.h" 35 36 #define MSNDMIDI_MODE_BIT_INPUT 0 37 #define MSNDMIDI_MODE_BIT_OUTPUT 1 38 #define MSNDMIDI_MODE_BIT_INPUT_TRIGGER 2 39 #define MSNDMIDI_MODE_BIT_OUTPUT_TRIGGER 3 40 41 struct snd_msndmidi { 42 struct snd_msnd *dev; 43 44 unsigned long mode; /* MSNDMIDI_MODE_XXXX */ 45 46 struct snd_rawmidi_substream *substream_input; 47 48 spinlock_t input_lock; 49 }; 50 51 /* 52 * input/output open/close - protected by open_mutex in rawmidi.c 53 */ 54 static int snd_msndmidi_input_open(struct snd_rawmidi_substream *substream) 55 { 56 struct snd_msndmidi *mpu; 57 58 snd_printdd("snd_msndmidi_input_open()\n"); 59 60 mpu = substream->rmidi->private_data; 61 62 mpu->substream_input = substream; 63 64 snd_msnd_enable_irq(mpu->dev); 65 66 snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_START); 67 set_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode); 68 return 0; 69 } 70 71 static int snd_msndmidi_input_close(struct snd_rawmidi_substream *substream) 72 { 73 struct snd_msndmidi *mpu; 74 75 mpu = substream->rmidi->private_data; 76 snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_STOP); 77 clear_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode); 78 mpu->substream_input = NULL; 79 snd_msnd_disable_irq(mpu->dev); 80 return 0; 81 } 82 83 static void snd_msndmidi_input_drop(struct snd_msndmidi *mpu) 84 { 85 u16 tail; 86 87 tail = readw(mpu->dev->MIDQ + JQS_wTail); 88 writew(tail, mpu->dev->MIDQ + JQS_wHead); 89 } 90 91 /* 92 * trigger input 93 */ 94 static void snd_msndmidi_input_trigger(struct snd_rawmidi_substream *substream, 95 int up) 96 { 97 unsigned long flags; 98 struct snd_msndmidi *mpu; 99 100 snd_printdd("snd_msndmidi_input_trigger(, %i)\n", up); 101 102 mpu = substream->rmidi->private_data; 103 spin_lock_irqsave(&mpu->input_lock, flags); 104 if (up) { 105 if (!test_and_set_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, 106 &mpu->mode)) 107 snd_msndmidi_input_drop(mpu); 108 } else { 109 clear_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode); 110 } 111 spin_unlock_irqrestore(&mpu->input_lock, flags); 112 if (up) 113 snd_msndmidi_input_read(mpu); 114 } 115 116 void snd_msndmidi_input_read(void *mpuv) 117 { 118 unsigned long flags; 119 struct snd_msndmidi *mpu = mpuv; 120 void *pwMIDQData = mpu->dev->mappedbase + MIDQ_DATA_BUFF; 121 122 spin_lock_irqsave(&mpu->input_lock, flags); 123 while (readw(mpu->dev->MIDQ + JQS_wTail) != 124 readw(mpu->dev->MIDQ + JQS_wHead)) { 125 u16 wTmp, val; 126 val = readw(pwMIDQData + 2 * readw(mpu->dev->MIDQ + JQS_wHead)); 127 128 if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, 129 &mpu->mode)) 130 snd_rawmidi_receive(mpu->substream_input, 131 (unsigned char *)&val, 1); 132 133 wTmp = readw(mpu->dev->MIDQ + JQS_wHead) + 1; 134 if (wTmp > readw(mpu->dev->MIDQ + JQS_wSize)) 135 writew(0, mpu->dev->MIDQ + JQS_wHead); 136 else 137 writew(wTmp, mpu->dev->MIDQ + JQS_wHead); 138 } 139 spin_unlock_irqrestore(&mpu->input_lock, flags); 140 } 141 EXPORT_SYMBOL(snd_msndmidi_input_read); 142 143 static struct snd_rawmidi_ops snd_msndmidi_input = { 144 .open = snd_msndmidi_input_open, 145 .close = snd_msndmidi_input_close, 146 .trigger = snd_msndmidi_input_trigger, 147 }; 148 149 static void snd_msndmidi_free(struct snd_rawmidi *rmidi) 150 { 151 struct snd_msndmidi *mpu = rmidi->private_data; 152 kfree(mpu); 153 } 154 155 int snd_msndmidi_new(struct snd_card *card, int device) 156 { 157 struct snd_msnd *chip = card->private_data; 158 struct snd_msndmidi *mpu; 159 struct snd_rawmidi *rmidi; 160 int err; 161 162 err = snd_rawmidi_new(card, "MSND-MIDI", device, 1, 1, &rmidi); 163 if (err < 0) 164 return err; 165 mpu = kcalloc(1, sizeof(*mpu), GFP_KERNEL); 166 if (mpu == NULL) { 167 snd_device_free(card, rmidi); 168 return -ENOMEM; 169 } 170 mpu->dev = chip; 171 chip->msndmidi_mpu = mpu; 172 rmidi->private_data = mpu; 173 rmidi->private_free = snd_msndmidi_free; 174 spin_lock_init(&mpu->input_lock); 175 strcpy(rmidi->name, "MSND MIDI"); 176 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 177 &snd_msndmidi_input); 178 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; 179 return 0; 180 } 181