patch-1.3.9 linux/drivers/sound/opl3.c

Next file: linux/drivers/sound/opl3.h
Previous file: linux/drivers/sound/mpu401.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.8/linux/drivers/sound/opl3.c linux/drivers/sound/opl3.c
@@ -46,6 +46,7 @@
 				   * * OP4 * * begin here   */
 
 static int      opl3_enabled = 0;
+static int      opl4_enabled = 0;
 static int      left_address = 0x388, right_address = 0x388, both_address = 0;
 
 static int      nr_voices = 9;
@@ -189,7 +190,7 @@
    * Note2! The chip is initialized if detected.
    */
 
-  unsigned char   stat1, stat2;
+  unsigned char   stat1, stat2, signature;
   int             i;
 
   if (already_initialized)
@@ -202,22 +203,13 @@
   if (opl3_enabled)
     ioaddr = left_address;
 
-  opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);	/*
-										 * Reset
-										 * timers
-										 * 1
-										 * and
-										 * 2
-										 */
-  opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET);	/*
-								 * Reset the
-								 * IRQ of FM
-								 * * chicp
-								 */
+  /* Reset timers 1 and 2 */
+  opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);
 
-  stat1 = INB (ioaddr);		/*
-				 * Read status register
-				 */
+  /* Reset the IRQ of the FM chip */
+  opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET);
+
+  signature = stat1 = INB (ioaddr);	/* Status register */
 
   if ((stat1 & 0xE0) != 0x00)
     {
@@ -226,23 +218,19 @@
 				 */
     }
 
-  opl3_command (ioaddr, TIMER1_REGISTER, 0xff);	/*
-							 * Set timer 1 to
-							 * 0xff
-							 */
+  opl3_command (ioaddr, TIMER1_REGISTER, 0xff);		/* Set timer1 to 0xff */
+
   opl3_command (ioaddr, TIMER_CONTROL_REGISTER,
 		TIMER2_MASK | TIMER1_START);	/*
 						 * Unmask and start timer 1
 						 */
 
   /*
-   * Now we have to delay at least 80 msec
+   * Now we have to delay at least 80 usec
    */
 
   for (i = 0; i < 50; i++)
-    tenmicrosec ();		/*
-				 * To be sure
-				 */
+    tenmicrosec ();
 
   stat2 = INB (ioaddr);		/*
 				 * Read status after timers have expired
@@ -252,18 +240,10 @@
    * Stop the timers
    */
 
-  opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);	/*
-										 * Reset
-										 * timers
-										 * 1
-										 * and
-										 * 2
-										 */
-  opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET);	/*
-								 * Reset the
-								 * IRQ of FM
-								 * * chicp
-								 */
+  /* Reset timers 1 and 2 */
+  opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);
+  /* Reset the IRQ of the FM chip */
+  opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET);
 
   if ((stat2 & 0xE0) != 0xc0)
     {
@@ -273,9 +253,41 @@
     }
 
   /*
-   * There is a FM chicp in this address. Now set some default values.
+   * There is a FM chicp in this address. Detect the type (OPL2 to OPL4)
    */
 
+  if (signature == 0x06)	/* OPL2 */
+    {
+      opl3_enabled = 0;
+    }
+  else if (signature == 0x00)	/* OPL3 or OPL4 */
+    {
+      unsigned char   tmp;
+
+      if (!opl3_enabled)	/* Was not already enabled */
+	{
+	  left_address = ioaddr;
+	  right_address = ioaddr + 2;
+	  opl3_enabled = 1;
+	}
+
+      /*
+       * Detect availability of OPL4 (_experimental_). Works propably
+       * only after a cold boot. In addition the OPL4 port
+       * of the chip may not be connected to the PC bus at all.
+       */
+
+      opl3_command (right_address, OPL3_MODE_REGISTER, 0x00);
+      opl3_command (right_address, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE);
+
+      if ((tmp = INB (ioaddr)) == 0x02)		/* Have a OPL4 */
+	{
+	  opl4_enabled = 1;
+	}
+      opl3_command (right_address, OPL3_MODE_REGISTER, 0);
+
+    }
+
   for (i = 0; i < 9; i++)
     opl3_command (ioaddr, KEYON_BLOCK + i, 0);	/*
 						 * Note off
@@ -374,14 +386,14 @@
  -11, -11, -10, -10, -10, -9, -9, -8,	/*
 					 * 24 -  31
 					 */
- -8, -8, -7, -7, -7, -6, -6, -6,/*
-					 * 32 -  39
+ -8, -8, -7, -7, -7, -6, -6, -6,	/*
+					   * 32 -  39
 					 */
- -5, -5, -5, -5, -4, -4, -4, -4,/*
-					 * 40 -  47
+ -5, -5, -5, -5, -4, -4, -4, -4,	/*
+					   * 40 -  47
 					 */
- -3, -3, -3, -3, -2, -2, -2, -2,/*
-					 * 48 -  55
+ -3, -3, -3, -3, -2, -2, -2, -2,	/*
+					   * 48 -  55
 					 */
  -2, -1, -1, -1, -1, 0, 0, 0,	/*
 				 * 56 -  63
@@ -473,13 +485,13 @@
 	  calc_vol (&vol2, volume);
 	}
 
-      opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1);	/*
-									 * Modulator
-									 * volume
+      opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1);		/*
+									   * Modulator
+									   * volume
 									 */
-      opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2);	/*
-									 * Carrier
-									 * volume
+      opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2);		/*
+									   * Carrier
+									   * volume
 									 */
     }
   else
@@ -526,7 +538,7 @@
 
 	default:		/*
 				 * Why ??
-	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  				 */ ;
+				 */ ;
 	}
 
       opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1);
@@ -858,12 +870,23 @@
 static int
 opl3_open (int dev, int mode)
 {
+  int             i;
+
   if (!opl3_ok)
     return RET_ERROR (ENXIO);
   if (opl3_busy)
     return RET_ERROR (EBUSY);
   opl3_busy = 1;
 
+  voice_alloc->max_voice = nr_voices = opl3_enabled ? 18 : 9;
+  voice_alloc->timestamp = 0;
+
+  for (i = 0; i < 18; i++)
+    {
+      voice_alloc->map[i] = 0;
+      voice_alloc->alloc_times[i] = 0;
+    }
+
   connection_mask = 0x00;	/*
 				 * Just 2 OP voices
 				 */
@@ -877,6 +900,7 @@
 {
   opl3_busy = 0;
   voice_alloc->max_voice = nr_voices = opl3_enabled ? 18 : 9;
+
   fm_info.nr_drums = 0;
   fm_info.perc_mode = 0;
 
@@ -1076,7 +1100,7 @@
 static int
 opl3_alloc_voice (int dev, int chn, int note, struct voice_alloc_info *alloc)
 {
-  int             i, p, avail_voices;
+  int             i, p, best, first, avail_voices, best_time = 0x7fffffff;
   struct sbi_instrument *instr;
   int             is4op;
   int             instr_no;
@@ -1097,21 +1121,22 @@
 
   if (is4op)
     {
-      p = 0;
+      first = p = 0;
       avail_voices = 6;
     }
   else
     {
       if (nr_voices == 12)	/* 4 OP mode. Use the '2 OP only' voices first */
-	p = 6;
+	first = p = 6;
       else
-	p = 0;
+	first = p = 0;
       avail_voices = nr_voices;
     }
 
   /*
- *    Now try to find a free voice
- */
+     *    Now try to find a free voice
+   */
+  best = first;
 
   for (i = 0; i < avail_voices; i++)
     {
@@ -1119,15 +1144,36 @@
 	{
 	  return p;
 	}
-      p = (p + 1) % nr_voices;
+      if (alloc->alloc_times[p] < best_time)	/* Find oldest playing note */
+	{
+	  best_time = alloc->alloc_times[p];
+	  best = p;
+	}
+      p = (p + 1) % avail_voices;
     }
 
   /*
- *    Insert some kind of priority mechanism here.
- */
+     *    Insert some kind of priority mechanism here.
+   */
+
+  if (best < 0)
+    best = 0;
+  if (best > nr_voices)
+    best -= nr_voices;
 
-  printk ("OPL3: Out of free voices\n");
-  return 0;			/* All voices in use. Select the first one. */
+  return best;			/* All voices in use. Select the first one. */
+}
+
+static void
+opl3_setup_voice (int dev, int voice, int chn)
+{
+  struct channel_info *info =
+  &synth_devs[dev]->chn_info[chn];
+
+  opl3_set_instr (dev, voice,
+		  info->pgm_num);
+
+  voices[voice].bender = info->bender_value;
 }
 
 static struct synth_operations opl3_operations =
@@ -1151,7 +1197,8 @@
   opl3_volume_method,
   opl3_patchmgr,
   opl3_bender,
-  opl3_alloc_voice
+  opl3_alloc_voice,
+  opl3_setup_voice
 };
 
 long
@@ -1175,14 +1222,16 @@
   opl3_ok = 1;
   if (opl3_enabled)
     {
-      printk (" <Yamaha OPL-3 FM>");
+      if (opl4_enabled)
+	printk (" <Yamaha OPL4/OPL3 FM>");
+      else
+	printk (" <Yamaha OPL-3 FM>");
+
       fm_model = 2;
       voice_alloc->max_voice = nr_voices = 18;
       fm_info.nr_drums = 0;
       fm_info.capabilities |= SYNTH_CAP_OPL3;
-#ifndef SCO
       strcpy (fm_info.name, "Yamaha OPL-3");
-#endif
 
       for (i = 0; i < 18; i++)
 	if (physical_voices[i].ioaddr == USE_LEFT)

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this