msnd_midi.c (4282B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 4 * Copyright (c) 2009 by Krzysztof Helt 5 * Routines for control of MPU-401 in UART mode 6 * 7 * MPU-401 supports UART mode which is not capable generate transmit 8 * interrupts thus output is done via polling. Also, if irq < 0, then 9 * input is done also via polling. Do not expect good performance. 10 */ 11 12#include <linux/io.h> 13#include <linux/slab.h> 14#include <linux/delay.h> 15#include <linux/ioport.h> 16#include <linux/errno.h> 17#include <linux/export.h> 18#include <sound/core.h> 19#include <sound/rawmidi.h> 20 21#include "msnd.h" 22 23#define MSNDMIDI_MODE_BIT_INPUT 0 24#define MSNDMIDI_MODE_BIT_OUTPUT 1 25#define MSNDMIDI_MODE_BIT_INPUT_TRIGGER 2 26#define MSNDMIDI_MODE_BIT_OUTPUT_TRIGGER 3 27 28struct snd_msndmidi { 29 struct snd_msnd *dev; 30 31 unsigned long mode; /* MSNDMIDI_MODE_XXXX */ 32 33 struct snd_rawmidi_substream *substream_input; 34 35 spinlock_t input_lock; 36}; 37 38/* 39 * input/output open/close - protected by open_mutex in rawmidi.c 40 */ 41static int snd_msndmidi_input_open(struct snd_rawmidi_substream *substream) 42{ 43 struct snd_msndmidi *mpu; 44 45 snd_printdd("snd_msndmidi_input_open()\n"); 46 47 mpu = substream->rmidi->private_data; 48 49 mpu->substream_input = substream; 50 51 snd_msnd_enable_irq(mpu->dev); 52 53 snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_START); 54 set_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode); 55 return 0; 56} 57 58static int snd_msndmidi_input_close(struct snd_rawmidi_substream *substream) 59{ 60 struct snd_msndmidi *mpu; 61 62 mpu = substream->rmidi->private_data; 63 snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_STOP); 64 clear_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode); 65 mpu->substream_input = NULL; 66 snd_msnd_disable_irq(mpu->dev); 67 return 0; 68} 69 70static void snd_msndmidi_input_drop(struct snd_msndmidi *mpu) 71{ 72 u16 tail; 73 74 tail = readw(mpu->dev->MIDQ + JQS_wTail); 75 writew(tail, mpu->dev->MIDQ + JQS_wHead); 76} 77 78/* 79 * trigger input 80 */ 81static void snd_msndmidi_input_trigger(struct snd_rawmidi_substream *substream, 82 int up) 83{ 84 unsigned long flags; 85 struct snd_msndmidi *mpu; 86 87 snd_printdd("snd_msndmidi_input_trigger(, %i)\n", up); 88 89 mpu = substream->rmidi->private_data; 90 spin_lock_irqsave(&mpu->input_lock, flags); 91 if (up) { 92 if (!test_and_set_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, 93 &mpu->mode)) 94 snd_msndmidi_input_drop(mpu); 95 } else { 96 clear_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode); 97 } 98 spin_unlock_irqrestore(&mpu->input_lock, flags); 99 if (up) 100 snd_msndmidi_input_read(mpu); 101} 102 103void snd_msndmidi_input_read(void *mpuv) 104{ 105 unsigned long flags; 106 struct snd_msndmidi *mpu = mpuv; 107 void __iomem *pwMIDQData = mpu->dev->mappedbase + MIDQ_DATA_BUFF; 108 u16 head, tail, size; 109 110 spin_lock_irqsave(&mpu->input_lock, flags); 111 head = readw(mpu->dev->MIDQ + JQS_wHead); 112 tail = readw(mpu->dev->MIDQ + JQS_wTail); 113 size = readw(mpu->dev->MIDQ + JQS_wSize); 114 if (head > size || tail > size) 115 goto out; 116 while (head != tail) { 117 unsigned char val = readw(pwMIDQData + 2 * head); 118 119 if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) 120 snd_rawmidi_receive(mpu->substream_input, &val, 1); 121 if (++head > size) 122 head = 0; 123 writew(head, mpu->dev->MIDQ + JQS_wHead); 124 } 125 out: 126 spin_unlock_irqrestore(&mpu->input_lock, flags); 127} 128EXPORT_SYMBOL(snd_msndmidi_input_read); 129 130static const struct snd_rawmidi_ops snd_msndmidi_input = { 131 .open = snd_msndmidi_input_open, 132 .close = snd_msndmidi_input_close, 133 .trigger = snd_msndmidi_input_trigger, 134}; 135 136static void snd_msndmidi_free(struct snd_rawmidi *rmidi) 137{ 138 struct snd_msndmidi *mpu = rmidi->private_data; 139 kfree(mpu); 140} 141 142int snd_msndmidi_new(struct snd_card *card, int device) 143{ 144 struct snd_msnd *chip = card->private_data; 145 struct snd_msndmidi *mpu; 146 struct snd_rawmidi *rmidi; 147 int err; 148 149 err = snd_rawmidi_new(card, "MSND-MIDI", device, 1, 1, &rmidi); 150 if (err < 0) 151 return err; 152 mpu = kzalloc(sizeof(*mpu), GFP_KERNEL); 153 if (mpu == NULL) { 154 snd_device_free(card, rmidi); 155 return -ENOMEM; 156 } 157 mpu->dev = chip; 158 chip->msndmidi_mpu = mpu; 159 rmidi->private_data = mpu; 160 rmidi->private_free = snd_msndmidi_free; 161 spin_lock_init(&mpu->input_lock); 162 strcpy(rmidi->name, "MSND MIDI"); 163 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 164 &snd_msndmidi_input); 165 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; 166 return 0; 167}