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
- Lines: 1472
- Date:
Mon Jul 10 01:45:16 1995
- Orig file:
v1.3.8/linux/drivers/sound/pss.c
- Orig date:
Mon Mar 6 11:22:09 1995
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 (¶ms, (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 (¶ms);
+ 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