615 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>4 * Routines for control of MPU-401 in UART mode5 *6 * MPU-401 supports UART mode which is not capable generate transmit7 * interrupts thus output is done via polling. Without interrupt,8 * input is done also via polling. Do not expect good performance.9 *10 * 13-03-2003:11 * Added support for different kind of hardware I/O. Build in choices12 * are port and mmio. For other kind of I/O, set mpu->read and13 * mpu->write to your own I/O functions.14 */15 16#include <linux/io.h>17#include <linux/delay.h>18#include <linux/init.h>19#include <linux/slab.h>20#include <linux/ioport.h>21#include <linux/module.h>22#include <linux/interrupt.h>23#include <linux/errno.h>24#include <sound/core.h>25#include <sound/mpu401.h>26 27MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");28MODULE_DESCRIPTION("Routines for control of MPU-401 in UART mode");29MODULE_LICENSE("GPL");30 31static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu);32static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu);33 34/*35 36 */37 38#define snd_mpu401_input_avail(mpu) \39 (!(mpu->read(mpu, MPU401C(mpu)) & MPU401_RX_EMPTY))40#define snd_mpu401_output_ready(mpu) \41 (!(mpu->read(mpu, MPU401C(mpu)) & MPU401_TX_FULL))42 43/* Build in lowlevel io */44static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data,45 unsigned long addr)46{47 outb(data, addr);48}49 50static unsigned char mpu401_read_port(struct snd_mpu401 *mpu,51 unsigned long addr)52{53 return inb(addr);54}55 56static void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data,57 unsigned long addr)58{59 writeb(data, (void __iomem *)addr);60}61 62static unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu,63 unsigned long addr)64{65 return readb((void __iomem *)addr);66}67/* */68 69static void snd_mpu401_uart_clear_rx(struct snd_mpu401 *mpu)70{71 int timeout = 100000;72 for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--)73 mpu->read(mpu, MPU401D(mpu));74#ifdef CONFIG_SND_DEBUG75 if (timeout <= 0)76 dev_err(mpu->rmidi->dev,77 "cmd: clear rx timeout (status = 0x%x)\n",78 mpu->read(mpu, MPU401C(mpu)));79#endif80}81 82static void uart_interrupt_tx(struct snd_mpu401 *mpu)83{84 unsigned long flags;85 86 if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) &&87 test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) {88 spin_lock_irqsave(&mpu->output_lock, flags);89 snd_mpu401_uart_output_write(mpu);90 spin_unlock_irqrestore(&mpu->output_lock, flags);91 }92}93 94static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu)95{96 unsigned long flags;97 98 if (mpu->info_flags & MPU401_INFO_INPUT) {99 spin_lock_irqsave(&mpu->input_lock, flags);100 if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))101 snd_mpu401_uart_input_read(mpu);102 else103 snd_mpu401_uart_clear_rx(mpu);104 spin_unlock_irqrestore(&mpu->input_lock, flags);105 }106 if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))107 /* ok. for better Tx performance try do some output108 when input is done */109 uart_interrupt_tx(mpu);110}111 112/**113 * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler114 * @irq: the irq number115 * @dev_id: mpu401 instance116 *117 * Processes the interrupt for MPU401-UART i/o.118 *119 * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise.120 */121irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id)122{123 struct snd_mpu401 *mpu = dev_id;124 125 if (!mpu)126 return IRQ_NONE;127 _snd_mpu401_uart_interrupt(mpu);128 return IRQ_HANDLED;129}130 131EXPORT_SYMBOL(snd_mpu401_uart_interrupt);132 133/**134 * snd_mpu401_uart_interrupt_tx - generic MPU401-UART transmit irq handler135 * @irq: the irq number136 * @dev_id: mpu401 instance137 *138 * Processes the interrupt for MPU401-UART output.139 *140 * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise.141 */142irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id)143{144 struct snd_mpu401 *mpu = dev_id;145 146 if (!mpu)147 return IRQ_NONE;148 uart_interrupt_tx(mpu);149 return IRQ_HANDLED;150}151 152EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx);153 154/*155 * timer callback156 * reprogram the timer and call the interrupt job157 */158static void snd_mpu401_uart_timer(struct timer_list *t)159{160 struct snd_mpu401 *mpu = from_timer(mpu, t, timer);161 unsigned long flags;162 163 spin_lock_irqsave(&mpu->timer_lock, flags);164 /*mpu->mode |= MPU401_MODE_TIMER;*/165 mod_timer(&mpu->timer, 1 + jiffies);166 spin_unlock_irqrestore(&mpu->timer_lock, flags);167 if (mpu->rmidi)168 _snd_mpu401_uart_interrupt(mpu);169}170 171/*172 * initialize the timer callback if not programmed yet173 */174static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input)175{176 unsigned long flags;177 178 spin_lock_irqsave (&mpu->timer_lock, flags);179 if (mpu->timer_invoked == 0) {180 timer_setup(&mpu->timer, snd_mpu401_uart_timer, 0);181 mod_timer(&mpu->timer, 1 + jiffies);182 } 183 mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER :184 MPU401_MODE_OUTPUT_TIMER;185 spin_unlock_irqrestore (&mpu->timer_lock, flags);186}187 188/*189 * remove the timer callback if still active190 */191static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input)192{193 unsigned long flags;194 195 spin_lock_irqsave (&mpu->timer_lock, flags);196 if (mpu->timer_invoked) {197 mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER :198 ~MPU401_MODE_OUTPUT_TIMER;199 if (! mpu->timer_invoked)200 del_timer(&mpu->timer);201 }202 spin_unlock_irqrestore (&mpu->timer_lock, flags);203}204 205/*206 * send a UART command207 * return zero if successful, non-zero for some errors208 */209 210static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,211 int ack)212{213 unsigned long flags;214 int timeout, ok;215 216 spin_lock_irqsave(&mpu->input_lock, flags);217 if (mpu->hardware != MPU401_HW_TRID4DWAVE) {218 mpu->write(mpu, 0x00, MPU401D(mpu));219 /*snd_mpu401_uart_clear_rx(mpu);*/220 }221 /* ok. standard MPU-401 initialization */222 if (mpu->hardware != MPU401_HW_SB) {223 for (timeout = 1000; timeout > 0 &&224 !snd_mpu401_output_ready(mpu); timeout--)225 udelay(10);226#ifdef CONFIG_SND_DEBUG227 if (!timeout)228 dev_err(mpu->rmidi->dev,229 "cmd: tx timeout (status = 0x%x)\n",230 mpu->read(mpu, MPU401C(mpu)));231#endif232 }233 mpu->write(mpu, cmd, MPU401C(mpu));234 if (ack && !(mpu->info_flags & MPU401_INFO_NO_ACK)) {235 ok = 0;236 timeout = 10000;237 while (!ok && timeout-- > 0) {238 if (snd_mpu401_input_avail(mpu)) {239 if (mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK)240 ok = 1;241 }242 }243 if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK)244 ok = 1;245 } else246 ok = 1;247 spin_unlock_irqrestore(&mpu->input_lock, flags);248 if (!ok) {249 dev_err(mpu->rmidi->dev,250 "cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n",251 cmd, mpu->port,252 mpu->read(mpu, MPU401C(mpu)),253 mpu->read(mpu, MPU401D(mpu)));254 return 1;255 }256 return 0;257}258 259static int snd_mpu401_do_reset(struct snd_mpu401 *mpu)260{261 if (snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1))262 return -EIO;263 if (snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 0))264 return -EIO;265 return 0;266}267 268/*269 * input/output open/close - protected by open_mutex in rawmidi.c270 */271static int snd_mpu401_uart_input_open(struct snd_rawmidi_substream *substream)272{273 struct snd_mpu401 *mpu;274 int err;275 276 mpu = substream->rmidi->private_data;277 if (mpu->open_input) {278 err = mpu->open_input(mpu);279 if (err < 0)280 return err;281 }282 if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) {283 if (snd_mpu401_do_reset(mpu) < 0)284 goto error_out;285 }286 mpu->substream_input = substream;287 set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode);288 return 0;289 290error_out:291 if (mpu->open_input && mpu->close_input)292 mpu->close_input(mpu);293 return -EIO;294}295 296static int snd_mpu401_uart_output_open(struct snd_rawmidi_substream *substream)297{298 struct snd_mpu401 *mpu;299 int err;300 301 mpu = substream->rmidi->private_data;302 if (mpu->open_output) {303 err = mpu->open_output(mpu);304 if (err < 0)305 return err;306 }307 if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) {308 if (snd_mpu401_do_reset(mpu) < 0)309 goto error_out;310 }311 mpu->substream_output = substream;312 set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode);313 return 0;314 315error_out:316 if (mpu->open_output && mpu->close_output)317 mpu->close_output(mpu);318 return -EIO;319}320 321static int snd_mpu401_uart_input_close(struct snd_rawmidi_substream *substream)322{323 struct snd_mpu401 *mpu;324 int err = 0;325 326 mpu = substream->rmidi->private_data;327 clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode);328 mpu->substream_input = NULL;329 if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode))330 err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0);331 if (mpu->close_input)332 mpu->close_input(mpu);333 if (err)334 return -EIO;335 return 0;336}337 338static int snd_mpu401_uart_output_close(struct snd_rawmidi_substream *substream)339{340 struct snd_mpu401 *mpu;341 int err = 0;342 343 mpu = substream->rmidi->private_data;344 clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode);345 mpu->substream_output = NULL;346 if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))347 err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0);348 if (mpu->close_output)349 mpu->close_output(mpu);350 if (err)351 return -EIO;352 return 0;353}354 355/*356 * trigger input callback357 */358static void359snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)360{361 unsigned long flags;362 struct snd_mpu401 *mpu;363 int max = 64;364 365 mpu = substream->rmidi->private_data;366 if (up) {367 if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER,368 &mpu->mode)) {369 /* first time - flush FIFO */370 while (max-- > 0)371 mpu->read(mpu, MPU401D(mpu));372 if (mpu->info_flags & MPU401_INFO_USE_TIMER)373 snd_mpu401_uart_add_timer(mpu, 1);374 }375 376 /* read data in advance */377 spin_lock_irqsave(&mpu->input_lock, flags);378 snd_mpu401_uart_input_read(mpu);379 spin_unlock_irqrestore(&mpu->input_lock, flags);380 } else {381 if (mpu->info_flags & MPU401_INFO_USE_TIMER)382 snd_mpu401_uart_remove_timer(mpu, 1);383 clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode);384 }385 386}387 388/*389 * transfer input pending data390 * call with input_lock spinlock held391 */392static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu)393{394 int max = 128;395 unsigned char byte;396 397 while (max-- > 0) {398 if (! snd_mpu401_input_avail(mpu))399 break; /* input not available */400 byte = mpu->read(mpu, MPU401D(mpu));401 if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode))402 snd_rawmidi_receive(mpu->substream_input, &byte, 1);403 }404}405 406/*407 * Tx FIFO sizes:408 * CS4237B - 16 bytes409 * AudioDrive ES1688 - 12 bytes410 * S3 SonicVibes - 8 bytes411 * SoundBlaster AWE 64 - 2 bytes (ugly hardware)412 */413 414/*415 * write output pending bytes416 * call with output_lock spinlock held417 */418static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu)419{420 unsigned char byte;421 int max = 256;422 423 do {424 if (snd_rawmidi_transmit_peek(mpu->substream_output,425 &byte, 1) == 1) {426 /*427 * Try twice because there is hardware that insists on428 * setting the output busy bit after each write.429 */430 if (!snd_mpu401_output_ready(mpu) &&431 !snd_mpu401_output_ready(mpu))432 break; /* Tx FIFO full - try again later */433 mpu->write(mpu, byte, MPU401D(mpu));434 snd_rawmidi_transmit_ack(mpu->substream_output, 1);435 } else {436 snd_mpu401_uart_remove_timer (mpu, 0);437 break; /* no other data - leave the tx loop */438 }439 } while (--max > 0);440}441 442/*443 * output trigger callback444 */445static void446snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)447{448 unsigned long flags;449 struct snd_mpu401 *mpu;450 451 mpu = substream->rmidi->private_data;452 if (up) {453 set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);454 455 /* try to add the timer at each output trigger,456 * since the output timer might have been removed in457 * snd_mpu401_uart_output_write().458 */459 if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))460 snd_mpu401_uart_add_timer(mpu, 0);461 462 /* output pending data */463 spin_lock_irqsave(&mpu->output_lock, flags);464 snd_mpu401_uart_output_write(mpu);465 spin_unlock_irqrestore(&mpu->output_lock, flags);466 } else {467 if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))468 snd_mpu401_uart_remove_timer(mpu, 0);469 clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);470 }471}472 473/*474 475 */476 477static const struct snd_rawmidi_ops snd_mpu401_uart_output =478{479 .open = snd_mpu401_uart_output_open,480 .close = snd_mpu401_uart_output_close,481 .trigger = snd_mpu401_uart_output_trigger,482};483 484static const struct snd_rawmidi_ops snd_mpu401_uart_input =485{486 .open = snd_mpu401_uart_input_open,487 .close = snd_mpu401_uart_input_close,488 .trigger = snd_mpu401_uart_input_trigger,489};490 491static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)492{493 struct snd_mpu401 *mpu = rmidi->private_data;494 if (mpu->irq >= 0)495 free_irq(mpu->irq, (void *) mpu);496 release_and_free_resource(mpu->res);497 kfree(mpu);498}499 500/**501 * snd_mpu401_uart_new - create an MPU401-UART instance502 * @card: the card instance503 * @device: the device index, zero-based504 * @hardware: the hardware type, MPU401_HW_XXXX505 * @port: the base address of MPU401 port506 * @info_flags: bitflags MPU401_INFO_XXX507 * @irq: the ISA irq number, -1 if not to be allocated508 * @rrawmidi: the pointer to store the new rawmidi instance509 *510 * Creates a new MPU-401 instance.511 *512 * Note that the rawmidi instance is returned on the rrawmidi argument,513 * not the mpu401 instance itself. To access to the mpu401 instance,514 * cast from rawmidi->private_data (with struct snd_mpu401 magic-cast).515 *516 * Return: Zero if successful, or a negative error code.517 */518int snd_mpu401_uart_new(struct snd_card *card, int device,519 unsigned short hardware,520 unsigned long port,521 unsigned int info_flags,522 int irq,523 struct snd_rawmidi ** rrawmidi)524{525 struct snd_mpu401 *mpu;526 struct snd_rawmidi *rmidi;527 int in_enable, out_enable;528 int err;529 530 if (rrawmidi)531 *rrawmidi = NULL;532 if (! (info_flags & (MPU401_INFO_INPUT | MPU401_INFO_OUTPUT)))533 info_flags |= MPU401_INFO_INPUT | MPU401_INFO_OUTPUT;534 in_enable = (info_flags & MPU401_INFO_INPUT) ? 1 : 0;535 out_enable = (info_flags & MPU401_INFO_OUTPUT) ? 1 : 0;536 err = snd_rawmidi_new(card, "MPU-401U", device,537 out_enable, in_enable, &rmidi);538 if (err < 0)539 return err;540 mpu = kzalloc(sizeof(*mpu), GFP_KERNEL);541 if (!mpu) {542 err = -ENOMEM;543 goto free_device;544 }545 rmidi->private_data = mpu;546 rmidi->private_free = snd_mpu401_uart_free;547 spin_lock_init(&mpu->input_lock);548 spin_lock_init(&mpu->output_lock);549 spin_lock_init(&mpu->timer_lock);550 mpu->hardware = hardware;551 mpu->irq = -1;552 mpu->rmidi = rmidi;553 if (! (info_flags & MPU401_INFO_INTEGRATED)) {554 int res_size = hardware == MPU401_HW_PC98II ? 4 : 2;555 mpu->res = request_region(port, res_size, "MPU401 UART");556 if (!mpu->res) {557 dev_err(rmidi->dev,558 "mpu401_uart: unable to grab port 0x%lx size %d\n",559 port, res_size);560 err = -EBUSY;561 goto free_device;562 }563 }564 if (info_flags & MPU401_INFO_MMIO) {565 mpu->write = mpu401_write_mmio;566 mpu->read = mpu401_read_mmio;567 } else {568 mpu->write = mpu401_write_port;569 mpu->read = mpu401_read_port;570 }571 mpu->port = port;572 if (hardware == MPU401_HW_PC98II)573 mpu->cport = port + 2;574 else575 mpu->cport = port + 1;576 if (irq >= 0) {577 if (request_irq(irq, snd_mpu401_uart_interrupt, 0,578 "MPU401 UART", (void *) mpu)) {579 dev_err(rmidi->dev,580 "mpu401_uart: unable to grab IRQ %d\n", irq);581 err = -EBUSY;582 goto free_device;583 }584 }585 if (irq < 0 && !(info_flags & MPU401_INFO_IRQ_HOOK))586 info_flags |= MPU401_INFO_USE_TIMER;587 mpu->info_flags = info_flags;588 mpu->irq = irq;589 if (card->shortname[0])590 snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI",591 card->shortname);592 else593 sprintf(rmidi->name, "MPU-401 MIDI %d-%d",card->number, device);594 if (out_enable) {595 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,596 &snd_mpu401_uart_output);597 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;598 }599 if (in_enable) {600 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,601 &snd_mpu401_uart_input);602 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;603 if (out_enable)604 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;605 }606 if (rrawmidi)607 *rrawmidi = rmidi;608 return 0;609free_device:610 snd_device_free(card, rmidi);611 return err;612}613 614EXPORT_SYMBOL(snd_mpu401_uart_new);615