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

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

diff -u --recursive --new-file v2.1.27/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
@@ -15,7 +15,7 @@
  */
 
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -42,17 +42,13 @@
     unsigned char   saved_regs[16];
     int             debug_flag;
 
-    int             speed;
-    unsigned char   speed_bits;
-    int             channels;
-    int             audio_format;
-    unsigned char   format_bits;
     int             audio_flags;
+    int             record_dev, playback_dev;
 
     int             xfer_count;
     int             audio_mode;
+    int             open_mode;
     int             intr_active;
-    int             opened;
     char           *chip_name;
     int             model;
 #define MD_1848		1
@@ -76,16 +72,29 @@
     mixer_ents     *mix_devices;
     int             mixer_output_port;
   }
-
 ad1848_info;
 
+typedef struct ad1848_port_info
+  {
+    int             open_mode;
+    int             speed;
+    unsigned char   speed_bits;
+    int             channels;
+    int             audio_format;
+    unsigned char   format_bits;
+  }
+ad1848_port_info;
+
 static int      nr_ad1848_devs = 0;
 static volatile char irq2dev[17] =
 {-1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1, -1};
 
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
 static int      timer_installed = -1;
 
+#endif
+
 static int      ad_format_mask[8 /*devc->model */ ] =
 {
   0,
@@ -107,19 +116,22 @@
 
 static int      ad1848_open (int dev, int mode);
 static void     ad1848_close (int dev);
-static int      ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg, int local);
-static void     ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
-static void     ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static int      ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg);
+static void     ad1848_output_block (int dev, unsigned long buf, int count, int intrflag);
+static void     ad1848_start_input (int dev, unsigned long buf, int count, int intrflag);
 static int      ad1848_prepare_for_output (int dev, int bsize, int bcount);
-static int      ad1848_prepare_for_IO (int dev, int bsize, int bcount);
-static void     ad1848_reset (int dev);
+static int      ad1848_prepare_for_input (int dev, int bsize, int bcount);
 static void     ad1848_halt (int dev);
 static void     ad1848_halt_input (int dev);
 static void     ad1848_halt_output (int dev);
 static void     ad1848_trigger (int dev, int bits);
+
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
 static int      ad1848_tmr_install (int dev);
 static void     ad1848_tmr_reprogram (int dev);
 
+#endif
+
 static int
 ad_read (ad1848_info * devc, int reg)
 {
@@ -134,7 +146,7 @@
   cli ();
   outb (((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr (devc));
   x = inb (io_Indexed_Data (devc));
-  /*  printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */
+/* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */
   restore_flags (flags);
 
   return x;
@@ -188,6 +200,7 @@
   if (ad_read (devc, 11) & 0x20)
     if (devc->model != MD_1845)
       printk ("ad1848: Auto calibration timed out(3).\n");
+  ad_write (devc, 9, ad_read (devc, 9) & ~0x18);	/* Disable autocalibration */
 }
 
 static void
@@ -205,24 +218,18 @@
       ad_write (devc, i, prev | 0x80);
     }
 
-/*
- * Let's have some delay
- */
-
-  for (i = 0; i < 1000; i++)
-    inb (devc->base);
 }
 
 static void
 ad_unmute (ad1848_info * devc)
 {
-  int             i;
+  int             i, dummy;
 
 /*
  * Let's have some delay
  */
   for (i = 0; i < 1000; i++)
-    inb (devc->base);
+    dummy = inb (devc->base);
 
   /*
    * Restore back old volume registers (unmute)
@@ -262,7 +269,7 @@
 ad_leave_MCE (ad1848_info * devc)
 {
   unsigned long   flags;
-  unsigned char   prev;
+  unsigned char   prev, acal;
   int             timeout = 1000;
 
   while (timeout > 0 && inb (devc->base) == 0x80)	/*Are we initializing */
@@ -271,6 +278,8 @@
   save_flags (flags);
   cli ();
 
+  acal = ad_read (devc, 9);
+
   devc->MCE_bit = 0x00;
   prev = inb (io_Index_Addr (devc));
   outb ((0x00), io_Index_Addr (devc));	/* Clear the MCE bit */
@@ -282,7 +291,8 @@
     }
 
   outb ((0x00), io_Index_Addr (devc));	/* Clear the MCE bit */
-  wait_for_calibration (devc);
+  if (acal & 0x08)		/* Auto calibration is enabled */
+    wait_for_calibration (devc);
   restore_flags (flags);
 }
 
@@ -526,10 +536,10 @@
     {
       int             val;
 
-      get_user (val, (int *) arg);
+      val = *(int *) arg;
 
       if (val == 0xffff)
-	return ioctl_out (arg, devc->mixer_output_port);
+	return (*(int *) arg = devc->mixer_output_port);
 
       val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT);
 
@@ -542,24 +552,24 @@
       else
 	ad_write (devc, 26, ad_read (devc, 26) | 0x40);		/* Mute mono out */
 
-      return ioctl_out (arg, devc->mixer_output_port);
+      return (*(int *) arg = devc->mixer_output_port);
     }
 
   if (((cmd >> 8) & 0xff) == 'M')
     {
       int             val;
 
-      if (_IOC_DIR (cmd) & _IOC_WRITE)
+      if (_SIOC_DIR (cmd) & _SIOC_WRITE)
 	switch (cmd & 0xff)
 	  {
 	  case SOUND_MIXER_RECSRC:
-	    get_user (val, (int *) arg);
-	    return ioctl_out (arg, ad1848_set_recmask (devc, val));
+	    val = *(int *) arg;
+	    return (*(int *) arg = ad1848_set_recmask (devc, val));
 	    break;
 
 	  default:
-	    get_user (val, (int *) arg);
-	    return ioctl_out (arg, ad1848_mixer_set (devc, cmd & 0xff, val));
+	    val = *(int *) arg;
+	    return (*(int *) arg = ad1848_mixer_set (devc, cmd & 0xff, val));
 	  }
       else
 	switch (cmd & 0xff)	/*
@@ -568,30 +578,30 @@
 	  {
 
 	  case SOUND_MIXER_RECSRC:
-	    return ioctl_out (arg, devc->recmask);
+	    return (*(int *) arg = devc->recmask);
 	    break;
 
 	  case SOUND_MIXER_DEVMASK:
-	    return ioctl_out (arg, devc->supported_devices);
+	    return (*(int *) arg = devc->supported_devices);
 	    break;
 
 	  case SOUND_MIXER_STEREODEVS:
 	    if (devc->model == MD_C930)
-	      return ioctl_out (arg, devc->supported_devices);
+	      return (*(int *) arg = devc->supported_devices);
 	    else
-	      return ioctl_out (arg, devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
+	      return (*(int *) arg = devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
 	    break;
 
 	  case SOUND_MIXER_RECMASK:
-	    return ioctl_out (arg, devc->supported_rec_devices);
+	    return (*(int *) arg = devc->supported_rec_devices);
 	    break;
 
 	  case SOUND_MIXER_CAPS:
-	    return ioctl_out (arg, SOUND_CAP_EXCL_INPUT);
+	    return (*(int *) arg = SOUND_CAP_EXCL_INPUT);
 	    break;
 
 	  default:
-	    return ioctl_out (arg, ad1848_mixer_get (devc, cmd & 0xff));
+	    return (*(int *) arg = ad1848_mixer_get (devc, cmd & 0xff));
 	  }
     }
   else
@@ -602,6 +612,7 @@
 ad1848_set_speed (int dev, int arg)
 {
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   /*
      * The sampling speed is encoded in the least significant nibble of I8. The
@@ -642,7 +653,7 @@
   n = sizeof (speed_table) / sizeof (speed_struct);
 
   if (arg <= 0)
-    return devc->speed;
+    return portc->speed;
 
   if (devc->model == MD_1845)	/* AD1845 has different timer than others */
     {
@@ -651,9 +662,9 @@
       if (arg > 50000)
 	arg = 50000;
 
-      devc->speed = arg;
-      devc->speed_bits = speed_table[3].bits;
-      return devc->speed;
+      portc->speed = arg;
+      portc->speed_bits = speed_table[3].bits;
+      return portc->speed;
     }
 
   if (arg < speed_table[0].speed)
@@ -683,20 +694,20 @@
       selected = 3;
     }
 
-  devc->speed = speed_table[selected].speed;
-  devc->speed_bits = speed_table[selected].bits;
-  return devc->speed;
+  portc->speed = speed_table[selected].speed;
+  portc->speed_bits = speed_table[selected].bits;
+  return portc->speed;
 }
 
 static short
 ad1848_set_channels (int dev, short arg)
 {
-  ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   if (arg != 1 && arg != 2)
-    return devc->channels;
+    return portc->channels;
 
-  devc->channels = arg;
+  portc->channels = arg;
   return arg;
 }
 
@@ -704,6 +715,7 @@
 ad1848_set_bits (int dev, unsigned int arg)
 {
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   static struct format_tbl
     {
@@ -755,25 +767,25 @@
   int             i, n = sizeof (format2bits) / sizeof (struct format_tbl);
 
   if (arg == 0)
-    return devc->audio_format;
+    return portc->audio_format;
 
   if (!(arg & ad_format_mask[devc->model]))
     arg = AFMT_U8;
 
-  devc->audio_format = arg;
+  portc->audio_format = arg;
 
   for (i = 0; i < n; i++)
     if (format2bits[i].format == arg)
       {
-	if ((devc->format_bits = format2bits[i].bits) == 0)
-	  return devc->audio_format = AFMT_U8;	/* Was not supported */
+	if ((portc->format_bits = format2bits[i].bits) == 0)
+	  return portc->audio_format = AFMT_U8;		/* Was not supported */
 
 	return arg;
       }
 
   /* Still hanging here. Something must be terribly wrong */
-  devc->format_bits = 0;
-  return devc->audio_format = AFMT_U8;
+  portc->format_bits = 0;
+  return portc->audio_format = AFMT_U8;
 }
 
 static struct audio_driver ad1848_audio_driver =
@@ -783,9 +795,8 @@
   ad1848_output_block,
   ad1848_start_input,
   ad1848_ioctl,
-  ad1848_prepare_for_IO,
+  ad1848_prepare_for_input,
   ad1848_prepare_for_output,
-  ad1848_reset,
   ad1848_halt,
   NULL,
   NULL,
@@ -808,17 +819,18 @@
 ad1848_open (int dev, int mode)
 {
   ad1848_info    *devc = NULL;
+  ad1848_port_info *portc;
   unsigned long   flags;
 
   if (dev < 0 || dev >= num_audiodevs)
     return -ENXIO;
 
   devc = (ad1848_info *) audio_devs[dev]->devc;
-
+  portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   save_flags (flags);
   cli ();
-  if (devc->opened)
+  if (portc->open_mode || (devc->open_mode & mode))
     {
       restore_flags (flags);
       return -EBUSY;
@@ -832,9 +844,15 @@
     }
 
   devc->intr_active = 0;
-  devc->opened = 1;
   devc->audio_mode = 0;
+  devc->open_mode |= mode;
+  portc->open_mode = mode;
   ad1848_trigger (dev, 0);
+
+  if (mode & OPEN_READ)
+    devc->record_dev = dev;
+  if (mode & OPEN_WRITE)
+    devc->playback_dev = dev;
   restore_flags (flags);
 /*
  * Mute output until the playback really starts. This decreases clicking (hope so).
@@ -849,6 +867,7 @@
 {
   unsigned long   flags;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   DEB (printk ("ad1848_close(void)\n"));
 
@@ -856,43 +875,41 @@
   cli ();
 
   devc->intr_active = 0;
-  ad1848_reset (dev);
-
+  ad1848_halt (dev);
 
-  devc->opened = 0;
   devc->audio_mode = 0;
+  devc->open_mode &= ~portc->open_mode;
+  portc->open_mode = 0;
 
   ad_unmute (devc);
   restore_flags (flags);
 }
 
 static int
-ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
+ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
   return -EINVAL;
 }
 
 static void
-ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+ad1848_output_block (int dev, unsigned long buf, int count, int intrflag)
 {
   unsigned long   flags, cnt;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
-
-  if (!dma_restart)
-    return;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   cnt = count;
 
-  if (devc->audio_format == AFMT_IMA_ADPCM)
+  if (portc->audio_format == AFMT_IMA_ADPCM)
     {
       cnt /= 4;
     }
   else
     {
-      if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
+      if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
 	cnt >>= 1;
     }
-  if (devc->channels > 1)
+  if (portc->channels > 1)
     cnt >>= 1;
   cnt--;
 
@@ -909,13 +926,9 @@
   save_flags (flags);
   cli ();
 
-  ad1848_halt_output (dev);
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
-
   ad_write (devc, 15, (unsigned char) (cnt & 0xff));
   ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
 
-
   devc->xfer_count = cnt;
   devc->audio_mode |= PCM_ENABLE_OUTPUT;
   devc->intr_active = 1;
@@ -923,25 +936,23 @@
 }
 
 static void
-ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+ad1848_start_input (int dev, unsigned long buf, int count, int intrflag)
 {
   unsigned long   flags, cnt;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
-
-  if (!dma_restart)
-    return;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   cnt = count;
-  if (devc->audio_format == AFMT_IMA_ADPCM)
+  if (portc->audio_format == AFMT_IMA_ADPCM)
     {
       cnt /= 4;
     }
   else
     {
-      if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
+      if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
 	cnt >>= 1;
     }
-  if (devc->channels > 1)
+  if (portc->channels > 1)
     cnt >>= 1;
   cnt--;
 
@@ -958,12 +969,6 @@
   save_flags (flags);
   cli ();
 
-  if (dma_restart)
-    {
-      ad1848_halt_input (dev);
-      DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
-    }
-
   if (devc->model == MD_1848 || !devc->dual_dma)	/* Single DMA channel mode */
     {
       ad_write (devc, 15, (unsigned char) (cnt & 0xff));
@@ -987,62 +992,99 @@
 static int
 ad1848_prepare_for_output (int dev, int bsize, int bcount)
 {
+  int             timeout;
+  unsigned char   fs, old_fs, tmp = 0;
+  unsigned long   flags;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   ad_mute (devc);
-  if (devc->model != MD_4232)
+
+  save_flags (flags);
+  cli ();
+  fs = portc->speed_bits | (portc->format_bits << 5);
+
+  if (portc->channels > 1)
+    fs |= 0x10;
+
+  if (devc->model == MD_1845)	/* Use alternate speed select registers */
     {
-/*
- * This code fragment ensures that the playback FIFO is empty before
- * setting the codec for playback. Enabling playback for a moment should
- * be enough to do that.
- */
-      int             tmout;
+      fs &= 0xf0;		/* Mask off the rate select bits */
 
-      ad_write (devc, 9, ad_read (devc, 9) | 0x01);	/* Enable playback */
-      disable_dma (audio_devs[dev]->dmachan1);
-      for (tmout = 0; tmout < 1000000; tmout++)
-	if (ad_read (devc, 11) & 0x10)	/* DRQ active */
-	  if (tmout > 10000)
-	    break;
-      ad_write (devc, 9, ad_read (devc, 9) & ~0x01);	/* Stop playback */
+      ad_write (devc, 22, (portc->speed >> 8) & 0xff);	/* Speed MSB */
+      ad_write (devc, 23, portc->speed & 0xff);		/* Speed LSB */
+    }
+
+  old_fs = ad_read (devc, 8);
 
-      enable_dma (audio_devs[dev]->dmachan1);
-      devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+  ad_enter_MCE (devc);		/* Enables changes to the format select reg */
+
+  if (devc->model == MD_4232)
+    {
+      tmp = ad_read (devc, 16);
+      ad_write (devc, 16, tmp | 0x30);
     }
 
-  return ad1848_prepare_for_IO (dev, bsize, bcount);
+  ad_write (devc, 8, fs);
+  /*
+   * Write to I8 starts resynchronization. Wait until it completes.
+   */
+  timeout = 0;
+  while (timeout < 100 && inb (devc->base) != 0x80)
+    timeout++;
+  timeout = 0;
+  while (timeout < 10000 && inb (devc->base) == 0x80)
+    timeout++;
+
+  if (devc->model == MD_4232)
+    ad_write (devc, 16, tmp & ~0x30);
+
+  ad_leave_MCE (devc);		/*
+				 * Starts the calibration process.
+				 */
+  restore_flags (flags);
+  devc->xfer_count = 0;
+
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
+  if (dev == timer_installed && devc->timer_running)
+    if ((fs & 0x01) != (old_fs & 0x01))
+      {
+	ad1848_tmr_reprogram (dev);
+      }
+#endif
+  ad1848_halt_output (dev);
+  return 0;
 }
 
 static int
-ad1848_prepare_for_IO (int dev, int bsize, int bcount)
+ad1848_prepare_for_input (int dev, int bsize, int bcount)
 {
   int             timeout;
   unsigned char   fs, old_fs, tmp = 0;
   unsigned long   flags;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   if (devc->audio_mode)
     return 0;
 
   save_flags (flags);
   cli ();
-  fs = devc->speed_bits | (devc->format_bits << 5);
+  fs = portc->speed_bits | (portc->format_bits << 5);
 
-  if (devc->channels > 1)
+  if (portc->channels > 1)
     fs |= 0x10;
 
   if (devc->model == MD_1845)	/* Use alternate speed select registers */
     {
       fs &= 0xf0;		/* Mask off the rate select bits */
 
-      ad_write (devc, 22, (devc->speed >> 8) & 0xff);	/* Speed MSB */
-      ad_write (devc, 23, devc->speed & 0xff);	/* Speed LSB */
+      ad_write (devc, 22, (portc->speed >> 8) & 0xff);	/* Speed MSB */
+      ad_write (devc, 23, portc->speed & 0xff);		/* Speed LSB */
     }
 
   old_fs = ad_read (devc, 8);
 
-
   ad_enter_MCE (devc);		/* Enables changes to the format select reg */
 
   if (devc->model == MD_4232)
@@ -1090,33 +1132,29 @@
   restore_flags (flags);
   devc->xfer_count = 0;
 
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
   if (dev == timer_installed && devc->timer_running)
     if ((fs & 0x01) != (old_fs & 0x01))
       {
 	ad1848_tmr_reprogram (dev);
       }
 #endif
+  ad1848_halt_input (dev);
   return 0;
 }
 
 static void
-ad1848_reset (int dev)
-{
-  ad1848_halt (dev);
-}
-
-static void
 ad1848_halt (int dev)
 {
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   unsigned char   bits = ad_read (devc, 9);
 
-  if (bits & 0x01)
+  if (bits & 0x01 && portc->open_mode & OPEN_WRITE)
     ad1848_halt_output (dev);
 
-  if (bits & 0x02)
+  if (bits & 0x02 && portc->open_mode & OPEN_READ)
     ad1848_halt_input (dev);
   devc->audio_mode = 0;
 }
@@ -1138,18 +1176,17 @@
   {
     int             tmout;
 
-    disable_dma (audio_devs[dev]->dmachan1);
+    disable_dma (audio_devs[dev]->dmap_out->dma);
 
     for (tmout = 0; tmout < 100000; tmout++)
       if (ad_read (devc, 11) & 0x10)
 	break;
     ad_write (devc, 9, ad_read (devc, 9) & ~0x02);	/* Stop capture */
 
-    enable_dma (audio_devs[dev]->dmachan1);
+    enable_dma (audio_devs[dev]->dmap_out->dma);
     devc->audio_mode &= ~PCM_ENABLE_INPUT;
   }
 
-
   outb ((0), io_Status (devc));	/* Clear interrupt status */
   outb ((0), io_Status (devc));	/* Clear interrupt status */
 
@@ -1174,18 +1211,17 @@
   {
     int             tmout;
 
-    disable_dma (audio_devs[dev]->dmachan1);
+    disable_dma (audio_devs[dev]->dmap_out->dma);
 
     for (tmout = 0; tmout < 100000; tmout++)
       if (ad_read (devc, 11) & 0x10)
 	break;
     ad_write (devc, 9, ad_read (devc, 9) & ~0x01);	/* Stop playback */
 
-    enable_dma (audio_devs[dev]->dmachan1);
+    enable_dma (audio_devs[dev]->dmap_out->dma);
     devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
   }
 
-
   outb ((0), io_Status (devc));	/* Clear interrupt status */
   outb ((0), io_Status (devc));	/* Clear interrupt status */
 
@@ -1198,6 +1234,7 @@
 ad1848_trigger (int dev, int state)
 {
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
   unsigned long   flags;
   unsigned char   tmp, old;
 
@@ -1205,22 +1242,35 @@
   cli ();
   state &= devc->audio_mode;
 
-  tmp = (old = ad_read (devc, 9)) & ~0x03;
-  if (state & PCM_ENABLE_INPUT)
-    tmp |= 0x02;
-  if (state & PCM_ENABLE_OUTPUT)
-    tmp |= 0x01;
+  tmp = old = ad_read (devc, 9);
 
-  ad_mute (devc);
+  if (portc->open_mode & OPEN_READ)
+    {
+      if (state & PCM_ENABLE_INPUT)
+	tmp |= 0x02;
+      else
+	tmp &= ~0x02;
+    }
 
-  ad_write (devc, 9, tmp);
+  if (portc->open_mode & OPEN_WRITE)
+    {
+      if (state & PCM_ENABLE_OUTPUT)
+	tmp |= 0x01;
+      else
+	tmp &= ~0x01;
+    }
 
-  ad_unmute (devc);
+  /* ad_mute(devc); */
+  if (tmp != old)
+    {
+      ad_write (devc, 9, tmp);
+      ad_unmute (devc);
+    }
 
   restore_flags (flags);
 }
 
-void
+static void
 ad1848_init_hw (ad1848_info * devc)
 {
   int             i;
@@ -1332,7 +1382,7 @@
   devc->timer_running = 0;
   devc->MCE_bit = 0x40;
   devc->irq = 0;
-  devc->opened = 0;
+  devc->open_mode = 0;
   devc->chip_name = "AD1848";
   devc->model = MD_1848;	/* AD1848 or CS4248 */
   devc->levels = NULL;
@@ -1348,12 +1398,10 @@
      * If the I/O address is unused, it typically returns 0xff.
    */
 
-  DDB (printk ("ad1848_detect() - step A\n"));
-
 /*
  * Wait for the device to stop initialization
  */
-  /* outb(( 0x0b),  devc->base); */
+  DDB (printk ("ad1848_detect() - step 0\n"));
 
   for (i = 0; i < 10000000; i++)
     {
@@ -1363,10 +1411,12 @@
 	break;
     }
 
+  DDB (printk ("ad1848_detect() - step A\n"));
+
   if ((inb (devc->base) & 0x80) != 0x00)	/* Not a AD1848 */
     {
       DDB (printk ("ad1848 detect error - step A (%02x)\n",
-		   inb (devc->base)));
+		   (int) inb (devc->base)));
       return 0;
     }
 
@@ -1391,7 +1441,7 @@
     else
       {
 	DDB (printk ("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2));
-	return 0;
+	/* return 0; */
       }
 
   DDB (printk ("ad1848_detect() - step C\n"));
@@ -1404,7 +1454,7 @@
     else
       {
 	DDB (printk ("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2));
-	return 0;
+	/* return 0; */
       }
 
   /*
@@ -1505,7 +1555,7 @@
 	  ad_write (devc, 25, ~tmp1);	/* Invert all bits */
 	  if ((ad_read (devc, 25) & 0xe7) == (tmp1 & 0xe7))
 	    {
-	      int             id;
+	      int             id, full_id;
 
 	      /*
 	       *      It's at least CS4231
@@ -1520,8 +1570,14 @@
 	       * while the CS4231A reports different.
 	       */
 
-	      DDB (printk ("ad1848_detect() - step I\n"));
 	      id = ad_read (devc, 25) & 0xe7;
+	      full_id = ad_read (devc, 25);
+	      if (id == 0x80)	/* Device busy??? */
+		id = ad_read (devc, 25) & 0xe7;
+	      if (id == 0x80)	/* Device still busy??? */
+		id = ad_read (devc, 25) & 0xe7;
+	      DDB (printk ("ad1848_detect() - step J (%02x/%02x)\n", id,
+			   ad_read (devc, 25)));
 
 	      switch (id)
 		{
@@ -1547,6 +1603,11 @@
 		  devc->model = MD_4232;
 		  break;
 
+		case 0x41:
+		  devc->chip_name = "CS4236B";
+		  devc->model = MD_4232;
+		  break;
+
 		case 0x80:
 		  {
 		    /* 
@@ -1559,23 +1620,25 @@
 		    unsigned char   tmp = ad_read (devc, 23);
 
 		    ad_write (devc, 23, ~tmp);
-		    if (ad_read (devc, 23) != tmp)	/* AD1845 ? */
-		      {
-			devc->chip_name = "AD1845";
-			devc->model = MD_1845;
-		      }
-		    else if (interwave)
+		    if (interwave)
 		      {
 			devc->model = MD_IWAVE;
 			devc->chip_name = "IWave";
 		      }
+		    else if (ad_read (devc, 23) != tmp)		/* AD1845 ? */
+		      {
+			devc->chip_name = "AD1845";
+			devc->model = MD_1845;
+		      }
 
 		    ad_write (devc, 23, tmp);	/* Restore */
 		  }
 		  break;
 
 		default:	/* Assume CS4231 or OPTi 82C930 */
-		  DDB (printk ("ad1848: I25 = %02x\n", ad_read (devc, 25)));
+		  DDB (printk ("ad1848: I25 = %02x/%02x\n",
+			       ad_read (devc, 25),
+			       ad_read (devc, 25) & 0xe7));
 		  if (optiC930)
 		    {
 		      devc->chip_name = "82C930";
@@ -1625,16 +1688,17 @@
 
   ad1848_info    *devc = &dev_info[nr_ad1848_devs];
 
-
+  ad1848_port_info *portc = NULL;
 
   request_region (devc->base, 4, devc->chip_name);
 
   devc->irq = (irq > 0) ? irq : 0;
-  devc->opened = 0;
+  devc->open_mode = 0;
   devc->timer_ticks = 0;
   devc->dma1 = dma_playback;
   devc->dma2 = dma_capture;
   devc->audio_flags = DMA_AUTOMODE;
+  devc->playback_dev = devc->record_dev = 0;
 
   if (name != NULL && name[0] != 0)
     sprintf (dev_name,
@@ -1670,6 +1734,14 @@
       return;
     }
 
+
+  portc = (ad1848_port_info *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (ad1848_port_info)));
+  sound_mem_sizes[sound_nblocks] = sizeof (ad1848_port_info);
+  if (sound_nblocks < 1024)
+    sound_nblocks++;;
+  audio_devs[my_dev]->portc = portc;
+  memset ((char *) portc, 0, sizeof (*portc));
+
   nr_ad1848_devs++;
 
   ad1848_init_hw (devc);
@@ -1712,7 +1784,7 @@
   else if (irq < 0)
     irq2dev[-irq] = devc->dev_no = my_dev;
 
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
   if (devc->model != MD_1848 &&
       devc->model != MD_C930 && devc->irq_ok)
     ad1848_tmr_install (my_dev);
@@ -1736,6 +1808,7 @@
     {
       audio_devs[my_dev]->mixer_dev = num_mixers - 1;
     }
+
 }
 
 void
@@ -1813,10 +1886,10 @@
 	  if (irq > 0)
 	    snd_release_irq (devc->irq);
 
-	  sound_free_dma (audio_devs[dev]->dmachan1);
+	  sound_free_dma (audio_devs[dev]->dmap_out->dma);
 
-	  if (audio_devs[dev]->dmachan2 != audio_devs[dev]->dmachan1)
-	    sound_free_dma (audio_devs[dev]->dmachan2);
+	  if (audio_devs[dev]->dmap_in->dma != audio_devs[dev]->dmap_out->dma)
+	    sound_free_dma (audio_devs[dev]->dmap_in->dma);
 	}
     }
   else
@@ -1832,6 +1905,7 @@
   int             dev;
   int             alt_stat = 0xff;
   unsigned char   c930_stat = 0;
+  int             cnt = 0;
 
   if (irq < 0 || irq > 15)
     {
@@ -1864,6 +1938,8 @@
 
   if (status == 0x80)
     printk ("ad1848_interrupt: Why?\n");
+  if (devc->model == MD_1848)
+    outb ((0), io_Status (devc));	/* Clear interrupt status */
 
   if (status & 0x01)
     {
@@ -1888,74 +1964,52 @@
       else if (devc->model != MD_1848)
 	alt_stat = ad_read (devc, 24);
 
-      if (devc->opened && devc->audio_mode & PCM_ENABLE_INPUT && alt_stat & 0x20)
+      /* Acknowledge the intr before proceeding */
+      if (devc->model == MD_C930)
+	{			/* 82C930 has interrupt status register in MAD16 register MC11 */
+	  unsigned long   flags;
+
+	  save_flags (flags);
+	  cli ();
+
+	  outb ((11), 0xe0e);
+	  outb ((~c930_stat), 0xe0f);
+	  restore_flags (flags);
+	}
+      else if (devc->model != MD_1848)
+	ad_write (devc, 24, ad_read (devc, 24) & ~alt_stat);	/* Selective ack */
+
+      if (devc->open_mode & OPEN_READ && devc->audio_mode & PCM_ENABLE_INPUT && alt_stat & 0x20)
 	{
-	  DMAbuf_inputintr (dev);
+	  DMAbuf_inputintr (devc->record_dev);
 	}
 
-      if (devc->opened && devc->audio_mode & PCM_ENABLE_OUTPUT &&
+      if (devc->open_mode & OPEN_WRITE && devc->audio_mode & PCM_ENABLE_OUTPUT &&
 	  alt_stat & 0x10)
 	{
-	  DMAbuf_outputintr (dev, 1);
+	  DMAbuf_outputintr (devc->playback_dev, 1);
 	}
 
       if (devc->model != MD_1848 && alt_stat & 0x40)	/* Timer interrupt */
 	{
 	  devc->timer_ticks++;
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
 	  if (timer_installed == dev && devc->timer_running)
 	    sound_timer_interrupt ();
 #endif
 	}
     }
 
-  if (devc->model == MD_C930)
-    {				/* 82C930 has interrupt status register in MAD16 register MC11 */
-      unsigned long   flags;
-
-      save_flags (flags);
-      cli ();
-
-      outb ((11), 0xe0e);
-      outb ((~c930_stat), 0xe0f);
-      restore_flags (flags);
-    }
-  else if (devc->model != MD_1848)
-    ad_write (devc, 24, ad_read (devc, 24) & ~alt_stat);	/* Selective ack */
-  else
-    outb ((0), io_Status (devc));	/* Clear interrupt status */
-
 /*
  * Sometimes playback or capture interrupts occur while a timer interrupt
  * is being handled. The interrupt will not be retriggered if we don't
  * handle it now. Check if an interrupt is still pending and restart
  * the handler in this case.
  */
-  if (inb (io_Status (devc)) & 0x01)
-    goto interrupt_again;
-}
-
-/*
- * Some extra code for the MS Sound System
- */
-
-void
-check_opl3 (int base, struct address_info *hw_config)
-{
-
-#ifdef CONFIG_YM3812
-  if (check_region (base, 4))
+  if (inb (io_Status (devc)) & 0x01 && cnt++ < 4)
     {
-      printk ("\n\nopl3.c: I/O port %x already in use\n\n", base);
-      return;
+      goto interrupt_again;
     }
-
-  if (!opl3_detect (base, hw_config->osp))
-    return;
-
-  opl3_init (base, hw_config->osp);
-  request_region (base, 4, "OPL3/OPL2");
-#endif
 }
 
 #ifdef DESKPROXL
@@ -2217,11 +2271,12 @@
       int             ret;
 
       DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n",
-		   hw_config->io_base, inb (hw_config->io_base + 3)));
+		   hw_config->io_base, (int) inb (hw_config->io_base + 3)));
       DDB (printk ("Trying to detect codec anyway but IRQ/DMA may not work\n"));
       if (!(ret = ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp)))
 	return 0;
 
+      hw_config->card_subtype = 1;
       return 1;
     }
 
@@ -2276,7 +2331,6 @@
   int             dma = hw_config->dma;
   int             dma2 = hw_config->dma2;
 
-
   if (hw_config->card_subtype == 1)	/* Has no IRQ/DMA registers */
     {
       ad1848_init ("MS Sound System", hw_config->io_base + 4,
@@ -2350,37 +2404,7 @@
   release_region (hw_config->io_base, 4);
 }
 
-/*
- * WSS compatible PnP codec support
- */
-
-int
-probe_pnp_ad1848 (struct address_info *hw_config)
-{
-  return ad1848_detect (hw_config->io_base, NULL, hw_config->osp);
-}
-
-void
-attach_pnp_ad1848 (struct address_info *hw_config)
-{
-
-  ad1848_init (hw_config->name, hw_config->io_base,
-	       hw_config->irq,
-	       hw_config->dma,
-	       hw_config->dma2, 0, hw_config->osp);
-}
-
-void
-unload_pnp_ad1848 (struct address_info *hw_config)
-{
-  ad1848_unload (hw_config->io_base,
-		 hw_config->irq,
-		 hw_config->dma,
-		 hw_config->dma2, 0);
-  release_region (hw_config->io_base, 4);
-}
-
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
 /*
  * Timer stuff (for /dev/music).
  */
@@ -2477,6 +2501,7 @@
 static struct sound_lowlev_timer ad1848_tmr =
 {
   0,
+  2,
   ad1848_tmr_start,
   ad1848_tmr_disable,
   ad1848_tmr_restart

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov