patch-1.3.9 linux/drivers/sound/pss.c

Next file: linux/drivers/sound/sb16_dsp.c
Previous file: linux/drivers/sound/patmgr.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.8/linux/drivers/sound/pss.c linux/drivers/sound/pss.c
@@ -1,664 +1,250 @@
-/* Marc.Hoffman@analog.com
+/*
+ * sound/pss.c
+ *
+ * The low level driver for the Personal Sound System (ECHO ESC614).
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
 
-   This is a pss driver.
-
-   it is based on Greg.Yukna@analog.com @file{host} for DOG
-
-   Unfortunately I can't distribute the ld file needed to
-   make the pss card to emulate the SB stuff.
-
-   I have provided a simple interface to the PSS unlike the
-   DOG version.  to download a new algorithm just cat it to
-   /dev/pss 14,9.
-
-   You really need to rebuild this with the synth.ld file
-
-   get the <synth>.ld from your dos directory maybe
-   voyetra\dsp001.ld
-
-   ld2inc < synth.ld > synth-ld.h
-   (make config does the same).
-
-   rebuild
-
-   Okay if you blow things away no problem just
-
-   main(){ioctl(open("/dev/pss"),SNDCTL_PSS_RESET)};
-
-   and everything will be okay.
-
-   At first I was going to worry about applications that were using
-   the sound stuff and disallow the use of /dev/pss.  But for
-   now I figured it doesn't matter.
-
-   And if you change algos all the other applications running die off
-   due to DMA problems.  Yeah just pull the plug and watch em die.
-
-   If the registers get hosed
-   main(){ioctl(open("/dev/pss"),SNDCTL_PSS_SETUP_REGISTERS)};
-
-   Probably everything else can be done via mmap
-
-   Oh if you want to develop code for the ADSP-21xx or Program the
-   1848 just send me mail and I will hook you up.
-
-               marc.hoffman@analog.com
-
-   */
 #include "sound_config.h"
 
-#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PSS)
-
-#ifndef PSS_MSS_BASE
-#define PSS_MSS_BASE 0
-#endif
-
-#ifndef PSS_MPU_BASE
-#define PSS_MPU_BASE 0
-#endif
-
-#ifndef PSS_MPU_IRQ
-#define PSS_MPU_IRQ 0
-#endif
-
-#undef DEB
-#define DEB(x) x
-
-#include "pss.h"
-
-static int      pss_ok = 0;
-static int      sb_ok = 0;
-
-static int      pss_base;
-static int      pss_irq;
-static int      pss_dma;
-
-static int      gamePort = 0;
-
-static int      sbInt;
-static int      cdPol;
-static int      cdAddr = 0;	/* 0x340;	*/
-static int      cdInt = 10;
-
-/* Define these by hand in local.h */
-static int      wssAddr = PSS_MSS_BASE;
-static int      midiAddr = PSS_MPU_BASE;
-static int      midiInt = PSS_MPU_IRQ;
-
-static int      SoundPortAddress;
-static int      SoundPortData;
-static int      speaker = 1;
+#if defined (CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PSS) && !defined(EXCLUDE_AUDIO)
 
+/*
+ * PSS registers.
+ */
+#define REG(x)	(devc->base+x)
+#define	PSS_DATA	0
+#define	PSS_STATUS	2
+#define PSS_CONTROL	2
+#define	PSS_ID		4
+#define	PSS_IRQACK	4
+#define	PSS_PIO		0x1a
+
+/*
+ * Config registers
+ */
+#define CONF_PSS	0x10
+#define CONF_WSS	0x12
+#define CONF_SB		0x13
+#define CONF_CDROM	0x16
+#define CONF_MIDI	0x18
+
+/*
+ * Status bits.
+ */
+#define PSS_FLAG3     0x0800
+#define PSS_FLAG2     0x0400
+#define PSS_FLAG1     0x1000
+#define PSS_FLAG0     0x0800
+#define PSS_WRITE_EMPTY  0x8000
+#define PSS_READ_FULL    0x4000
 
-static struct pss_speaker default_speaker =
-{0, 0, 0, PSS_STEREO};
-
-DEFINE_WAIT_QUEUE (pss_sleeper, pss_sleep_flag);
-
+#include "coproc.h"
 #include "synth-ld.h"
 
-static int      pss_download_boot (unsigned char *block, int size);
-static int      pss_reset_dsp (void);
+typedef struct pss_config
+  {
+    int             base;
+    int             irq;
+    int             dma;
+  }
 
-static inline void
-pss_outpw (unsigned short port, unsigned short value)
-{
-  __asm__         __volatile__ ("outw %w0, %w1"
-				:	/* no outputs */
-				:"a"            (value), "d" (port));
-}
+pss_config;
 
-static inline unsigned int
-pss_inpw (unsigned short port)
-{
-  unsigned int    _v;
-  __asm__         __volatile__ ("inw %w1,%w0"
-				:"=a"           (_v):"d" (port), "0" (0));
+static pss_config pss_data;
+static pss_config *devc = &pss_data;
 
-  return _v;
-}
+static int      pss_initialized = 0;
+static int      nonstandard_microcode = 0;
 
-static void
-PSS_write (int data)
+int
+probe_pss (struct address_info *hw_config)
 {
-  int             i, limit;
+  unsigned short  id;
+  int             irq, dma;
 
-  limit = GET_TIME () + 10;	/* The timeout is 0.1 seconds */
-  /*
-   * Note! the i<5000000 is an emergency exit. The 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 < 5000000 && GET_TIME () < limit; i++)
-    {
-      if (pss_inpw (pss_base + PSS_STATUS) & PSS_WRITE_EMPTY)
-	{
-	  pss_outpw (pss_base + PSS_DATA, data);
-	  return;
-	}
-    }
-  printk ("PSS: DSP Command (%04x) Timeout.\n", data);
-  printk ("IRQ conflict???\n");
-}
-
-
-static void
-pss_setaddr (int addr, int configAddr)
-{
-  int             val;
+  devc->base = hw_config->io_base;
+  irq = devc->irq = hw_config->irq;
+  dma = devc->dma = hw_config->dma;
 
-  val = pss_inpw (configAddr);
-  val &= ADDR_MASK;
-  val |= (addr << 4);
-  pss_outpw (configAddr, val);
-}
-
-/*_____ pss_checkint
-         This function tests an interrupt number to see if
-	 it is available. It takes the interrupt button
-	 as its argument and returns TRUE if the interrupt
-	 is ok.
-*/
-static int
-pss_checkint (int intNum)
-{
-  int             val;
-  int             ret;
-  int             i;
-
-  /*_____ Set the interrupt bits */
-  switch (intNum)
-    {
-    case 3:
-      val = pss_inpw (pss_base + PSS_CONFIG);
-      val &= INT_MASK;
-      val |= INT_3_BITS;
-      pss_outpw (pss_base + PSS_CONFIG, val);
-      break;
-    case 5:
-      val = pss_inpw (pss_base + PSS_CONFIG);
-      val &= INT_MASK;
-      val |= INT_5_BITS;
-      pss_outpw (pss_base + PSS_CONFIG, val);
-      break;
-    case 7:
-      val = pss_inpw (pss_base + PSS_CONFIG);
-      val &= INT_MASK;
-      val |= INT_7_BITS;
-      pss_outpw (pss_base + PSS_CONFIG, val);
-      break;
-    case 9:
-      val = pss_inpw (pss_base + PSS_CONFIG);
-      val &= INT_MASK;
-      val |= INT_9_BITS;
-      pss_outpw (pss_base + PSS_CONFIG, val);
-      break;
-    case 10:
-      val = pss_inpw (pss_base + PSS_CONFIG);
-      val &= INT_MASK;
-      val |= INT_10_BITS;
-      pss_outpw (pss_base + PSS_CONFIG, val);
-      break;
-    case 11:
-      val = pss_inpw (pss_base + PSS_CONFIG);
-      val &= INT_MASK;
-      val |= INT_11_BITS;
-      pss_outpw (pss_base + PSS_CONFIG, val);
-      break;
-    case 12:
-      val = pss_inpw (pss_base + PSS_CONFIG);
-      val &= INT_MASK;
-      val |= INT_12_BITS;
-      pss_outpw (pss_base + PSS_CONFIG, val);
-      break;
-    default:
-      printk ("unknown interrupt selected. %d\n", intNum);
+  if (devc->base != 0x220 && devc->base != 0x240)
+    if (devc->base != 0x230 && devc->base != 0x250)	/* Some cards use these */
       return 0;
-    }
-
-  /*_____ Set the interrupt test bit */
-  val = pss_inpw (pss_base + PSS_CONFIG);
-  val |= INT_TEST_BIT;
-  pss_outpw (pss_base + PSS_CONFIG, val);
 
-  /*_____ Check if the interrupt is in use */
-  /*_____ Do it a few times in case there is a delay */
-  ret = 0;
-  for (i = 0; i < 5; i++)
-    {
-      val = pss_inpw (pss_base + PSS_CONFIG);
-      if (val & INT_TEST_PASS)
-	{
-	  ret = 1;
-	  break;
-	}
-    }
-  /*_____ Clear the Test bit and the interrupt bits */
-  val = pss_inpw (pss_base + PSS_CONFIG);
-  val &= INT_TEST_BIT_MASK;
-  val &= INT_MASK;
-  pss_outpw (pss_base + PSS_CONFIG, val);
-  return (ret);
-}
+  if (irq != 3 && irq != 5 && irq != 7 && irq != 9 &&
+      irq != 10 && irq != 11 && irq != 12)
+    return 0;
 
-/*____ pss_setint
-        This function sets the correct bits in the
-	configuration register to
-	enable the chosen interrupt.
-*/
-static void
-pss_setint (int intNum, int configAddress)
-{
-  int             val;
+  if (dma != 5 && dma != 6 && dma != 7)
+    return 0;
 
-  switch (intNum)
+  id = INW (REG (PSS_ID));
+  if ((id >> 8) != 'E')
     {
-    case 0:
-      val = pss_inpw (configAddress);
-      val &= INT_MASK;
-      pss_outpw (configAddress, val);
-      break;
-    case 3:
-      val = pss_inpw (configAddress);
-      val &= INT_MASK;
-      val |= INT_3_BITS;
-      pss_outpw (configAddress, val);
-      break;
-    case 5:
-      val = pss_inpw (configAddress);
-      val &= INT_MASK;
-      val |= INT_5_BITS;
-      pss_outpw (configAddress, val);
-      break;
-    case 7:
-      val = pss_inpw (configAddress);
-      val &= INT_MASK;
-      val |= INT_7_BITS;
-      pss_outpw (configAddress, val);
-      break;
-    case 9:
-      val = pss_inpw (configAddress);
-      val &= INT_MASK;
-      val |= INT_9_BITS;
-      pss_outpw (configAddress, val);
-      break;
-    case 10:
-      val = pss_inpw (configAddress);
-      val &= INT_MASK;
-      val |= INT_10_BITS;
-      pss_outpw (configAddress, val);
-      break;
-    case 11:
-      val = pss_inpw (configAddress);
-      val &= INT_MASK;
-      val |= INT_11_BITS;
-      pss_outpw (configAddress, val);
-      break;
-    case 12:
-      val = pss_inpw (configAddress);
-      val &= INT_MASK;
-      val |= INT_12_BITS;
-      pss_outpw (configAddress, val);
-      break;
-    default:
-      printk ("pss_setint unknown int\n");
+      printk ("No PSS signature detected at 0x%x (0x%x)\n", devc->base, id);
+      return 0;
     }
-}
 
-
-/*____ pss_setsbint
-        This function sets the correct bits in the
-	SoundBlaster configuration PSS register to
-	enable the chosen interrupt.
-	It takes a interrupt button as its argument.
-*/
-static void
-pss_setsbint (int intNum)
-{
-  int             val;
-  int             sbConfigAddress;
-
-  sbConfigAddress = pss_base + SB_CONFIG;
-  switch (intNum)
-    {
-    case 3:
-      val = pss_inpw (sbConfigAddress);
-      val &= INT_MASK;
-      val |= INT_3_BITS;
-      pss_outpw (sbConfigAddress, val);
-      break;
-    case 5:
-      val = pss_inpw (sbConfigAddress);
-      val &= INT_MASK;
-      val |= INT_5_BITS;
-      pss_outpw (sbConfigAddress, val);
-      break;
-    case 7:
-      val = pss_inpw (sbConfigAddress);
-      val &= INT_MASK;
-      val |= INT_7_BITS;
-      pss_outpw (sbConfigAddress, val);
-      break;
-    default:
-      printk ("pss_setsbint: unknown_int\n");
-    }
+  return 1;
 }
 
-/*____ pss_setsbdma
-        This function sets the correct bits in the
-	SoundBlaster configuration PSS register to
-	enable the chosen DMA channel.
-	It takes a DMA button as its argument.
-*/
-static void
-pss_setsbdma (int dmaNum)
+static int
+set_irq (pss_config * devc, int dev, int irq)
 {
-  int             val;
-  int             sbConfigAddress;
+  static unsigned short irq_bits[16] =
+  {
+    0x0000, 0x0000, 0x0000, 0x0008,
+    0x0000, 0x0010, 0x0000, 0x0018,
+    0x0000, 0x0020, 0x0028, 0x0030,
+    0x0038, 0x0000, 0x0000, 0x0000
+  };
 
-  sbConfigAddress = pss_base + SB_CONFIG;
+  unsigned short  tmp, bits;
 
-  switch (dmaNum)
-    {
-    case 1:
-      val = pss_inpw (sbConfigAddress);
-      val &= DMA_MASK;
-      val |= DMA_1_BITS;
-      pss_outpw (sbConfigAddress, val);
-      break;
-    default:
-      printk ("Personal Sound System ERROR! pss_setsbdma: unknown_dma\n");
-    }
-}
-
-/*____ pss_setwssdma
-        This function sets the correct bits in the
-	WSS configuration PSS register to
-	enable the chosen DMA channel.
-	It takes a DMA button as its argument.
-*/
-static void
-pss_setwssdma (int dmaNum)
-{
-  int             val;
-  int             wssConfigAddress;
+  if (irq < 1 || irq > 15)
+    return 0;
 
-  wssConfigAddress = pss_base + PSS_WSS_CONFIG;
+  tmp = INW (REG (dev)) & ~0x38;	/* Load confreg, mask IRQ bits out */
 
-  switch (dmaNum)
+  if ((bits = irq_bits[irq]) == 0)
     {
-    case 0:
-      val = pss_inpw (wssConfigAddress);
-      val &= DMA_MASK;
-      val |= DMA_0_BITS;
-      pss_outpw (wssConfigAddress, val);
-      break;
-    case 1:
-      val = pss_inpw (wssConfigAddress);
-      val &= DMA_MASK;
-      val |= DMA_1_BITS;
-      pss_outpw (wssConfigAddress, val);
-      break;
-    case 3:
-      val = pss_inpw (wssConfigAddress);
-      val &= DMA_MASK;
-      val |= DMA_3_BITS;
-      pss_outpw (wssConfigAddress, val);
-      break;
-    default:
-      printk ("Personal Sound System ERROR! pss_setwssdma: unknown_dma\n");
+      printk ("PSS: Invalid IRQ %d\n", irq);
+      return 0;
     }
-}
 
-
-/*_____ SetSpeakerOut
-         This function sets the Volume, Bass, Treble and Mode of
-	 the speaker out channel.
-	 */
-void
-pss_setspeaker (struct pss_speaker *spk)
-{
-  PSS_write (SET_MASTER_COMMAND);
-  if (spk->volume > PHILLIPS_VOL_MAX)
-    spk->volume = PHILLIPS_VOL_MAX;
-  if (spk->volume < PHILLIPS_VOL_MIN)
-    spk->volume = PHILLIPS_VOL_MIN;
-
-  PSS_write (MASTER_VOLUME_LEFT
-	     | (PHILLIPS_VOL_CONSTANT + spk->volume / PHILLIPS_VOL_STEP));
-  PSS_write (SET_MASTER_COMMAND);
-  PSS_write (MASTER_VOLUME_RIGHT
-	     | (PHILLIPS_VOL_CONSTANT + spk->volume / PHILLIPS_VOL_STEP));
-
-  if (spk->bass > PHILLIPS_BASS_MAX)
-    spk->bass = PHILLIPS_BASS_MAX;
-  if (spk->bass < PHILLIPS_BASS_MIN)
-    spk->bass = PHILLIPS_BASS_MIN;
-  PSS_write (SET_MASTER_COMMAND);
-  PSS_write (MASTER_BASS
-	     | (PHILLIPS_BASS_CONSTANT + spk->bass / PHILLIPS_BASS_STEP));
-
-  if (spk->treb > PHILLIPS_TREBLE_MAX)
-    spk->treb = PHILLIPS_TREBLE_MAX;
-  if (spk->treb < PHILLIPS_TREBLE_MIN)
-    spk->treb = PHILLIPS_TREBLE_MIN;
-  PSS_write (SET_MASTER_COMMAND);
-  PSS_write (MASTER_TREBLE
-	   | (PHILLIPS_TREBLE_CONSTANT + spk->treb / PHILLIPS_TREBLE_STEP));
-
-  PSS_write (SET_MASTER_COMMAND);
-  PSS_write (MASTER_SWITCH | spk->mode);
+  OUTW (tmp | bits, REG (dev));
+  return 1;
 }
 
-static void
-pss_init1848 (void)
+static int
+set_io_base (pss_config * devc, int dev, int base)
 {
-  /*_____ Wait for 1848 to init */
-  while (INB (SoundPortAddress) & SP_IN_INIT);
+  unsigned short  tmp = INW (REG (dev)) & 0x003f;
+  unsigned short  bits = (base & 0x0ffc) << 4;
 
-  /*_____ Wait for 1848 to autocal */
-  OUTB (SoundPortAddress, SP_TEST_AND_INIT);
-  while (INB (SoundPortData) & AUTO_CAL_IN_PROG);
+  OUTW (bits | tmp, REG (dev));
+
+  return 1;
 }
 
 static int
-pss_configure_registers_to_look_like_sb (void)
+set_dma (pss_config * devc, int dev, int dma)
 {
-  pss_setaddr (wssAddr, pss_base + PSS_WSS_CONFIG);
-
-  SoundPortAddress = wssAddr + 4;
-  SoundPortData = wssAddr + 5;
-
-  DEB (printk ("Turning Game Port %s.\n",
-	       gamePort ? "On" : "Off"));
-
-  /*_____ Turn on the Game port */
-  if (gamePort)
-    pss_outpw (pss_base + PSS_STATUS,
-	       pss_inpw (pss_base + PSS_STATUS) | GAME_BIT);
-  else
-    pss_outpw (pss_base + PSS_STATUS,
-	       pss_inpw (pss_base + PSS_STATUS) & GAME_BIT_MASK);
+  static unsigned short dma_bits[8] =
+  {
+    0x0001, 0x0002, 0x0000, 0x0003,
+    0x0000, 0x0005, 0x0006, 0x0007
+  };
 
+  unsigned short  tmp, bits;
 
-  DEB (printk ("PSS attaching base %x irq %d dma %d\n",
-	       pss_base, pss_irq, pss_dma));
-
-  /* Check if sb is enabled if it is check the interrupt */
-  pss_outpw (pss_base + SB_CONFIG, 0);
-
-  if (pss_irq != 0)
-    {
-      DEB (printk ("PSS Emulating Sound Blaster ADDR %04x\n", pss_base));
-      DEB (printk ("PSS SBC: attaching base %x irq %d dma %d\n",
-		   SBC_BASE, SBC_IRQ, SBC_DMA));
-
-      if (pss_checkint (SBC_IRQ) == 0)
-	{
-	  printk ("PSS! attach: int_error\n");
-	  return 0;
-	}
-
-      pss_setsbint (SBC_IRQ);
-      pss_setsbdma (SBC_DMA);
-      sb_ok = 1;
-    }
-  else
-    {
-      sb_ok = 0;
-      printk ("PSS: sound blaster error init\n");
-    }
+  if (dma < 0 || dma > 7)
+    return 0;
 
-  /* Check if cd is enabled if it is check the interrupt */
-  pss_outpw (pss_base + CD_CONFIG, 0);
+  tmp = INW (REG (dev)) & ~0x07;	/* Load confreg, mask DMA bits out */
 
-  if (cdAddr != 0)
+  if ((bits = dma_bits[dma]) == 0)
     {
-      DEB (printk ("PSS:CD drive %x irq: %d", cdAddr, cdInt));
-      if (cdInt != 0)
-	{
-	  if (pss_checkint (cdInt) == 0)
-	    {
-	      printk ("Can't allocate cdInt %d\n", cdInt);
-	    }
-	  else
-	    {
-	      int             val;
-
-	      printk ("CD poll ");
-	      pss_setaddr (cdAddr, pss_base + CD_CONFIG);
-	      pss_setint (cdInt, pss_base + CD_CONFIG);
-
-	      /* set the correct bit in the
-		 configuration register to
-		 set the irq polarity for the CD-Rom.
-		 NOTE: This bit is in the address config
-		 field, It must be configured after setting
-		 the CD-ROM ADDRESS!!! */
-	      val = pss_inpw (pss_base + CD_CONFIG);
-	      pss_outpw (pss_base + CD_CONFIG, 0);
-	      val &= CD_POL_MASK;
-	      if (cdPol)
-		val |= CD_POL_BIT;
-	      pss_outpw (pss_base + CD_CONFIG, val);
-	    }
-	}
+      printk ("PSS: Invalid DMA %d\n", dma);
+      return 0;
     }
 
-  /* Check if midi is enabled if it is check the interrupt */
-  pss_outpw (pss_base + MIDI_CONFIG, 0);
-  if (midiAddr != 0)
-    {
-      printk ("midi init %x %d\n", midiAddr, midiInt);
-      if (pss_checkint (midiInt) == 0)
-	{
-	  printk ("midi init int error %x %d\n", midiAddr, midiInt);
-	}
-      else
-	{
-	  pss_setaddr (midiAddr, pss_base + MIDI_CONFIG);
-	  pss_setint (midiInt, pss_base + MIDI_CONFIG);
-	}
-    }
+  OUTW (tmp | bits, REG (dev));
   return 1;
 }
 
-long
-attach_pss (long mem_start, struct address_info *hw_config)
+static int
+pss_reset_dsp (pss_config * devc)
 {
-  if (pss_ok)
-    {
-      if (hw_config)
-	{
-	  printk (" <PSS-ESC614>");
-	}
-
-      return mem_start;
-    }
+  unsigned long   i, limit = GET_TIME () + 10;
 
-  pss_ok = 1;
+  OUTW (0x2000, REG (PSS_CONTROL));
 
-  if (pss_configure_registers_to_look_like_sb () == 0)
-    return mem_start;
+  for (i = 0; i < 32768 && GET_TIME () < limit; i++)
+    INW (REG (PSS_CONTROL));
 
-  if (sb_ok)
-    if (pss_synthLen
-	&& pss_download_boot (pss_synth, pss_synthLen))
-      {
-	if (speaker)
-	  pss_setspeaker (&default_speaker);
-	pss_ok = 1;
-      }
-    else
-      pss_reset_dsp ();
+  OUTW (0x0000, REG (PSS_CONTROL));
 
-  return mem_start;
+  return 1;
 }
 
-int
-probe_pss (struct address_info *hw_config)
+static int
+pss_put_dspword (pss_config * devc, unsigned short word)
 {
-  pss_base = hw_config->io_base;
-  pss_irq = hw_config->irq;
-  pss_dma = hw_config->dma;
+  int             i, val;
 
-  if ((pss_inpw (pss_base + 4) & 0xff00) == 0x4500)
+  for (i = 0; i < 327680; i++)
     {
-      attach_pss (0, hw_config);
-      return 1;
+      val = INW (REG (PSS_STATUS));
+      if (val & PSS_WRITE_EMPTY)
+	{
+	  OUTW (word, REG (PSS_DATA));
+	  return 1;
+	}
     }
-  printk (" fail base %x irq %d dma %d\n", pss_base, pss_irq, pss_dma);
   return 0;
 }
 
-
 static int
-pss_reattach (void)
+pss_get_dspword (pss_config * devc, unsigned short *word)
 {
-  pss_ok = 0;
-  attach_pss (0, 0);
-  return 1;
-}
+  int             i, val;
 
-static int
-pss_reset_dsp ()
-{
-  unsigned long   i, limit = GET_TIME () + 10;
-
-  pss_outpw (pss_base + PSS_CONTROL, 0x2000);
-
-  for (i = 0; i < 32768 && GET_TIME () < limit; i++)
-    pss_inpw (pss_base + PSS_CONTROL);
-
-  pss_outpw (pss_base + PSS_CONTROL, 0x0000);
+  for (i = 0; i < 327680; i++)
+    {
+      val = INW (REG (PSS_STATUS));
+      if (val & PSS_READ_FULL)
+	{
+	  *word = INW (REG (PSS_DATA));
+	  return 1;
+	}
+    }
 
-  return 1;
+  return 0;
 }
 
-
 static int
-pss_download_boot (unsigned char *block, int size)
+pss_download_boot (pss_config * devc, unsigned char *block, int size, int flags)
 {
   int             i, limit, val, count;
 
-  printk ("PSS: downloading boot code synth.ld... ");
-
-  /*_____ Warn DSP software that a boot is coming */
-  pss_outpw (pss_base + PSS_DATA, 0x00fe);
+  if (flags & CPF_FIRST)
+    {
+/*_____ Warn DSP software that a boot is coming */
+      OUTW (0x00fe, REG (PSS_DATA));
 
-  limit = GET_TIME () + 10;
+      limit = GET_TIME () + 10;
 
-  for (i = 0; i < 32768 && GET_TIME () < limit; i++)
-    if (pss_inpw (pss_base + PSS_DATA) == 0x5500)
-      break;
+      for (i = 0; i < 32768 && GET_TIME () < limit; i++)
+	if (INW (REG (PSS_DATA)) == 0x5500)
+	  break;
 
-  pss_outpw (pss_base + PSS_DATA, *block++);
+      OUTW (*block++, REG (PSS_DATA));
 
-  pss_reset_dsp ();
-  printk ("start ");
+      pss_reset_dsp (devc);
+    }
 
   count = 1;
   while (1)
@@ -667,15 +253,15 @@
 
       for (j = 0; j < 327670; j++)
 	{
-	  /*_____ Wait for BG to appear */
-	  if (pss_inpw (pss_base + PSS_STATUS) & PSS_FLAG3)
+/*_____ Wait for BG to appear */
+	  if (INW (REG (PSS_STATUS)) & PSS_FLAG3)
 	    break;
 	}
 
       if (j == 327670)
 	{
 	  /* It's ok we timed out when the file was empty */
-	  if (count >= size)
+	  if (count >= size && flags & CPF_LAST)
 	    break;
 	  else
 	    {
@@ -684,241 +270,460 @@
 	      return 0;
 	    }
 	}
-      /*_____ Send the next byte */
-      pss_outpw (pss_base + PSS_DATA, *block++);
+/*_____ Send the next byte */
+      OUTW (*block++, REG (PSS_DATA));
       count++;
     }
 
-  /*_____ Why */
-  pss_outpw (pss_base + PSS_DATA, 0);
+  if (flags & CPF_LAST)
+    {
+/*_____ Why */
+      OUTW (0, REG (PSS_DATA));
 
-  limit = GET_TIME () + 10;
-  for (i = 0; i < 32768 && GET_TIME () < limit; i++)
-    val = pss_inpw (pss_base + PSS_STATUS);
+      limit = GET_TIME () + 10;
+      for (i = 0; i < 32768 && GET_TIME () < limit; i++)
+	val = INW (REG (PSS_STATUS));
 
-  printk ("downloaded\n");
+      limit = GET_TIME () + 10;
+      for (i = 0; i < 32768 && GET_TIME () < limit; i++)
+	{
+	  val = INW (REG (PSS_STATUS));
+	  if (val & 0x4000)
+	    break;
+	}
 
-  limit = GET_TIME () + 10;
-  for (i = 0; i < 32768 && GET_TIME () < limit; i++)
-    {
-      val = pss_inpw (pss_base + PSS_STATUS);
-      if (val & 0x4000)
-	break;
-    }
+      /* now read the version */
+      for (i = 0; i < 32000; i++)
+	{
+	  val = INW (REG (PSS_STATUS));
+	  if (val & PSS_READ_FULL)
+	    break;
+	}
+      if (i == 32000)
+	return 0;
 
-  /* now read the version */
-  for (i = 0; i < 32000; i++)
-    {
-      val = pss_inpw (pss_base + PSS_STATUS_REG);
-      if (val & PSS_READ_FULL)
-	break;
+      val = INW (REG (PSS_DATA));
+      /* printk("<PSS: microcode version %d.%d loaded>", val/16, val % 16); */
     }
-  if (i == 32000)
-    return 0;
-
-  val = pss_inpw (pss_base + PSS_DATA_REG);
 
   return 1;
 }
 
+long
+attach_pss (long mem_start, struct address_info *hw_config)
+{
+  unsigned short  id;
 
-/* The following is a simple device driver for the pss.
-   All I really care about is communication to and from the pss.
+  devc->base = hw_config->io_base;
+  devc->irq = hw_config->irq;
+  devc->dma = hw_config->dma;
 
-   The ability to reinitialize the <synth.ld>  This will be
-   default when release is chosen.
+  if (!probe_pss (hw_config))
+    return mem_start;
 
-   SNDCTL_PSS_DOWNLOAD:
+  id = INW (REG (PSS_ID)) & 0x00ff;
 
-   Okay we need to creat new minor numbers for the
-   DOWNLOAD functionality.
-
-   14,0x19 -- /dev/pssld where a read operation would output the
-                         current ld to user space
-                         where a write operation would effectively
-			 download a new ld.
-
-   14,0x09 -- /dev/psecho  would open up a communication path to the
-                         esc614 asic.  Given the ability to send
-			 messages to the asic and receive messages too.
-
-			 All messages would get read and written in the
-			 same manner.  It would be up to the application
-			 and the ld to maintain a relationship
-			 of what the messages mean.
-			
-			 for this device we need to implement select. */
-#define CODE_BUFFER_LEN (64*1024)
-static char    *code_buffer;
-static int      code_length;
+  /*
+     * Disable all emulations. Will be enabled later (if required).
+   */
+  OUTW (0x0000, REG (CONF_PSS));
+  OUTW (0x0000, REG (CONF_WSS));
+  OUTW (0x0000, REG (CONF_SB));
+  OUTW (0x0000, REG (CONF_MIDI));
+  OUTW (0x0000, REG (CONF_CDROM));
 
-static int      lock_pss = 0;
+  if (!set_irq (devc, CONF_PSS, devc->irq))
+    {
+      printk ("PSS: IRQ error\n");
+      return mem_start;
+    }
+
+  if (!set_dma (devc, CONF_PSS, devc->dma))
+    {
+      printk ("PSS: DRQ error\n");
+      return mem_start;
+    }
+
+  pss_initialized = 1;
+  printk (" <ECHO-PSS  Rev. %d>", id);
+
+  return mem_start;
+}
 
 int
-pss_open (int dev, struct fileinfo *file)
+probe_pss_mpu (struct address_info *hw_config)
 {
-  int             mode;
+  int             timeout;
+
+  if (!pss_initialized)
+    return 0;
 
-  DEB (printk ("pss_open\n"));
+  if (!set_io_base (devc, CONF_MIDI, hw_config->io_base))
+    {
+      printk ("PSS: MIDI base error.\n");
+      return 0;
+    }
 
-  if (pss_ok == 0)
-    return RET_ERROR (EIO);
+  if (!set_irq (devc, CONF_MIDI, hw_config->irq))
+    {
+      printk ("PSS: MIDI IRQ error.\n");
+      return 0;
+    }
 
-  if (lock_pss)
-    return 0;
+  if (!pss_synthLen)
+    {
+      printk ("PSS: Can't enable MPU. MIDI synth microcode not available.\n");
+      return 0;
+    }
+
+  if (!pss_download_boot (devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
+    {
+      printk ("PSS: Unable to load MIDI synth microcode to DSP.\n");
+      return 0;
+    }
 
-  lock_pss = 1;
+/*
+ * Finally wait until the DSP algorithm has initialized itself and
+ * deactivates receive interrupt.
+ */
 
-  dev = dev >> 4;
-  mode = file->mode & O_ACCMODE;
-  if (mode == O_WRONLY)
+  for (timeout = 900000; timeout > 0; timeout--)
     {
-      printk ("pss-open for WRONLY\n");
-      code_length = 0;
+      if ((INB (hw_config->io_base + 1) & 0x80) == 0)	/* Input data avail */
+	INB (hw_config->io_base);	/* Discard it */
+      else
+	break;			/* No more input */
     }
 
-  RESET_WAIT_QUEUE (pss_sleeper, pss_sleep_flag);
-  return 1;
+#ifdef EXCLUDE_MIDI
+  return 0
+#else
+  return probe_mpu401 (hw_config);
+#endif
 }
 
-void
-pss_release (int dev, struct fileinfo *file)
+static int
+pss_coproc_open (void *dev_info, int sub_device)
 {
-  int             mode;
+  switch (sub_device)
+    {
+    case COPR_MIDI:
 
-  DEB (printk ("pss_release\n"));
-  if (pss_ok == 0)
-    return RET_ERROR (EIO);
-
-  dev = dev >> 4;
-  mode = file->mode & O_ACCMODE;
-  if (mode == O_WRONLY && code_length > 0)
-    {
-#ifdef linux
-      /* This just allows interrupts while the conversion is running */
-      __asm__ ("sti");
-#endif
-      if (!pss_download_boot (code_buffer, code_length))
+      if (pss_synthLen == 0)
 	{
-	  pss_reattach ();
+	  printk ("PSS: MIDI synth microcode not available.\n");
+	  return RET_ERROR (EIO);
 	}
+
+      if (nonstandard_microcode)
+	if (!pss_download_boot (devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
+	  {
+	    printk ("PSS: Unable to load MIDI synth microcode to DSP.\n");
+	    return RET_ERROR (EIO);
+	  }
+      nonstandard_microcode = 0;
+      break;
+
+    default:;
     }
-  lock_pss = 0;
+  return 0;
 }
 
-int
-pss_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+static void
+pss_coproc_close (void *dev_info, int sub_device)
 {
-  int             c, p;
-
-  DEB (printk ("pss_read\n"));
-  if (pss_ok == 0)
-    return RET_ERROR (EIO);
-
-  dev = dev >> 4;
-  p = 0;
-  c = count;
+  return;
+}
 
-  return count - c;
+static void
+pss_coproc_reset (void *dev_info)
+{
+  if (pss_synthLen)
+    if (!pss_download_boot (devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
+      {
+	printk ("PSS: Unable to load MIDI synth microcode to DSP.\n");
+      }
+  nonstandard_microcode = 0;
 }
 
-int
-pss_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+static int
+download_boot_block (void *dev_info, copr_buffer * buf)
 {
-  DEB (printk ("pss_write\n"));
-  if (pss_ok == 0)
-    return RET_ERROR (EIO);
-  dev = dev >> 4;
+  if (buf->len <= 0 || buf->len > sizeof (buf->data))
+    return RET_ERROR (EINVAL);
 
-  if (count)			/* Flush output */
+  if (!pss_download_boot (devc, buf->data, buf->len, buf->flags))
     {
-      COPY_FROM_USER (&code_buffer[code_length], buf, 0, count);
-      code_length += count;
+      printk ("PSS: Unable to load microcode block to DSP.\n");
+      return RET_ERROR (EIO);
     }
-  return count;
-}
+  nonstandard_microcode = 1;	/* The MIDI microcode has been overwritten */
 
+  return 0;
+}
 
-int
-pss_ioctl (int dev, struct fileinfo *file,
-	   unsigned int cmd, unsigned int arg)
+static int
+pss_coproc_ioctl (void *dev_info, unsigned int cmd, unsigned int arg, int local)
 {
-  DEB (printk ("pss_ioctl dev=%d cmd=%x\n", dev, cmd));
-  if (pss_ok == 0)
-    return RET_ERROR (EIO);
-
-  dev = dev >> 4;
+  /* printk("PSS coproc ioctl %x %x %d\n", cmd, arg, local); */
 
   switch (cmd)
     {
-    case SNDCTL_PSS_RESET:
-      pss_reattach ();
-      return 1;
-
-    case SNDCTL_PSS_SETUP_REGISTERS:
-      pss_configure_registers_to_look_like_sb ();
-      return 1;
+    case SNDCTL_COPR_RESET:
+      pss_coproc_reset (dev_info);
+      return 0;
+      break;
+
+    case SNDCTL_COPR_LOAD:
+      {
+	copr_buffer    *buf;
+	int             err;
+
+	buf = (copr_buffer *) KERNEL_MALLOC (sizeof (copr_buffer));
+	IOCTL_FROM_USER ((char *) buf, (char *) arg, 0, sizeof (*buf));
+	err = download_boot_block (dev_info, buf);
+	KERNEL_FREE (buf);
+	return err;
+      }
+      break;
+
+    case SNDCTL_COPR_RDATA:
+      {
+	copr_debug_buf  buf;
+	unsigned long   flags;
+	unsigned short  tmp;
+
+	IOCTL_FROM_USER ((char *) &buf, (char *) arg, 0, sizeof (buf));
+
+	DISABLE_INTR (flags);
+	if (!pss_put_dspword (devc, 0x00d0))
+	  {
+	    RESTORE_INTR (flags);
+	    return RET_ERROR (EIO);
+	  }
+
+	if (!pss_put_dspword (devc, (unsigned short) (buf.parm1 & 0xffff)))
+	  {
+	    RESTORE_INTR (flags);
+	    return RET_ERROR (EIO);
+	  }
+
+	if (!pss_get_dspword (devc, &tmp))
+	  {
+	    RESTORE_INTR (flags);
+	    return RET_ERROR (EIO);
+	  }
+
+	buf.parm1 = tmp;
+	RESTORE_INTR (flags);
+
+	IOCTL_TO_USER ((char *) arg, 0, &buf, sizeof (buf));
+	return 0;
+      }
+      break;
+
+    case SNDCTL_COPR_WDATA:
+      {
+	copr_debug_buf  buf;
+	unsigned long   flags;
+	unsigned short  tmp;
+
+	IOCTL_FROM_USER ((char *) &buf, (char *) arg, 0, sizeof (buf));
+
+	DISABLE_INTR (flags);
+	if (!pss_put_dspword (devc, 0x00d1))
+	  {
+	    RESTORE_INTR (flags);
+	    return RET_ERROR (EIO);
+	  }
+
+	if (!pss_put_dspword (devc, (unsigned short) (buf.parm1 & 0xffff)))
+	  {
+	    RESTORE_INTR (flags);
+	    return RET_ERROR (EIO);
+	  }
+
+	tmp = (unsigned int) buf.parm2 & 0xffff;
+	if (!pss_put_dspword (devc, tmp))
+	  {
+	    RESTORE_INTR (flags);
+	    return RET_ERROR (EIO);
+	  }
 
-    case SNDCTL_PSS_SPEAKER:
+	RESTORE_INTR (flags);
+	return 0;
+      }
+      break;
+
+    case SNDCTL_COPR_WCODE:
       {
-	struct pss_speaker params;
-	COPY_FROM_USER (&params, (char *) arg, 0, sizeof (struct pss_speaker));
+	copr_debug_buf  buf;
+	unsigned long   flags;
+	unsigned short  tmp;
+
+	IOCTL_FROM_USER ((char *) &buf, (char *) arg, 0, sizeof (buf));
+
+	DISABLE_INTR (flags);
+	if (!pss_put_dspword (devc, 0x00d3))
+	  {
+	    RESTORE_INTR (flags);
+	    return RET_ERROR (EIO);
+	  }
+
+	if (!pss_put_dspword (devc, (unsigned short) (buf.parm1 & 0xffff)))
+	  {
+	    RESTORE_INTR (flags);
+	    return RET_ERROR (EIO);
+	  }
+
+	tmp = ((unsigned int) buf.parm2 >> 8) & 0xffff;
+	if (!pss_put_dspword (devc, tmp))
+	  {
+	    RESTORE_INTR (flags);
+	    return RET_ERROR (EIO);
+	  }
+
+	tmp = (unsigned int) buf.parm2 & 0x00ff;
+	if (!pss_put_dspword (devc, tmp))
+	  {
+	    RESTORE_INTR (flags);
+	    return RET_ERROR (EIO);
+	  }
 
-	pss_setspeaker (&params);
+	RESTORE_INTR (flags);
 	return 0;
       }
+      break;
+
+    case SNDCTL_COPR_RCODE:
+      {
+	copr_debug_buf  buf;
+	unsigned long   flags;
+	unsigned short  tmp;
+
+	IOCTL_FROM_USER ((char *) &buf, (char *) arg, 0, sizeof (buf));
+
+	DISABLE_INTR (flags);
+	if (!pss_put_dspword (devc, 0x00d2))
+	  {
+	    RESTORE_INTR (flags);
+	    return RET_ERROR (EIO);
+	  }
+
+	if (!pss_put_dspword (devc, (unsigned short) (buf.parm1 & 0xffff)))
+	  {
+	    RESTORE_INTR (flags);
+	    return RET_ERROR (EIO);
+	  }
+
+	if (!pss_get_dspword (devc, &tmp))	/* Read msb */
+	  {
+	    RESTORE_INTR (flags);
+	    return RET_ERROR (EIO);
+	  }
+
+	buf.parm1 = tmp << 8;
+
+	if (!pss_get_dspword (devc, &tmp))	/* Read lsb */
+	  {
+	    RESTORE_INTR (flags);
+	    return RET_ERROR (EIO);
+	  }
+
+	buf.parm1 |= tmp & 0x00ff;
+
+	RESTORE_INTR (flags);
+
+	IOCTL_TO_USER ((char *) arg, 0, &buf, sizeof (buf));
+	return 0;
+      }
+      break;
+
     default:
-      return RET_ERROR (EIO);
+      return RET_ERROR (EINVAL);
     }
+
+  return RET_ERROR (EINVAL);
 }
 
-/* This is going to be used to implement
-   waiting on messages sent from the DSP and to the
-   DSP when communication is used via the pss directly.
+static coproc_operations pss_coproc_operations =
+{
+  "ADSP-2115",
+  pss_coproc_open,
+  pss_coproc_close,
+  pss_coproc_ioctl,
+  pss_coproc_reset,
+  &pss_data
+};
 
-   We need to find out if the pss can generate a different
-   interrupt other than the one it has been setup for.
+long
+attach_pss_mpu (long mem_start, struct address_info *hw_config)
+{
+  int             prev_devs;
+  long            ret;
 
-   This way we can carry on a conversation with the pss
-   on a separate channel.  This would be useful for debugging. */
+#ifndef EXCLUDE_MIDI
+  prev_devs = num_midis;
+  ret = attach_mpu401 (mem_start, hw_config);
 
-pss_select (int dev, struct fileinfo * file, int sel_type, select_table * wait)
+  if (num_midis == (prev_devs + 1))	/* The MPU driver installed itself */
+    midi_devs[prev_devs]->coproc = &pss_coproc_operations;
+#endif
+  return ret;
+}
+
+int
+probe_pss_mss (struct address_info *hw_config)
 {
-  return 0;
-  if (pss_ok == 0)
-    return RET_ERROR (EIO);
+  int             timeout;
 
-  dev = dev >> 4;
+  if (!pss_initialized)
+    return 0;
 
-  switch (sel_type)
+  if (!set_io_base (devc, CONF_WSS, hw_config->io_base))
     {
-    case SEL_IN:
-      select_wait (&pss_sleeper, wait);
+      printk ("PSS: WSS base error.\n");
       return 0;
-      break;
+    }
 
-    case SEL_OUT:
-      select_wait (&pss_sleeper, wait);
+  if (!set_irq (devc, CONF_WSS, hw_config->irq))
+    {
+      printk ("PSS: WSS IRQ error.\n");
       return 0;
-      break;
+    }
 
-    case SEL_EX:
+  if (!set_dma (devc, CONF_WSS, hw_config->dma))
+    {
+      printk ("PSS: WSS DRQ error\n");
       return 0;
     }
 
-  return 0;
+  /*
+     * For some reason the card returns 0xff in the WSS status register
+     * immediately after boot. Propably MIDI+SB emulation algorithm
+     * downloaded to the ADSP2115 spends some time initializing the card.
+     * Let's try to wait until it finishes this task.
+   */
+  for (timeout = 0;
+       timeout < 100000 && (INB (hw_config->io_base + 3) & 0x3f) != 0x04;
+       timeout++);
+
+  return probe_ms_sound (hw_config);
 }
 
 long
-pss_init (long mem_start)
+attach_pss_mss (long mem_start, struct address_info *hw_config)
 {
-  DEB (printk ("pss_init\n"));
-  if (pss_ok)
-    {
-      code_buffer = mem_start;
-      mem_start += CODE_BUFFER_LEN;
-    }
-  return mem_start;
+  int             prev_devs;
+  long            ret;
+
+  prev_devs = num_audiodevs;
+  ret = attach_ms_sound (mem_start, hw_config);
+
+  if (num_audiodevs == (prev_devs + 1))		/* The MSS driver installed itself */
+    audio_devs[prev_devs]->coproc = &pss_coproc_operations;
+
+  return ret;
 }
 
 #endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this