msnd_midi.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (c) by Jaroslav Kysela <[email protected]>
  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. #include <linux/io.h>
  12. #include <linux/slab.h>
  13. #include <linux/delay.h>
  14. #include <linux/ioport.h>
  15. #include <linux/errno.h>
  16. #include <linux/export.h>
  17. #include <sound/core.h>
  18. #include <sound/rawmidi.h>
  19. #include "msnd.h"
  20. #define MSNDMIDI_MODE_BIT_INPUT 0
  21. #define MSNDMIDI_MODE_BIT_OUTPUT 1
  22. #define MSNDMIDI_MODE_BIT_INPUT_TRIGGER 2
  23. #define MSNDMIDI_MODE_BIT_OUTPUT_TRIGGER 3
  24. struct snd_msndmidi {
  25. struct snd_msnd *dev;
  26. unsigned long mode; /* MSNDMIDI_MODE_XXXX */
  27. struct snd_rawmidi_substream *substream_input;
  28. spinlock_t input_lock;
  29. };
  30. /*
  31. * input/output open/close - protected by open_mutex in rawmidi.c
  32. */
  33. static int snd_msndmidi_input_open(struct snd_rawmidi_substream *substream)
  34. {
  35. struct snd_msndmidi *mpu;
  36. snd_printdd("snd_msndmidi_input_open()\n");
  37. mpu = substream->rmidi->private_data;
  38. mpu->substream_input = substream;
  39. snd_msnd_enable_irq(mpu->dev);
  40. snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_START);
  41. set_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode);
  42. return 0;
  43. }
  44. static int snd_msndmidi_input_close(struct snd_rawmidi_substream *substream)
  45. {
  46. struct snd_msndmidi *mpu;
  47. mpu = substream->rmidi->private_data;
  48. snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_STOP);
  49. clear_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode);
  50. mpu->substream_input = NULL;
  51. snd_msnd_disable_irq(mpu->dev);
  52. return 0;
  53. }
  54. static void snd_msndmidi_input_drop(struct snd_msndmidi *mpu)
  55. {
  56. u16 tail;
  57. tail = readw(mpu->dev->MIDQ + JQS_wTail);
  58. writew(tail, mpu->dev->MIDQ + JQS_wHead);
  59. }
  60. /*
  61. * trigger input
  62. */
  63. static void snd_msndmidi_input_trigger(struct snd_rawmidi_substream *substream,
  64. int up)
  65. {
  66. unsigned long flags;
  67. struct snd_msndmidi *mpu;
  68. snd_printdd("snd_msndmidi_input_trigger(, %i)\n", up);
  69. mpu = substream->rmidi->private_data;
  70. spin_lock_irqsave(&mpu->input_lock, flags);
  71. if (up) {
  72. if (!test_and_set_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER,
  73. &mpu->mode))
  74. snd_msndmidi_input_drop(mpu);
  75. } else {
  76. clear_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode);
  77. }
  78. spin_unlock_irqrestore(&mpu->input_lock, flags);
  79. if (up)
  80. snd_msndmidi_input_read(mpu);
  81. }
  82. void snd_msndmidi_input_read(void *mpuv)
  83. {
  84. unsigned long flags;
  85. struct snd_msndmidi *mpu = mpuv;
  86. void __iomem *pwMIDQData = mpu->dev->mappedbase + MIDQ_DATA_BUFF;
  87. u16 head, tail, size;
  88. spin_lock_irqsave(&mpu->input_lock, flags);
  89. head = readw(mpu->dev->MIDQ + JQS_wHead);
  90. tail = readw(mpu->dev->MIDQ + JQS_wTail);
  91. size = readw(mpu->dev->MIDQ + JQS_wSize);
  92. if (head > size || tail > size)
  93. goto out;
  94. while (head != tail) {
  95. unsigned char val = readw(pwMIDQData + 2 * head);
  96. if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode))
  97. snd_rawmidi_receive(mpu->substream_input, &val, 1);
  98. if (++head > size)
  99. head = 0;
  100. writew(head, mpu->dev->MIDQ + JQS_wHead);
  101. }
  102. out:
  103. spin_unlock_irqrestore(&mpu->input_lock, flags);
  104. }
  105. EXPORT_SYMBOL(snd_msndmidi_input_read);
  106. static const struct snd_rawmidi_ops snd_msndmidi_input = {
  107. .open = snd_msndmidi_input_open,
  108. .close = snd_msndmidi_input_close,
  109. .trigger = snd_msndmidi_input_trigger,
  110. };
  111. static void snd_msndmidi_free(struct snd_rawmidi *rmidi)
  112. {
  113. struct snd_msndmidi *mpu = rmidi->private_data;
  114. kfree(mpu);
  115. }
  116. int snd_msndmidi_new(struct snd_card *card, int device)
  117. {
  118. struct snd_msnd *chip = card->private_data;
  119. struct snd_msndmidi *mpu;
  120. struct snd_rawmidi *rmidi;
  121. int err;
  122. err = snd_rawmidi_new(card, "MSND-MIDI", device, 1, 1, &rmidi);
  123. if (err < 0)
  124. return err;
  125. mpu = kzalloc(sizeof(*mpu), GFP_KERNEL);
  126. if (mpu == NULL) {
  127. snd_device_free(card, rmidi);
  128. return -ENOMEM;
  129. }
  130. mpu->dev = chip;
  131. chip->msndmidi_mpu = mpu;
  132. rmidi->private_data = mpu;
  133. rmidi->private_free = snd_msndmidi_free;
  134. spin_lock_init(&mpu->input_lock);
  135. strcpy(rmidi->name, "MSND MIDI");
  136. snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
  137. &snd_msndmidi_input);
  138. rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
  139. return 0;
  140. }