patch-2.1.29 linux/drivers/sbus/audio/cs4231.c

Next file: linux/drivers/sbus/audio/cs4231.h
Previous file: linux/drivers/sbus/audio/audio.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/audio/cs4231.c linux/drivers/sbus/audio/cs4231.c
@@ -14,13 +14,15 @@
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/interrupt.h>
+#include <linux/malloc.h>
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 #include <asm/system.h>
 #include <asm/irq.h>
 #include <asm/io.h>
-#include <asm/auxio.h>
 #include <asm/delay.h>
+#include <asm/sbus.h>
+
 #include "audio.h"
 #include "cs4231.h"
 
@@ -32,219 +34,215 @@
 #define MIN(_a,_b)      ((_a)<(_b)?(_a):(_b))
 #endif
 
-static int cs4231_node, cs4231_irq, cs4231_is_revision_a, cs4231_ints_on = 0;
-static unsigned int cs4231_monitor_gain_value; 
-cs4231_regs_size
-
-static int cs4231_output_muted_value;
-
-static struct cs4231_stream_info cs4231_input;
-static struct cs4231_stream_info cs4231_output;
-
-static int cs4231_busy = 0, cs4231_need_init = 0;
+#define MAX_DRIVERS 1
+static struct sparcaudio_driver drivers[MAX_DRIVERS];
+static int num_drivers;
+
+static int cs4231_playintr(struct sparcaudio_driver *drv);
+static int cs4231_recintr(struct sparcaudio_driver *drv);
+static void cs4231_output_muted(struct sparcaudio_driver *drv, unsigned int value);
+static void cs4231_mute(struct sparcaudio_driver *drv);
+static void cs4231_pollinput(struct sparcaudio_driver *drv);
+static int cs4231_attach(struct sparcaudio_driver *drv, int node,
+                          struct linux_sbus *sbus);
+
+#define CHIP_BUG udelay(100); cs4231_ready(drv); udelay(1000);
 
-static volatile struct cs4231_chip *cs4231_chip = NULL;
-
-static __u8 * ptr;
-static size_t count;
-
-#define CHIP_BUG udelay(100); cs4231_ready(); udelay(1000);
-
-static void cs4231_ready(void) 
+/* Disable mode change, let chip auto-calibrate */
+static void cs4231_ready(struct sparcaudio_driver *drv) 
 {
-  register unsigned int x = 0;
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+  unsigned int x = 0;
 
-  cs4231_chip->pioregs.iar = (u_char)IAR_AUTOCAL_END;
-  while (cs4231_chip->pioregs.iar == IAR_NOT_READY && x <= CS_TIMEOUT) {
+  cs4231_chip->pioregs->iar = (u_char)IAR_AUTOCAL_END;
+  while (cs4231_chip->pioregs->iar == IAR_NOT_READY && x <= CS_TIMEOUT) {
     x++;
   }
 
   x = 0;
-
-  cs4231_chip->pioregs.iar = 0x0b;
-
-  while (cs4231_chip->pioregs.idr == AUTOCAL_IN_PROGRESS && x <= CS_TIMEOUT) {
+  cs4231_chip->pioregs->iar = 0x0b;
+  while (cs4231_chip->pioregs->idr == AUTOCAL_IN_PROGRESS && x <= CS_TIMEOUT) {
     x++;
   }
 }
 
-/* Enable cs4231 interrupts atomically. */
-static __inline__ void cs4231_enable_ints(void)
-{
-	register unsigned long flags;
-
-	if (cs4231_ints_on)
-		return;
-
-	save_and_cli(flags);
-	/* do init here
-	amd7930_regs->cr = AMR_INIT;
-	amd7930_regs->dr = AM_INIT_ACTIVE;
-	*/
-	restore_flags(flags);
-
-	cs4231_ints_on = 1;
-}
-
-/* Disable cs4231 interrupts atomically. */
-static __inline__ void cs4231_disable_ints(void)
-{
-	register unsigned long flags;
-
-	if (!cs4231_ints_on)
-		return;
-
-	save_and_cli(flags);
-/*
-	amd7930_regs->cr = AMR_INIT;
-	amd7930_regs->dr = AM_INIT_ACTIVE | AM_INIT_DISABLE_INTS;
-*/
-	restore_flags(flags);
-
-	cs4231_ints_on = 0;
-}  
-
-
 /* Audio interrupt handler. */
 static void cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	__u8 dummy;
-
-        /* Clear the interrupt. */
-        dummy = cs4231_chip->dmaregs.dmacsr;
-
-        cs4231_chip->dmaregs.dmacsr = dummy;
-
+  struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id;
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+  __u8 dummy;
+  int ic = 1;
+  
+  /* Clear the interrupt. */
+  dummy = cs4231_chip->dmaregs.dmacsr;
+  cs4231_chip->dmaregs.dmacsr = dummy;
+
+  /* now go through and figure out what gets to claim the interrupt */
+  if (dummy & CS_PLAY_INT) {
+    if (dummy & CS_XINT_PNVA) {
+      /* recalculate number of samples */
+      cs4231_playintr(drv);
+    }
+    ic = 0;
+  }
+  if (dummy & CS_CAPT_INT) {
+    if (dummy & CS_XINT_CNVA) {
+      /* recalculate number of samples */
+      cs4231_recintr(drv);
+    }
+    ic = 0;
+  }
+  if ((dummy & CS_XINT_CEMP) 
+      && (cs4231_chip->perchip_info.record.active == 0)) 
+    {
+      ic = 0;
+    }
+  if ((dummy & CS_XINT_EMPT) && (cs4231_chip->perchip_info.play.active == 0)) {
+    cs4231_chip->dmaregs.dmacsr |= (CS_PPAUSE);
+    cs4231_chip->pioregs->iar = 0x9;
+    cs4231_chip->pioregs->idr &= PEN_DISABLE;
+    
+    cs4231_mute(drv);
+    
+    /* recalculate number of samples */
+    /* cleanup DMA */
+    ic = 0;
+  }
+  if (dummy & CS_GENL_INT) {
+    ic = 0;
+  }
 }
 
-static unsigned int cs4231_output_muted(unsigned int value)
+/* Set output mute */
+static void cs4231_output_muted(struct sparcaudio_driver *drv, unsigned int value)
 {
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   if (!value) {
-    cs4231_chip->pioregs.iar = 0x7;
-    cs4231_chip->pioregs.idr &= OUTCR_UNMUTE;
-    cs4231_chip->pioregs.iar = 0x6;
-    cs4231_chip->pioregs.idr &= OUTCR_UNMUTE;
-    cs4231_output_muted_value = 0;
+    cs4231_chip->pioregs->iar = 0x7;
+    cs4231_chip->pioregs->idr &= OUTCR_UNMUTE;
+    cs4231_chip->pioregs->iar = 0x6;
+    cs4231_chip->pioregs->idr &= OUTCR_UNMUTE;
+    cs4231_chip->perchip_info.output_muted = 0;
   } else {
-    cs4231_chip->pioregs.iar = 0x7;
-    cs4231_chip->pioregs.idr |= OUTCR_MUTE;
-    cs4231_chip->pioregs.iar = 0x6;
-    cs4231_chip->pioregs.idr |= OUTCR_MUTE;
-    cs4231_output_muted_value = 1;
+    cs4231_chip->pioregs->iar = 0x7;
+    cs4231_chip->pioregs->idr |= OUTCR_MUTE;
+    cs4231_chip->pioregs->iar = 0x6;
+    cs4231_chip->pioregs->idr |= OUTCR_MUTE;
+    cs4231_chip->perchip_info.output_muted = 1;
   }
-  return (cs4231_output_muted_value);
+  return /*(cs4231_chip->perchip_info.output_muted)*/;
 }
 
-static unsigned int cs4231_out_port(unsigned int value)
+/* Set chip "output" port */
+static unsigned int cs4231_out_port(struct sparcaudio_driver *drv, unsigned int value)
 {
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   unsigned int r = 0;
 
   /* You can have any combo you want. Just don't tell anyone. */
 
-  cs4231_chip->pioregs.iar = 0x1a;
-  cs4231_chip->pioregs.idr |= MONO_IOCR_MUTE;
-  cs4231_chip->pioregs.iar = 0x0a;
-  cs4231_chip->pioregs.idr |= PINCR_LINE_MUTE;
-  cs4231_chip->pioregs.idr |= PINCR_HDPH_MUTE;
+  cs4231_chip->pioregs->iar = 0x1a;
+  cs4231_chip->pioregs->idr |= MONO_IOCR_MUTE;
+  cs4231_chip->pioregs->iar = 0x0a;
+  cs4231_chip->pioregs->idr |= PINCR_LINE_MUTE;
+  cs4231_chip->pioregs->idr |= PINCR_HDPH_MUTE;
 
   if (value & AUDIO_SPEAKER) {
-    cs4231_chip->pioregs.iar = 0x1a;
-    cs4231_chip->pioregs.idr &= ~MONO_IOCR_MUTE;
+    cs4231_chip->pioregs->iar = 0x1a;
+    cs4231_chip->pioregs->idr &= ~MONO_IOCR_MUTE;
     r |= AUDIO_SPEAKER;
   }
 
   if (value & AUDIO_HEADPHONE) {
-   cs4231_chip->pioregs.iar = 0x0a;
-   cs4231_chip->pioregs.idr &= ~PINCR_HDPH_MUTE;
+   cs4231_chip->pioregs->iar = 0x0a;
+   cs4231_chip->pioregs->idr &= ~PINCR_HDPH_MUTE;
    r |= AUDIO_HEADPHONE;
   }
 
   if (value & AUDIO_LINE_OUT) {
-    cs4231_chip->pioregs.iar = 0x0a;
-    cs4231_chip->pioregs.idr &= ~PINCR_LINE_MUTE;
+    cs4231_chip->pioregs->iar = 0x0a;
+    cs4231_chip->pioregs->idr &= ~PINCR_LINE_MUTE;
     r |= AUDIO_LINE_OUT;
   }
   
   return (r);
 }
 
-static unsigned int cs4231_in_port(unsigned int value)
+/* Set chip "input" port */
+static unsigned int cs4231_in_port(struct sparcaudio_driver *drv, unsigned int value)
 {
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   unsigned int r = 0;
 
   /* The order of these seems to matter. Can't tell yet why. */
-
   if (value & AUDIO_INTERNAL_CD_IN) {
-    cs4231_chip->pioregs.iar = 0x1;
-    cs4231_chip->pioregs.idr = CDROM_ENABLE(cs4231_chip->pioregs.idr);
-    cs4231_chip->pioregs.iar = 0x0;
-    cs4231_chip->pioregs.idr = CDROM_ENABLE(cs4231_chip->pioregs.idr);
+    cs4231_chip->pioregs->iar = 0x1;
+    cs4231_chip->pioregs->idr = CDROM_ENABLE(cs4231_chip->pioregs->idr);
+    cs4231_chip->pioregs->iar = 0x0;
+    cs4231_chip->pioregs->idr = CDROM_ENABLE(cs4231_chip->pioregs->idr);
     r = AUDIO_INTERNAL_CD_IN;
   }
   if ((value & AUDIO_LINE_IN)) {
-    cs4231_chip->pioregs.iar = 0x1;
-    cs4231_chip->pioregs.idr = LINE_ENABLE(cs4231_chip->pioregs.idr);
-    cs4231_chip->pioregs.iar = 0x0;
-    cs4231_chip->pioregs.idr = LINE_ENABLE(cs4231_chip->pioregs.idr);
+    cs4231_chip->pioregs->iar = 0x1;
+    cs4231_chip->pioregs->idr = LINE_ENABLE(cs4231_chip->pioregs->idr);
+    cs4231_chip->pioregs->iar = 0x0;
+    cs4231_chip->pioregs->idr = LINE_ENABLE(cs4231_chip->pioregs->idr);
     r = AUDIO_LINE_IN;
   } else if (value & AUDIO_MICROPHONE) {
-    cs4231_chip->pioregs.iar = 0x1;
-    cs4231_chip->pioregs.idr = MIC_ENABLE(cs4231_chip->pioregs.idr);
-    cs4231_chip->pioregs.iar = 0x0;
-    cs4231_chip->pioregs.idr = MIC_ENABLE(cs4231_chip->pioregs.idr);
+    cs4231_chip->pioregs->iar = 0x1;
+    cs4231_chip->pioregs->idr = MIC_ENABLE(cs4231_chip->pioregs->idr);
+    cs4231_chip->pioregs->iar = 0x0;
+    cs4231_chip->pioregs->idr = MIC_ENABLE(cs4231_chip->pioregs->idr);
     r = AUDIO_MICROPHONE;
   }
 
   return (r);
 }
 
-static unsigned int cs4231_monitor_gain(unsigned int value)
+/* Set chip "monitor" gain */
+static unsigned int cs4231_monitor_gain(struct sparcaudio_driver *drv, unsigned int value)
 {
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   int a = 0;
 
-  a = CS4231_MON_MAX_ATEN - (value * (CS4231_MON_MAX_ATEN + 1) /
-                            (AUDIO_MAX_GAIN + 1));
+  a = CS4231_MON_MAX_ATEN - (value * (CS4231_MON_MAX_ATEN + 1) / (AUDIO_MAX_GAIN + 1));
 
-  cs4231_chip->pioregs.iar = 0x0d;
+  cs4231_chip->pioregs->iar = 0x0d;
   if (a >= CS4231_MON_MAX_ATEN) 
-    cs4231_chip->pioregs.idr = LOOPB_OFF;
+    cs4231_chip->pioregs->idr = LOOPB_OFF;
   else 
-    cs4231_chip->pioregs.idr = ((a << 2) | LOOPB_ON);
-
+    cs4231_chip->pioregs->idr = ((a << 2) | LOOPB_ON);
 
-  if (value == AUDIO_MAX_GAIN)
-    return AUDIO_MAX_GAIN;
+  if (value == AUDIO_MAX_GAIN) return AUDIO_MAX_GAIN;
 
-  return ((CS4231_MAX_DEV_ATEN - a) * (AUDIO_MAX_GAIN + 1) /
-          (CS4231_MAX_DEV_ATEN + 1));
+  return ((CS4231_MAX_DEV_ATEN - a) * (AUDIO_MAX_GAIN + 1) / (CS4231_MAX_DEV_ATEN + 1));
 }
 
-/* Set record gain */
-static unsigned int cs4231_record_gain(unsigned int value, unsigned char balance)
+/* Set chip record gain */
+static unsigned int cs4231_record_gain(struct sparcaudio_driver *drv, unsigned int value, unsigned char balance)
 {
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   unsigned int tmp = 0, r, l, ra, la;
   unsigned char old_gain;
 
-
   r = l = value;
 
   if (balance < AUDIO_MID_BALANCE) {
-    r = MAX(0, (int)(value -
-                     ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT)));
+    r = MAX(0, (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT)));
   } else if (balance > AUDIO_MID_BALANCE) {
-    l = MAX(0, (int)(value -
-                     ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT)));
+    l = MAX(0, (int)(value - ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT)));
   }
 
   la = l * (CS4231_MAX_GAIN + 1) / (AUDIO_MAX_GAIN + 1);
   ra = r * (CS4231_MAX_GAIN + 1) / (AUDIO_MAX_GAIN + 1);
   
-  cs4231_chip->pioregs.iar = 0x0;
-  old_gain = cs4231_chip->pioregs.idr;
-  cs4231_chip->pioregs.idr = RECGAIN_SET(old_gain, la);
-  cs4231_chip->pioregs.iar = 0x1;
-  old_gain = cs4231_chip->pioregs.idr;
-  cs4231_chip->pioregs.idr = RECGAIN_SET(old_gain, ra);
+  cs4231_chip->pioregs->iar = 0x0;
+  old_gain = cs4231_chip->pioregs->idr;
+  cs4231_chip->pioregs->idr = RECGAIN_SET(old_gain, la);
+  cs4231_chip->pioregs->iar = 0x1;
+  old_gain = cs4231_chip->pioregs->idr;
+  cs4231_chip->pioregs->idr = RECGAIN_SET(old_gain, ra);
   
   if (l == value) {
     (l == 0) ? (tmp = 0) : (tmp = ((la + 1) * AUDIO_MAX_GAIN) / (CS4231_MAX_GAIN + 1));
@@ -254,194 +252,374 @@
   return (tmp);
 }
 
-/* Set play gain */
-static unsigned int cs4231_play_gain(unsigned int value, unsigned char balance)
+/* Set chip play gain */
+static unsigned int cs4231_play_gain(struct sparcaudio_driver *drv, unsigned int value, unsigned char balance)
 {
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   unsigned int tmp = 0, r, l, ra, la;
   unsigned char old_gain;
 
   r = l = value;
   if (balance < AUDIO_MID_BALANCE) {
-    r = MAX(0, (int)(value -
-                     ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT)));
+    r = MAX(0, (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT)));
   } else if (balance > AUDIO_MID_BALANCE) {
-    l = MAX(0, (int)(value -
-                     ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT)));
+    l = MAX(0, (int)(value - ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT)));
   }
 
   if (l == 0) {
     la = CS4231_MAX_DEV_ATEN;
   } else {
-    la = CS4231_MAX_ATEN -
-      (l * (CS4231_MAX_ATEN + 1) / (AUDIO_MAX_GAIN + 1));
+    la = CS4231_MAX_ATEN - (l * (CS4231_MAX_ATEN + 1) / (AUDIO_MAX_GAIN + 1));
   }
   if (r == 0) {
     ra = CS4231_MAX_DEV_ATEN;
   } else {
-    ra = CS4231_MAX_ATEN -
-      (r * (CS4231_MAX_ATEN + 1) / (AUDIO_MAX_GAIN + 1));
+    ra = CS4231_MAX_ATEN - (r * (CS4231_MAX_ATEN + 1) / (AUDIO_MAX_GAIN + 1));
   }
   
-  cs4231_chip->pioregs.iar = 0x6;
-  old_gain = cs4231_chip->pioregs.idr;
-  cs4231_chip->pioregs.idr = GAIN_SET(old_gain, la);
-  cs4231_chip->pioregs.iar = 0x7;
-  old_gain = cs4231_chip->pioregs.idr;
-  cs4231_chip->pioregs.idr = GAIN_SET(old_gain, ra);
+  cs4231_chip->pioregs->iar = 0x6;
+  old_gain = cs4231_chip->pioregs->idr;
+  cs4231_chip->pioregs->idr = GAIN_SET(old_gain, la);
+  cs4231_chip->pioregs->iar = 0x7;
+  old_gain = cs4231_chip->pioregs->idr;
+  cs4231_chip->pioregs->idr = GAIN_SET(old_gain, ra);
   
   if ((value == 0) || (value == AUDIO_MAX_GAIN)) {
     tmp = value;
   } else {
     if (l == value) {
-      tmp = ((CS4231_MAX_ATEN - la) *
-                 (AUDIO_MAX_GAIN + 1) /
-                 (CS4231_MAX_ATEN + 1));
+      tmp = ((CS4231_MAX_ATEN - la) * (AUDIO_MAX_GAIN + 1) / (CS4231_MAX_ATEN + 1));
     } else if (r == value) {
-      tmp = ((CS4231_MAX_ATEN - ra) *
-                 (AUDIO_MAX_GAIN + 1) /
-                 (CS4231_MAX_ATEN + 1));
+      tmp = ((CS4231_MAX_ATEN - ra) * (AUDIO_MAX_GAIN + 1) / (CS4231_MAX_ATEN + 1));
     }
   }
   return (tmp);
 }
 
 /* Reset the audio chip to a sane state. */
-static void cs4231_reset(void)
+static void cs4231_reset(struct sparcaudio_driver *drv)
 {
-        cs4231_chip->dmaregs.dmacsr = APC_RESET;
-        cs4231_chip->dmaregs.dmacsr = 0x00;
-        cs4231_chip->dmaregs.dmacsr |= APC_CODEC_PDN;
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
-        udelay(20);
+  cs4231_chip->dmaregs.dmacsr = CS_CHIP_RESET;
+  cs4231_chip->dmaregs.dmacsr = 0x00;
+  cs4231_chip->dmaregs.dmacsr |= CS_CDC_RESET;
+  
+  udelay(100);
+  
+  cs4231_chip->dmaregs.dmacsr &= ~(CS_CDC_RESET);
+  cs4231_chip->pioregs->iar |= IAR_AUTOCAL_BEGIN;
+  
+  CHIP_BUG
+    
+  cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x0c;
+  cs4231_chip->pioregs->idr = MISC_IR_MODE2;
+  cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x08;
+  cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT; /* Ulaw */
+  
+  CHIP_BUG
+    
+  cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x1c;
+  cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT; /* Ulaw */
+  
+  CHIP_BUG
+    
+  cs4231_chip->pioregs->iar = 0x19;
+  
+  /* see what we can turn on */
+  if (cs4231_chip->pioregs->idr & CS4231A)
+    cs4231_chip->status |= CS_STATUS_REV_A;
+  else
+    cs4231_chip->status &= ~CS_STATUS_REV_A;
+  
+  cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x10;
+  cs4231_chip->pioregs->idr = OLB_ENABLE;
+  
+  cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x11;
+  if (cs4231_chip->status & CS_STATUS_REV_A)
+    cs4231_chip->pioregs->idr = (HPF_ON | XTALE_ON);
+  else
+    cs4231_chip->pioregs->idr = (HPF_ON);
+  
+  cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x1a;
+  cs4231_chip->pioregs->idr = 0x00;
+  
+  /* Now set things up for defaults */
+  cs4231_chip->perchip_info.play.port = cs4231_out_port(drv, AUDIO_SPEAKER);
+  cs4231_chip->perchip_info.record.port = cs4231_in_port(drv, AUDIO_MICROPHONE);
+  cs4231_chip->perchip_info.play.gain = cs4231_play_gain(drv, CS4231_DEFAULT_PLAYGAIN, AUDIO_MID_BALANCE);
+  cs4231_chip->perchip_info.record.gain = cs4231_record_gain(drv, CS4231_DEFAULT_RECGAIN, AUDIO_MID_BALANCE);
+  cs4231_chip->perchip_info.monitor_gain = cs4231_monitor_gain(drv, LOOPB_OFF);
+  
+  cs4231_chip->pioregs->iar = (u_char)IAR_AUTOCAL_END;
+  
+  cs4231_ready(drv);
+  
+  cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x09;
+  cs4231_chip->pioregs->idr &= ACAL_DISABLE;
+  cs4231_chip->pioregs->iar = (u_char)IAR_AUTOCAL_END;
+  
+  cs4231_ready(drv);
 
-        cs4231_chip->dmaregs.dmacsr &= ~(APC_CODEC_PDN);
-        cs4231_chip->pioregs.iar |= IAR_AUTOCAL_BEGIN;
+  cs4231_output_muted(drv, 0);
+}
 
-        CHIP_BUG
+static void cs4231_mute(struct sparcaudio_driver *drv)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x0c;
-        cs4231_chip->pioregs.idr = MISC_IR_MODE2;
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x08;
-        cs4231_chip->pioregs.idr = DEFAULT_DATA_FMAT;
+  if (!(cs4231_chip->status & CS_STATUS_REV_A)) {
+    cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN;
+    udelay(100);
+    cs4231_chip->pioregs->iar = IAR_AUTOCAL_END;
+    CHIP_BUG
+  }
+}
 
-        CHIP_BUG
+/* Not yet useful */
+#if 0
+static int cs4231_len_to_sample(struct sparcaudio_driver *drv, int length, int direction)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+  int sample;
 
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x1c;
-        cs4231_chip->pioregs.idr = DEFAULT_DATA_FMAT;
+  if (/* number of channels == 2*/0) {
+    sample = (length/2);
+  } else {
+    sample = length;
+  }
+  if (/*encoding == AUDIO_ENCODING_LINEAR*/0) {
+    sample = sample/2;
+  }
+  return (sample);
+}
+#endif
 
-        CHIP_BUG
+static int cs4231_open(struct inode * inode, struct file * file, struct sparcaudio_driver *drv)
+{	
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
-        cs4231_chip->pioregs.iar = 0x19;
+  /* Set the default audio parameters. */
+  
+  cs4231_chip->perchip_info.play.sample_rate = CS4231_RATE;
+  cs4231_chip->perchip_info.play.channels = CS4231_CHANNELS;
+  cs4231_chip->perchip_info.play.precision = CS4231_PRECISION;
+  cs4231_chip->perchip_info.play.encoding = AUDIO_ENCODING_ULAW;
+  
+  cs4231_chip->perchip_info.record.sample_rate = CS4231_RATE;
+  cs4231_chip->perchip_info.record.channels = CS4231_CHANNELS;
+  cs4231_chip->perchip_info.record.precision = CS4231_PRECISION;
+  cs4231_chip->perchip_info.record.encoding = AUDIO_ENCODING_ULAW;
+  
+  cs4231_ready(drv);
+  
+  cs4231_chip->status |= CS_STATUS_NEED_INIT;
+  
+  CHIP_BUG
+    
+  MOD_INC_USE_COUNT;
+  
+  return 0;
+}
 
-        if (cs4231_chip->pioregs.idr & CS4231A)
-                cs4231_is_revision_a = 1;
-        else
-                cs4231_is_revision_a = 0;
+static void cs4231_release(struct inode * inode, struct file * file, struct sparcaudio_driver *drv)
+{
+  /* zero out any info about what data we have as well */
+  /* should insert init on close variable optionally calling cs4231_reset() */
+  MOD_DEC_USE_COUNT;
+}
+
+static int cs4231_playintr(struct sparcaudio_driver *drv)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+  /* Send the next byte of outgoing data. */
+#if 0
+  if (cs4231_chip->output_ptr && cs4231_chip->output_count > 0) {
+    cs4231_chip->dmaregs.dmapnva = dma_handle;
+    cs4231_chip->dmaregs.dmapnc = length; 
+    cs4231_chip->output_ptr++;
+    cs4231_chip->output_count--;
+    
+    /* Done with the buffer? Notify the midlevel driver. */
+    if (cs4231_chip->output_count == 0) {
+      cs4231_chip->output_ptr = NULL;
+      cs4231_chip->output_count = 0;
+      sparcaudio_output_done(drv);
+    }
+  }
+#endif
+}
 
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x10;
-        cs4231_chip->pioregs.idr = (u_char)OLB_ENABLE;
+static void cs4231_recmute(int fmt)
+{
+  switch (fmt) {
+  case AUDIO_ENCODING_LINEAR:
+    /* Insert 0x00 from "here" to end of data stream */
+    break;
+  case AUDIO_ENCODING_ALAW:
+    /* Insert 0xd5 from "here" to end of data stream */
+    break;
+  case AUDIO_ENCODING_ULAW:
+    /* Insert 0xff from "here" to end of data stream */
+    break;
+  }
+}
 
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x11;
-        if (cs4231_is_revision_a)
-                cs4231_chip->pioregs.idr = (HPF_ON | XTALE_ON);
-        else
-                cs4231_chip->pioregs.idr = (HPF_ON);
+static int cs4231_recintr(struct sparcaudio_driver *drv)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+  cs4231_recmute(cs4231_chip->perchip_info.record.encoding);
+
+  if (cs4231_chip->perchip_info.record.active == 0) {
+    cs4231_pollinput(drv);
+    cs4231_chip->pioregs->iar = 0x9;
+    cs4231_chip->pioregs->idr &= CEN_DISABLE;
+  }
+  /* Read the next byte of incoming data. */
+#if 0
+  if (cs4231_chip->input_ptr && cs4231_chip->input_count > 0) {
+    cs4231_chip->dmaregs.dmacnva = dma_handle;
+    cs4231_chip->dmaregs.dmacnc = length;
+    cs4231_chip->input_ptr++;
+    cs4231_chip->input_count--;
+    
+    /* Done with the buffer? Notify the midlevel driver. */
+    if (cs4231_chip->input_count == 0) {
+      cs4231_chip->input_ptr = NULL;
+      cs4231_chip->input_count = 0;
+      sparcaudio_input_done(drv);
+    }
+  }
+#endif
+}
 
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x1a;
-        cs4231_chip->pioregs.idr = 0x00;
+static void cs4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer, unsigned long count)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
-        cs4231_output.gain = cs4231_play_gain(CS4231_DEFAULT_PLAYGAIN,
-                                              AUDIO_MID_BALANCE);
-        cs4231_input.gain = cs4231_record_gain(CS4231_DEFAULT_RECGAIN,
-                                               AUDIO_MID_BALANCE);
+  if (cs4231_chip->perchip_info.play.active || (cs4231_chip->perchip_info.play.pause))
+    return;
 
-        cs4231_output.port = cs4231_out_port(AUDIO_SPEAKER);
-        cs4231_input.port = cs4231_in_port(AUDIO_MICROPHONE);
+  cs4231_ready(drv);
 
-        cs4231_monitor_gain_value = cs4231_monitor_gain(LOOPB_OFF);
+  if (cs4231_chip->status & CS_STATUS_NEED_INIT)
+    {
+      cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x08;
+      cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT;
+      cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x1c;
+      cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT;
 
-        cs4231_chip->pioregs.iar = (u_char)IAR_AUTOCAL_END;
+      CHIP_BUG
 
-        cs4231_ready();
+      cs4231_chip->status &= ~CS_STATUS_NEED_INIT;
+    }
 
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x09;
-        cs4231_chip->pioregs.idr &= ACAL_DISABLE;
-        cs4231_chip->pioregs.iar = (u_char)IAR_AUTOCAL_END;
+  if (!cs4231_chip->perchip_info.play.pause) 
+    {
+      /* init dma foo here */
+      cs4231_chip->dmaregs.dmacsr &= ~CS_XINT_PLAY;
+      cs4231_chip->dmaregs.dmacsr &= ~CS_PPAUSE;
+      if (cs4231_playintr(drv)) {
+	cs4231_chip->dmaregs.dmacsr |= CS_PLAY_SETUP;
+	cs4231_chip->pioregs->iar = 0x9;
+	cs4231_chip->pioregs->idr |= PEN_ENABLE;
+      }
+    }
+  cs4231_chip->perchip_info.play.active = 1;
+}
 
-        cs4231_ready();
+static void cs4231_stop_output(struct sparcaudio_driver *drv)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
-        cs4231_output_muted_value = cs4231_output_muted(0x0);
+  cs4231_chip->perchip_info.play.active = 0;
+  cs4231_chip->dmaregs.dmacsr |= (CS_PPAUSE);
 }
 
-static int cs4231_open(struct inode * inode, struct file * file, struct sparcaudio_driver *drv)
+static void cs4231_pollinput(struct sparcaudio_driver *drv)
 {
-	int level;
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+  int x = 0;
 
-        /* Set the default audio parameters. */
+  while (!(cs4231_chip->dmaregs.dmacsr & CS_XINT_COVF) && x <= CS_TIMEOUT) {
+    x++;
+  }
+  cs4231_chip->dmaregs.dmacsr |= CS_XINT_CEMP;
+}
 
-        cs4231_output.sample_rate = CS4231_RATE;
-        cs4231_output.channels = CS4231_CHANNELS;
-        cs4231_output.precision = CS4231_PRECISION;
-        cs4231_output.encoding = AUDIO_ENCODING_ULAW;
-
-        cs4231_input.sample_rate = CS4231_RATE;
-        cs4231_input.channels = CS4231_CHANNELS;
-        cs4231_input.precision = CS4231_PRECISION;
-        cs4231_input.encoding = AUDIO_ENCODING_ULAW;
-
-        cs4231_ready();
-
-        cs4231_need_init = 1;
-#if 1
-        /* Arguably this should only happen once. I need to play around 
-         * on a Solaris box and see what happens
-         */
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x08;
-        cs4231_chip->pioregs.idr = DEFAULT_DATA_FMAT;
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x1c;
-        cs4231_chip->pioregs.idr = DEFAULT_DATA_FMAT;
+static void cs4231_start_input(struct sparcaudio_driver *drv, __u8 * buffer, unsigned long count)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
-#endif
+  if (cs4231_chip->perchip_info.record.active || (cs4231_chip->perchip_info.record.pause))
+    return;
 
-        CHIP_BUG
+  cs4231_ready(drv);
 
-	MOD_INC_USE_COUNT;
+  if (cs4231_chip->status & CS_STATUS_NEED_INIT)
+    {
+      cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x08;
+      cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT;
+      cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x1c;
+      cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT;
 
-	return 0;
-}
+      CHIP_BUG
 
-static void cs4231_release(struct inode * inode, struct file * file, struct sparcaudio_driver *drv)
-{
-	cs4231_disable_ints();
-	MOD_DEC_USE_COUNT;
+      cs4231_chip->status &= ~CS_STATUS_NEED_INIT;
+    }
+
+  if (!cs4231_chip->perchip_info.record.pause)
+    {
+      /* init dma foo here */
+      cs4231_chip->dmaregs.dmacsr &= ~CS_XINT_CAPT;
+      cs4231_chip->dmaregs.dmacsr &= ~CS_CPAUSE;
+      cs4231_recintr(drv);
+      cs4231_chip->dmaregs.dmacsr |= CS_CAPT_SETUP;
+      cs4231_chip->pioregs->iar = 0x9;
+      cs4231_chip->pioregs->idr |= CEN_ENABLE;
+    }
+  cs4231_chip->perchip_info.record.active = 1;
 }
 
-static void cs4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer, size_t the_count)
+static void cs4231_stop_input(struct sparcaudio_driver *drv)
 {
-	count = the_count;
-	ptr = buffer;
-	cs4231_enable_ints();
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+  cs4231_chip->perchip_info.record.active = 0;
+  cs4231_chip->dmaregs.dmacsr |= (CS_CPAUSE);
+
+  cs4231_pollinput(drv);
+
+  /* need adjust the end pointer, process the input, and clean up the dma */
+
+  cs4231_chip->pioregs->iar = 0x09;
+  cs4231_chip->pioregs->idr &= CEN_DISABLE;
 }
 
-static void cs4231_stop_output(struct sparcaudio_driver *drv)
+static void cs4231_audio_getdev(struct sparcaudio_driver *drv,
+                                 audio_device_t * audinfo)
 {
-	cs4231_disable_ints();
-	ptr = NULL;
-	count = 0;
+        strncpy(audinfo->name, "cs4231", sizeof(audinfo->name) - 1);
+        strncpy(audinfo->version, "x", sizeof(audinfo->version) - 1);
+        strncpy(audinfo->config, "audio", sizeof(audinfo->config) - 1);
 }
 
 
+/* The ioctl handler should be expected to identify itself and handle loopback
+   mode */
+/* There will also be a handler for getinfo and setinfo */
+
 static struct sparcaudio_operations cs4231_ops = {
 	cs4231_open,
 	cs4231_release,
 	NULL,			/* cs4231_ioctl */
 	cs4231_start_output,
 	cs4231_stop_output,
-};
-
-static struct sparcaudio_driver cs4231_drv = {
-	"cs4231",
-	&cs4231_ops,
+	cs4231_start_input,
+        cs4231_stop_input,
+	cs4231_audio_getdev,
 };
 
 /* Probe for the cs4231 chip and then attach the driver. */
@@ -451,68 +629,127 @@
 __initfunc(int cs4231_init(void))
 #endif
 {
-	struct linux_prom_registers regs[1];
-	struct linux_prom_irqs irq;
-	int err;
+  struct linux_sbus *bus;
+  struct linux_sbus_device *sdev;
+  int cs4231_node;
+  
+  /* Find the PROM CS4231 node. */
+  /* There's an easier way, and I should FIXME */
+  cs4231_node = prom_getchild(prom_root_node);
+  cs4231_node = prom_searchsiblings(cs4231_node,"iommu");
+  cs4231_node = prom_getchild(cs4231_node);
+  cs4231_node = prom_searchsiblings(cs4231_node,"sbus");
+  cs4231_node = prom_getchild(cs4231_node);
+  cs4231_node = prom_searchsiblings(cs4231_node,"SUNW,CS4231");
+  
+  if (cs4231_node && cs4231_attach(&drivers[0], cs4231_node, NULL) == 0)
+    num_drivers = 1;
+  else
+    num_drivers = 0;
+  
+  /* Probe each SBUS for cs4231 chips. */
+  for_all_sbusdev(sdev,bus) {
+    if (!strcmp(sdev->prom_name, "SUNW,CS4231")) {
+      /* Don't go over the max number of drivers. */
+      if (num_drivers >= MAX_DRIVERS)
+	continue;
+      
+      if (cs4231_attach(&drivers[num_drivers],
+			sdev->prom_node, sdev->my_bus) == 0)
+	num_drivers++;
+    }
+  }
+  
+  /* Only return success if we found some cs4231 chips. */
+  return (num_drivers > 0) ? 0 : -EIO;
+}
 
-#ifdef MODULE
-	register_symtab(0);
+/* Attach to an cs4231 chip given its PROM node. */
+static int cs4231_attach(struct sparcaudio_driver *drv, int node,
+                          struct linux_sbus *sbus)
+{
+  struct linux_prom_registers regs;
+  struct linux_prom_irqs irq;
+  struct cs4231_chip *cs4231_chip;
+  int err;
+
+  /* Allocate our private information structure. */
+  drv->private = kmalloc(sizeof(struct cs4231_chip), GFP_KERNEL);
+  if (!drv->private)
+    return -ENOMEM;
+
+  /* Point at the information structure and initialize it. */
+  drv->ops = &cs4231_ops;
+  cs4231_chip = (struct cs4231_chip *)drv->private;
+#if 0
+  cs4231_chip->input_ptr = NULL;
+  cs4231_chip->input_count = 0;
+  cs4231_chip->output_ptr = NULL;
+  cs4231_chip->output_count = 0;
 #endif
 
-        /* Find the PROM CS4231 node. */
-        cs4231_node = prom_getchild(prom_root_node);
-        cs4231_node = prom_searchsiblings(cs4231_node,"iommu");
-        cs4231_node = prom_getchild(cs4231_node);
-        cs4231_node = prom_searchsiblings(cs4231_node,"sbus");
-        cs4231_node = prom_getchild(cs4231_node);
-        cs4231_node = prom_searchsiblings(cs4231_node,"SUNW,CS4231");
-
-	if (!cs4231_node)
-		return -EIO;
-
-	/* XXX Add for_each_sbus() search as well for LX and friends. */
-	/* XXX Copy out for prom_apply_sbus_ranges. */
-
-	/* Map the registers into memory. */
-        prom_getproperty(cs4231_node, "reg", (char *)regs, sizeof(regs));
-	cs4231_regs_size = regs[0].reg_size;
-	cs4231_regs = sparc_alloc_io(regs[0].phys_addr, 0, regs[0].reg_size,
-				      "cs4231", regs[0].which_io, 0);
-	if (!cs4231_regs) {
-		printk(KERN_ERR "cs4231: could not allocate registers\n");
-		return -EIO;
-	}
-
-	/* Disable cs4231 interrupt generation. */
-	cs4231_disable_ints();
-
-        /* Reset the audio chip. */
-        cs4231_reset();
-
-	/* Attach the interrupt handler to the audio interrupt. */
-        prom_getproperty(cs4231_node, "intr", (char *)&irq, sizeof(irq));
-        cs4231_irq = irq.pri;
-        request_irq(cs4231_irq, cs4231_interrupt, SA_INTERRUPT, "cs4231", NULL);
-        enable_irq(cs4231_irq);
-
-	/* Register ourselves with the midlevel audio driver. */
-	err = register_sparcaudio_driver(&cs4231_drv);
-	if (err < 0) {
-		/* XXX We should do something. Complain for now. */
-		printk(KERN_ERR "cs4231: really screwed now\n");
-		return -EIO;
-	}
-
-	return 0;
+  /* Map the registers into memory. */
+  prom_getproperty(node, "reg", (char *)&regs, sizeof(regs));
+  if (sbus)
+    prom_apply_sbus_ranges(sbus, &regs, 1);
+  cs4231_chip->regs_size = regs.reg_size;
+  cs4231_chip->pioregs = sparc_alloc_io(regs.phys_addr, 0, regs.reg_size,
+				      "cs4231", regs.which_io, 0);
+  if (!cs4231_chip->pioregs) {
+    printk(KERN_ERR "cs4231: could not allocate registers\n");
+    kfree(drv->private);
+    return -EIO;
+  }
+
+  /* Reset the audio chip. */
+  cs4231_reset(drv);
+
+  /* Attach the interrupt handler to the audio interrupt. */
+  prom_getproperty(node, "intr", (char *)&irq, sizeof(irq));
+  cs4231_chip->irq = irq.pri;
+  request_irq(cs4231_chip->irq, cs4231_interrupt, SA_INTERRUPT, "cs4231", NULL);
+  enable_irq(cs4231_chip->irq);
+
+  /* Register ourselves with the midlevel audio driver. */
+  err = register_sparcaudio_driver(drv);
+  if (err < 0) {
+    printk(KERN_ERR "cs4231: unable to register\n");
+    disable_irq(cs4231_chip->irq);
+    free_irq(cs4231_chip->irq, drv);
+    sparc_free_io(cs4231_chip->pioregs, cs4231_chip->regs_size);
+    kfree(drv->private);
+    return -EIO;
+  }
+
+  /* Announce the hardware to the user. */
+  printk(KERN_INFO "cs4231 at 0x%lx irq %d\n",
+	 (unsigned long)cs4231_chip->pioregs, cs4231_chip->irq);
+  
+  /* Success! */
+  return 0;
 }
 
 #ifdef MODULE
+/* Detach from an cs4231 chip given the device structure. */
+static void cs4231_detach(struct sparcaudio_driver *drv)
+{
+        struct cs4231_chip *info = (struct cs4231_chip *)drv->private;
+
+        unregister_sparcaudio_driver(drv);
+        disable_irq(info->irq);
+        free_irq(info->irq, drv);
+        sparc_free_io(info->pioregs, info->regs_size);
+        kfree(drv->private);
+}
+
 void cleanup_module(void)
 {
-	unregister_sparcaudio_driver(&cs4231_drv);
-	cs4231_disable_ints();
-	disable_irq(cs4231_irq);
-	free_irq(cs4231_irq, NULL);
-	sparc_free_io(cs4231_regs, cs4231_regs_size);
+        register int i;
+
+        for (i = 0; i < num_drivers; i++) {
+                cs4231_detach(&drivers[i]);
+                num_drivers--;
+        }
 }
 #endif
+

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