patch-2.2.3 linux/drivers/sound/ad1848.c

Next file: linux/drivers/sound/ad1848_mixer.h
Previous file: linux/drivers/sound/Config.in
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.2/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
@@ -100,6 +100,11 @@
 
 static int nr_ad1848_devs = 0;
 int deskpro_xl = 0;
+#ifdef CONFIG_SOUND_SPRO
+int soundpro = 1;
+#else
+int soundpro = 0;
+#endif
 
 static volatile char irq2dev[17] = {
 	-1, -1, -1, -1, -1, -1, -1, -1,
@@ -312,22 +317,21 @@
 		if (mask & (1 << i))
 			n++;
 
-	if (n == 0)
-		mask = SOUND_MASK_MIC;
-	else if (n != 1)	/* Too many devices selected */
-	{
-		  mask &= ~devc->recmask;	/* Filter out active settings */
+	if (!soundpro) {
+		if (n == 0)
+			mask = SOUND_MASK_MIC;
+		else if (n != 1) {	/* Too many devices selected */
+			mask &= ~devc->recmask;	/* Filter out active settings */
 
-		n = 0;
-		for (i = 0; i < 32; i++)	/* Count selected device bits */
-			if (mask & (1 << i))
-				n++;
+			n = 0;
+			for (i = 0; i < 32; i++)	/* Count selected device bits */
+				if (mask & (1 << i))
+					n++;
 
-		if (n != 1)
-			mask = SOUND_MASK_MIC;
-	}
-	switch (mask)
-	{
+			if (n != 1)
+				mask = SOUND_MASK_MIC;
+		}
+		switch (mask) {
 		case SOUND_MASK_MIC:
 			recdev = 2;
 			break;
@@ -349,11 +353,38 @@
 		default:
 			mask = SOUND_MASK_MIC;
 			recdev = 2;
-	}
+		}
+
+		recdev <<= 6;
+		ad_write(devc, 0, (ad_read(devc, 0) & 0x3f) | recdev);
+		ad_write(devc, 1, (ad_read(devc, 1) & 0x3f) | recdev);
+	} else { /* soundpro */
+		unsigned char val;
+		int set_rec_bit;
+		int j;
+
+		for (i = 0; i < 32; i++) {	/* For each bit */
+			if ((devc->supported_rec_devices & (1 << i)) == 0)
+				continue;	/* Device not supported */
+
+			for (j = LEFT_CHN; j <= RIGHT_CHN; j++) {
+				if (devc->mix_devices[i][j].nbits == 0) /* Inexistent channel */
+					continue;
+
+				/*
+				 * This is tricky:
+				 * set_rec_bit becomes 1 if the corresponding bit in mask is set
+				 * then it gets flipped if the polarity is inverse
+				 */
+				set_rec_bit = ((mask & (1 << i)) != 0) ^ devc->mix_devices[i][j].recpol;
 
-	recdev <<= 6;
-	ad_write(devc, 0, (ad_read(devc, 0) & 0x3f) | recdev);
-	ad_write(devc, 1, (ad_read(devc, 1) & 0x3f) | recdev);
+				val = ad_read(devc, devc->mix_devices[i][j].recreg);
+				val &= ~(1 << devc->mix_devices[i][j].recpos);
+				val |= (set_rec_bit << devc->mix_devices[i][j].recpos);
+				ad_write(devc, devc->mix_devices[i][j].recreg, val);
+			}
+		}
+	}
 
 	/* Rename the mixer bits back if necessary */
 	for (i = 0; i < 32; i++)
@@ -371,7 +402,8 @@
 	return mask;
 }
 
-static void change_bits(ad1848_info * devc, unsigned char *regval, int dev, int chn, int newval)
+static void change_bits(ad1848_info * devc, unsigned char *regval,
+			unsigned char *muteval, int dev, int chn, int newval)
 {
 	unsigned char mask;
 	int shift;
@@ -379,7 +411,7 @@
 	int mutemask;
 	int set_mute_bit;
 
-	set_mute_bit = (newval == 0);
+	set_mute_bit = (newval == 0) ^ devc->mix_devices[dev][chn].mutepol;
 
 	if (devc->mix_devices[dev][chn].polarity == 1)	/* Reverse */
 		newval = 100 - newval;
@@ -399,8 +431,11 @@
 	}
 
 	newval = (int) ((newval * mask) + 50) / 100;	/* Scale it */
-	*regval &= (~(mask << shift)) & (mutemask);	/* Clear bits */
-	*regval |= ((newval & mask) << shift) | mute;	/* Set new value */
+	*regval &= ~(mask << shift);			/* Clear bits */
+	*regval |= (newval & mask) << shift;		/* Set new value */
+
+	*muteval &= mutemask;
+	*muteval |= mute;
 }
 
 static int ad1848_mixer_get(ad1848_info * devc, int dev)
@@ -413,15 +448,36 @@
 	return devc->levels[dev];
 }
 
+static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int channel)
+{
+	int regoffs, muteregoffs;
+	unsigned char val, muteval;
+
+	regoffs = devc->mix_devices[dev][channel].regno;
+	muteregoffs = devc->mix_devices[dev][channel].mutereg;
+	val = ad_read(devc, regoffs);
+
+	if (muteregoffs != regoffs) {
+		muteval = ad_read(devc, muteregoffs);
+		change_bits(devc, &val, &muteval, dev, channel, value);
+	}
+	else
+		change_bits(devc, &val, &val, dev, channel, value);
+
+	ad_write(devc, regoffs, val);
+	devc->saved_regs[regoffs] = val;
+	if (muteregoffs != regoffs) {
+		ad_write(devc, muteregoffs, muteval);
+		devc->saved_regs[muteregoffs] = muteval;
+	}
+}
+
 static int ad1848_mixer_set(ad1848_info * devc, int dev, int value)
 {
 	int left = value & 0x000000ff;
 	int right = (value & 0x0000ff00) >> 8;
 	int retvol;
 
-	int regoffs;
-	unsigned char val;
-
 	if (dev > 31)
 		return -EINVAL;
 
@@ -430,6 +486,9 @@
 
 	dev = devc->mixer_reroute[dev];
 
+	if (devc->mix_devices[dev][LEFT_CHN].nbits == 0)
+		return -EINVAL;
+
 	if (left > 100)
 		left = 100;
 	if (right > 100)
@@ -444,34 +503,21 @@
 	left = mix_cvt[left];
 	right = mix_cvt[right];
 
-	if (devc->mix_devices[dev][LEFT_CHN].nbits == 0)
-		return -EINVAL;
-
 	devc->levels[dev] = retvol;
 
 	/*
 	 * Set the left channel
 	 */
-
-	regoffs = devc->mix_devices[dev][LEFT_CHN].regno;
-	val = ad_read(devc, regoffs);
-	change_bits(devc, &val, dev, LEFT_CHN, left);
-	ad_write(devc, regoffs, val);
-	devc->saved_regs[regoffs] = val;
+	ad1848_mixer_set_channel(devc, dev, left, LEFT_CHN);
 
 	/*
 	 * Set the right channel
 	 */
-
 	if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0)
-		return retvol;	/* Was just a mono channel */
-
-	regoffs = devc->mix_devices[dev][RIGHT_CHN].regno;
-	val = ad_read(devc, regoffs);
-	change_bits(devc, &val, dev, RIGHT_CHN, right);
-	ad_write(devc, regoffs, val);
-	devc->saved_regs[regoffs] = val;
+		goto out;
+	ad1848_mixer_set_channel(devc, dev, right, RIGHT_CHN);
 
+ out:
 	return retvol;
 }
 
@@ -487,6 +533,8 @@
 	for (i = 0; i < 32; i++)
 		devc->mixer_reroute[i] = i;
 
+	devc->supported_rec_devices = MODE1_REC_DEVICES;
+
 	switch (devc->model)
 	{
 		case MD_4231:
@@ -509,11 +557,18 @@
 			devc->supported_devices = MODE3_MIXER_DEVICES;
 			break;
 
+		case MD_1848:
+			if (soundpro) {
+				devc->supported_devices = SPRO_MIXER_DEVICES;
+				devc->supported_rec_devices = SPRO_REC_DEVICES;
+				devc->mix_devices = &(spro_mix_devices[0]);
+				break;
+			}
+
 		default:
 			devc->supported_devices = MODE1_MIXER_DEVICES;
 	}
 
-	devc->supported_rec_devices = MODE1_REC_DEVICES;
 	devc->orig_devices = devc->supported_devices;
 	devc->orig_rec_devices = devc->supported_rec_devices;
 
@@ -528,10 +583,20 @@
 	ad1848_set_recmask(devc, SOUND_MASK_MIC);
 	
 	devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT;
-	if (devc->mixer_output_port & AUDIO_SPEAKER)
-		ad_write(devc, 26, ad_read(devc, 26) & ~0x40);	/* Unmute mono out */
-	else
-		ad_write(devc, 26, ad_read(devc, 26) | 0x40);	/* Mute mono out */
+
+	if (!soundpro) {
+		if (devc->mixer_output_port & AUDIO_SPEAKER)
+			ad_write(devc, 26, ad_read(devc, 26) & ~0x40);	/* Unmute mono out */
+		else
+			ad_write(devc, 26, ad_read(devc, 26) | 0x40);	/* Mute mono out */
+	} else {
+		/*
+		 * From the "wouldn't it be nice if the mixer API had (better)
+		 * support for custom stuff" category
+		 */
+		/* Enable surround mode and SB16 mixer */
+		ad_write(devc, 16, 0x60);
+	}
 }
 
 static int ad1848_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
@@ -1357,6 +1422,8 @@
 	{
 		  devc->audio_flags &= ~DMA_DUPLEX;
 		  ad_write(devc, 9, ad_read(devc, 9) | 0x04);	/* Single DMA mode */
+		  if (soundpro)
+			  ad_write(devc, 12, ad_read(devc, 12) | 0x40);	/* Mode2 = enabled */
 	}
 
 	outb((0), io_Status(devc));	/* Clear pending interrupts */
@@ -1706,7 +1773,27 @@
 
 			DDB(printk("ad1848_detect() - step K\n"));
 		}
+	} else if (tmp1 == 0x0a) {
+		/*
+		 * Is it perhaps a SoundPro CMI8330?
+		 * If so, then we should be able to change indirect registers
+		 * greater than I15 after activating MODE2, even though reading
+		 * back I12 does not show it.
+		 */
+
+		/*
+		 * Let's try comparing register values
+		 */
+		for (i = 0; i < 16; i++) {
+			if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) {
+				DDB(printk("ad1848 detect step H(%d/%x/%x) - SoundPro chip?\n", i, tmp1, tmp2));
+				soundpro = 1;
+				devc->chip_name = "SoundPro CMI 8330";
+				break;
+			}
+		}
 	}
+
 	DDB(printk("ad1848_detect() - step L\n"));
 	if (ad_flags)
 	{
@@ -2302,7 +2389,8 @@
 	    (hw_config->irq != 7)  &&
 	    (hw_config->irq != 9)  &&
 	    (hw_config->irq != 10) &&
-	    (hw_config->irq != 11))
+	    (hw_config->irq != 11) &&
+	    (hw_config->irq != 12))
 	{
 		printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq);
 		return 0;
@@ -2555,6 +2643,7 @@
 MODULE_PARM(dma2, "i");			/* Second DMA channel */
 MODULE_PARM(type, "i");			/* Card type */
 MODULE_PARM(deskpro_xl, "i");		/* Special magic for Deskpro XL boxen */
+MODULE_PARM(soundpro, "i");		/* More special magic for SoundPro chips */
 
 int io = -1;
 int irq = -1;

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