patch-2.4.0-test7 linux/drivers/sound/emu10k1/mixer.c

Next file: linux/drivers/sound/emu10k1/osutils.c
Previous file: linux/drivers/sound/emu10k1/midi.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test6/linux/drivers/sound/emu10k1/mixer.c linux/drivers/sound/emu10k1/mixer.c
@@ -1,4 +1,3 @@
-
 /*
  **********************************************************************
  *     mixer.c - /dev/mixer interface for emu10k1 driver
@@ -36,9 +35,13 @@
 
 #define __NO_VERSION__		/* Kernel version only defined once */
 #include <linux/module.h>
+#include <linux/version.h>
+#include <linux/bitops.h>
 #include <asm/uaccess.h>
 
 #include "hwaccess.h"
+#include "8010.h"
+#include "recmgr.h"
 
 #define AC97_PESSIMISTIC
 #undef OSS_DOCUMENTED_MIXER_SEMANTICS
@@ -49,31 +52,13 @@
 #define vol_to_sw_5(hwvol) (((31 - (hwvol)) * 100) / 31)
 #define vol_to_sw_4(hwvol) (((15 - (hwvol)) * 100) / 15)
 
+#define DM_MUTE 0x80000000
+
 #ifdef PRIVATE_PCM_VOLUME
 struct sblive_pcm_volume_rec sblive_pcm_volume[MAX_PCM_CHANNELS];
+u16 pcm_last_mixer = 0x6464;
 #endif
 
-/* --------------------------------------------------------------------- */
-
-/*
- * hweightN: returns the hamming weight (i.e. the number
- * of bits set) of a N-bit word
- */
-
-#ifdef hweight32
-#undef hweight32
-#endif
-
-extern __inline__ unsigned int hweight32(unsigned int w)
-{
-	unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
-
-	res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
-	res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
-	res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
-	return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
-}
-
 /* Mapping arrays */
 static const unsigned int recsrc[] = {
 	SOUND_MASK_MIC,
@@ -84,10 +69,10 @@
 	SOUND_MASK_VOLUME,
 	SOUND_MASK_OGAIN,	/* Used to be PHONEOUT */
 	SOUND_MASK_PHONEIN,
+#ifdef TONE_CONTROL
 	SOUND_MASK_TREBLE,
 	SOUND_MASK_BASS,
-	SOUND_MASK_MONITOR,
-	SOUND_MASK_PCM,
+#endif
 };
 
 static const unsigned char volreg[SOUND_MIXER_NRDEVICES] = {
@@ -115,7 +100,7 @@
 	/* 4 bit mono */
 	[SOUND_MIXER_IGAIN] = AC97_RECORDGAINMIC,
 	/* test code */
-	[SOUND_MIXER_BASS] = AC97_GENERALPUPOSE,
+	[SOUND_MIXER_BASS] = AC97_GENERALPURPOSE,
 	[SOUND_MIXER_TREBLE] = AC97_MASTERTONE,
 	[SOUND_MIXER_LINE2] = AC97_PCMOUTVOLUME,
 	[SOUND_MIXER_DIGITAL2] = AC97_MASTERVOLUME
@@ -134,12 +119,25 @@
 	int nL, nR;
 
 	switch (ch) {
+	case SOUND_MIXER_PCM:
+	case SOUND_MIXER_VOLUME:
+#ifdef TONE_CONTROL
+	case SOUND_MIXER_TREBLE:
+        case SOUND_MIXER_BASS:
+#endif
+                return put_user(0x0000, (int *) arg);
+	default:
+		break;
+	}
+
+	if(card->isaps)
+		return -EINVAL;
+
+	switch (ch) {
 	case SOUND_MIXER_LINE:
 	case SOUND_MIXER_CD:
 	case SOUND_MIXER_VIDEO:
 	case SOUND_MIXER_LINE1:
-	case SOUND_MIXER_PCM:
-	case SOUND_MIXER_VOLUME:
 		sblive_readac97(card, volreg[ch], &reg);
 		nL = ((~(reg >> 8) & 0x1f) * 100) / 32;
 		nR = (~(reg & 0x1f) * 100) / 32;
@@ -165,9 +163,6 @@
 		nR = (~(reg & 0x1f) * 100) / 16;
 		return put_user(reg & 0x8000 ? 0 : (nL << 8) | nR, (int *) arg);
 
-	case SOUND_MIXER_TREBLE:
-	case SOUND_MIXER_BASS:
-		return put_user(0x0000, (int *) arg);
 	default:
 		return -EINVAL;
 	}
@@ -204,7 +199,9 @@
 	[SOUND_MIXER_DIGITAL2] = 19
 };
 
-u32 bass_table[41][5] = {
+#ifdef TONE_CONTROL
+
+static const u32 bass_table[41][5] = {
 	{ 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 },
 	{ 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d },
 	{ 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee },
@@ -248,7 +245,7 @@
 	{ 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 }
 };
 
-u32 treble_table[41][5] = {
+static const u32 treble_table[41][5] = {
 	{ 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 },
 	{ 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 },
 	{ 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 },
@@ -299,8 +296,8 @@
 	l = (l * 40 + 50) / 100;
 	r = (r * 40 + 50) / 100;
 	for (i = 0; i < 5; i++) {
-		sblive_writeptr(card, FXGPREGBASE + 0x70 + (i * 2), 0, bass_table[l][i]);
-		sblive_writeptr(card, FXGPREGBASE + 0x70 + (i * 2) + 1, 0, bass_table[r][i]);
+		sblive_writeptr(card, FXGPREGBASE + 0x80 + (i * 2), 0, bass_table[l][i]);
+		sblive_writeptr(card, FXGPREGBASE + 0x80 + (i * 2) + 1, 0, bass_table[r][i]);
 	}
 }
 
@@ -311,12 +308,14 @@
 	l = (l * 40 + 50) / 100;
 	r = (r * 40 + 50) / 100;
 	for (i = 0; i < 5; i++) {
-		sblive_writeptr(card, FXGPREGBASE + 0x80 + (i * 2), 0, treble_table[l][i]);
-		sblive_writeptr(card, FXGPREGBASE + 0x80 + (i * 2) + 1, 0, treble_table[r][i]);
+		sblive_writeptr(card, FXGPREGBASE + 0x90 + (i * 2), 0, treble_table[l][i]);
+		sblive_writeptr(card, FXGPREGBASE + 0x90 + (i * 2) + 1, 0, treble_table[r][i]);
 	}
 }
 
-u32 db_table[101] = {
+#endif
+
+static const u32 db_table[101] = {
 	0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540,
 	0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8,
 	0x0207567a, 0x021fd03d, 0x0239714c, 0x02544792, 0x027061a1,
@@ -340,6 +339,29 @@
 	0x7fffffff,
 };
 
+static void aps_update_digital(struct emu10k1_card *card)
+{
+	int i, l1, r1, l2, r2;
+	
+	i = card->arrwVol[volidx[SOUND_MIXER_VOLUME]];
+	l1 = (i & 0xff);
+	r1 = ((i >> 8) & 0xff);
+
+	i = card->arrwVol[volidx[SOUND_MIXER_PCM]];
+	l2 = (i & 0xff);
+	r2 = ((i >> 8) & 0xff);
+	
+	for (i = 0; i < 108; i++) {
+		if (card->digmix[i] != DM_MUTE) {
+			if ((i % 18 >= 0) && (i % 18 < 4))
+				card->digmix[i] = ((i & 1) ? ((u64) db_table[r1] * (u64) db_table[r2]) : ((u64) db_table[l1] * (u64) db_table[l2])) >> 31;
+			else
+				card->digmix[i] = (i & 1) ? db_table[r1] : db_table[l1];
+			sblive_writeptr(card, FXGPREGBASE + 0x10 + i, 0, card->digmix[i]);
+		}
+	}
+}
+
 static void update_digital(struct emu10k1_card *card)
 {
 	int i, k, l1, r1, l2, r2, l3, r3, l4, r4;
@@ -376,11 +398,11 @@
 		l1 = i;
 	}
 
-	for (i = 0; i < 16; i++) {
-		if (card->digmix[i] != 0x80000000) {
-			if ((i >= 0) && (i < 4))
+	for (i = 0; i < 36; i++) {
+		if (card->digmix[i] != DM_MUTE) {
+			if (((i >= 0) && (i < 4)) || ((i >= 18) && (i < 22)))
 				j = (i & 1) ? ((u64) db_table[r1] * (u64) db_table[r3]) : ((u64) db_table[l1] * (u64) db_table[l3]);
-			else if ((i == 6) || (i == 7))
+			else if ((i == 6) || (i == 7) || (i == 24) || (i == 25))
 				j = (i & 1) ? ((u64) db_table[r1] * (u64) db_table[r4]) : ((u64) db_table[l1] * (u64) db_table[l4]);
 			else
 				j = ((i & 1) ? db_table[r1] : db_table[l1]) << 31;
@@ -389,11 +411,11 @@
 		}
 	}
 
-	for (i = 64; i < 80; i++) {
-		if (card->digmix[i] != 0x80000000) {
-			if ((i >= 64) && (i < 68))
+	for (i = 72; i < 90; i++) {
+		if (card->digmix[i] != DM_MUTE) {
+			if ((i >= 72) && (i < 76))
 				j = (i & 1) ? ((u64) db_table[r2] * (u64) db_table[r3]) : ((u64) db_table[l2] * (u64) db_table[l3]);
-			else if ((i == 70) || (i == 71))
+			else if ((i == 78) || (i == 79))
 				j = (i & 1) ? ((u64) db_table[r2] * (u64) db_table[r4]) : ((u64) db_table[l2] * (u64) db_table[l4]);
 			else
 				j = ((i & 1) ? db_table[r2] : db_table[l2]) << 31;
@@ -402,18 +424,18 @@
 		}
 	}
 
-	for (i = 16; i <= 80; i += 16) {
-		if (i != 64) {
+	for (i = 36; i <= 90; i += 18) {
+		if (i != 72) {
 			for (k = 0; k < 4; k++)
-				if (card->digmix[i + k] != 0x80000000) {
+				if (card->digmix[i + k] != DM_MUTE) {
 					card->digmix[i + k] = db_table[l3];
 					sblive_writeptr(card, FXGPREGBASE + 0x10 + i + k, 0, card->digmix[i + k]);
 				}
-			if (card->digmix[i + 6] != 0x80000000) {
+			if (card->digmix[i + 6] != DM_MUTE) {
 				card->digmix[i + 6] = db_table[l4];
 				sblive_writeptr(card, FXGPREGBASE + 0x10 + i + 6, 0, card->digmix[i + 6]);
 			}
-			if (card->digmix[i + 7] != 0x80000000) {
+			if (card->digmix[i + 7] != DM_MUTE) {
 				card->digmix[i + 7] = db_table[r4];
 				sblive_writeptr(card, FXGPREGBASE + 0x10 + i + 7, 0, card->digmix[i + 7]);
 			}
@@ -428,9 +450,9 @@
 static int set_pcm_attn(struct emu10k1_card *card, int ch, int l)
 {
 #ifndef PCMLEVEL
-#define PCMLEVEL 140		// almost silence
+#define PCMLEVEL 110		/* almost silence */
 #endif
-	int vol = IFATN_ATTENUATION_MASK;	// silence
+	int vol = IFATN_ATTENUATION_MASK;	/* silence */
 
 	if (l > 0)
 		vol = (PCMLEVEL - (l * PCMLEVEL + 50) / 100);
@@ -454,29 +476,29 @@
 
 	for (i = 0; i < MAX_PCM_CHANNELS; i++) {
 		if (sblive_pcm_volume[i].files == current->files) {
-			sblive_pcm_volume[i].mixer = mixer;
+			sblive_pcm_volume[i].mixer = pcm_last_mixer = mixer;
 			if (sblive_pcm_volume[i].opened) {
 				if (sblive_pcm_volume[i].channel_r < NUM_G) {
+					sblive_pcm_volume[i].attn_r = set_pcm_attn(card, sblive_pcm_volume[i].channel_r, r1);
 					if (sblive_pcm_volume[i].channel_l < NUM_G)
 						sblive_pcm_volume[i].attn_l = set_pcm_attn(card, sblive_pcm_volume[i].channel_l, l1);
-					sblive_pcm_volume[i].attn_r = set_pcm_attn(card, sblive_pcm_volume[i].channel_r, r1);
 				} else {
-					// mono voice
+					/* mono voice */
 					if (sblive_pcm_volume[i].channel_l < NUM_G)
 						sblive_pcm_volume[i].attn_l =
 						    set_pcm_attn(card, sblive_pcm_volume[i].channel_l, (l1 >= r1) ? l1 : r1);
-					// to correctly handle mono voice here we would need
-					// to go into stereo mode and move the voice to the right & left
-					// looks a bit overcomlicated...
+						/* to correctly handle mono voice here we would need
+						   to go into stereo mode and move the voice to the right & left
+						   looks a bit overcomplicated... */
 				}
-
 			}
+
 			return 1;
+
 		}
 	}
-	if (i == MAX_PCM_CHANNELS)
-		card->arrwVol[volidx[SOUND_MIXER_PCM]] = mixer;
 
+	card->arrwVol[volidx[SOUND_MIXER_PCM]] = mixer;
 	return 0;
 }
 #endif
@@ -505,10 +527,11 @@
 
 	switch (ch) {
 	case SOUND_MIXER_VOLUME:
-	case SOUND_MIXER_DIGITAL1:
-	case SOUND_MIXER_LINE3:
-		DPD(4, "SOUND_MIXER_%s:\n", (ch == SOUND_MIXER_VOLUME) ? "VOLUME" : (ch == SOUND_MIXER_DIGITAL1) ? "DIGITAL1" : "LINE3");
-		update_digital(card);
+		DPF(4, "SOUND_MIXER_VOLUME:\n");
+		if (card->isaps)
+			aps_update_digital(card);
+		else
+			update_digital(card);
 		return 0;
 	case SOUND_MIXER_PCM:
 		DPF(4, "SOUND_MIXER_PCM\n");
@@ -516,6 +539,34 @@
 		if (update_pcm_attn(card, l1, r1))
 			return 0;
 #endif
+		if (card->isaps)
+			aps_update_digital(card);
+		else
+			update_digital(card);
+		return 0;
+#ifdef TONE_CONTROL
+	case SOUND_MIXER_TREBLE:
+                DPF(4, "SOUND_MIXER_TREBLE:\n");
+                set_treble(card, l1, r1);
+                return 0;
+
+        case SOUND_MIXER_BASS:
+                DPF(4, "SOUND_MIXER_BASS:\n");
+                set_bass(card, l1, r1);
+		return 0;
+#endif
+	default:
+		break;
+	}
+
+
+	if (card->isaps)
+		return -EINVAL;
+
+	switch (ch) {
+	case SOUND_MIXER_DIGITAL1:
+	case SOUND_MIXER_LINE3:
+		DPD(4, "SOUND_MIXER_%s:\n", (ch == SOUND_MIXER_DIGITAL1) ? "DIGITAL1" : "LINE3");
 		update_digital(card);
 		return 0;
 	case SOUND_MIXER_DIGITAL2:
@@ -549,7 +600,7 @@
 		DPF(4, "SOUND_MIXER_MIC:\n");
 		i = 0;
 		if (l1 >= 30)
-			// 20dB / (34.5dB + 12dB + 20dB) * 100 = 30
+			/* 20dB / (34.5dB + 12dB + 20dB) * 100 = 30 */
 		{
 			l1 -= 30;
 			i = 0x40;
@@ -572,16 +623,6 @@
 		sblive_writeac97(card, volreg[ch], wval);
 		return 0;
 
-	case SOUND_MIXER_TREBLE:
-		DPF(4, "SOUND_MIXER_TREBLE:\n");
-		set_treble(card, l1, r1);
-		return 0;
-
-	case SOUND_MIXER_BASS:
-		DPF(4, "SOUND_MIXER_BASS:\n");
-		set_bass(card, l1, r1);
-		return 0;
-
 	default:
 		DPF(2, "Got unknown SOUND_MIXER ioctl\n");
 		return -EINVAL;
@@ -597,6 +638,10 @@
 /* Mixer file operations */
 
 /* FIXME: Do we need spinlocks in here? */
+/* WARNING! not all the ioctl's are supported by the emu-APS
+   (anything AC97 related). As a general rule keep the AC97 related ioctls
+   separate from the rest. This will make it easier to rewrite the mixer
+   using the kernel AC97 interface. */ 
 static int emu10k1_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	static const char id[] = "SBLive";
@@ -658,10 +703,201 @@
 			return -EFAULT;
 
 		for (i = 0; i < sizeof(card->digmix) / sizeof(card->digmix[0]); i++)
-			sblive_writeptr(card, FXGPREGBASE + 0x10 + i, 0, (card->digmix[i] & 0x80000000) ? 0 : card->digmix[i]);
+			sblive_writeptr(card, FXGPREGBASE + 0x10 + i, 0, (card->digmix[i] & DM_MUTE) ? 0 : card->digmix[i]);
 		return 0;
 
 		break;
+	case SOUND_MIXER_PRIVATE3: {
+			struct mixer_private_ioctl ctl;
+
+			if (copy_from_user(&ctl, (void *) arg, sizeof(struct mixer_private_ioctl)))
+				return -EFAULT;
+
+			switch (ctl.cmd) {
+#ifdef EMU10K1_DEBUG
+			case CMD_WRITEFN0:
+				emu10k1_writefn0(card, ctl.val[0], ctl.val[1]);
+				return 0;
+				break;
+
+			case CMD_WRITEPTR:
+				if(ctl.val[1] >= 0x40)
+					return -EINVAL;
+
+				if(ctl.val[0] > 0xff)
+					return -EINVAL;
+
+				if((ctl.val[0] & 0x7ff) > 0x3f)
+					ctl.val[1] = 0x00;
+
+				sblive_writeptr(card, ctl.val[0], ctl.val[1], ctl.val[2]);
+
+				return 0;
+				break;
+#endif
+			case CMD_READFN0:
+				ctl.val[2] = emu10k1_readfn0(card, ctl.val[0]);
+
+				if (copy_to_user((void *) arg, &ctl, sizeof(struct mixer_private_ioctl)))
+					return -EFAULT;
+
+				return 0;
+				break;
+
+			case CMD_READPTR:
+				if(ctl.val[1] >= 0x40)
+					return -EINVAL;
+
+				if((ctl.val[0] & 0x7ff) > 0xff)
+					return -EINVAL;
+
+				if((ctl.val[0] & 0x7ff) > 0x3f)
+					ctl.val[1] = 0x00;
+
+				ctl.val[2] = sblive_readptr(card, ctl.val[0], ctl.val[1]);
+
+				if (copy_to_user((void *) arg, &ctl, sizeof(struct mixer_private_ioctl)))
+					return -EFAULT;
+
+				return 0;
+				break;
+
+			case CMD_SETRECSRC:
+				switch(ctl.val[0]){
+				case WAVERECORD_AC97:
+					if(card->isaps)
+						return -EINVAL;
+					card->wavein.recsrc = WAVERECORD_AC97;
+					break;
+				case WAVERECORD_MIC:	
+					card->wavein.recsrc = WAVERECORD_MIC;
+					break;
+				case WAVERECORD_FX:
+					card->wavein.recsrc = WAVERECORD_FX;
+					card->wavein.fxwc = ctl.val[1] & 0xffff;
+					if(!card->wavein.fxwc)
+						return -EINVAL;
+					break;
+				default:
+					return -EINVAL;
+				}
+				return 0;
+				break;
+
+			case CMD_GETRECSRC:
+				ctl.val[0] = card->wavein.recsrc;
+				ctl.val[1] = card->wavein.fxwc;
+				if (copy_to_user((void *) arg, &ctl, sizeof(struct mixer_private_ioctl)))
+					return -EFAULT;
+
+				return 0;
+				break;
+
+			case CMD_GETVOICEPARAM:
+
+				ctl.val[0] = card->waveout.send_routing[0];
+				ctl.val[1] = card->waveout.send_a[0] | card->waveout.send_b[0] << 8 |
+				             card->waveout.send_c[0] << 16 | card->waveout.send_d[0] << 24;
+
+				ctl.val[2] = card->waveout.send_routing[1]; 
+				ctl.val[3] = card->waveout.send_a[1] | card->waveout.send_b[1] << 8 |
+					     card->waveout.send_c[1] << 16 | card->waveout.send_d[1] << 24;
+
+				ctl.val[4] = card->waveout.send_routing[2]; 
+				ctl.val[5] = card->waveout.send_a[2] | card->waveout.send_b[2] << 8 |
+					     card->waveout.send_c[2] << 16 | card->waveout.send_d[2] << 24;
+
+				if (copy_to_user((void *) arg, &ctl, sizeof(struct mixer_private_ioctl)))
+					return -EFAULT;
+
+				return 0;
+				break;
+
+			case CMD_SETVOICEPARAM:
+				card->waveout.send_routing[0] = ctl.val[0] & 0xffff;
+				card->waveout.send_a[0] = ctl.val[1] & 0xff;
+				card->waveout.send_b[0] = (ctl.val[1] >> 8) & 0xff;
+				card->waveout.send_c[0] = (ctl.val[1] >> 16) & 0xff;
+				card->waveout.send_d[0] = (ctl.val[1] >> 24) & 0xff;
+
+				card->waveout.send_routing[1] = ctl.val[2] & 0xffff;
+				card->waveout.send_a[1] = ctl.val[3] & 0xff;
+				card->waveout.send_b[1] = (ctl.val[3] >> 8) & 0xff;
+				card->waveout.send_c[1] = (ctl.val[3] >> 16) & 0xff;
+				card->waveout.send_d[1] = (ctl.val[3] >> 24) & 0xff;
+
+				card->waveout.send_routing[2] = ctl.val[4] & 0xffff;
+				card->waveout.send_a[2] = ctl.val[5] & 0xff;
+				card->waveout.send_b[2] = (ctl.val[5] >> 8) & 0xff;
+				card->waveout.send_c[2] = (ctl.val[5] >> 16) & 0xff;
+				card->waveout.send_d[2] = (ctl.val[5] >> 24) & 0xff;
+
+				return 0;
+				break;
+
+			default:
+				return -EINVAL;
+				break;
+			}
+		}
+		break;
+
+	case SOUND_MIXER_PRIVATE4:{
+			u32 size;
+			int size_reg = 0;
+
+			if (copy_from_user(&size, (void *) arg, sizeof(size)))
+				return -EFAULT;
+
+			DPD(2,"External tram size 0x%x\n", size);
+
+			if(size > 0x1fffff)
+				return -EINVAL;
+
+			if (size != 0) {	
+				size = (size - 1) >> 14;
+
+        	                while (size) {
+                	                size >>= 1;
+                        	        size_reg++;
+                        	}
+
+                        	size = 0x4000 << size_reg;
+			}
+
+			DPD(2,"External tram size 0x%x 0x%x\n", size, size_reg);
+
+			if (size != card->tankmem.size) {
+				if (card->tankmem.size > 0) {
+					emu10k1_writefn0(card, HCFG_LOCKTANKCACHE, 1);
+
+					sblive_writeptr_tag(card, 0, TCB, 0,
+							    TCBS, 0,
+							    TAGLIST_END);
+
+					pci_free_consistent(card->pci_dev, card->tankmem.size,
+							    card->tankmem.addr, card->tankmem.dma_handle);
+
+					card->tankmem.size = 0;
+				}
+
+				if (size != 0) {
+					if ((card->tankmem.addr = pci_alloc_consistent(card->pci_dev, size,
+					     &card->tankmem.dma_handle)) == NULL)
+						return -ENOMEM;
+
+					card->tankmem.size = size;
+
+					sblive_writeptr_tag(card, 0, TCB, card->tankmem.dma_handle,
+                        	                            TCBS, size_reg,
+                                	                    TAGLIST_END);
+
+					emu10k1_writefn0(card, HCFG_LOCKTANKCACHE, 0);
+				}
+			}
+			return 0;
+		}
+		break;
 
 	default:
 		break;
@@ -672,57 +908,107 @@
 
 	if (_IOC_DIR(cmd) == _IOC_READ) {
 		switch (_IOC_NR(cmd)) {
-		case SOUND_MIXER_RECSRC:	/* Arg contains a bit for each recording source */
-			DPF(2, "SOUND_MIXER_READ_RECSRC\n");
-			sblive_readac97(card, AC97_RECORDSELECT, &reg);
-			return put_user(recsrc[reg & 7], (int *) arg);
-
-		case SOUND_MIXER_DEVMASK:	/* Arg contains a bit for each supported device */
-			DPF(4, "SOUND_MIXER_READ_DEVMASK\n");
+			case SOUND_MIXER_DEVMASK:       /* Arg contains a bit for each supported device */
+                        DPF(4, "SOUND_MIXER_READ_DEVMASK\n");
+			if (card->isaps)
+#ifdef TONE_CONTROL
+				return put_user(SOUND_MASK_PCM | SOUND_MASK_VOLUME |
+						SOUND_MASK_BASS | SOUND_MASK_TREBLE,
+						(int *) arg); 
+#else
+				return put_user(SOUND_MASK_PCM | SOUND_MASK_VOLUME,
+						(int *) arg); 
+#endif
+		
+#ifdef TONE_CONTROL
 			return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
+                                        SOUND_MASK_OGAIN | SOUND_MASK_LINE1 |
+                                        SOUND_MASK_PCM | SOUND_MASK_VOLUME |
+                                        SOUND_MASK_PHONEIN | SOUND_MASK_MIC |
+                                        SOUND_MASK_BASS | SOUND_MASK_TREBLE |
+                                        SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER |
+                                        SOUND_MASK_LINE3 | SOUND_MASK_DIGITAL1 | 
+                                        SOUND_MASK_DIGITAL2 | SOUND_MASK_LINE2, (int *) arg);
+#else
+			return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
+                                        SOUND_MASK_OGAIN | SOUND_MASK_LINE1 |
+                                        SOUND_MASK_PCM | SOUND_MASK_VOLUME |
+                                        SOUND_MASK_PHONEIN | SOUND_MASK_MIC |
+                                        SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER |
+                                        SOUND_MASK_LINE3 | SOUND_MASK_DIGITAL1 | 
+                                        SOUND_MASK_DIGITAL2 | SOUND_MASK_LINE2, (int *) arg);
+#endif
+
+			case SOUND_MIXER_RECMASK:       /* Arg contains a bit for each supported recording source */
+				DPF(2, "SOUND_MIXER_READ_RECMASK\n");
+				if (card->isaps)
+					return put_user(0, (int *) arg);
+
+				return put_user(SOUND_MASK_MIC | SOUND_MASK_CD |
+					SOUND_MASK_LINE1 | SOUND_MASK_LINE |
+					SOUND_MASK_VOLUME | SOUND_MASK_OGAIN |
+					SOUND_MASK_PHONEIN, (int *) arg);
+
+			case SOUND_MIXER_STEREODEVS:    /* Mixer channels supporting stereo */
+				DPF(2, "SOUND_MIXER_READ_STEREODEVS\n");
+
+				if (card->isaps)
+#ifdef TONE_CONTROL
+					return put_user(SOUND_MASK_PCM | SOUND_MASK_VOLUME |
+                                        		SOUND_MASK_BASS | SOUND_MASK_TREBLE,
+                                        		(int *) arg);
+#else
+					return put_user(SOUND_MASK_PCM | SOUND_MASK_VOLUME,
+                                         		(int *) arg);
+#endif
+
+#ifdef TONE_CONTROL
+				return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
 					SOUND_MASK_OGAIN | SOUND_MASK_LINE1 |
 					SOUND_MASK_PCM | SOUND_MASK_VOLUME |
-					SOUND_MASK_PHONEIN | SOUND_MASK_MIC |
 					SOUND_MASK_BASS | SOUND_MASK_TREBLE |
-					SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER |
-					SOUND_MASK_LINE3 | SOUND_MASK_DIGITAL1 | 
-					SOUND_MASK_DIGITAL2 | SOUND_MASK_LINE2, (int *) arg);
-		case SOUND_MIXER_RECMASK:	/* Arg contains a bit for each supported recording source */
-			DPF(2, "SOUND_MIXER_READ_RECMASK\n");
-			return put_user(SOUND_MASK_MIC | SOUND_MASK_CD |
-					SOUND_MASK_LINE1 | SOUND_MASK_LINE | 
-					SOUND_MASK_VOLUME | SOUND_MASK_OGAIN | 
-					SOUND_MASK_PHONEIN | SOUND_MASK_MONITOR | 
-					SOUND_MASK_PCM, (int *) arg);
-
-		case SOUND_MIXER_STEREODEVS:	/* Mixer channels supporting stereo */
-			DPF(2, "SOUND_MIXER_READ_STEREODEVS\n");
-			return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
+					SOUND_MASK_RECLEV | SOUND_MASK_LINE3 |
+					SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 |
+					SOUND_MASK_LINE2, (int *) arg);
+#else
+				return put_user(SOUND_MASK_LINE | SOUND_MASK_CD |
 					SOUND_MASK_OGAIN | SOUND_MASK_LINE1 |
 					SOUND_MASK_PCM | SOUND_MASK_VOLUME |
-					SOUND_MASK_BASS | SOUND_MASK_TREBLE |
 					SOUND_MASK_RECLEV | SOUND_MASK_LINE3 |
-					SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 | 
+					SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 |
 					SOUND_MASK_LINE2, (int *) arg);
+#endif
 
-		case SOUND_MIXER_CAPS:
-			DPF(2, "SOUND_MIXER_READ_CAPS\n");
-			return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg);
-
+			case SOUND_MIXER_CAPS:
+				DPF(2, "SOUND_MIXER_READ_CAPS\n");
+				return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg);
 #ifdef PRIVATE_PCM_VOLUME
-		case SOUND_MIXER_PCM:
-			// needs to be before default: !!
-			{
-				int i;
-
-				for (i = 0; i < MAX_PCM_CHANNELS; i++) {
-					if (sblive_pcm_volume[i].files == current->files) {
-						return put_user((int) sblive_pcm_volume[i].mixer, (int *) arg);
-					}
-				}
-			}
+                case SOUND_MIXER_PCM:
+                        /* needs to be before default: !!*/
+                        {
+                                int i;
+
+                                for (i = 0; i < MAX_PCM_CHANNELS; i++) {
+                                        if (sblive_pcm_volume[i].files == current->files) {
+                                                return put_user((int) sblive_pcm_volume[i].mixer, (int *) arg);
+                                        }
+                                }
+                        }
 #endif
 		default:
+			break;
+		}
+
+		switch (_IOC_NR(cmd)) {
+		case SOUND_MIXER_RECSRC:	/* Arg contains a bit for each recording source */
+			DPF(2, "SOUND_MIXER_READ_RECSRC\n");
+			if (card->isaps)
+				return put_user(0, (int *) arg);
+
+			sblive_readac97(card, AC97_RECORDSELECT, &reg);
+			return put_user(recsrc[reg & 7], (int *) arg);
+
+		default:
 			i = _IOC_NR(cmd);
 			DPD(4, "SOUND_MIXER_READ(%d)\n", i);
 			if (i >= SOUND_MIXER_NRDEVICES)
@@ -737,6 +1023,7 @@
 #endif				/* OSS_DOCUMENTED_MIXER_SEMANTICS */
 		}
 	}
+
 	/* End of _IOC_READ */
 	if (_IOC_DIR(cmd) != (_IOC_READ | _IOC_WRITE))
 		return -EINVAL;
@@ -748,10 +1035,13 @@
 	case SOUND_MIXER_RECSRC:	/* Arg contains a bit for each recording source */
 		DPF(2, "SOUND_MIXER_WRITE_RECSRC\n");
 
+		if (card->isaps)
+			return -EINVAL;
+
 		get_user_ret(val, (int *) arg, -EFAULT);
 		i = hweight32(val);
 		if (i == 0)
-			return 0;	/*val = mixer_recmask(s); */
+			return 0;	/* val = mixer_recmask(s); */
 		else if (i > 1) {
 			sblive_readac97(card, AC97_RECORDSELECT, &reg);
 			val &= ~recsrc[reg & 7];
@@ -809,14 +1099,14 @@
 
 static int emu10k1_mixer_release(struct inode *inode, struct file *file)
 {
-	DPF(3, "emu10k1_mixer_release()\n");
+	DPF(4, "emu10k1_mixer_release()\n");
 	return 0;
 }
 
 struct file_operations emu10k1_mixer_fops = {
-	owner:THIS_MODULE,
-	llseek:emu10k1_mixer_llseek,
-	ioctl:emu10k1_mixer_ioctl,
-	open:emu10k1_mixer_open,
-	release:emu10k1_mixer_release,
+        owner:		THIS_MODULE,
+	llseek:		emu10k1_mixer_llseek,
+	ioctl:		emu10k1_mixer_ioctl,
+	open:		emu10k1_mixer_open,
+	release:	emu10k1_mixer_release,
 };

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