patch-2.2.18 linux/drivers/sound/waveartist.c

Next file: linux/drivers/sound/waveartist.h
Previous file: linux/drivers/sound/vidc_synth.c
Back to the patch index
Back to the overall index

diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/drivers/sound/waveartist.c linux/drivers/sound/waveartist.c
@@ -2,13 +2,13 @@
  * drivers/sound/waveartist.c
  *
  * The low level driver for the RWA010 Rockwell Wave Artist
- * codec chip used in the Corel Computer NetWinder.
+ * codec chip used in the Rebel.com NetWinder.
  *
  * Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk)
  */
 
 /*
- * Copyright (C) by Corel Computer 1998
+ * Copyright (C) by Rebel.com 1998-1999
  *
  * RWA010 specs received under NDA from Rockwell
  *
@@ -40,12 +40,20 @@
 #include <linux/delay.h>
 #include <linux/smp.h>
 
+#include <asm/dec21285.h>
 #include <asm/hardware.h>
 
 #include "soundmodule.h"
 #include "sound_config.h"
 #include "waveartist.h"
 
+#ifndef _ISA_DMA
+#define _ISA_DMA(x) (x)
+#endif
+#ifndef _ISA_IRQ
+#define _ISA_IRQ(x) (x)
+#endif
+
 #define	VNC_TIMER_PERIOD (HZ/4)	//check slider 4 times/sec
 
 #define	MIXER_PRIVATE3_RESET	0x53570000
@@ -141,8 +149,288 @@
 
 static int		 nr_waveartist_devs;
 static wavnc_info	 adev_info[MAX_AUDIO_DEV];
+
+static int waveartist_mixer_set(wavnc_info *devc, int whichDev, unsigned int level);
+
+/*
+ * Rebel.com Netwinder specifics...
+ */
 static struct timer_list vnc_timer;
 
+static void
+vnc_mute(wavnc_info *devc, int mute)
+{
+	extern spinlock_t gpio_lock;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE);
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	devc->mute_state = mute;
+}
+
+static int
+vnc_volume_slider(wavnc_info *devc)
+{
+	static signed int old_slider_volume;
+	unsigned long flags;
+	signed int volume = 255;
+
+	*CSR_TIMER1_LOAD = 0x00ffffff;
+
+	save_flags(flags);
+	cli();
+
+	outb(0xFF, 0x201);
+	*CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
+
+	while (volume && (inb(0x201) & 0x01))
+		volume--;
+
+	*CSR_TIMER1_CNTL = 0;
+
+	restore_flags(flags);
+	
+	volume = 0x00ffffff - *CSR_TIMER1_VALUE;
+
+
+#ifndef REVERSE
+	volume = 150 - (volume >> 5);
+#else
+	volume = (volume >> 6) - 25;
+#endif
+
+	if (volume < 0)
+		volume = 0;
+
+	if (volume > 100)
+		volume = 100;
+
+	/*
+	 * slider quite often reads +-8, so debounce this random noise
+	 */
+	if ((volume - old_slider_volume) > 7 ||
+	    (old_slider_volume - volume) > 7) {
+		old_slider_volume = volume;
+
+		DEB(printk("Slider volume: %d.\n", old_slider_volume));
+	}
+
+	return old_slider_volume;
+}
+
+static int
+vnc_slider(wavnc_info *devc)
+{
+	signed int slider_volume;
+	unsigned int temp;
+
+	/*
+	 * read the "buttons" state.
+	 *  Bit 4 = handset present,
+	 *  Bit 5 = offhook
+	 */
+	// the state should be "querable" via a private IOCTL call
+	temp = inb(0x201) & 0x30;
+
+	if (!devc->handset_mute_sw &&
+	    (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) {
+		devc->handset_state = temp;
+		devc->handset_mute_sw = 0;
+
+		vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0);
+	}
+
+	slider_volume = vnc_volume_slider(devc);
+
+	/*
+	 * If we're using software controlled volume, and
+	 * the slider moves by more than 20%, then we
+	 * switch back to slider controlled volume.
+	 */
+	if (devc->slider_vol > slider_volume) {
+		if (devc->slider_vol - slider_volume > 20)
+			devc->use_slider = 1;
+	} else {
+		if (slider_volume - devc->slider_vol > 20)
+			devc->use_slider = 1;
+	}
+
+	/*
+	 * use only left channel
+	 */
+	temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
+
+	if (slider_volume != temp && devc->use_slider) {
+		devc->slider_vol = slider_volume;
+
+		waveartist_mixer_set(devc, SOUND_MIXER_VOLUME,
+			slider_volume | slider_volume << 8);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+static void
+vnc_slider_tick(unsigned long data)
+{
+	int next_timeout;
+
+	if (vnc_slider(adev_info + data))
+		next_timeout = 5;	// mixer reported change
+	else
+		next_timeout = VNC_TIMER_PERIOD;
+
+	mod_timer(&vnc_timer, jiffies + next_timeout);
+}
+
+static int
+vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
+	int val, temp;
+
+	if (cmd == SOUND_MIXER_PRIVATE1) {
+		/*
+		 * Use this call to override the automatic handset
+		 * behaviour - ignore handset.
+		 *  bit 7 = total control over handset - do not
+		 *          to plug/unplug
+		 *  bit 4 = internal mic
+		 *  bit 0 = mute internal speaker
+		 */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		devc->handset_mute_sw = val & 0x80;
+
+		temp = val & VNC_INTERNAL_SPKR;
+		if (devc->mute_state != temp)
+			vnc_mute(devc, temp);
+
+		devc->handset_state = val & VNC_INTERNAL_MIC;
+		waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC);
+		return 0;
+	} else if (cmd == SOUND_MIXER_PRIVATE2) {
+		/*
+		 * Use this call to get private settings
+		 */
+
+		val = 0;
+		if (devc->handset_mute_sw)
+		    val |= 0x80;	
+
+		if (devc->mute_state)
+		    val |= VNC_INTERNAL_SPKR;
+		    
+		if (devc->handset_state)
+		    val |= VNC_INTERNAL_MIC;
+
+		if (put_user(val, (int *)arg))
+			return -EFAULT;
+
+		return 0;
+	}
+#if 0
+	if (cmd == SOUND_MIXER_PRIVATE2) {
+#define VNC_SOUND_PAUSE         0x53    //to pause the DSP
+#define VNC_SOUND_RESUME        0x57    //to unpause the DSP
+		int             val;
+
+		val = *(int *) arg;
+
+		printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val);
+
+		if (val == VNC_SOUND_PAUSE) {
+			wa_sendcmd(0x16);    //PAUSE the ADC
+		} else if (val == VNC_SOUND_RESUME) {
+			wa_sendcmd(0x18);    //RESUME the ADC
+		} else {
+			return -EINVAL;      //invalid parameters...
+		}
+		return 0;
+	}
+
+	if (cmd == SOUND_MIXER_PRIVATE3) {
+		long unsigned   flags;
+		int             mixer_reg[15];	//reg 14 is actually a command: read,write,reset
+
+		int             val;
+		int             i;
+
+		val = *(int *) arg;
+
+		if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT))
+			return (-EFAULT);
+
+		memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg));
+
+		if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) { 	//reset command??
+			wavnc_mixer_reset(devc);
+			return (0);
+		} else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) {	//write command??
+//			printk("WaveArtist Mixer: Private write command.\n");
+
+			wa_sendcmd(0x32);	//Pair1 - word 1 and 5
+			wa_sendcmd(mixer_reg[0]);
+			wa_sendcmd(mixer_reg[4]);
+
+			wa_sendcmd(0x32);	//Pair2 - word 2 and 6
+			wa_sendcmd(mixer_reg[1]);
+			wa_sendcmd(mixer_reg[5]);
+
+			wa_sendcmd(0x32);	//Pair3 - word 3 and 7
+			wa_sendcmd(mixer_reg[2]);
+			wa_sendcmd(mixer_reg[6]);
+
+			wa_sendcmd(0x32);	//Pair4 - word 4 and 8
+			wa_sendcmd(mixer_reg[3]);
+			wa_sendcmd(mixer_reg[7]);
+
+			wa_sendcmd(0x32);	//Pair5 - word 9 and 10
+			wa_sendcmd(mixer_reg[8]);
+			wa_sendcmd(mixer_reg[9]);
+
+			wa_sendcmd(0x0031);		//set left and right PCM
+			wa_sendcmd(mixer_reg[0x0A]);
+			wa_sendcmd(mixer_reg[0x0B]);
+
+			wa_sendcmd(0x0131);		//set left and right FM
+			wa_sendcmd(mixer_reg[0x0C]);
+			wa_sendcmd(mixer_reg[0x0D]);
+
+			return 0;
+		} else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) {	//read command?
+//			printk("WaveArtist Mixer: Private read command.\n");
+
+			//first read all current values...
+			save_flags(flags);
+			cli();
+
+			for (i = 0; i < 14; i++) {
+				wa_sendcmd((i << 8) + 0x30);	// get ready for command nn30H
+
+				while (!(inb(STATR) & CMD_RF)) {
+				};	//wait for response ready...
+
+				mixer_reg[i] = inw(CMDR);
+			}
+			restore_flags(flags);
+
+			if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT))
+				return (-EFAULT);
+
+			memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg));
+			return 0;
+		} else
+			return -EINVAL;
+	}
+#endif
+	return -EINVAL;
+}
 
 static inline void
 waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set)
@@ -368,10 +656,12 @@
 	udelay(20);
 	inw(hw->io_base + CMDR);	// discard second word == 0
 
-	rev[0] = temp >> 8;
-	rev[1] = temp & 255;
-	rev[2] = '\0';
-
+	if (rev)
+	{
+	    rev[0] = temp >> 8;
+	    rev[1] = temp & 255;
+	    rev[2] = '\0';
+	}
 	return temp;
 }
 
@@ -1368,138 +1658,12 @@
 waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
 	wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
-#if 0
-	//use this call to override the automatic handset behaviour - ignore handset
-	//bit 0x80 = total control over handset - do not react to plug/unplug
-	//bit 0x10 = 1 == internal mic, otherwise handset mic
-	//bit 0x01 = 1 == mute internal speaker, otherwise unmute
-
-	if (cmd == SOUND_MIXER_PRIVATE1) {
-		int             val, temp;
-
-		val = *(int *) arg;
-
-//		printk("MIXER_PRIVATE1: passed parameter = 0x%X.\n",val);
-		return -EINVAL;		//check if parameter is logical...
-
-		devc->soft_mute_flag = val;
-
-		temp = val & VNC_INTERNAL_SPKR;
-		if (temp != devc->mute_state) {
-//			printk("MIXER_PRIVATE1: mute_mono(0x%X).\n",temp);
-			vnc_mute(devc, temp);
-		}
-
-//		temp = devc->handset_state;
-
-		// do not check if it is not already in
-		// the right setting, since we are
-		// laying about the current state...
-
-//		if ((val & VNC_INTERNAL_MIC) != temp) {
-			devc->handset_state = val & VNC_INTERNAL_MIC;
-//			printk("MIXER_PRIVATE1: mixer_set(0x%X).\n",devc->handset_state);
-			wa_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC);
-//			devc->handset_state = temp;
-		}
-		return 0;
-	}
-#if 0
-	if (cmd == SOUND_MIXER_PRIVATE2) {
-#define VNC_SOUND_PAUSE         0x53    //to pause the DSP
-#define VNC_SOUND_RESUME        0x57    //to unpause the DSP
-		int             val;
-
-		val = *(int *) arg;
-
-		printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val);
-
-		if (val == VNC_SOUND_PAUSE) {
-			wa_sendcmd(0x16);    //PAUSE the ADC
-		} else if (val == VNC_SOUND_RESUME) {
-			wa_sendcmd(0x18);    //RESUME the ADC
-		} else {
-			return -EINVAL;      //invalid parameters...
-		}
-		return 0;
-	}
-#endif
-
-	if (cmd == SOUND_MIXER_PRIVATE3) {
-		long unsigned   flags;
-		int             mixer_reg[15];	//reg 14 is actually a command: read,write,reset
-
-		int             val;
-		int             i;
-
-		val = *(int *) arg;
 
-		if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT))
-			return (-EFAULT);
-
-		memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg));
-
-		if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) { 	//reset command??
-			wavnc_mixer_reset(devc);
-			return (0);
-		} else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) {	//write command??
-//			printk("WaveArtist Mixer: Private write command.\n");
-
-			wa_sendcmd(0x32);	//Pair1 - word 1 and 5
-			wa_sendcmd(mixer_reg[0]);
-			wa_sendcmd(mixer_reg[4]);
+	if (cmd == SOUND_MIXER_PRIVATE1 ||
+	    cmd == SOUND_MIXER_PRIVATE2 ||
+	    cmd == SOUND_MIXER_PRIVATE3)
+		return vnc_private_ioctl(dev, cmd, arg);
 
-			wa_sendcmd(0x32);	//Pair2 - word 2 and 6
-			wa_sendcmd(mixer_reg[1]);
-			wa_sendcmd(mixer_reg[5]);
-
-			wa_sendcmd(0x32);	//Pair3 - word 3 and 7
-			wa_sendcmd(mixer_reg[2]);
-			wa_sendcmd(mixer_reg[6]);
-
-			wa_sendcmd(0x32);	//Pair4 - word 4 and 8
-			wa_sendcmd(mixer_reg[3]);
-			wa_sendcmd(mixer_reg[7]);
-
-			wa_sendcmd(0x32);	//Pair5 - word 9 and 10
-			wa_sendcmd(mixer_reg[8]);
-			wa_sendcmd(mixer_reg[9]);
-
-			wa_sendcmd(0x0031);		//set left and right PCM
-			wa_sendcmd(mixer_reg[0x0A]);
-			wa_sendcmd(mixer_reg[0x0B]);
-
-			wa_sendcmd(0x0131);		//set left and right FM
-			wa_sendcmd(mixer_reg[0x0C]);
-			wa_sendcmd(mixer_reg[0x0D]);
-
-			return 0;
-		} else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) {	//read command?
-//			printk("WaveArtist Mixer: Private read command.\n");
-
-			//first read all current values...
-			save_flags(flags);
-			cli();
-
-			for (i = 0; i < 14; i++) {
-				wa_sendcmd((i << 8) + 0x30);	// get ready for command nn30H
-
-				while (!(inb(STATR) & CMD_RF)) {
-				};	//wait for response ready...
-
-				mixer_reg[i] = inw(CMDR);
-			}
-			restore_flags(flags);
-
-			if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT))
-				return (-EFAULT);
-
-			memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg));
-			return 0;
-		} else
-			return -EINVAL;
-	}
-#endif
 	if (((cmd >> 8) & 0xff) == 'M') {
 		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
 			int val;
@@ -1660,143 +1824,11 @@
 	return -1;
 }
 
-/*
- * Corel Netwinder specifics...
- */
-static void
-vnc_mute(wavnc_info *devc, int mute)
-{
-	extern spinlock_t gpio_lock;
-	unsigned long flags;
-
-	spin_lock_irqsave(&gpio_lock, flags);
-	cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE);
-	spin_unlock_irqrestore(&gpio_lock, flags);
-
-	devc->mute_state = mute;
-}
-
-static int
-vnc_volume_slider(wavnc_info *devc)
-{
-	static signed int old_slider_volume;
-	unsigned long flags;
-	signed int volume = 255;
-
-	*CSR_TIMER1_LOAD = 0x00ffffff;
-
-	save_flags(flags);
-	cli();
-
-	outb(0xFF, 0x201);
-	*CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
-
-	while (volume && (inb(0x201) & 0x01))
-		volume--;
-
-	*CSR_TIMER1_CNTL = 0;
-
-	restore_flags(flags);
-	
-	volume = 0x00ffffff - *CSR_TIMER1_VALUE;
-
-
-#ifndef REVERSE
-	volume = 150 - (volume >> 5);
-#else
-	volume = (volume >> 6) - 25;
-#endif
-
-	if (volume < 0)
-		volume = 0;
-
-	if (volume > 100)
-		volume = 100;
-
-	/*
-	 * slider quite often reads +-8, so debounce this random noise
-	 */
-	if ((volume - old_slider_volume) > 7 ||
-	    (old_slider_volume - volume) > 7) {
-		old_slider_volume = volume;
-
-		DEB(printk("Slider volume: %d.\n", old_slider_volume));
-	}
-
-	return old_slider_volume;
-}
-
-static int
-vnc_slider(wavnc_info *devc)
-{
-	signed int slider_volume;
-	unsigned int temp;
-
-	/*
-	 * read the "buttons" state.
-	 *  Bit 4 = handset present,
-	 *  Bit 5 = offhook
-	 */
-	// the state should be "querable" via a private IOCTL call
-	temp = inb(0x201) & 0x30;
-
-	if (!devc->handset_mute_sw &&
-	    (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) {
-		devc->handset_state = temp;
-		devc->handset_mute_sw = 0;
-
-		vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0);
-	}
-
-	slider_volume = vnc_volume_slider(devc);
-
-	/*
-	 * If we're using software controlled volume, and
-	 * the slider moves by more than 20%, then we
-	 * switch back to slider controlled volume.
-	 */
-	if (devc->slider_vol > slider_volume) {
-		if (devc->slider_vol - slider_volume > 20)
-			devc->use_slider = 1;
-	} else {
-		if (slider_volume - devc->slider_vol > 20)
-			devc->use_slider = 1;
-	}
-
-	/*
-	 * use only left channel
-	 */
-	temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
-
-	if (slider_volume != temp && devc->use_slider) {
-		devc->slider_vol = slider_volume;
-
-		waveartist_mixer_set(devc, SOUND_MIXER_VOLUME,
-			slider_volume | slider_volume << 8);
-
-		return 1;
-	}
-
-	return 0;
-}
-
-static void
-vnc_slider_tick(unsigned long data)
-{
-	int next_timeout;
-
-	if (vnc_slider(adev_info + data))
-		next_timeout = 5;	// mixer reported change
-	else
-		next_timeout = VNC_TIMER_PERIOD;
-
-	mod_timer(&vnc_timer, jiffies + next_timeout);
-}
-
 int
 probe_waveartist(struct address_info *hw_config)
 {
 	wavnc_info *devc = &adev_info[nr_waveartist_devs];
+	int temp;
 
 	if (nr_waveartist_devs >= MAX_AUDIO_DEV) {
 		printk("waveartist: too many audio devices\n");
@@ -1808,14 +1840,39 @@
 		return 0;
 	}
 
-	if (hw_config->irq > 31 || hw_config->irq < 16) {
+	if (hw_config->irq > _ISA_IRQ(15) || hw_config->irq < _ISA_IRQ(0)) {
 		printk("WaveArtist: Bad IRQ %d\n", hw_config->irq);
 		return 0;
 	}
 
-	if (hw_config->dma != 3) {
+	if (hw_config->dma != _ISA_DMA(3)) {
 		printk("WaveArtist: Bad DMA %d\n", hw_config->dma);
 		return 0;
+	}
+
+	/*
+	 * check if hardware is there... Get version, expect something
+	 * like 'B1' = 0x4231 first check if SB regs are present - if
+	 * so - WA is there as well.  Try to write to the SB CD volume
+	 * mixer reg (and read it back) if OK - the hardware is there.
+	 * If bad rev the first time, wait and retry...
+	 */
+	outb(0x28, 0x224);
+	outb(0x55, 0x225);
+	if (inb(0x225) != 0x55) {
+		printk(KERN_INFO "WaveArtist: probe failed at port 0x%X.\n",
+			hw_config->io_base);
+		return 0;
+	}
+
+	temp = waveartist_getrev(hw_config, 0);
+	if ((temp>>8) != 0x42 || ((temp&0xFF) < 0x30)) {
+		mdelay(200);
+		temp = waveartist_getrev(hw_config, 0);
+		if ((temp>>8) != 0x42 || ((temp&0xFF) < 0x30)) {
+		    	printk(KERN_DEBUG "WaveArtist probe: error reading "
+				"device rev.(0x%04X).\n", temp);
+		}
 	}
 
 	hw_config->name = "WaveArtist";

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)