patch-2.3.30 linux/drivers/sound/cmpci.c

Next file: linux/drivers/sound/es1371.c
Previous file: linux/drivers/sound/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.29/linux/drivers/sound/cmpci.c linux/drivers/sound/cmpci.c
@@ -21,7 +21,7 @@
  *      along with this program; if not, write to the Free Software
  *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * Special thanks to David C. Niemi
+ * Special thanks to David C. Niemi, Jan Pfeifer
  *
  *
  * Module command line parameters:
@@ -59,8 +59,43 @@
  *                     read/write cannot be executed
  *    20 09 99   0.13  merged the generic changes in sonicvibes since this
  *		       diverged.
+ *    18.08.99   1.5   Only deallocate DMA buffer when unloading.
+ *    02.09.99   1.6   Enable SPDIF LOOP
+ *                     Change the mixer read back
+ *    21.09.99   2.33  Use RCS version aas driver version.
+ *                     Add support for modem, S/PDIF loop and 4 channels.
+ *                     (8738 only)
+ *                     Fix bug cause x11amp cannot play.
+ *    $Log: cmpci.c,v $
+ *    Revision 2.41  1999/10/27 02:00:05  cltien
+ *    Now the fragsize for modem is activated by parameter.
+ *
+ *    Revision 2.40  1999/10/26 23:38:26  cltien
+ *    Remove debugging message in cm_write which may cause module counter not 0.
+ *
+ *    Revision 2.39  1999/10/26 21:52:50  cltien
+ *    I forgor too adjust mic recording volume, as it should be moved to 5MUTEMONO.
+ *    Change the DYNAMIC macro to FIXEDDMA, which means static DMA buffer.
+ *
+ *    Revision 2.38  1999/10/08 21:59:03  cltien
+ *    Set FLINKON and reset FLINKOFF for modem.
+ *
+ *    Revision 2.37  1999/09/28 02:57:04  cltien
+ *    Add set_bus_master() to make sure bus master enabled.
+ *
+ *    Revision 2.36  1999/09/22 14:15:03  cltien
+ *    Use open_sem to avoid multiple access to open_mode.
+ *    Use wakeup in IntrClose to activate process in waiting queue.
+ *
+ *    Revision 2.35  1999/09/22 13:20:53  cltien
+ *    Use open_mode to check if DAC in used. Also more check in IntrWrite and IntrClose. Now the modem can access DAC safely.
+ *
+ *    Revision 2.34  1999/09/22 03:29:57  cltien
+ *    Use module count to decide which one to access the dac.
+ *
+ *
  */
-
+ 
 /*****************************************************************************/
       
 #include <linux/config.h>
@@ -252,6 +287,7 @@
 		unsigned fragsize;
 		unsigned dmasize;
 		unsigned fragsamples;
+		unsigned dmasamples;
 		/* OSS stuff */
 		unsigned mapped:1;
 		unsigned ready:1;
@@ -276,6 +312,7 @@
 /* --------------------------------------------------------------------- */
 
 static struct cm_state *devs = NULL;
+static struct cm_state *devaudio = NULL;
 static unsigned long wavetable_mem = 0;
 
 /* --------------------------------------------------------------------- */
@@ -331,7 +368,7 @@
 	outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1);
 	outw(count, s->iobase + CODEC_CMI_CH0_FRAME2);
 	outb(inb(s->iobase + CODEC_CMI_FUNCTRL0) & ~1, s->iobase + CODEC_CMI_FUNCTRL0);
-	outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 1, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+//	outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 1, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
 }
 
 static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count)
@@ -340,13 +377,16 @@
 	outl(addr, s->iobase + CODEC_CMI_CH1_FRAME1);
 	outw(count, s->iobase + CODEC_CMI_CH1_FRAME2);
 	outb(inb(s->iobase + CODEC_CMI_FUNCTRL0) | 2, s->iobase + CODEC_CMI_FUNCTRL0);
-	outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 2, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+//	outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 2, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
 }
 
 extern __inline__ unsigned get_dmadac(struct cm_state *s)
 {
 	unsigned int curr_addr;
 
+	if (!s->dma_dac.dmasize || !(s->enable & CM_CENABLE_PE))
+		return 0;
+
 	curr_addr = inl(s->iobase + CODEC_CMI_CH0_FRAME1);
 	curr_addr -= virt_to_bus(s->dma_dac.rawbuf);
 	curr_addr = s->dma_dac.dmasize - curr_addr;
@@ -358,6 +398,9 @@
 {
 	unsigned int curr_addr;
 
+	if (!s->dma_adc.dmasize || !(s->enable & CM_CENABLE_RE))
+		return 0;
+
 	curr_addr = inl(s->iobase + CODEC_CMI_CH1_FRAME1);
 	curr_addr -= virt_to_bus(s->dma_adc.rawbuf);
 	curr_addr = s->dma_adc.dmasize - curr_addr;
@@ -421,7 +464,7 @@
 	{ 22050,	(16000 + 22050) / 2,	(22050 + 32000) / 2,	2 },
 	{ 32000,	(22050 + 32000) / 2,	(32000 + 44100) / 2,	6 },
 	{ 44100,	(32000 + 44100) / 2,	(44100 + 48000) / 2,	3 },
-	{ 48000,	48000,			48000,			7 }
+	{ 48000,	(44100 + 48000) /2,	48000,			7 }
 };
 
 static void set_dac_rate(struct cm_state *s, unsigned rate)
@@ -485,10 +528,12 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&s->lock, flags);
+	/* disable channel */
+	outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
 	s->enable &= ~CM_CENABLE_RE;
 	/* disable interrupt */
 	outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) & ~2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
-	/* disable channel and reset */
+	/* reset */
 	outb(s->enable | CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
 	udelay(10);
 	outb(s->enable & ~CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
@@ -500,10 +545,12 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&s->lock, flags);
+	/* disable channel */
 	s->enable &= ~CM_CENABLE_PE;
+	outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
 	/* disable interrupt */
 	outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) & ~1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
-	/* disable channel and reset */
+	/* reset */
 	outb(s->enable | CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
 	udelay(10);
 	outb(s->enable & ~CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
@@ -516,10 +563,10 @@
 
 	spin_lock_irqsave(&s->lock, flags);
 	if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) {
+		outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
 		s->enable |= CM_CENABLE_PE;
 		outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
 	}
-	outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
 	spin_unlock_irqrestore(&s->lock, flags);
 }	
 
@@ -530,10 +577,10 @@
 	spin_lock_irqsave(&s->lock, flags);
 	if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) 
 	    && s->dma_adc.ready) {
+		outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
 		s->enable |= CM_CENABLE_RE;
 		outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
 	}
-	outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
 	spin_unlock_irqrestore(&s->lock, flags);
 }	
 
@@ -593,10 +640,10 @@
 			return -ENOMEM;
 		db->buforder = order;
 		if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff)
-			printk(KERN_DEBUG "cm: DMA buffer crosses 64k boundary: busaddr 0x%lx  size %ld\n", 
+			printk(KERN_DEBUG "cmpci: DMA buffer crosses 64k boundary: busaddr 0x%lx  size %ld\n", 
 			       virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder);
 		if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff)
-			printk(KERN_DEBUG "cm: DMA buffer beyond 16MB: busaddr 0x%lx  size %ld\n", 
+			printk(KERN_DEBUG "cmpci: DMA buffer beyond 16MB: busaddr 0x%lx  size %ld\n", 
 			       virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder);
 		/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
 		mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
@@ -623,17 +670,21 @@
 	db->fragsize = 1 << db->fragshift;
 	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
 		db->numfrag = db->ossmaxfrags;
-#if 1
-	/* to make fragsize >= 4096 */
-	while (db->fragsize < 4096 && db->numfrag >= 4)
-	{
-		db->fragsize *= 2;
-		db->fragshift++;
-		db->numfrag /= 2;
+ 	/* to make fragsize >= 4096 */
+#if 0 	
+ 	if(s->modem)
+ 	{
+	 	while (db->fragsize < 4096 && db->numfrag >= 4)
+		{
+			db->fragsize *= 2;
+ 			db->fragshift++;
+ 			db->numfrag /= 2;
+ 		}
 	}
-#endif
+#endif	
 	db->fragsamples = db->fragsize >> sample_shift[fmt];
 	db->dmasize = db->numfrag << db->fragshift;
+	db->dmasamples = db->dmasize >> sample_shift[fmt];
 	memset(db->rawbuf, (fmt & CM_CFMT_16BIT) ? 0 : 0x80, db->dmasize);
 	spin_lock_irqsave(&s->lock, flags);
 	if (rec) {
@@ -665,6 +716,7 @@
 		len -= x;
 	}
 	memset(buf + bptr, c, len);
+	outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
 }
 
 /* call with spinlock held! */
@@ -752,25 +804,26 @@
 	unsigned int intsrc, intstat;
 	
 	/* fastpath out, to ease interrupt sharing */
-	intsrc = inb(s->iobase + CODEC_CMI_INT_STATUS);
-	if (!(intsrc & (CM_INT_CH0 | CM_INT_CH1)))
+	intsrc = inl(s->iobase + CODEC_CMI_INT_STATUS);
+	if (!(intsrc & 0x80000000))
 		return;
 	spin_lock(&s->lock);
 	intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2);
-	/* disable interrupt */
+	/* acknowledge interrupt */
 	if (intsrc & CM_INT_CH0)
+	{
 		outb(intstat & ~1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+		udelay(10);
+		outb(intstat | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+	}
 	if (intsrc & CM_INT_CH1)
+	{
 		outb(intstat & ~2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+		udelay(10);
+		outb(intstat | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+	}
 	cm_update_ptr(s);
-#ifdef SOUND_CONFIG_CMPCI_MIDI
 	cm_handle_midi(s);
-#endif
-	/* enable interrupt */
-	if (intsrc & CM_INT_CH0)
-		outb(intstat | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
-	if (intsrc & CM_INT_CH1)
-		outb(intstat | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
 	spin_unlock(&s->lock);
 }
 
@@ -788,7 +841,7 @@
 
 /* --------------------------------------------------------------------- */
 
-static const char invalid_magic[] = KERN_CRIT "cm: invalid magic value\n";
+static const char invalid_magic[] = KERN_CRIT "cmpci: invalid magic value\n";
 
 #ifdef CONFIG_SOUND_CMPCI	/* support multiple chips */
 #define VALIDATE_STATE(s)
@@ -808,6 +861,7 @@
 #define MT_5MUTE      2
 #define MT_4MUTEMONO  3
 #define MT_6MUTE      4
+#define MT_5MUTEMONO  5
 
 static const struct {
 	unsigned left;
@@ -818,7 +872,7 @@
 } mixtable[SOUND_MIXER_NRDEVICES] = {
 	[SOUND_MIXER_CD]     = { DSP_MIX_CDVOLIDX_L,     DSP_MIX_CDVOLIDX_R,     MT_5MUTE,     0x04, 0x02 },
 	[SOUND_MIXER_LINE]   = { DSP_MIX_LINEVOLIDX_L,   DSP_MIX_LINEVOLIDX_R,   MT_5MUTE,     0x10, 0x08 },
-	[SOUND_MIXER_MIC]    = { DSP_MIX_MICVOLIDX,      CODEC_CMI_MIXER2,       MT_4MUTEMONO, 0x01, 0x01 },
+	[SOUND_MIXER_MIC]    = { DSP_MIX_MICVOLIDX,      DSP_MIX_MICVOLIDX,      MT_5MUTEMONO, 0x01, 0x01 },
 	[SOUND_MIXER_SYNTH]  = { DSP_MIX_FMVOLIDX_L,  	 DSP_MIX_FMVOLIDX_R,     MT_5MUTE,     0x40, 0x00 },
 	[SOUND_MIXER_VOLUME] = { DSP_MIX_MASTERVOLIDX_L, DSP_MIX_MASTERVOLIDX_R, MT_5MUTE,     0x00, 0x00 },
 	[SOUND_MIXER_PCM]    = { DSP_MIX_VOICEVOLIDX_L,  DSP_MIX_VOICEVOLIDX_R,  MT_5MUTE,     0x00, 0x00 }
@@ -851,10 +905,16 @@
 		r = l;
 		break;
 
+	case MT_5MUTEMONO:
+		r = l;
+		rl = 100 - 3 * ((l >> 3) & 31);
+		rr = rl;
+		break;
+				
 	case MT_5MUTE:
 	default:
-		rl = 100 - 3 * (l & 31);
-		rr = 100 - 3 * (r & 31);
+		rl = 100 - 3 * ((l >> 3) & 31);
+		rr = 100 - 3 * ((r >> 3) & 31);
 		break;
 				
 	case MT_6MUTE:
@@ -992,7 +1052,7 @@
 		}
 		spin_lock_irqsave(&s->lock, flags);
 		wrmixer(s, DSP_MIX_ADCMIXIDX_L, j);
-		wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | j>>1);
+		wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | (j>>1));
 		spin_unlock_irqrestore(&s->lock, flags);
 		return 0;
 
@@ -1041,6 +1101,14 @@
 			outb((inb(s->iobase + CODEC_CMI_MIXER2) & ~0x0e) | rr<<1, s->iobase + CODEC_CMI_MIXER2);
 			break;
 			
+		case MT_5MUTEMONO:
+			r = l;
+			rl = l < 4 ? 0 : (l - 5) / 3;
+			rr = rl >> 2;
+ 			wrmixer(s, mixtable[i].left, rl<<3);
+			outb((inb(s->iobase + CODEC_CMI_MIXER2) & ~0x0e) | rr<<1, s->iobase + CODEC_CMI_MIXER2);
+			break;
+				
 		case MT_5MUTE:
 			rl = l < 4 ? 0 : (l - 5) / 3;
 			rr = r < 4 ? 0 : (r - 5) / 3;
@@ -1154,10 +1222,10 @@
                         current->state = TASK_RUNNING;
                         return -EBUSY;
                 }
-		tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac;
+		tmo = (count * HZ) / s->ratedac;
 		tmo >>= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK];
-		if (!schedule_timeout(tmo + 1))
-			printk(KERN_DEBUG "cm: dma timed out??\n");
+		if (!schedule_timeout(tmo ? : 1) && tmo)
+			printk(KERN_DEBUG "cmpci: dma timed out??\n");
         }
         remove_wait_queue(&s->dma_dac.wait, &wait);
         current->state = TASK_RUNNING;
@@ -1204,7 +1272,18 @@
 			start_adc(s);
 			if (file->f_flags & O_NONBLOCK)
 				return ret ? ret : -EAGAIN;
-			interruptible_sleep_on(&s->dma_adc.wait);
+			if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) {
+				printk(KERN_DEBUG "cmpci: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				       s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count,
+				       s->dma_adc.hwptr, s->dma_adc.swptr);
+				stop_adc(s);
+				spin_lock_irqsave(&s->lock, flags);
+				set_dmaadc(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.dmasamples);
+				/* program sample counts */
+				outw(s->dma_adc.fragsamples-1, s->iobase + CODEC_CMI_CH1_FRAME2 + 2);
+				s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0;
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
 			continue;
@@ -1264,7 +1343,18 @@
 			start_dac(s);
 			if (file->f_flags & O_NONBLOCK)
 				return ret ? ret : -EAGAIN;
-			interruptible_sleep_on(&s->dma_dac.wait);
+			if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) {
+				printk(KERN_DEBUG "cmpci: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				       s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count,
+				       s->dma_dac.hwptr, s->dma_dac.swptr);
+				stop_dac(s);
+				spin_lock_irqsave(&s->lock, flags);
+				set_dmadac(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.dmasamples);
+				/* program sample counts */
+				outw(s->dma_dac.fragsamples-1, s->iobase + CODEC_CMI_CH0_FRAME2 + 2);
+				s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0;
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
 			continue;
@@ -1730,7 +1820,6 @@
 	NULL,  /* lock */
 };
 
-#ifdef CONFIG_SOUND_CMPCI_MIDI
 /* --------------------------------------------------------------------- */
 
 static ssize_t cm_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
@@ -1971,7 +2060,7 @@
 			}
 			tmo = (count * HZ) / 3100;
 			if (!schedule_timeout(tmo ? : 1) && tmo)
-				printk(KERN_DEBUG "cm: midi timed out??\n");
+				printk(KERN_DEBUG "cmpci: midi timed out??\n");
 		}
 		remove_wait_queue(&s->midi.owait, &wait);
 		set_current_state(TASK_RUNNING);
@@ -2011,11 +2100,9 @@
 	NULL,  /* revalidate */
 	NULL,  /* lock */
 };
-#endif
 
 /* --------------------------------------------------------------------- */
 
-#ifdef CONFIG_SOUND_CMPCI_FM
 static int cm_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	static const unsigned char op_offset[18] = {
@@ -2187,7 +2274,6 @@
 	NULL,  /* revalidate */
 	NULL,  /* lock */
 };
-#endif /* CONFIG_SOUND_CMPCI_FM */
 
 /* --------------------------------------------------------------------- */
 
@@ -2215,8 +2301,31 @@
 };
 
 #ifdef MODULE
-int __init init_module(void)
+static int	spdif_loop = 0;
+static int	four_ch = 0;
+static int	rear_out = 0;
+MODULE_PARM(spdif_loop, "i");
+MODULE_PARM(four_ch, "i");
+MODULE_PARM(rear_out, "i");
+
+int  __init init_module(void)
+#else
+#ifdef CONFIG_SOUND_CMPCI_SPDIFLOOP
+static int	spdif_loop = 1;
 #else
+static int	spdif_loop = 0;
+#endif
+#ifdef CONFIG_SOUND_CMPCI_4CH
+static int	four_ch = 1;
+#else
+static int	four_ch = 0;
+#endif
+#ifdef CONFIG_SOUND_CMPCI_REAR
+static int	rear_out = 1;
+#else
+static int	read_out = 0;
+#endif
+
 int __init init_cmpci(void)
 #endif
 {
@@ -2224,6 +2333,7 @@
 	struct pci_dev *pcidev = NULL;
 	mm_segment_t fs;
 	int i, val, index = 0;
+	
 	struct {
 		unsigned short	deviceid;
 		char		*devicename;
@@ -2239,10 +2349,10 @@
 	if (!pci_present())   /* No PCI bus in this machine! */
 #endif
 		return -ENODEV;
-	printk(KERN_INFO "cm: version v1.1 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "cmpci: version v2.41-nomodem time " __TIME__ " " __DATE__ "\n");
 #if 0
 	if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
-		printk(KERN_INFO "cm: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
+		printk(KERN_INFO "cmpci: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
 #endif
 	while (index < NR_DEVICE && pcidev == NULL && (
  	       (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, pcidev)) ||
@@ -2251,7 +2361,7 @@
 		if (pcidev->irq == 0)
 			continue;
 		if (!(s = kmalloc(sizeof(struct cm_state), GFP_KERNEL))) {
-			printk(KERN_WARNING "cm: out of memory\n");
+			printk(KERN_WARNING "cmpci: out of memory\n");
 			continue;
 		}
 		/* search device name */
@@ -2272,65 +2382,60 @@
 		init_MUTEX(&s->open_sem);
 		s->magic = CM_MAGIC;
 		s->iobase = pcidev->resource[0].start;
-#ifdef CONFIG_SOUND_CMPCI_FM
 		s->iosynth = 0x388;
-#endif
-#ifdef CONFIG_SOUND_CMPCI_MIDI
 		s->iomidi = 0x330;
-#endif
 		if (s->iobase == 0)
 			continue;
 		s->irq = pcidev->irq;
 
 		if (check_region(s->iobase, CM_EXTENT_CODEC)) {
-			printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1);
+			printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1);
 			goto err_region5;
 		}
 		request_region(s->iobase, CM_EXTENT_CODEC, "cmpci");
-#ifdef CONFIG_SOUND_CMPCI_MIDI
 		if (check_region(s->iomidi, CM_EXTENT_MIDI)) {
-			printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1);
-			goto err_region4;
+			printk(KERN_WARNING "cmpci: io ports %#x-%#x in use, midi disabled.\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1);
+			s->iomidi = 0;
+		}
+		else
+		{
+			request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi");
+			/* set IO based at 0x330 */
+			outb(inb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3) & ~0x60, s->iobase + CODEC_CMI_LEGACY_CTRL + 3);
 		}
-		request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi");
-		/* set IO based at 0x330 */
-		outb(inb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3) & ~0x60, s->iobase + CODEC_CMI_LEGACY_CTRL + 3);
-#endif
-#ifdef CONFIG_SOUND_CMPCI_FM
 		if (check_region(s->iosynth, CM_EXTENT_SYNTH)) {
-			printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1);
-			goto err_region1;
+			printk(KERN_WARNING "cmpci: io ports %#x-%#x in use, synth disabled.\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1);
+			s->iosynth = 0;
+		}
+		else
+		{
+			request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM");
+			/* enable FM */
+			outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 2) | 8, s->iobase + CODEC_CMI_MISC_CTRL);
 		}
-		request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM");
-		/* enable FM */
-		outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 2) | 8, s->iobase + CODEC_CMI_MISC_CTRL);
-#endif
 		/* initialize codec registers */
 		outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2);  /* disable ints */
-		outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* reset channels */
+		outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */
 		/* reset mixer */
 		wrmixer(s, DSP_MIX_DATARESETIDX, 0);
 
 		/* request irq */
 		if (request_irq(s->irq, cm_interrupt, SA_SHIRQ, "cmpci", s)) {
-			printk(KERN_ERR "cm: irq %u in use\n", s->irq);
+			printk(KERN_ERR "cmpci: irq %u in use\n", s->irq);
 			goto err_irq;
 		}
-		printk(KERN_INFO "cm: found %s adapter at io %#06x irq %u\n",
+		printk(KERN_INFO "cmpci: found %s adapter at io %#06x irq %u\n",
 		       devicename, s->iobase, s->irq);
 		/* register devices */
 		if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0)
 			goto err_dev1;
 		if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0)
 			goto err_dev2;
-#ifdef CONFIG_SOUND_CMPCI_MIDI
-		if ((s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0)
+		if (s->iomidi && (s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0)
 			goto err_dev3;
-#endif
-#ifdef CONFIG_SOUND_CMPCI_FM
-		if ((s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0)
+		if (s->iosynth && (s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0)
 			goto err_dev4;
-#endif
+		pci_set_master(pcidev);
 		/* initialize the chips */
 		fs = get_fs();
 		set_fs(KERNEL_DS);
@@ -2344,6 +2449,38 @@
 			mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
 		}
 		set_fs(fs);
+		if (pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738)
+		{
+			/* enable SPDIF loop */
+			if (spdif_loop)
+			{
+				/* turn on spdif-in to spdif-out */
+				outb(inb(s->iobase + CODEC_CMI_FUNCTRL1) | 0x80, s->iobase + CODEC_CMI_FUNCTRL1);
+				printk(KERN_INFO "cmpci: Enable SPDIF loop\n");
+			}
+			else
+				outb(inb(s->iobase + CODEC_CMI_FUNCTRL1) & ~0x80, s->iobase + CODEC_CMI_FUNCTRL1);
+			/* enable 4 channels mode */
+			if (four_ch)
+			{
+				/* 4 channel mode (analog duplicate) */
+				outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 3) | 0x04, s->iobase + CODEC_CMI_MISC_CTRL + 3);
+				printk(KERN_INFO "cmpci: Enable 4 channels mode\n");
+				/* has separate rear-out jack ? */
+				if (rear_out)
+				{
+					/* has separate rear out jack */
+					outb(inb(s->iobase + CODEC_CMI_MIXER1) & ~0x20, s->iobase + CODEC_CMI_MIXER1);
+				}
+				else
+				{
+					outb(inb(s->iobase + CODEC_CMI_MIXER1) | 0x20, s->iobase + CODEC_CMI_MIXER1);
+					printk(KERN_INFO "cmpci: line-in routed as rear-out\n");
+				}
+			}
+			else
+				outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 3) & ~0x04, s->iobase + CODEC_CMI_MISC_CTRL + 3);
+		}
 		/* queue it for later freeing */
 		s->next = devs;
 		devs = s;
@@ -2357,16 +2494,14 @@
 	err_dev2:
 		unregister_sound_dsp(s->dev_audio);
 	err_dev1:
-		printk(KERN_ERR "cm: cannot register misc device\n");
+		printk(KERN_ERR "cmpci: cannot register misc device\n");
 		free_irq(s->irq, s);
 	err_irq:
-#ifdef CONFIG_SOUND_CMPCI_FM
-		release_region(s->iosynth, CM_EXTENT_SYNTH);
+		if(s->iosynth)
+			release_region(s->iosynth, CM_EXTENT_SYNTH);
 	err_region1:
-#endif
-#ifdef CONFIG_SOUND_CMPCI_MIDI
-		release_region(s->iomidi, CM_EXTENT_MIDI);
-#endif
+		if(s->iomidi)
+			release_region(s->iomidi, CM_EXTENT_MIDI);
 	err_region4:
 		release_region(s->iobase, CM_EXTENT_CODEC);
 	err_region5:
@@ -2395,32 +2530,30 @@
 		devs = devs->next;
 		outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2);  /* disable ints */
 		synchronize_irq();
-		outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* reset channels */
+		outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */
 		free_irq(s->irq, s);
 
 		/* reset mixer */
 		wrmixer(s, DSP_MIX_DATARESETIDX, 0);
 
 		release_region(s->iobase, CM_EXTENT_CODEC);
-#ifdef CONFIG_SOUND_CMPCI_MIDI
-		release_region(s->iomidi, CM_EXTENT_MIDI);
-#endif
-#ifdef CONFIG_SOUND_CMPCI_FM
-		release_region(s->iosynth, CM_EXTENT_SYNTH);
-#endif
+		if(s->iomidi)
+		{
+			release_region(s->iomidi, CM_EXTENT_MIDI);
+			unregister_sound_midi(s->dev_midi);
+		}
+		if(s->iosynth)
+		{
+			release_region(s->iosynth, CM_EXTENT_SYNTH);
+			unregister_sound_special(s->dev_dmfm);
+		}
 		unregister_sound_dsp(s->dev_audio);
 		unregister_sound_mixer(s->dev_mixer);
-#ifdef CONFIG_SOUND_CMPCI_MIDI
-		unregister_sound_midi(s->dev_midi);
-#endif
-#ifdef CONFIG_SOUND_CMPCI_FM
-		unregister_sound_special(s->dev_dmfm);
-#endif
 		kfree_s(s, sizeof(struct cm_state));
 	}
 	if (wavetable_mem)
 		free_pages(wavetable_mem, 20-PAGE_SHIFT);
-	printk(KERN_INFO "cm: unloading\n");
+	printk(KERN_INFO "cmpci: unloading\n");
 }
 
 #endif /* MODULE */

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