Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/sound/oss/pas2_pcm.c b/sound/oss/pas2_pcm.c
new file mode 100644
index 0000000..4af6aaf
--- /dev/null
+++ b/sound/oss/pas2_pcm.c
@@ -0,0 +1,437 @@
+/*
+ * pas2_pcm.c Audio routines for PAS16
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free 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.
+ *
+ *
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ * Alan Cox	   : Swatted a double allocation of device bug. Made a few
+ *		     more things module options.
+ * Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init()
+ */
+
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <asm/timex.h>
+#include "sound_config.h"
+
+#include "pas2.h"
+
+#ifndef DEB
+#define DEB(WHAT)
+#endif
+
+#define PAS_PCM_INTRBITS (0x08)
+/*
+ * Sample buffer timer interrupt enable
+ */
+
+#define PCM_NON	0
+#define PCM_DAC	1
+#define PCM_ADC	2
+
+static unsigned long pcm_speed; 	/* sampling rate */
+static unsigned char pcm_channels = 1;	/* channels (1 or 2) */
+static unsigned char pcm_bits = 8;	/* bits/sample (8 or 16) */
+static unsigned char pcm_filter;	/* filter FLAG */
+static unsigned char pcm_mode = PCM_NON;
+static unsigned long pcm_count;
+static unsigned short pcm_bitsok = 8;	/* mask of OK bits */
+static int      pcm_busy;
+int             pas_audiodev = -1;
+static int      open_mode;
+
+extern spinlock_t pas_lock;
+
+static int pcm_set_speed(int arg)
+{
+	int foo, tmp;
+	unsigned long flags;
+
+	if (arg == 0)
+		return pcm_speed;
+
+	if (arg > 44100)
+		arg = 44100;
+	if (arg < 5000)
+		arg = 5000;
+
+	if (pcm_channels & 2)
+	{
+		foo = ((CLOCK_TICK_RATE / 2) + (arg / 2)) / arg;
+		arg = ((CLOCK_TICK_RATE / 2) + (foo / 2)) / foo;
+	}
+	else
+	{
+		foo = (CLOCK_TICK_RATE + (arg / 2)) / arg;
+		arg = (CLOCK_TICK_RATE + (foo / 2)) / foo;
+	}
+
+	pcm_speed = arg;
+
+	tmp = pas_read(0x0B8A);
+
+	/*
+	 * Set anti-aliasing filters according to sample rate. You really *NEED*
+	 * to enable this feature for all normal recording unless you want to
+	 * experiment with aliasing effects.
+	 * These filters apply to the selected "recording" source.
+	 * I (pfw) don't know the encoding of these 5 bits. The values shown
+	 * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
+	 *
+	 * I cleared bit 5 of these values, since that bit controls the master
+	 * mute flag. (Olav Wölfelschneider)
+	 *
+	 */
+#if !defined NO_AUTO_FILTER_SET
+	tmp &= 0xe0;
+	if (pcm_speed >= 2 * 17897)
+		tmp |= 0x01;
+	else if (pcm_speed >= 2 * 15909)
+		tmp |= 0x02;
+	else if (pcm_speed >= 2 * 11931)
+		tmp |= 0x09;
+	else if (pcm_speed >= 2 * 8948)
+		tmp |= 0x11;
+	else if (pcm_speed >= 2 * 5965)
+		tmp |= 0x19;
+	else if (pcm_speed >= 2 * 2982)
+		tmp |= 0x04;
+	pcm_filter = tmp;
+#endif
+
+	spin_lock_irqsave(&pas_lock, flags);
+
+	pas_write(tmp & ~(0x40 | 0x80), 0x0B8A);
+	pas_write(0x00 | 0x30 | 0x04, 0x138B);
+	pas_write(foo & 0xff, 0x1388);
+	pas_write((foo >> 8) & 0xff, 0x1388);
+	pas_write(tmp, 0x0B8A);
+
+	spin_unlock_irqrestore(&pas_lock, flags);
+
+	return pcm_speed;
+}
+
+static int pcm_set_channels(int arg)
+{
+
+	if ((arg != 1) && (arg != 2))
+		return pcm_channels;
+
+	if (arg != pcm_channels)
+	{
+		pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);
+
+		pcm_channels = arg;
+		pcm_set_speed(pcm_speed);	/* The speed must be reinitialized */
+	}
+	return pcm_channels;
+}
+
+static int pcm_set_bits(int arg)
+{
+	if (arg == 0)
+		return pcm_bits;
+
+	if ((arg & pcm_bitsok) != arg)
+		return pcm_bits;
+
+	if (arg != pcm_bits)
+	{
+		pas_write(pas_read(0x8389) ^ 0x04, 0x8389);
+
+		pcm_bits = arg;
+	}
+	return pcm_bits;
+}
+
+static int pas_audio_ioctl(int dev, unsigned int cmd, void __user *arg)
+{
+	int val, ret;
+	int __user *p = arg;
+
+	DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+
+	switch (cmd) 
+	{
+	case SOUND_PCM_WRITE_RATE:
+		if (get_user(val, p)) 
+			return -EFAULT;
+		ret = pcm_set_speed(val);
+		break;
+
+	case SOUND_PCM_READ_RATE:
+		ret = pcm_speed;
+		break;
+		
+	case SNDCTL_DSP_STEREO:
+		if (get_user(val, p)) 
+			return -EFAULT;
+		ret = pcm_set_channels(val + 1) - 1;
+		break;
+
+	case SOUND_PCM_WRITE_CHANNELS:
+		if (get_user(val, p)) 
+			return -EFAULT;
+		ret = pcm_set_channels(val);
+		break;
+
+	case SOUND_PCM_READ_CHANNELS:
+		ret = pcm_channels;
+		break;
+
+	case SNDCTL_DSP_SETFMT:
+		if (get_user(val, p))
+			return -EFAULT;
+		ret = pcm_set_bits(val);
+		break;
+		
+	case SOUND_PCM_READ_BITS:
+		ret = pcm_bits;
+		break;
+  
+	default:
+		return -EINVAL;
+	}
+	return put_user(ret, p);
+}
+
+static void pas_audio_reset(int dev)
+{
+	DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n"));
+
+	pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);	/* Disable PCM */
+}
+
+static int pas_audio_open(int dev, int mode)
+{
+	int             err;
+	unsigned long   flags;
+
+	DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode));
+
+	spin_lock_irqsave(&pas_lock, flags);
+	if (pcm_busy)
+	{
+		spin_unlock_irqrestore(&pas_lock, flags);
+		return -EBUSY;
+	}
+	pcm_busy = 1;
+	spin_unlock_irqrestore(&pas_lock, flags);
+
+	if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0)
+		return err;
+
+
+	pcm_count = 0;
+	open_mode = mode;
+
+	return 0;
+}
+
+static void pas_audio_close(int dev)
+{
+	unsigned long   flags;
+
+	DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n"));
+
+	spin_lock_irqsave(&pas_lock, flags);
+
+	pas_audio_reset(dev);
+	pas_remove_intr(PAS_PCM_INTRBITS);
+	pcm_mode = PCM_NON;
+
+	pcm_busy = 0;
+	spin_unlock_irqrestore(&pas_lock, flags);
+}
+
+static void pas_audio_output_block(int dev, unsigned long buf, int count,
+		       int intrflag)
+{
+	unsigned long   flags, cnt;
+
+	DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count));
+
+	cnt = count;
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		cnt >>= 1;
+
+	if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+	    intrflag &&
+	    cnt == pcm_count)
+		return;
+
+	spin_lock_irqsave(&pas_lock, flags);
+
+	pas_write(pas_read(0xF8A) & ~0x40,
+		  0xF8A);
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+
+	if (count != pcm_count)
+	{
+		pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
+		pas_write(0x40 | 0x30 | 0x04, 0x138B);
+		pas_write(count & 0xff, 0x1389);
+		pas_write((count >> 8) & 0xff, 0x1389);
+		pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
+
+		pcm_count = count;
+	}
+	pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
+#ifdef NO_TRIGGER
+	pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
+#endif
+
+	pcm_mode = PCM_DAC;
+
+	spin_unlock_irqrestore(&pas_lock, flags);
+}
+
+static void pas_audio_start_input(int dev, unsigned long buf, int count,
+		      int intrflag)
+{
+	unsigned long   flags;
+	int             cnt;
+
+	DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count));
+
+	cnt = count;
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		cnt >>= 1;
+
+	if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
+	    intrflag &&
+	    cnt == pcm_count)
+		return;
+
+	spin_lock_irqsave(&pas_lock, flags);
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+
+	if (count != pcm_count)
+	{
+		pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
+		pas_write(0x40 | 0x30 | 0x04, 0x138B);
+		pas_write(count & 0xff, 0x1389);
+		pas_write((count >> 8) & 0xff, 0x1389);
+		pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
+
+		pcm_count = count;
+	}
+	pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
+#ifdef NO_TRIGGER
+	pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
+#endif
+
+	pcm_mode = PCM_ADC;
+
+	spin_unlock_irqrestore(&pas_lock, flags);
+}
+
+#ifndef NO_TRIGGER
+static void pas_audio_trigger(int dev, int state)
+{
+	unsigned long   flags;
+
+	spin_lock_irqsave(&pas_lock, flags);
+	state &= open_mode;
+
+	if (state & PCM_ENABLE_OUTPUT)
+		pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
+	else if (state & PCM_ENABLE_INPUT)
+		pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
+	else
+		pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
+
+	spin_unlock_irqrestore(&pas_lock, flags);
+}
+#endif
+
+static int pas_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+	pas_audio_reset(dev);
+	return 0;
+}
+
+static int pas_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+	pas_audio_reset(dev);
+	return 0;
+}
+
+static struct audio_driver pas_audio_driver =
+{
+	.owner			= THIS_MODULE,
+	.open			= pas_audio_open,
+	.close			= pas_audio_close,
+	.output_block		= pas_audio_output_block,
+	.start_input		= pas_audio_start_input,
+	.ioctl			= pas_audio_ioctl,
+	.prepare_for_input	= pas_audio_prepare_for_input,
+	.prepare_for_output	= pas_audio_prepare_for_output,
+	.halt_io		= pas_audio_reset,
+	.trigger		= pas_audio_trigger
+};
+
+void __init pas_pcm_init(struct address_info *hw_config)
+{
+	DEB(printk("pas2_pcm.c: long pas_pcm_init()\n"));
+
+	pcm_bitsok = 8;
+	if (pas_read(0xEF8B) & 0x08)
+		pcm_bitsok |= 16;
+
+	pcm_set_speed(DSP_DEFAULT_SPEED);
+
+	if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+					"Pro Audio Spectrum",
+					&pas_audio_driver,
+					sizeof(struct audio_driver),
+					DMA_AUTOMODE,
+					AFMT_U8 | AFMT_S16_LE,
+					NULL,
+					hw_config->dma,
+					hw_config->dma)) < 0)
+		printk(KERN_WARNING "PAS16: Too many PCM devices available\n");
+}
+
+void pas_pcm_interrupt(unsigned char status, int cause)
+{
+	if (cause == 1)
+	{
+		/*
+		 * Halt the PCM first. Otherwise we don't have time to start a new
+		 * block before the PCM chip proceeds to the next sample
+		 */
+
+		if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
+			pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
+
+		switch (pcm_mode)
+		{
+			case PCM_DAC:
+				DMAbuf_outputintr(pas_audiodev, 1);
+				break;
+
+			case PCM_ADC:
+				DMAbuf_inputintr(pas_audiodev);
+				break;
+
+			default:
+				printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n");
+		}
+	}
+}