linux/sound/pci/emu10k1/emumpu401.c
<<
>>
Prefs
   1/*
   2 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   3 *  Routines for control of EMU10K1 MPU-401 in UART mode
   4 *
   5 *
   6 *   This program is free software; you can redistribute it and/or modify
   7 *   it under the terms of the GNU General Public License as published by
   8 *   the Free Software Foundation; either version 2 of the License, or
   9 *   (at your option) any later version.
  10 *
  11 *   This program is distributed in the hope that it will be useful,
  12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *   GNU General Public License for more details.
  15 *
  16 *   You should have received a copy of the GNU General Public License
  17 *   along with this program; if not, write to the Free Software
  18 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19 *
  20 */
  21
  22#include <linux/time.h>
  23#include <linux/init.h>
  24#include <sound/core.h>
  25#include <sound/emu10k1.h>
  26
  27#define EMU10K1_MIDI_MODE_INPUT         (1<<0)
  28#define EMU10K1_MIDI_MODE_OUTPUT        (1<<1)
  29
  30static inline unsigned char mpu401_read(struct snd_emu10k1 *emu,
  31                                        struct snd_emu10k1_midi *mpu, int idx)
  32{
  33        if (emu->audigy)
  34                return (unsigned char)snd_emu10k1_ptr_read(emu, mpu->port + idx, 0);
  35        else
  36                return inb(emu->port + mpu->port + idx);
  37}
  38
  39static inline void mpu401_write(struct snd_emu10k1 *emu,
  40                                struct snd_emu10k1_midi *mpu, int data, int idx)
  41{
  42        if (emu->audigy)
  43                snd_emu10k1_ptr_write(emu, mpu->port + idx, 0, data);
  44        else
  45                outb(data, emu->port + mpu->port + idx);
  46}
  47
  48#define mpu401_write_data(emu, mpu, data)       mpu401_write(emu, mpu, data, 0)
  49#define mpu401_write_cmd(emu, mpu, data)        mpu401_write(emu, mpu, data, 1)
  50#define mpu401_read_data(emu, mpu)              mpu401_read(emu, mpu, 0)
  51#define mpu401_read_stat(emu, mpu)              mpu401_read(emu, mpu, 1)
  52
  53#define mpu401_input_avail(emu,mpu)     (!(mpu401_read_stat(emu,mpu) & 0x80))
  54#define mpu401_output_ready(emu,mpu)    (!(mpu401_read_stat(emu,mpu) & 0x40))
  55
  56#define MPU401_RESET            0xff
  57#define MPU401_ENTER_UART       0x3f
  58#define MPU401_ACK              0xfe
  59
  60static void mpu401_clear_rx(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *mpu)
  61{
  62        int timeout = 100000;
  63        for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--)
  64                mpu401_read_data(emu, mpu);
  65#ifdef CONFIG_SND_DEBUG
  66        if (timeout <= 0)
  67                snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu));
  68#endif
  69}
  70
  71/*
  72
  73 */
  74
  75static void do_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, unsigned int status)
  76{
  77        unsigned char byte;
  78
  79        if (midi->rmidi == NULL) {
  80                snd_emu10k1_intr_disable(emu, midi->tx_enable | midi->rx_enable);
  81                return;
  82        }
  83
  84        spin_lock(&midi->input_lock);
  85        if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
  86                if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
  87                        mpu401_clear_rx(emu, midi);
  88                } else {
  89                        byte = mpu401_read_data(emu, midi);
  90                        if (midi->substream_input)
  91                                snd_rawmidi_receive(midi->substream_input, &byte, 1);
  92                }
  93        }
  94        spin_unlock(&midi->input_lock);
  95
  96        spin_lock(&midi->output_lock);
  97        if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
  98                if (midi->substream_output &&
  99                    snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
 100                        mpu401_write_data(emu, midi, byte);
 101                } else {
 102                        snd_emu10k1_intr_disable(emu, midi->tx_enable);
 103                }
 104        }
 105        spin_unlock(&midi->output_lock);
 106}
 107
 108static void snd_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, unsigned int status)
 109{
 110        do_emu10k1_midi_interrupt(emu, &emu->midi, status);
 111}
 112
 113static void snd_emu10k1_midi_interrupt2(struct snd_emu10k1 *emu, unsigned int status)
 114{
 115        do_emu10k1_midi_interrupt(emu, &emu->midi2, status);
 116}
 117
 118static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack)
 119{
 120        unsigned long flags;
 121        int timeout, ok;
 122
 123        spin_lock_irqsave(&midi->input_lock, flags);
 124        mpu401_write_data(emu, midi, 0x00);
 125        /* mpu401_clear_rx(emu, midi); */
 126
 127        mpu401_write_cmd(emu, midi, cmd);
 128        if (ack) {
 129                ok = 0;
 130                timeout = 10000;
 131                while (!ok && timeout-- > 0) {
 132                        if (mpu401_input_avail(emu, midi)) {
 133                                if (mpu401_read_data(emu, midi) == MPU401_ACK)
 134                                        ok = 1;
 135                        }
 136                }
 137                if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
 138                        ok = 1;
 139        } else {
 140                ok = 1;
 141        }
 142        spin_unlock_irqrestore(&midi->input_lock, flags);
 143        if (!ok) {
 144                snd_printk(KERN_ERR "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
 145                           cmd, emu->port,
 146                           mpu401_read_stat(emu, midi),
 147                           mpu401_read_data(emu, midi));
 148                return 1;
 149        }
 150        return 0;
 151}
 152
 153static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)
 154{
 155        struct snd_emu10k1 *emu;
 156        struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
 157        unsigned long flags;
 158
 159        emu = midi->emu;
 160        if (snd_BUG_ON(!emu))
 161                return -ENXIO;
 162        spin_lock_irqsave(&midi->open_lock, flags);
 163        midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
 164        midi->substream_input = substream;
 165        if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
 166                spin_unlock_irqrestore(&midi->open_lock, flags);
 167                if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
 168                        goto error_out;
 169                if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
 170                        goto error_out;
 171        } else {
 172                spin_unlock_irqrestore(&midi->open_lock, flags);
 173        }
 174        return 0;
 175
 176error_out:
 177        return -EIO;
 178}
 179
 180static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)
 181{
 182        struct snd_emu10k1 *emu;
 183        struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
 184        unsigned long flags;
 185
 186        emu = midi->emu;
 187        if (snd_BUG_ON(!emu))
 188                return -ENXIO;
 189        spin_lock_irqsave(&midi->open_lock, flags);
 190        midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
 191        midi->substream_output = substream;
 192        if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
 193                spin_unlock_irqrestore(&midi->open_lock, flags);
 194                if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
 195                        goto error_out;
 196                if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
 197                        goto error_out;
 198        } else {
 199                spin_unlock_irqrestore(&midi->open_lock, flags);
 200        }
 201        return 0;
 202
 203error_out:
 204        return -EIO;
 205}
 206
 207static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
 208{
 209        struct snd_emu10k1 *emu;
 210        struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
 211        unsigned long flags;
 212        int err = 0;
 213
 214        emu = midi->emu;
 215        if (snd_BUG_ON(!emu))
 216                return -ENXIO;
 217        spin_lock_irqsave(&midi->open_lock, flags);
 218        snd_emu10k1_intr_disable(emu, midi->rx_enable);
 219        midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
 220        midi->substream_input = NULL;
 221        if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
 222                spin_unlock_irqrestore(&midi->open_lock, flags);
 223                err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
 224        } else {
 225                spin_unlock_irqrestore(&midi->open_lock, flags);
 226        }
 227        return err;
 228}
 229
 230static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream)
 231{
 232        struct snd_emu10k1 *emu;
 233        struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
 234        unsigned long flags;
 235        int err = 0;
 236
 237        emu = midi->emu;
 238        if (snd_BUG_ON(!emu))
 239                return -ENXIO;
 240        spin_lock_irqsave(&midi->open_lock, flags);
 241        snd_emu10k1_intr_disable(emu, midi->tx_enable);
 242        midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
 243        midi->substream_output = NULL;
 244        if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
 245                spin_unlock_irqrestore(&midi->open_lock, flags);
 246                err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
 247        } else {
 248                spin_unlock_irqrestore(&midi->open_lock, flags);
 249        }
 250        return err;
 251}
 252
 253static void snd_emu10k1_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
 254{
 255        struct snd_emu10k1 *emu;
 256        struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
 257        emu = midi->emu;
 258        if (snd_BUG_ON(!emu))
 259                return;
 260
 261        if (up)
 262                snd_emu10k1_intr_enable(emu, midi->rx_enable);
 263        else
 264                snd_emu10k1_intr_disable(emu, midi->rx_enable);
 265}
 266
 267static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
 268{
 269        struct snd_emu10k1 *emu;
 270        struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
 271        unsigned long flags;
 272
 273        emu = midi->emu;
 274        if (snd_BUG_ON(!emu))
 275                return;
 276
 277        if (up) {
 278                int max = 4;
 279                unsigned char byte;
 280        
 281                /* try to send some amount of bytes here before interrupts */
 282                spin_lock_irqsave(&midi->output_lock, flags);
 283                while (max > 0) {
 284                        if (mpu401_output_ready(emu, midi)) {
 285                                if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
 286                                    snd_rawmidi_transmit(substream, &byte, 1) != 1) {
 287                                        /* no more data */
 288                                        spin_unlock_irqrestore(&midi->output_lock, flags);
 289                                        return;
 290                                }
 291                                mpu401_write_data(emu, midi, byte);
 292                                max--;
 293                        } else {
 294                                break;
 295                        }
 296                }
 297                spin_unlock_irqrestore(&midi->output_lock, flags);
 298                snd_emu10k1_intr_enable(emu, midi->tx_enable);
 299        } else {
 300                snd_emu10k1_intr_disable(emu, midi->tx_enable);
 301        }
 302}
 303
 304/*
 305
 306 */
 307
 308static struct snd_rawmidi_ops snd_emu10k1_midi_output =
 309{
 310        .open =         snd_emu10k1_midi_output_open,
 311        .close =        snd_emu10k1_midi_output_close,
 312        .trigger =      snd_emu10k1_midi_output_trigger,
 313};
 314
 315static struct snd_rawmidi_ops snd_emu10k1_midi_input =
 316{
 317        .open =         snd_emu10k1_midi_input_open,
 318        .close =        snd_emu10k1_midi_input_close,
 319        .trigger =      snd_emu10k1_midi_input_trigger,
 320};
 321
 322static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi)
 323{
 324        struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)rmidi->private_data;
 325        midi->interrupt = NULL;
 326        midi->rmidi = NULL;
 327}
 328
 329static int __devinit emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, int device, char *name)
 330{
 331        struct snd_rawmidi *rmidi;
 332        int err;
 333
 334        if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
 335                return err;
 336        midi->emu = emu;
 337        spin_lock_init(&midi->open_lock);
 338        spin_lock_init(&midi->input_lock);
 339        spin_lock_init(&midi->output_lock);
 340        strcpy(rmidi->name, name);
 341        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output);
 342        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input);
 343        rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
 344                             SNDRV_RAWMIDI_INFO_INPUT |
 345                             SNDRV_RAWMIDI_INFO_DUPLEX;
 346        rmidi->private_data = midi;
 347        rmidi->private_free = snd_emu10k1_midi_free;
 348        midi->rmidi = rmidi;
 349        return 0;
 350}
 351
 352int __devinit snd_emu10k1_midi(struct snd_emu10k1 *emu)
 353{
 354        struct snd_emu10k1_midi *midi = &emu->midi;
 355        int err;
 356
 357        if ((err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)")) < 0)
 358                return err;
 359
 360        midi->tx_enable = INTE_MIDITXENABLE;
 361        midi->rx_enable = INTE_MIDIRXENABLE;
 362        midi->port = MUDATA;
 363        midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
 364        midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
 365        midi->interrupt = snd_emu10k1_midi_interrupt;
 366        return 0;
 367}
 368
 369int __devinit snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
 370{
 371        struct snd_emu10k1_midi *midi;
 372        int err;
 373
 374        midi = &emu->midi;
 375        if ((err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)")) < 0)
 376                return err;
 377
 378        midi->tx_enable = INTE_MIDITXENABLE;
 379        midi->rx_enable = INTE_MIDIRXENABLE;
 380        midi->port = A_MUDATA1;
 381        midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
 382        midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
 383        midi->interrupt = snd_emu10k1_midi_interrupt;
 384
 385        midi = &emu->midi2;
 386        if ((err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2")) < 0)
 387                return err;
 388
 389        midi->tx_enable = INTE_A_MIDITXENABLE2;
 390        midi->rx_enable = INTE_A_MIDIRXENABLE2;
 391        midi->port = A_MUDATA2;
 392        midi->ipr_tx = IPR_A_MIDITRANSBUFEMPTY2;
 393        midi->ipr_rx = IPR_A_MIDIRECVBUFEMPTY2;
 394        midi->interrupt = snd_emu10k1_midi_interrupt2;
 395        return 0;
 396}
 397