/*
 * sound/sb_common.c
 *
 * Common routines for Sound Blaster compatible cards.
 */
/*
 * Copyright (C) by Hannu Savolainen 1993-1996
 *
 * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
 * Version 2 (June 1991). See the "COPYING" file distributed with this software
 * for more info.
 */
#include <linux/config.h>


#include "sound_config.h"

#if defined(CONFIG_SBDSP)

#ifndef CONFIG_AUDIO
#error You will need to configure the sound driver with CONFIG_AUDIO option.
#endif

#include "sb_mixer.h"
#include "sb.h"

static sb_devc *detected_devc = NULL;	/* For communication from probe to init */
static sb_devc *last_devc = NULL;	/* For MPU401 initialization */
static sb_devc *irq2devc[16] =
{NULL};
static unsigned char jazz_irq_bits[] =
{0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6};
static unsigned char jazz_dma_bits[] =
{0, 1, 0, 2, 0, 3, 0, 4};

/*
 * Jazz16 chipset specific control variables
 */

static int      jazz16_base = 0;	/* Not detected */
static unsigned char jazz16_bits = 0;	/* I/O relocation bits */

/*
 * Logitech SoundMan Wave specific initialization code
 */

#ifdef SMW_MIDI0001_INCLUDED
#include "smw-midi0001.h"
#else
unsigned char  *smw_ucode = NULL;
int             smw_ucodeLen = 0;

#endif

int
sb_dsp_command (sb_devc * devc, unsigned char val)
{
  int             i;
  unsigned long   limit;

  limit = jiffies + HZ / 10;	/*
				   * The timeout is 0.1 seconds
				 */

  /*
   * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes
   * called while interrupts are disabled. This means that the timer is
   * disabled also. However the timeout situation is a abnormal condition.
   * Normally the DSP should be ready to accept commands after just couple of
   * loops.
   */

  for (i = 0; i < 500000 && jiffies < limit; i++)
    {
      if ((inb (DSP_STATUS) & 0x80) == 0)
	{
	  outb (val, DSP_COMMAND);
	  return 1;
	}
    }

  printk ("Sound Blaster: DSP Command(%x) Timeout.\n", val);
  return 0;
}

int
sb_dsp_get_byte (sb_devc * devc)
{
  int             i;

  for (i = 1000; i; i--)
    if (inb (DSP_DATA_AVAIL) & 0x80)
      {
	return inb (DSP_READ);
      }

  return 0xffff;
}

int
ess_write (sb_devc * devc, unsigned char reg, unsigned char data)
{
  /* Write a byte to an extended mode register of ES1688 */

  if (!sb_dsp_command (devc, reg))
    return 0;

  return sb_dsp_command (devc, data);
}

int
ess_read (sb_devc * devc, unsigned char reg)
{
/* Read a byte from an extended mode register of ES1688 */

  if (!sb_dsp_command (devc, 0xc0))	/* Read register command */
    return -1;

  if (!sb_dsp_command (devc, reg))
    return -1;

  return sb_dsp_get_byte (devc);
}

void
sbintr (int irq, void *dev_id, struct pt_regs *dummy)
{
  int             status;
  unsigned char   src = 0xff;

  sb_devc        *devc = irq2devc[irq];

  if (devc == NULL || devc->irq != irq)
    {
      DEB (printk ("sbintr: Bogus interrupt IRQ%d\n", irq));
      return;
    }

  devc->irq_ok = 1;

  if (devc->model == MDL_SB16)
    {

      src = sb_getmixer (devc, IRQ_STAT);	/* Interrupt source register */

#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
      if (src & 4)
	uart401intr (devc->irq, NULL, NULL);	/* MPU401 interrupt */
#endif

      if (!(src & 3))
	return;			/* Not a DSP interrupt */
    }

  if (devc->intr_active)
    switch (devc->irq_mode)
      {
      case IMODE_OUTPUT:
	DMAbuf_outputintr (devc->dev, 1);
	break;

      case IMODE_INPUT:
	DMAbuf_inputintr (devc->dev);
	break;

      case IMODE_INIT:
	break;

      case IMODE_MIDI:
#ifdef CONFIG_MIDI
	sb_midi_interrupt (devc);
#endif
	break;

      default:
	printk ("Sound Blaster: Unexpected interrupt\n");
      }
/*
 * Acknowledge interrupts 
 */

  if (src & 0x01)
    status = inb (DSP_DATA_AVAIL);

  if (devc->model == MDL_SB16 && src & 0x02)
    status = inb (DSP_DATA_AVL16);
}

int
sb_dsp_reset (sb_devc * devc)
{
  int             loopc;

  if (devc->model == MDL_ESS)
    outb (3, DSP_RESET);	/* Reset FIFO too */
  else
    outb (1, DSP_RESET);

  tenmicrosec (devc->osp);
  outb (0, DSP_RESET);
  tenmicrosec (devc->osp);
  tenmicrosec (devc->osp);
  tenmicrosec (devc->osp);

  for (loopc = 0; loopc < 1000 && !(inb (DSP_DATA_AVAIL) & 0x80); loopc++);

  if (inb (DSP_READ) != 0xAA)
    return 0;			/* Sorry */

  if (devc->model == MDL_ESS)
    sb_dsp_command (devc, 0xc6);	/* Enable extended mode */

  return 1;
}

static void
dsp_get_vers (sb_devc * devc)
{
  int             i;

  unsigned long   flags;

  save_flags (flags);
  cli ();
  devc->major = devc->minor = 0;
  sb_dsp_command (devc, 0xe1);	/* Get version */

  for (i = 100000; i; i--)
    {
      if (inb (DSP_DATA_AVAIL) & 0x80)
	{
	  if (devc->major == 0)
	    devc->major = inb (DSP_READ);
	  else
	    {
	      devc->minor = inb (DSP_READ);
	      break;
	    }
	}
    }
  restore_flags (flags);
}

static int
sb16_set_dma_hw (sb_devc * devc)
{
  int             bits;

  if (devc->dma8 != 0 && devc->dma8 != 1 && devc->dma8 != 3)
    {
      printk ("SB16: Invalid 8 bit DMA (%d)\n", devc->dma8);
      return 0;
    }

  bits = (1 << devc->dma8);

  if (devc->dma16 >= 5 && devc->dma16 <= 7)
    bits |= (1 << devc->dma16);

  sb_setmixer (devc, DMA_NR, bits);
  return 1;
}

#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
static void
sb16_set_mpu_port(sb_devc *devc, struct address_info *hw_config)
{
/*
 * This routine initializes new MIDI port setup register of SB Vibra.
 */
	unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06;
	switch (hw_config->io_base)
	{
	case 0x300:
  		sb_setmixer (devc, 0x84, bits | 0x04);
		break;

	case 0x330:
  		sb_setmixer (devc, 0x84, bits | 0x00);
		break;

	default:
  		sb_setmixer (devc, 0x84, bits | 0x02); /* Disable MPU */
		printk("SB16: Invalid MIDI I/O port %x\n", hw_config->io_base);
	}
}
#endif

static int
sb16_set_irq_hw (sb_devc * devc, int level)
{
  int             ival;

  switch (level)
    {
    case 5:
      ival = 2;
      break;
    case 7:
      ival = 4;
      break;
    case 9:
      ival = 1;
      break;
    case 10:
      ival = 8;
      break;
    default:
      printk ("SB16 IRQ%d is not possible\n", level);
      return 0;
    }
  sb_setmixer (devc, IRQ_NR, ival);
  return 1;
}

static void
relocate_Jazz16 (sb_devc * devc, struct address_info *hw_config)
{
  unsigned char   bits = 0;
  unsigned long   flags;

  if (jazz16_base != 0 && jazz16_base != hw_config->io_base)
    return;

  switch (hw_config->io_base)
    {
    case 0x220:
      bits = 1;
      break;
    case 0x240:
      bits = 2;
      break;
    case 0x260:
      bits = 3;
      break;

    default:
      return;
    }

  bits = jazz16_bits = bits << 5;

  jazz16_base = hw_config->io_base;

/*
 * Magic wake up sequence by writing to 0x201 (aka Joystick port)
 */
  save_flags (flags);
  cli ();
  outb (0xAF, 0x201);
  outb (0x50, 0x201);
  outb (bits, 0x201);
  restore_flags (flags);
}

static int
init_Jazz16 (sb_devc * devc, struct address_info *hw_config)
{
  char            name[100];

/*
 * First try to check that the card has Jazz16 chip. It identifies itself
 * by returning 0x12 as response to DSP command 0xfa.
 */

  if (!sb_dsp_command (devc, 0xfa))
    return 0;

  if (sb_dsp_get_byte (devc) != 0x12)
    return 0;

/*
 * OK so far. Now configure the IRQ and DMA channel used by the card.
 */
  if (hw_config->irq < 1 || hw_config->irq > 15 ||
      jazz_irq_bits[hw_config->irq] == 0)
    {
      printk ("Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq);
      return 0;
    }

  if (hw_config->dma < 0 || hw_config->dma > 3 ||
      jazz_dma_bits[hw_config->dma] == 0)
    {
      printk ("Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma);
      return 0;
    }

  if (hw_config->dma2 < 0)
    {
      printk ("Jazz16: No 16 bit DMA channel defined\n");
      return 0;
    }

  if (hw_config->dma2 < 5 || hw_config->dma2 > 7 ||
      jazz_dma_bits[hw_config->dma2] == 0)
    {
      printk ("Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2);
      return 0;
    }

  devc->dma16 = hw_config->dma2;

  if (!sb_dsp_command (devc, 0xfb))
    return 0;

  if (!sb_dsp_command (devc, jazz_dma_bits[hw_config->dma] |
		       (jazz_dma_bits[hw_config->dma2] << 4)))
    return 0;

  if (!sb_dsp_command (devc, jazz_irq_bits[hw_config->irq]))
    return 0;

/*
 * Now we have configured a standard Jazz16 device. 
 */
  devc->model = MDL_JAZZ;
  strcpy (name, "Jazz16");


  hw_config->name = (char *) (sound_mem_blocks[sound_nblocks] = vmalloc (strlen (name + 1)));
  if (sound_nblocks < 1024)
    sound_nblocks++;;
  if (hw_config->name != NULL)
    strcpy (hw_config->name, name);
  devc->caps |= SB_NO_MIDI;
  return 1;
}

static int
ess_init (sb_devc * devc, struct address_info *hw_config)
{
  unsigned char   cfg, irq_bits = 0, dma_bits = 0;
  int             ess_major = 0, ess_minor = 0;
  int             i;
  char            name[100];

/*
 * Try to detect ESS chips.
 */

  sb_dsp_command (devc, 0xe7);	/* Return identification */

  for (i = 1000; i; i--)
    {
      if (inb (DSP_DATA_AVAIL) & 0x80)
	{
	  if (ess_major == 0)
	    ess_major = inb (DSP_READ);
	  else
	    {
	      ess_minor = inb (DSP_READ);
	      break;
	    }
	}
    }

  if (ess_major == 0)
    return 0;

  if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80)
    {
      sprintf (name, "ESS ES488 AudioDrive (rev %d)",
	       ess_minor & 0x0f);
      hw_config->name = name;
      devc->model = MDL_SBPRO;
      return 1;
    }
  else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80)
    {
      sprintf (name,
	       "ESS ES1688 AudioDrive (rev %d)",
	       ess_minor & 0x0f);
    }
  else
    strcpy (name, "Jazz16");

  devc->model = MDL_ESS;
  devc->submodel = ess_minor & 0x0f;

  hw_config->name = (char *) (sound_mem_blocks[sound_nblocks] = vmalloc (strlen (name + 1)));
  if (sound_nblocks < 1024)
    sound_nblocks++;;
  if (hw_config->name != NULL)
    strcpy (hw_config->name, name);


  sb_dsp_reset (devc);		/* Turn on extended mode */

/*
 *    Set IRQ configuration register
 */

  cfg = 0x50;			/* Enable only DMA counter interrupt */

  switch (devc->irq)
    {
    case 2:
    case 9:
      irq_bits = 0;
      break;

    case 5:
      irq_bits = 1;
      break;

    case 7:
      irq_bits = 2;
      break;

    case 10:
      irq_bits = 3;
      break;

    default:
      irq_bits = 0;
      cfg = 0x10;		/* Disable all interrupts */
      printk ("\nESS1688: Invalid IRQ %d\n", devc->irq);
      return 0;
    }

  if (!ess_write (devc, 0xb1, cfg | (irq_bits << 2)))
    printk ("\nESS1688: Failed to write to IRQ config register\n");

/*
 *    Set DMA configuration register
 */

  cfg = 0x50;			/* Extended mode DMA enable */

  if (devc->dma8 > 3 || devc->dma8 < 0 || devc->dma8 == 2)
    {
      dma_bits = 0;
      cfg = 0x00;		/* Disable all DMA */
      printk ("\nESS1688: Invalid DMA %d\n", devc->dma8);
    }
  else
    {
      if (devc->dma8 == 3)
	dma_bits = 3;
      else
	dma_bits = devc->dma8 + 1;
    }

  if (!ess_write (devc, 0xb2, cfg | (dma_bits << 2)))
    printk ("\nESS1688: Failed to write to DMA config register\n");

/*
 * Enable joystick and OPL3
 */

  cfg = sb_getmixer (devc, 0x40);
  sb_setmixer (devc, 0x40, cfg | 0x03);
  if (devc->submodel >= 8)	/* ES1688 */
    devc->caps |= SB_NO_MIDI;	/* ES1688 uses MPU401 MIDI mode */
  sb_dsp_reset (devc);
  return 1;
}

int
sb_dsp_detect (struct address_info *hw_config)
{
  sb_devc         sb_info;
  sb_devc        *devc = &sb_info;

/*
 * Initialize variables 
 */
  DDB (printk ("sb_dsp_detect(%x) entered\n", hw_config->io_base));
  if (check_region (hw_config->io_base, 16))
    return 0;

  memset ((char *) &sb_info, 0, sizeof (sb_info));	/* Zero everything */

  devc->osp = hw_config->osp;
  devc->type = hw_config->card_subtype;

  devc->base = hw_config->io_base;
  devc->irq = hw_config->irq;
  devc->dma8 = hw_config->dma;

  devc->dma16 = -1;

/*
 * Detect the device
 */

  cli();			/* Some ESS1688 cards need this */
  inb (devc->base + 0x9);
  inb (devc->base + 0x9);
  inb (devc->base + 0x9);
  inb (devc->base + 0xb);
  inb (devc->base + 0x9);
  inb (devc->base + 0xb);
  inb (devc->base + 0x9);
  inb (devc->base + 0x9);
  inb (devc->base + 0xb);
  inb (devc->base + 0x9);
  inb (devc->base);
  sti();

  if (sb_dsp_reset (devc))
    dsp_get_vers (devc);
  else
    devc->major = 0;

  if (devc->type == 0 || devc->type == MDL_JAZZ || devc->type == MDL_SMW)
    if (devc->major == 0 || (devc->major == 3 && devc->minor == 1))
      relocate_Jazz16 (devc, hw_config);

  if (!sb_dsp_reset (devc))
    {
      DDB (printk ("SB reset failed\n"));
      return 0;
    }

  if (devc->major == 0)
    dsp_get_vers (devc);

  if (devc->major == 3 && devc->minor == 1)
    if (devc->type == MDL_AZTECH)	/* SG Washington? */
      {
	if (sb_dsp_command (devc, 0x09))
	  if (sb_dsp_command (devc, 0x00))	/* Enter WSS mode */
	    {
	      int             i;

	      /* Have some delay */
	      for (i = 0; i < 10000; i++)
		inb (DSP_DATA_AVAIL);
	      devc->caps = SB_NO_AUDIO | SB_NO_MIDI;	/* Mixer only */
	    }
      }

/*
 * Save device information for sb_dsp_init()
 */


  detected_devc = (sb_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (sb_devc)));
  if (sound_nblocks < 1024)
    sound_nblocks++;;

  if (detected_devc == NULL)
    {
      printk ("sb: Can't allocate memory for device information\n");
      return 0;
    }

  memcpy ((char *) detected_devc, (char *) devc, sizeof (sb_devc));

  DDB (printk ("SB %d.%d detected OK (%x)\n", devc->major, devc->minor,
	       hw_config->io_base));
  return 1;
}

void
sb_dsp_init (struct address_info *hw_config)
{
  sb_devc        *devc;
  char            name[100];

#ifndef NO_SB_IRQ_TEST
  int             n;
#endif

/*
 * Check if we had detected a SB device earlier
 */
  DDB (printk ("sb_dsp_init(%x) entered\n", hw_config->io_base));

  if (detected_devc == NULL)
    {
      DDB (printk ("No detected device\n"));
      return;
    }

  devc = detected_devc;
  detected_devc = NULL;

  if (devc->base != hw_config->io_base)
    {
      DDB (printk ("I/O port mismatch\n"));
      return;
    }

/*
 * Now continue initialization of the device
 */
  devc->dev = num_audiodevs;
  devc->caps = hw_config->driver_use_1;

  if (snd_set_irq_handler (hw_config->irq,
			   sbintr, "sound blaster", devc->osp) < 0)
    {
      printk ("SB: Can't allocate IRQ%d\n", hw_config->irq);
      irq2devc[hw_config->irq] = NULL;
      return;
    }

  irq2devc[hw_config->irq] = devc;
  devc->irq_ok = 0;

  if (devc->major == 4)
    if (!sb16_set_irq_hw (devc, devc->irq))	/* Unsupported IRQ */
      {
	snd_release_irq (devc->irq);
	return;
      }

  if ((devc->type == 0 || devc->type == MDL_ESS) &&
      devc->major == 3 && devc->minor == 1)
    {				/* Handle various chipsets which claim they are SB Pro compatible */
      if ((devc->type != 0 && devc->type != MDL_ESS) ||
	  !ess_init (devc, hw_config))
	if ((devc->type != 0 && devc->type != MDL_JAZZ &&
	     devc->type != MDL_SMW) || !init_Jazz16 (devc, hw_config))
	  {
	    DDB (printk ("This is a genuine SB Pro\n"));
	  }
    }

#ifndef NO_SB_IRQ_TEST
  if (devc->major != 4 || devc->minor > 11) /* Not Sb16 v4.5 or v4.11 */
  {
    for (n = 0; n < 3 && devc->irq_ok == 0; n++)
    if (sb_dsp_command (devc, 0xf2))	/* Cause interrupt immediately */
      {
	int             i;

	for (i = 0; !devc->irq_ok && i < 10000; i++);
      }

  if (!devc->irq_ok)
    {
      printk ("sb: Interrupt test on IRQ%d failed - device disabled\n", devc->irq);
      snd_release_irq (devc->irq);
      return;
    }
  else
    {
      DDB (printk ("IRQ test OK (IRQ%d)\n", devc->irq));
    }
  }
#endif

  request_region (hw_config->io_base, 16, "sound blaster");

  switch (devc->major)
    {
    case 1:			/* SB 1.0 or 1.5 */
      devc->model = hw_config->card_subtype = MDL_SB1;
      break;

    case 2:			/* SB 2.x */
      if (devc->minor == 0)
	devc->model = hw_config->card_subtype = MDL_SB2;
      else
	devc->model = hw_config->card_subtype = MDL_SB201;
      break;

    case 3:			/* SB Pro and most clones */
      if (devc->model == 0)
	{
	  devc->model = hw_config->card_subtype = MDL_SBPRO;
	  if (hw_config->name == NULL)
	    hw_config->name = "Sound Blaster Pro";
	}
      break;

    case 4:
      devc->model = hw_config->card_subtype = MDL_SB16;
      if (hw_config->name == NULL)
	hw_config->name = "Sound Blaster 16";

      if (hw_config->dma2 == -1)
	devc->dma16 = devc->dma8;
      else if (hw_config->dma2 < 5 || hw_config->dma2 > 7)
	{
	  printk ("SB16: Bad or missing 16 bit DMA channel\n");
	  devc->dma16 = devc->dma8;
	}
      else
	devc->dma16 = hw_config->dma2;

      sb16_set_dma_hw (devc);
      devc->caps |= SB_NO_MIDI;
    }

  if (!(devc->caps & SB_NO_MIXER))
    if (devc->major == 3 || devc->major == 4)
      sb_mixer_init (devc);

#ifdef CONFIG_MIDI
  if (!(devc->caps & SB_NO_MIDI))
    sb_dsp_midi_init (devc);
#endif

  if (hw_config->name == NULL)
    hw_config->name = "Sound Blaster";

  sprintf (name, "%s (%d.%d)", hw_config->name, devc->major, devc->minor);
  conf_printf (name, hw_config);
  hw_config->card_subtype = devc->model;
  last_devc = devc;		/* For SB MPU detection */

  if (!(devc->caps & SB_NO_AUDIO))
    {
      if (sound_alloc_dma (devc->dma8, "Sound Blaster8"))
	{
	  printk ("SB: Can't allocate 8 bit DMA channel %d\n", devc->dma8);
	}
      if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
	if (sound_alloc_dma (devc->dma16, "Sound Blaster16"))
	  {
	    printk ("SB: Can't allocate 16 bit DMA channel %d\n", devc->dma16);
	  }
      sb_audio_init (devc, name);
    }
}

void
sb_dsp_disable_midi (int io_base)
{
}

void
sb_dsp_disable_recording (int io_base)
{
}

void
sb_dsp_unload (struct address_info *hw_config)
{
  sb_devc        *devc;
  int             irq = hw_config->irq;

  if (irq < 0)
    irq *= -1;

  devc = irq2devc[irq];

  if (devc && devc->base == hw_config->io_base)
    {
      release_region (devc->base, 16);
      if (!(devc->caps & SB_NO_AUDIO))
	{
	  sound_free_dma (devc->dma8);

	  if (devc->dma16 >= 0)
	    sound_free_dma (devc->dma16);
	}

      snd_release_irq (devc->irq);
      irq2devc[devc->irq] = NULL;
    }
}

/*
 * Mixer access routines
 */

void
sb_setmixer (sb_devc * devc, unsigned int port, unsigned int value)
{
  unsigned long   flags;

  save_flags (flags);
  cli ();
  outb ((unsigned char) (port & 0xff), MIXER_ADDR);

  tenmicrosec (devc->osp);
  outb ((unsigned char) (value & 0xff), MIXER_DATA);
  tenmicrosec (devc->osp);
  restore_flags (flags);
}

unsigned int
sb_getmixer (sb_devc * devc, unsigned int port)
{
  unsigned int    val;
  unsigned long   flags;

  save_flags (flags);
  cli ();
  outb ((unsigned char) (port & 0xff), MIXER_ADDR);

  tenmicrosec (devc->osp);
  val = inb (MIXER_DATA);
  tenmicrosec (devc->osp);
  restore_flags (flags);

  return val;
}

#ifdef CONFIG_MIDI
/*
 * MPU401 MIDI initialization.
 */

static void
smw_putmem (sb_devc * devc, int base, int addr, unsigned char val)
{
  unsigned long   flags;

  save_flags (flags);
  cli ();

  outb (addr & 0xff, base + 1);	/* Low address bits */
  outb (addr >> 8, base + 2);	/* High address bits */
  outb (val, base);		/* Data */

  restore_flags (flags);
}

static unsigned char
smw_getmem (sb_devc * devc, int base, int addr)
{
  unsigned long   flags;
  unsigned char   val;

  save_flags (flags);
  cli ();

  outb (addr & 0xff, base + 1);	/* Low address bits */
  outb (addr >> 8, base + 2);	/* High address bits */
  val = inb (base);		/* Data */

  restore_flags (flags);
  return val;
}

static int
smw_midi_init (sb_devc * devc, struct address_info *hw_config)
{
  int             mpu_base = hw_config->io_base;
  int             mp_base = mpu_base + 4;	/* Microcontroller base */
  int             i;
  unsigned char   control;


  /*
     *  Reset the microcontroller so that the RAM can be accessed
   */

  control = inb (mpu_base + 7);
  outb (control | 3, mpu_base + 7);	/* Set last two bits to 1 (?) */
  outb ((control & 0xfe) | 2, mpu_base + 7);	/* xxxxxxx0 resets the mc */

  for (i = 0; i < 300; i++)	/* Wait at least 1ms */
    tenmicrosec (devc->osp);

  outb (control & 0xfc, mpu_base + 7);	/* xxxxxx00 enables RAM */

  /*
     *  Detect microcontroller by probing the 8k RAM area
   */
  smw_putmem (devc, mp_base, 0, 0x00);
  smw_putmem (devc, mp_base, 1, 0xff);
  tenmicrosec (devc->osp);

  if (smw_getmem (devc, mp_base, 0) != 0x00 || smw_getmem (devc, mp_base, 1) != 0xff)
    {
      DDB (printk ("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n",
	     smw_getmem (devc, mp_base, 0), smw_getmem (devc, mp_base, 1)));
      return 0;			/* No RAM */
    }

  /*
     *  There is RAM so assume it's really a SM Wave
   */

  devc->model = MDL_SMW;
  smw_mixer_init (devc);

  if (smw_ucodeLen > 0)
    {
      if (smw_ucodeLen != 8192)
	{
	  printk ("\nSM Wave: Invalid microcode (MIDI0001.BIN) length\n");
	  return 1;
	}

      /*
       *  Download microcode
       */

      for (i = 0; i < 8192; i++)
	smw_putmem (devc, mp_base, i, smw_ucode[i]);

      /*
       *  Verify microcode
       */

      for (i = 0; i < 8192; i++)
	if (smw_getmem (devc, mp_base, i) != smw_ucode[i])
	  {
	    printk ("SM Wave: Microcode verification failed\n");
	    return 0;
	  }
    }

  control = 0;
#ifdef SMW_SCSI_IRQ
  /*
     * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt
     * is disabled by default.
     *
     * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10.
   */
  {
    static unsigned char scsi_irq_bits[] =
    {0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0};

    control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6;
  }
#endif

#ifdef SMW_OPL4_ENABLE
  /*
     *  Make the OPL4 chip visible on the PC bus at 0x380.
     *
     *  There is no need to enable this feature since this driver
     *  doesn't support OPL4 yet. Also there is no RAM in SM Wave so
     *  enabling OPL4 is pretty useless.
   */
  control |= 0x10;		/* Uses IRQ12 if bit 0x20 == 0 */
  /* control |= 0x20;      Uncomment this if you want to use IRQ7 */
#endif

  outb (control | 0x03, mpu_base + 7);	/* xxxxxx11 restarts */
  hw_config->name = "SoundMan Wave";
  return 1;
}

static int
ess_midi_init (sb_devc * devc, struct address_info *hw_config)
{
  unsigned char   cfg, tmp;

  cfg = sb_getmixer (devc, 0x40) & 0x03;

  if (devc->submodel < 8)
    {
      sb_setmixer (devc, 0x40, cfg | 0x03);	/* Enable OPL3 & joystick */
      return 0;			/* ES688 doesn't support MPU401 mode */
    }

  tmp = (hw_config->io_base & 0x0f0) >> 4;

  if (tmp > 3)
    {
      sb_setmixer (devc, 0x40, cfg);
      return 0;
    }

  cfg |= tmp << 3;

  tmp = 1;			/* MPU enabled without interrupts */

  switch (hw_config->irq)
    {
    case 9:
      tmp = 0x4;
      break;
    case 5:
      tmp = 0x5;
      break;
    case 7:
      tmp = 0x6;
      break;
    case 10:
      tmp = 0x7;
      break;
    default:
      return 0;
    }

  cfg |= tmp << 5;

  sb_setmixer (devc, 0x40, cfg | 0x03);
  return 1;
}

static int
init_Jazz16_midi (sb_devc * devc, struct address_info *hw_config)
{
  int             mpu_base = hw_config->io_base;
  int             sb_base = devc->base;
  int             irq = hw_config->irq;

  unsigned char   bits = 0;
  unsigned long   flags;

  if (irq < 0)
    irq *= -1;

  if (irq < 1 || irq > 15 ||
      jazz_irq_bits[irq] == 0)
    {
      printk ("Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq);
      return 0;
    }

  switch (sb_base)
    {
    case 0x220:
      bits = 1;
      break;
    case 0x240:
      bits = 2;
      break;
    case 0x260:
      bits = 3;
      break;

    default:
      return 0;
    }

  bits = jazz16_bits = bits << 5;

  switch (mpu_base)
    {
    case 0x310:
      bits |= 1;
      break;
    case 0x320:
      bits |= 2;
      break;
    case 0x330:
      bits |= 3;
      break;

    default:
      printk ("Jazz16: Invalid MIDI I/O port %x\n", mpu_base);
      return 0;
    }
/*
 * Magic wake up sequence by writing to 0x201 (aka Joystick port)
 */
  save_flags (flags);
  cli ();
  outb (0xAF, 0x201);
  outb (0x50, 0x201);
  outb (bits, 0x201);
  restore_flags (flags);

  hw_config->name = "Jazz16";
  smw_midi_init (devc, hw_config);

  if (!sb_dsp_command (devc, 0xfb))
    return 0;

  if (!sb_dsp_command (devc, jazz_dma_bits[devc->dma8] |
		       (jazz_dma_bits[devc->dma16] << 4)))
    return 0;

  if (!sb_dsp_command (devc, jazz_irq_bits[devc->irq] |
		       (jazz_irq_bits[irq] << 4)))
    return 0;

  return 1;
}

void
attach_sbmpu (struct address_info *hw_config)
{
#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
  attach_uart401 (hw_config);
#endif
}

int
probe_sbmpu (struct address_info *hw_config)
{
#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
  sb_devc        *devc = last_devc;

  if (last_devc == NULL)
    return 0;

  last_devc = 0;

  if (hw_config->io_base <= 0)
     return 0;

  if (check_region (hw_config->io_base, 4))
    {
      printk ("sbmpu: I/O port conflict (%x)\n", hw_config->io_base);
      return 0;
    }

  switch (devc->model)
    {
    case MDL_SB16:
      if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330)
	{
	  printk ("SB16: Invalid MIDI port %x\n", hw_config->io_base);
	  return 0;
	}
      hw_config->name = "Sound Blaster 16";
      hw_config->irq = -devc->irq;
      hw_config->dma = -1;
      hw_config->dma2 = -1;
      sb16_set_mpu_port(devc, hw_config);
      break;

    case MDL_ESS:
      if (hw_config->irq < 3 || hw_config->irq == devc->irq)
	hw_config->irq = -devc->irq;
      if (!ess_midi_init (devc, hw_config))
	return 0;
      hw_config->name = "ESS ES1688";
      break;

    case MDL_JAZZ:
      if (hw_config->irq < 3 || hw_config->irq == devc->irq)
	hw_config->irq = -devc->irq;
      if (!init_Jazz16_midi (devc, hw_config))
	return 0;
      break;

    default:
      return 0;
    }

  return probe_uart401 (hw_config);
#else
  return 0;
#endif
}

void 
unload_sbmpu (struct address_info *hw_config)
{
#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
  unload_uart401 (hw_config);
#endif
}
#else /* !CONFIG_MIDI */

void 
unload_sbmpu (struct address_info *hw_config)
{
}

int
probe_sbmpu (struct address_info *hw_config)
{
  return 0;
}

void
attach_sbmpu (struct address_info *hw_config)
{
}
#endif


#endif
