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                dev_err(emu->card->dev,
  68                        "cmd: clear rx timeout (status = 0x%x)\n",
  69                        mpu401_read_stat(emu, mpu));
  70#endif
  71}
  72
  73/*
  74
  75 */
  76
  77static void do_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, unsigned int status)
  78{
  79        unsigned char byte;
  80
  81        if (midi->rmidi == NULL) {
  82                snd_emu10k1_intr_disable(emu, midi->tx_enable | midi->rx_enable);
  83                return;
  84        }
  85
  86        spin_lock(&midi->input_lock);
  87        if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
  88                if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
  89                        mpu401_clear_rx(emu, midi);
  90                } else {
  91                        byte = mpu401_read_data(emu, midi);
  92                        if (midi->substream_input)
  93                                snd_rawmidi_receive(midi->substream_input, &byte, 1);
  94                }
  95        }
  96        spin_unlock(&midi->input_lock);
  97
  98        spin_lock(&midi->output_lock);
  99        if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
 100                if (midi->substream_output &&
 101                    snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
 102                        mpu401_write_data(emu, midi, byte);
 103                } else {
 104                        snd_emu10k1_intr_disable(emu, midi->tx_enable);
 105                }
 106        }
 107        spin_unlock(&midi->output_lock);
 108}
 109
 110static void snd_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, unsigned int status)
 111{
 112        do_emu10k1_midi_interrupt(emu, &emu->midi, status);
 113}
 114
 115static void snd_emu10k1_midi_interrupt2(struct snd_emu10k1 *emu, unsigned int status)
 116{
 117        do_emu10k1_midi_interrupt(emu, &emu->midi2, status);
 118}
 119
 120static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack)
 121{
 122        unsigned long flags;
 123        int timeout, ok;
 124
 125        spin_lock_irqsave(&midi->input_lock, flags);
 126        mpu401_write_data(emu, midi, 0x00);
 127        /* mpu401_clear_rx(emu, midi); */
 128
 129        mpu401_write_cmd(emu, midi, cmd);
 130        if (ack) {
 131                ok = 0;
 132                timeout = 10000;
 133                while (!ok && timeout-- > 0) {
 134                        if (mpu401_input_avail(emu, midi)) {
 135                                if (mpu401_read_data(emu, midi) == MPU401_ACK)
 136                                        ok = 1;
 137                        }
 138                }
 139                if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
 140                        ok = 1;
 141        } else {
 142                ok = 1;
 143        }
 144        spin_unlock_irqrestore(&midi->input_lock, flags);
 145        if (!ok) {
 146                dev_err(emu->card->dev,
 147                        "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
 148                           cmd, emu->port,
 149                           mpu401_read_stat(emu, midi),
 150                           mpu401_read_data(emu, midi));
 151                return 1;
 152        }
 153        return 0;
 154}
 155
 156static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)
 157{
 158        struct snd_emu10k1 *emu;
 159        struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
 160        unsigned long flags;
 161
 162        emu = midi->emu;
 163        if (snd_BUG_ON(!emu))
 164                return -ENXIO;
 165        spin_lock_irqsave(&midi->open_lock, flags);
 166        midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
 167        midi->substream_input = substream;
 168        if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
 169                spin_unlock_irqrestore(&midi->open_lock, flags);
 170                if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
 171                        goto error_out;
 172                if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
 173                        goto error_out;
 174        } else {
 175                spin_unlock_irqrestore(&midi->open_lock, flags);
 176        }
 177        return 0;
 178
 179error_out:
 180        return -EIO;
 181}
 182
 183static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)
 184{
 185        struct snd_emu10k1 *emu;
 186        struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
 187        unsigned long flags;
 188
 189        emu = midi->emu;
 190        if (snd_BUG_ON(!emu))
 191                return -ENXIO;
 192        spin_lock_irqsave(&midi->open_lock, flags);
 193        midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
 194        midi->substream_output = substream;
 195        if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
 196                spin_unlock_irqrestore(&midi->open_lock, flags);
 197                if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
 198                        goto error_out;
 199                if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
 200                        goto error_out;
 201        } else {
 202                spin_unlock_irqrestore(&midi->open_lock, flags);
 203        }
 204        return 0;
 205
 206error_out:
 207        return -EIO;
 208}
 209
 210static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
 211{
 212        struct snd_emu10k1 *emu;
 213        struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
 214        unsigned long flags;
 215        int err = 0;
 216
 217        emu = midi->emu;
 218        if (snd_BUG_ON(!emu))
 219                return -ENXIO;
 220        spin_lock_irqsave(&midi->open_lock, flags);
 221        snd_emu10k1_intr_disable(emu, midi->rx_enable);
 222        midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
 223        midi->substream_input = NULL;
 224        if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
 225                spin_unlock_irqrestore(&midi->open_lock, flags);
 226                err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
 227        } else {
 228                spin_unlock_irqrestore(&midi->open_lock, flags);
 229        }
 230        return err;
 231}
 232
 233static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream)
 234{
 235        struct snd_emu10k1 *emu;
 236        struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
 237        unsigned long flags;
 238        int err = 0;
 239
 240        emu = midi->emu;
 241        if (snd_BUG_ON(!emu))
 242                return -ENXIO;
 243        spin_lock_irqsave(&midi->open_lock, flags);
 244        snd_emu10k1_intr_disable(emu, midi->tx_enable);
 245        midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
 246        midi->substream_output = NULL;
 247        if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
 248                spin_unlock_irqrestore(&midi->open_lock, flags);
 249                err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
 250        } else {
 251                spin_unlock_irqrestore(&midi->open_lock, flags);
 252        }
 253        return err;
 254}
 255
 256static void snd_emu10k1_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
 257{
 258        struct snd_emu10k1 *emu;
 259        struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
 260        emu = midi->emu;
 261        if (snd_BUG_ON(!emu))
 262                return;
 263
 264        if (up)
 265                snd_emu10k1_intr_enable(emu, midi->rx_enable);
 266        else
 267                snd_emu10k1_intr_disable(emu, midi->rx_enable);
 268}
 269
 270static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
 271{
 272        struct snd_emu10k1 *emu;
 273        struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
 274        unsigned long flags;
 275
 276        emu = midi->emu;
 277        if (snd_BUG_ON(!emu))
 278                return;
 279
 280        if (up) {
 281                int max = 4;
 282                unsigned char byte;
 283        
 284                /* try to send some amount of bytes here before interrupts */
 285                spin_lock_irqsave(&midi->output_lock, flags);
 286                while (max > 0) {
 287                        if (mpu401_output_ready(emu, midi)) {
 288                                if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
 289                                    snd_rawmidi_transmit(substream, &byte, 1) != 1) {
 290                                        /* no more data */
 291                                        spin_unlock_irqrestore(&midi->output_lock, flags);
 292                                        return;
 293                                }
 294                                mpu401_write_data(emu, midi, byte);
 295                                max--;
 296                        } else {
 297                                break;
 298                        }
 299                }
 300                spin_unlock_irqrestore(&midi->output_lock, flags);
 301                snd_emu10k1_intr_enable(emu, midi->tx_enable);
 302        } else {
 303                snd_emu10k1_intr_disable(emu, midi->tx_enable);
 304        }
 305}
 306
 307/*
 308
 309 */
 310
 311static struct snd_rawmidi_ops snd_emu10k1_midi_output =
 312{
 313        .open =         snd_emu10k1_midi_output_open,
 314        .close =        snd_emu10k1_midi_output_close,
 315        .trigger =      snd_emu10k1_midi_output_trigger,
 316};
 317
 318static struct snd_rawmidi_ops snd_emu10k1_midi_input =
 319{
 320        .open =         snd_emu10k1_midi_input_open,
 321        .close =        snd_emu10k1_midi_input_close,
 322        .trigger =      snd_emu10k1_midi_input_trigger,
 323};
 324
 325static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi)
 326{
 327        struct snd_emu10k1_midi *midi = rmidi->private_data;
 328        midi->interrupt = NULL;
 329        midi->rmidi = NULL;
 330}
 331
 332static int emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, int device, char *name)
 333{
 334        struct snd_rawmidi *rmidi;
 335        int err;
 336
 337        if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
 338                return err;
 339        midi->emu = emu;
 340        spin_lock_init(&midi->open_lock);
 341        spin_lock_init(&midi->input_lock);
 342        spin_lock_init(&midi->output_lock);
 343        strcpy(rmidi->name, name);
 344        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output);
 345        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input);
 346        rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
 347                             SNDRV_RAWMIDI_INFO_INPUT |
 348                             SNDRV_RAWMIDI_INFO_DUPLEX;
 349        rmidi->private_data = midi;
 350        rmidi->private_free = snd_emu10k1_midi_free;
 351        midi->rmidi = rmidi;
 352        return 0;
 353}
 354
 355int snd_emu10k1_midi(struct snd_emu10k1 *emu)
 356{
 357        struct snd_emu10k1_midi *midi = &emu->midi;
 358        int err;
 359
 360        if ((err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)")) < 0)
 361                return err;
 362
 363        midi->tx_enable = INTE_MIDITXENABLE;
 364        midi->rx_enable = INTE_MIDIRXENABLE;
 365        midi->port = MUDATA;
 366        midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
 367        midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
 368        midi->interrupt = snd_emu10k1_midi_interrupt;
 369        return 0;
 370}
 371
 372int snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
 373{
 374        struct snd_emu10k1_midi *midi;
 375        int err;
 376
 377        midi = &emu->midi;
 378        if ((err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)")) < 0)
 379                return err;
 380
 381        midi->tx_enable = INTE_MIDITXENABLE;
 382        midi->rx_enable = INTE_MIDIRXENABLE;
 383        midi->port = A_MUDATA1;
 384        midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
 385        midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
 386        midi->interrupt = snd_emu10k1_midi_interrupt;
 387
 388        midi = &emu->midi2;
 389        if ((err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2")) < 0)
 390                return err;
 391
 392        midi->tx_enable = INTE_A_MIDITXENABLE2;
 393        midi->rx_enable = INTE_A_MIDIRXENABLE2;
 394        midi->port = A_MUDATA2;
 395        midi->ipr_tx = IPR_A_MIDITRANSBUFEMPTY2;
 396        midi->ipr_rx = IPR_A_MIDIRECVBUFEMPTY2;
 397        midi->interrupt = snd_emu10k1_midi_interrupt2;
 398        return 0;
 399}
 400