162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 462306a36Sopenharmony_ci * Copyright (c) 2009 by Krzysztof Helt 562306a36Sopenharmony_ci * Routines for control of MPU-401 in UART mode 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * MPU-401 supports UART mode which is not capable generate transmit 862306a36Sopenharmony_ci * interrupts thus output is done via polling. Also, if irq < 0, then 962306a36Sopenharmony_ci * input is done also via polling. Do not expect good performance. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/ioport.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/export.h> 1862306a36Sopenharmony_ci#include <sound/core.h> 1962306a36Sopenharmony_ci#include <sound/rawmidi.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "msnd.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define MSNDMIDI_MODE_BIT_INPUT 0 2462306a36Sopenharmony_ci#define MSNDMIDI_MODE_BIT_OUTPUT 1 2562306a36Sopenharmony_ci#define MSNDMIDI_MODE_BIT_INPUT_TRIGGER 2 2662306a36Sopenharmony_ci#define MSNDMIDI_MODE_BIT_OUTPUT_TRIGGER 3 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct snd_msndmidi { 2962306a36Sopenharmony_ci struct snd_msnd *dev; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci unsigned long mode; /* MSNDMIDI_MODE_XXXX */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci struct snd_rawmidi_substream *substream_input; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci spinlock_t input_lock; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * input/output open/close - protected by open_mutex in rawmidi.c 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_cistatic int snd_msndmidi_input_open(struct snd_rawmidi_substream *substream) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct snd_msndmidi *mpu; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci snd_printdd("snd_msndmidi_input_open()\n"); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci mpu = substream->rmidi->private_data; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci mpu->substream_input = substream; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci snd_msnd_enable_irq(mpu->dev); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_START); 5462306a36Sopenharmony_ci set_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode); 5562306a36Sopenharmony_ci return 0; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int snd_msndmidi_input_close(struct snd_rawmidi_substream *substream) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct snd_msndmidi *mpu; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci mpu = substream->rmidi->private_data; 6362306a36Sopenharmony_ci snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_STOP); 6462306a36Sopenharmony_ci clear_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode); 6562306a36Sopenharmony_ci mpu->substream_input = NULL; 6662306a36Sopenharmony_ci snd_msnd_disable_irq(mpu->dev); 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void snd_msndmidi_input_drop(struct snd_msndmidi *mpu) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci u16 tail; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci tail = readw(mpu->dev->MIDQ + JQS_wTail); 7562306a36Sopenharmony_ci writew(tail, mpu->dev->MIDQ + JQS_wHead); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * trigger input 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistatic void snd_msndmidi_input_trigger(struct snd_rawmidi_substream *substream, 8262306a36Sopenharmony_ci int up) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci unsigned long flags; 8562306a36Sopenharmony_ci struct snd_msndmidi *mpu; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci snd_printdd("snd_msndmidi_input_trigger(, %i)\n", up); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci mpu = substream->rmidi->private_data; 9062306a36Sopenharmony_ci spin_lock_irqsave(&mpu->input_lock, flags); 9162306a36Sopenharmony_ci if (up) { 9262306a36Sopenharmony_ci if (!test_and_set_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, 9362306a36Sopenharmony_ci &mpu->mode)) 9462306a36Sopenharmony_ci snd_msndmidi_input_drop(mpu); 9562306a36Sopenharmony_ci } else { 9662306a36Sopenharmony_ci clear_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci spin_unlock_irqrestore(&mpu->input_lock, flags); 9962306a36Sopenharmony_ci if (up) 10062306a36Sopenharmony_ci snd_msndmidi_input_read(mpu); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_civoid snd_msndmidi_input_read(void *mpuv) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci unsigned long flags; 10662306a36Sopenharmony_ci struct snd_msndmidi *mpu = mpuv; 10762306a36Sopenharmony_ci void __iomem *pwMIDQData = mpu->dev->mappedbase + MIDQ_DATA_BUFF; 10862306a36Sopenharmony_ci u16 head, tail, size; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci spin_lock_irqsave(&mpu->input_lock, flags); 11162306a36Sopenharmony_ci head = readw(mpu->dev->MIDQ + JQS_wHead); 11262306a36Sopenharmony_ci tail = readw(mpu->dev->MIDQ + JQS_wTail); 11362306a36Sopenharmony_ci size = readw(mpu->dev->MIDQ + JQS_wSize); 11462306a36Sopenharmony_ci if (head > size || tail > size) 11562306a36Sopenharmony_ci goto out; 11662306a36Sopenharmony_ci while (head != tail) { 11762306a36Sopenharmony_ci unsigned char val = readw(pwMIDQData + 2 * head); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) 12062306a36Sopenharmony_ci snd_rawmidi_receive(mpu->substream_input, &val, 1); 12162306a36Sopenharmony_ci if (++head > size) 12262306a36Sopenharmony_ci head = 0; 12362306a36Sopenharmony_ci writew(head, mpu->dev->MIDQ + JQS_wHead); 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci out: 12662306a36Sopenharmony_ci spin_unlock_irqrestore(&mpu->input_lock, flags); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_msndmidi_input_read); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_msndmidi_input = { 13162306a36Sopenharmony_ci .open = snd_msndmidi_input_open, 13262306a36Sopenharmony_ci .close = snd_msndmidi_input_close, 13362306a36Sopenharmony_ci .trigger = snd_msndmidi_input_trigger, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void snd_msndmidi_free(struct snd_rawmidi *rmidi) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct snd_msndmidi *mpu = rmidi->private_data; 13962306a36Sopenharmony_ci kfree(mpu); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ciint snd_msndmidi_new(struct snd_card *card, int device) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct snd_msnd *chip = card->private_data; 14562306a36Sopenharmony_ci struct snd_msndmidi *mpu; 14662306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 14762306a36Sopenharmony_ci int err; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci err = snd_rawmidi_new(card, "MSND-MIDI", device, 1, 1, &rmidi); 15062306a36Sopenharmony_ci if (err < 0) 15162306a36Sopenharmony_ci return err; 15262306a36Sopenharmony_ci mpu = kzalloc(sizeof(*mpu), GFP_KERNEL); 15362306a36Sopenharmony_ci if (mpu == NULL) { 15462306a36Sopenharmony_ci snd_device_free(card, rmidi); 15562306a36Sopenharmony_ci return -ENOMEM; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci mpu->dev = chip; 15862306a36Sopenharmony_ci chip->msndmidi_mpu = mpu; 15962306a36Sopenharmony_ci rmidi->private_data = mpu; 16062306a36Sopenharmony_ci rmidi->private_free = snd_msndmidi_free; 16162306a36Sopenharmony_ci spin_lock_init(&mpu->input_lock); 16262306a36Sopenharmony_ci strcpy(rmidi->name, "MSND MIDI"); 16362306a36Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 16462306a36Sopenharmony_ci &snd_msndmidi_input); 16562306a36Sopenharmony_ci rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 168