/*
 * sound/opl3.c
 *
 * A low level driver for Yamaha YM3812 and OPL-3 -chips
 */
/*
 * Copyright (C) by Hannu Savolainen 1993-1996
 *
 * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
 * Version 2 (June 1991). See the "COPYING" file distributed with this software
 * for more info.
 */
#include <linux/config.h>


/*
 * Major improvements to the FM handling 30AUG92 by Rob Hooft,
 */
/*
 * hooft@chem.ruu.nl
 */

#include "sound_config.h"

#if defined(CONFIG_YM3812)

#include "opl3.h"

#define MAX_VOICE	18
#define OFFS_4OP	11

struct voice_info
  {
    unsigned char   keyon_byte;
    long            bender;
    long            bender_range;
    unsigned long   orig_freq;
    unsigned long   current_freq;
    int             volume;
    int             mode;
  };

typedef struct opl_devinfo
  {
    int             base;
    int             left_io, right_io;
    int             nr_voice;
    int             lv_map[MAX_VOICE];

    struct voice_info voc[MAX_VOICE];
    struct voice_alloc_info *v_alloc;
    struct channel_info *chn_info;

    struct sbi_instrument i_map[SBFM_MAXINSTR];
    struct sbi_instrument *act_i[MAX_VOICE];

    struct synth_info fm_info;

    int             busy;
    int             model;
    unsigned char   cmask;

    int             is_opl4;
    int            *osp;
  }
opl_devinfo;

static struct opl_devinfo *devc = NULL;

static int      force_opl3_mode = 0;

static int      detected_model;

static int      store_instr (int instr_no, struct sbi_instrument *instr);
static void     freq_to_fnum (int freq, int *block, int *fnum);
static void     opl3_command (int io_addr, unsigned int addr, unsigned int val);
static int      opl3_kill_note (int dev, int voice, int note, int velocity);

void
enable_opl3_mode (int left, int right, int both)
{
  force_opl3_mode = 1;
}

static void
enter_4op_mode (void)
{
  int             i;
  static int      v4op[MAX_VOICE] =
  {0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17};

  devc->cmask = 0x3f;		/* Connect all possible 4 OP voice operators */
  opl3_command (devc->right_io, CONNECTION_SELECT_REGISTER, 0x3f);

  for (i = 0; i < 3; i++)
    pv_map[i].voice_mode = 4;
  for (i = 3; i < 6; i++)
    pv_map[i].voice_mode = 0;

  for (i = 9; i < 12; i++)
    pv_map[i].voice_mode = 4;
  for (i = 12; i < 15; i++)
    pv_map[i].voice_mode = 0;

  for (i = 0; i < 12; i++)
    devc->lv_map[i] = v4op[i];
  devc->v_alloc->max_voice = devc->nr_voice = 12;
}

static int
opl3_ioctl (int dev,
	    unsigned int cmd, caddr_t arg)
{
  switch (cmd)
    {

    case SNDCTL_FM_LOAD_INSTR:
      {
	struct sbi_instrument ins;

	memcpy_fromfs ((char *) &ins, &((char *) arg)[0], sizeof (ins));

	if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
	  {
	    printk ("FM Error: Invalid instrument number %d\n", ins.channel);
	    return -(EINVAL);
	  }

	pmgr_inform (dev, PM_E_PATCH_LOADED, ins.channel, 0, 0, 0);
	return store_instr (ins.channel, &ins);
      }
      break;

    case SNDCTL_SYNTH_INFO:
      devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice;

      memcpy_tofs (&((char *) arg)[0], &devc->fm_info, sizeof (devc->fm_info));
      return 0;
      break;

    case SNDCTL_SYNTH_MEMAVL:
      return 0x7fffffff;
      break;

    case SNDCTL_FM_4OP_ENABLE:
      if (devc->model == 2)
	enter_4op_mode ();
      return 0;
      break;

    default:
      return -(EINVAL);
    }

}

int
opl3_detect (int ioaddr, int *osp)
{
  /*
   * This function returns 1 if the FM chip is present at the given I/O port
   * The detection algorithm plays with the timer built in the FM chip and
   * looks for a change in the status register.
   *
   * Note! The timers of the FM chip are not connected to AdLib (and compatible)
   * boards.
   *
   * Note2! The chip is initialized if detected.
   */

  unsigned char   stat1, stat2, signature;
  int             i;

  if (devc != NULL)
    return 0;


  devc = (struct opl_devinfo *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (*devc)));
  if (sound_nblocks < 1024)
    sound_nblocks++;;

  if (devc == NULL)
    {
      printk ("OPL3: Can't allocate memory for the device control structure\n");
      return 0;
    }

  devc->osp = osp;
  devc->base = ioaddr;

  /* 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);

  signature = stat1 = inb (ioaddr);	/* Status register */

  if ((stat1 & 0xE0) != 0x00)
    {
      return 0;			/*
				 * Should be 0x00
				 */
    }

  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 usec
   */

  for (i = 0; i < 50; i++)
    tenmicrosec (devc->osp);

  stat2 = inb (ioaddr);		/*
				   * Read status after timers have expired
				 */

  /*
   * Stop the timers
   */

  /* 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)
    {
      return 0;			/*
				 * There is no YM3812
				 */
    }

  /*
   * There is a FM chip in this address. Detect the type (OPL2 to OPL4)
   */

  if (signature == 0x06 && !force_opl3_mode)	/* OPL2 */
    {
      detected_model = 2;
    }
  else if (signature == 0x00)	/* OPL3 or OPL4 */
    {
      unsigned char   tmp;

      detected_model = 3;

      /*
       * Detect availability of OPL4 (_experimental_). Works probably
       * 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 (ioaddr + 2, OPL3_MODE_REGISTER, 0x00);
      opl3_command (ioaddr + 2, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE);

      if ((tmp = inb (ioaddr)) == 0x02)		/* Have a OPL4 */
	{
	  detected_model = 4;
	}

      if (!check_region (ioaddr - 8, 2))	/* OPL4 port is free */
	{
	  int             tmp;

	  outb (0x02, ioaddr - 8);	/* Select OPL4 ID register */
	  tenmicrosec (devc->osp);
	  tmp = inb (ioaddr - 7);	/* Read it */
	  tenmicrosec (devc->osp);

	  if (tmp == 0x20)	/* OPL4 should return 0x20 here */
	    {
	      detected_model = 4;

	      outb (0xF8, ioaddr - 8);	/* Select OPL4 FM mixer control */
	      tenmicrosec (devc->osp);
	      outb (0x1B, ioaddr - 7);	/* Write value */
	      tenmicrosec (devc->osp);
	    }
	  else
	    detected_model = 3;
	}

      opl3_command (ioaddr + 2, OPL3_MODE_REGISTER, 0);

    }

  for (i = 0; i < 9; i++)
    opl3_command (ioaddr, KEYON_BLOCK + i, 0);	/*
						 * Note off
						 */

  opl3_command (ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT);
  opl3_command (ioaddr, PERCUSSION_REGISTER, 0x00);	/*
							 * Melodic mode.
							 */

  return 1;
}

static int
opl3_kill_note (int devno, int voice, int note, int velocity)
{
  struct physical_voice_info *map;

  if (voice < 0 || voice >= devc->nr_voice)
    return 0;

  devc->v_alloc->map[voice] = 0;

  map = &pv_map[devc->lv_map[voice]];

  DEB (printk ("Kill note %d\n", voice));

  if (map->voice_mode == 0)
    return 0;

  opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, devc->voc[voice].keyon_byte & ~0x20);

  devc->voc[voice].keyon_byte = 0;
  devc->voc[voice].bender = 0;
  devc->voc[voice].volume = 64;
  devc->voc[voice].bender_range = 200;	/*
					 * 200 cents = 2 semitones
					 */
  devc->voc[voice].orig_freq = 0;
  devc->voc[voice].current_freq = 0;
  devc->voc[voice].mode = 0;

  return 0;
}

#define HIHAT			0
#define CYMBAL			1
#define TOMTOM			2
#define SNARE			3
#define BDRUM			4
#define UNDEFINED		TOMTOM
#define DEFAULT			TOMTOM

static int
store_instr (int instr_no, struct sbi_instrument *instr)
{

  if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || devc->model != 2))
    printk ("FM warning: Invalid patch format field (key) 0x%x\n", instr->key);
  memcpy ((char *) &(devc->i_map[instr_no]), (char *) instr, sizeof (*instr));

  return 0;
}

static int
opl3_set_instr (int dev, int voice, int instr_no)
{
  if (voice < 0 || voice >= devc->nr_voice)
    return 0;

  if (instr_no < 0 || instr_no >= SBFM_MAXINSTR)
    return 0;

  devc->act_i[voice] = &devc->i_map[instr_no];
  return 0;
}

/*
 * The next table looks magical, but it certainly is not. Its values have
 * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
 * for i=0. This log-table converts a linear volume-scaling (0..127) to a
 * logarithmic scaling as present in the FM-synthesizer chips. so :    Volume
 * 64 =  0 db = relative volume  0 and:    Volume 32 = -6 db = relative
 * volume -8 it was implemented as a table because it is only 128 bytes and
 * it saves a lot of log() calculations. (RH)
 */
char            fm_volume_table[128] =
{-64, -48, -40, -35, -32, -29, -27, -26,
 -24, -23, -21, -20, -19, -18, -18, -17,
 -16, -15, -15, -14, -13, -13, -12, -12,
 -11, -11, -10, -10, -10, -9, -9, -8,
 -8, -8, -7, -7, -7, -6, -6, -6,
 -5, -5, -5, -5, -4, -4, -4, -4,
 -3, -3, -3, -3, -2, -2, -2, -2,
 -2, -1, -1, -1, -1, 0, 0, 0,
 0, 0, 0, 1, 1, 1, 1, 1,
 1, 2, 2, 2, 2, 2, 2, 2,
 3, 3, 3, 3, 3, 3, 3, 4,
 4, 4, 4, 4, 4, 4, 4, 5,
 5, 5, 5, 5, 5, 5, 5, 5,
 6, 6, 6, 6, 6, 6, 6, 6,
 6, 7, 7, 7, 7, 7, 7, 7,
 7, 7, 7, 8, 8, 8, 8, 8};

static void
calc_vol (unsigned char *regbyte, int volume, int main_vol)
{
  int             level = (~*regbyte & 0x3f);

  if (main_vol > 127)
    main_vol = 127;

  volume = (volume * main_vol) / 127;

  if (level)
    level += fm_volume_table[volume];

  if (level > 0x3f)
    level = 0x3f;
  if (level < 0)
    level = 0;

  *regbyte = (*regbyte & 0xc0) | (~level & 0x3f);
}

static void
set_voice_volume (int voice, int volume, int main_vol)
{
  unsigned char   vol1, vol2, vol3, vol4;
  struct sbi_instrument *instr;
  struct physical_voice_info *map;

  if (voice < 0 || voice >= devc->nr_voice)
    return;

  map = &pv_map[devc->lv_map[voice]];

  instr = devc->act_i[voice];

  if (!instr)
    instr = &devc->i_map[0];

  if (instr->channel < 0)
    return;

  if (devc->voc[voice].mode == 0)
    return;

  if (devc->voc[voice].mode == 2)
    {

      vol1 = instr->operators[2];
      vol2 = instr->operators[3];

      if ((instr->operators[10] & 0x01))
	{
	  calc_vol (&vol1, volume, main_vol);
	  calc_vol (&vol2, volume, main_vol);
	}
      else
	{
	  calc_vol (&vol2, volume, main_vol);
	}

      opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1);
      opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2);
    }
  else
    {				/*
				 * 4 OP voice
				 */
      int             connection;

      vol1 = instr->operators[2];
      vol2 = instr->operators[3];
      vol3 = instr->operators[OFFS_4OP + 2];
      vol4 = instr->operators[OFFS_4OP + 3];

      /*
       * The connection method for 4 OP devc->voc is defined by the rightmost
       * bits at the offsets 10 and 10+OFFS_4OP
       */

      connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);

      switch (connection)
	{
	case 0:
	  calc_vol (&vol4, volume, main_vol);
	  break;

	case 1:
	  calc_vol (&vol2, volume, main_vol);
	  calc_vol (&vol4, volume, main_vol);
	  break;

	case 2:
	  calc_vol (&vol1, volume, main_vol);
	  calc_vol (&vol4, volume, main_vol);
	  break;

	case 3:
	  calc_vol (&vol1, volume, main_vol);
	  calc_vol (&vol3, volume, main_vol);
	  calc_vol (&vol4, volume, main_vol);
	  break;

	default:;
	}

      opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1);
      opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2);
      opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], vol3);
      opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], vol4);
    }
}

static int
opl3_start_note (int dev, int voice, int note, int volume)
{
  unsigned char   data, fpc;
  int             block, fnum, freq, voice_mode;
  struct sbi_instrument *instr;
  struct physical_voice_info *map;

  if (voice < 0 || voice >= devc->nr_voice)
    return 0;

  map = &pv_map[devc->lv_map[voice]];

  if (map->voice_mode == 0)
    return 0;

  if (note == 255)		/*
				 * Just change the volume
				 */
    {
      set_voice_volume (voice, volume, devc->voc[voice].volume);
      return 0;
    }

  /*
   * Kill previous note before playing
   */
  opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], 0xff);	/*
								 * Carrier
								 * volume to
								 * min
								 */
  opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], 0xff);	/*
								 * Modulator
								 * volume to
								 */

  if (map->voice_mode == 4)
    {
      opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], 0xff);
      opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], 0xff);
    }

  opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00);	/*
									 * Note
									 * off
									 */

  instr = devc->act_i[voice];

  if (!instr)
    instr = &devc->i_map[0];

  if (instr->channel < 0)
    {
      printk (
	       "OPL3: Initializing voice %d with undefined instrument\n",
	       voice);
      return 0;
    }

  if (map->voice_mode == 2 && instr->key == OPL3_PATCH)
    return 0;			/*
				 * Cannot play
				 */

  voice_mode = map->voice_mode;

  if (voice_mode == 4)
    {
      int             voice_shift;

      voice_shift = (map->ioaddr == devc->left_io) ? 0 : 3;
      voice_shift += map->voice_num;

      if (instr->key != OPL3_PATCH)	/*
					 * Just 2 OP patch
					 */
	{
	  voice_mode = 2;
	  devc->cmask &= ~(1 << voice_shift);
	}
      else
	{
	  devc->cmask |= (1 << voice_shift);
	}

      opl3_command (devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask);
    }

  /*
   * Set Sound Characteristics
   */
  opl3_command (map->ioaddr, AM_VIB + map->op[0], instr->operators[0]);
  opl3_command (map->ioaddr, AM_VIB + map->op[1], instr->operators[1]);

  /*
   * Set Attack/Decay
   */
  opl3_command (map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]);
  opl3_command (map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]);

  /*
   * Set Sustain/Release
   */
  opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]);
  opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]);

  /*
   * Set Wave Select
   */
  opl3_command (map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]);
  opl3_command (map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]);

  /*
   * Set Feedback/Connection
   */
  fpc = instr->operators[10];
  if (!(fpc & 0x30))
    fpc |= 0x30;		/*
				 * Ensure that at least one chn is enabled
				 */
  opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num,
		fpc);

  /*
   * If the voice is a 4 OP one, initialize the operators 3 and 4 also
   */

  if (voice_mode == 4)
    {

      /*
       * Set Sound Characteristics
       */
      opl3_command (map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]);
      opl3_command (map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]);

      /*
       * Set Attack/Decay
       */
      opl3_command (map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]);
      opl3_command (map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]);

      /*
       * Set Sustain/Release
       */
      opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]);
      opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]);

      /*
       * Set Wave Select
       */
      opl3_command (map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]);
      opl3_command (map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]);

      /*
       * Set Feedback/Connection
       */
      fpc = instr->operators[OFFS_4OP + 10];
      if (!(fpc & 0x30))
	fpc |= 0x30;		/*
				 * Ensure that at least one chn is enabled
				 */
      opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc);
    }

  devc->voc[voice].mode = voice_mode;

  set_voice_volume (voice, volume, devc->voc[voice].volume);

  freq = devc->voc[voice].orig_freq = note_to_freq (note) / 1000;

  /*
   * Since the pitch bender may have been set before playing the note, we
   * have to calculate the bending now.
   */

  freq = compute_finetune (devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range);
  devc->voc[voice].current_freq = freq;

  freq_to_fnum (freq, &block, &fnum);

  /*
   * Play note
   */

  data = fnum & 0xff;		/*
				 * Least significant bits of fnumber
				 */
  opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data);

  data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
  devc->voc[voice].keyon_byte = data;
  opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
  if (voice_mode == 4)
    opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data);

  return 0;
}

static void
freq_to_fnum (int freq, int *block, int *fnum)
{
  int             f, octave;

  /*
   * Converts the note frequency to block and fnum values for the FM chip
   */
  /*
   * First try to compute the block -value (octave) where the note belongs
   */

  f = freq;

  octave = 5;

  if (f == 0)
    octave = 0;
  else if (f < 261)
    {
      while (f < 261)
	{
	  octave--;
	  f <<= 1;
	}
    }
  else if (f > 493)
    {
      while (f > 493)
	{
	  octave++;
	  f >>= 1;
	}
    }

  if (octave > 7)
    octave = 7;

  *fnum = freq * (1 << (20 - octave)) / 49716;
  *block = octave;
}

static void
opl3_command (int io_addr, unsigned int addr, unsigned int val)
{
  int             i;

  /*
   * The original 2-OP synth requires a quite long delay after writing to a
   * register. The OPL-3 survives with just two INBs
   */

  outb ((unsigned char) (addr & 0xff), io_addr);

  if (!devc->model != 2)
    tenmicrosec (devc->osp);
  else
    for (i = 0; i < 2; i++)
      inb (io_addr);

  outb ((unsigned char) (val & 0xff), io_addr + 1);

  if (devc->model != 2)
    {
      tenmicrosec (devc->osp);
      tenmicrosec (devc->osp);
      tenmicrosec (devc->osp);
    }
  else
    for (i = 0; i < 2; i++)
      inb (io_addr);
}

static void
opl3_reset (int devno)
{
  int             i;

  for (i = 0; i < 18; i++)
    devc->lv_map[i] = i;

  for (i = 0; i < devc->nr_voice; i++)
    {
      opl3_command (pv_map[devc->lv_map[i]].ioaddr,
		    KSL_LEVEL + pv_map[devc->lv_map[i]].op[0], 0xff);

      opl3_command (pv_map[devc->lv_map[i]].ioaddr,
		    KSL_LEVEL + pv_map[devc->lv_map[i]].op[1], 0xff);

      if (pv_map[devc->lv_map[i]].voice_mode == 4)
	{
	  opl3_command (pv_map[devc->lv_map[i]].ioaddr,
			KSL_LEVEL + pv_map[devc->lv_map[i]].op[2], 0xff);

	  opl3_command (pv_map[devc->lv_map[i]].ioaddr,
			KSL_LEVEL + pv_map[devc->lv_map[i]].op[3], 0xff);
	}

      opl3_kill_note (devno, i, 0, 64);
    }

  if (devc->model == 2)
    {
      devc->v_alloc->max_voice = devc->nr_voice = 18;

      for (i = 0; i < 18; i++)
	pv_map[i].voice_mode = 2;

    }

}

static int
opl3_open (int dev, int mode)
{
  int             i;

  if (devc->busy)
    return -(EBUSY);
  devc->busy = 1;

  devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9;
  devc->v_alloc->timestamp = 0;

  for (i = 0; i < 18; i++)
    {
      devc->v_alloc->map[i] = 0;
      devc->v_alloc->alloc_times[i] = 0;
    }

  devc->cmask = 0x00;		/*
				 * Just 2 OP mode
				 */
  if (devc->model == 2)
    opl3_command (devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask);
  return 0;
}

static void
opl3_close (int dev)
{
  devc->busy = 0;
  devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9;

  devc->fm_info.nr_drums = 0;
  devc->fm_info.perc_mode = 0;

  opl3_reset (dev);
}

static void
opl3_hw_control (int dev, unsigned char *event)
{
}

static int
opl3_load_patch (int dev, int format, const char *addr,
		 int offs, int count, int pmgr_flag)
{
  struct sbi_instrument ins;

  if (count < sizeof (ins))
    {
      printk ("FM Error: Patch record too short\n");
      return -(EINVAL);
    }

  memcpy_fromfs (&((char *) &ins)[offs], &(addr)[offs], sizeof (ins) - offs);

  if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
    {
      printk ("FM Error: Invalid instrument number %d\n", ins.channel);
      return -(EINVAL);
    }
  ins.key = format;

  return store_instr (ins.channel, &ins);
}

static void
opl3_panning (int dev, int voice, int pressure)
{
}

static void
opl3_volume_method (int dev, int mode)
{
}

#define SET_VIBRATO(cell) { \
      tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \
      if (pressure > 110) \
	tmp |= 0x40;		/* Vibrato on */ \
      opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);}

static void
opl3_aftertouch (int dev, int voice, int pressure)
{
  int             tmp;
  struct sbi_instrument *instr;
  struct physical_voice_info *map;

  if (voice < 0 || voice >= devc->nr_voice)
    return;

  map = &pv_map[devc->lv_map[voice]];

  DEB (printk ("Aftertouch %d\n", voice));

  if (map->voice_mode == 0)
    return;

  /*
   * Adjust the amount of vibrato depending the pressure
   */

  instr = devc->act_i[voice];

  if (!instr)
    instr = &devc->i_map[0];

  if (devc->voc[voice].mode == 4)
    {
      int             connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);

      switch (connection)
	{
	case 0:
	  SET_VIBRATO (4);
	  break;

	case 1:
	  SET_VIBRATO (2);
	  SET_VIBRATO (4);
	  break;

	case 2:
	  SET_VIBRATO (1);
	  SET_VIBRATO (4);
	  break;

	case 3:
	  SET_VIBRATO (1);
	  SET_VIBRATO (3);
	  SET_VIBRATO (4);
	  break;

	}
      /*
       * Not implemented yet
       */
    }
  else
    {
      SET_VIBRATO (1);

      if ((instr->operators[10] & 0x01))	/*
						 * Additive synthesis
						 */
	SET_VIBRATO (2);
    }
}

#undef SET_VIBRATO

static void
bend_pitch (int dev, int voice, int value)
{
  unsigned char   data;
  int             block, fnum, freq;
  struct physical_voice_info *map;

  map = &pv_map[devc->lv_map[voice]];

  if (map->voice_mode == 0)
    return;

  devc->voc[voice].bender = value;
  if (!value)
    return;
  if (!(devc->voc[voice].keyon_byte & 0x20))
    return;			/*
				 * Not keyed on
				 */

  freq = compute_finetune (devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range);
  devc->voc[voice].current_freq = freq;

  freq_to_fnum (freq, &block, &fnum);

  data = fnum & 0xff;		/*
				 * Least significant bits of fnumber
				 */
  opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data);

  data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);	/*
								 * *
								 * KEYON|OCTAVE|MS
								 *
								 * * bits * *
								 * of * f-num
								 *
								 */
  devc->voc[voice].keyon_byte = data;
  opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
}

static void
opl3_controller (int dev, int voice, int ctrl_num, int value)
{
  if (voice < 0 || voice >= devc->nr_voice)
    return;

  switch (ctrl_num)
    {
    case CTRL_PITCH_BENDER:
      bend_pitch (dev, voice, value);
      break;

    case CTRL_PITCH_BENDER_RANGE:
      devc->voc[voice].bender_range = value;
      break;

    case CTL_MAIN_VOLUME:
      devc->voc[voice].volume = value / 128;
      break;
    }
}

static int
opl3_patchmgr (int dev, struct patmgr_info *rec)
{
  return -(EINVAL);
}

static void
opl3_bender (int dev, int voice, int value)
{
  if (voice < 0 || voice >= devc->nr_voice)
    return;

  bend_pitch (dev, voice, value - 8192);
}

static int
opl3_alloc_voice (int dev, int chn, int note, struct voice_alloc_info *alloc)
{
  int             i, p, best, first, avail, best_time = 0x7fffffff;
  struct sbi_instrument *instr;
  int             is4op;
  int             instr_no;

  if (chn < 0 || chn > 15)
    instr_no = 0;
  else
    instr_no = devc->chn_info[chn].pgm_num;

  instr = &devc->i_map[instr_no];
  if (instr->channel < 0 ||	/* Instrument not loaded */
      devc->nr_voice != 12)	/* Not in 4 OP mode */
    is4op = 0;
  else if (devc->nr_voice == 12)	/* 4 OP mode */
    is4op = (instr->key == OPL3_PATCH);
  else
    is4op = 0;

  if (is4op)
    {
      first = p = 0;
      avail = 6;
    }
  else
    {
      if (devc->nr_voice == 12)	/* 4 OP mode. Use the '2 OP only' operators first */
	first = p = 6;
      else
	first = p = 0;
      avail = devc->nr_voice;
    }

  /*
     *    Now try to find a free voice
   */
  best = first;

  for (i = 0; i < avail; i++)
    {
      if (alloc->map[p] == 0)
	{
	  return p;
	}
      if (alloc->alloc_times[p] < best_time)	/* Find oldest playing note */
	{
	  best_time = alloc->alloc_times[p];
	  best = p;
	}
      p = (p + 1) % avail;
    }

  /*
     *    Insert some kind of priority mechanism here.
   */

  if (best < 0)
    best = 0;
  if (best > devc->nr_voice)
    best -= devc->nr_voice;

  return best;			/* All devc->voc 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);

  devc->voc[voice].bender = info->bender_value;
  devc->voc[voice].volume =
    info->controllers[CTL_MAIN_VOLUME];
}

static struct synth_operations opl3_operations =
{
  NULL,
  0,
  SYNTH_TYPE_FM,
  FM_TYPE_ADLIB,
  opl3_open,
  opl3_close,
  opl3_ioctl,
  opl3_kill_note,
  opl3_start_note,
  opl3_set_instr,
  opl3_reset,
  opl3_hw_control,
  opl3_load_patch,
  opl3_aftertouch,
  opl3_controller,
  opl3_panning,
  opl3_volume_method,
  opl3_patchmgr,
  opl3_bender,
  opl3_alloc_voice,
  opl3_setup_voice
};

void
opl3_init (int ioaddr, int *osp)
{
  int             i;

  if (num_synths >= MAX_SYNTH_DEV)
    {
      printk ("OPL3 Error: Too many synthesizers\n");
      return;
    }

  if (devc == NULL)
    {
      printk ("OPL3: Device control structure not initialized.\n");
      return;
    }

  memset ((char *) devc, 0x00, sizeof (*devc));
  devc->osp = osp;
  devc->base = ioaddr;

  devc->nr_voice = 9;
  strcpy (devc->fm_info.name, "OPL2");

  devc->fm_info.device = 0;
  devc->fm_info.synth_type = SYNTH_TYPE_FM;
  devc->fm_info.synth_subtype = FM_TYPE_ADLIB;
  devc->fm_info.perc_mode = 0;
  devc->fm_info.nr_voices = 9;
  devc->fm_info.nr_drums = 0;
  devc->fm_info.instr_bank_size = SBFM_MAXINSTR;
  devc->fm_info.capabilities = 0;
  devc->left_io = ioaddr;
  devc->right_io = ioaddr + 2;

  if (detected_model <= 2)
    devc->model = 1;
  else
    {
      devc->model = 2;
      if (detected_model == 4)
	devc->is_opl4 = 1;
    }

  opl3_operations.info = &devc->fm_info;

  synth_devs[num_synths++] = &opl3_operations;
  devc->v_alloc = &opl3_operations.alloc;
  devc->chn_info = &opl3_operations.chn_info[0];

  if (devc->model == 2)
    {
      if (devc->is_opl4)
	conf_printf2 ("Yamaha OPL4/OPL3 FM", ioaddr, 0, -1, -1);
      else
	conf_printf2 ("Yamaha OPL3 FM", ioaddr, 0, -1, -1);

      devc->v_alloc->max_voice = devc->nr_voice = 18;
      devc->fm_info.nr_drums = 0;
      devc->fm_info.synth_subtype = FM_TYPE_OPL3;
      devc->fm_info.capabilities |= SYNTH_CAP_OPL3;
      strcpy (devc->fm_info.name, "Yamaha OPL-3");

      for (i = 0; i < 18; i++)
	if (pv_map[i].ioaddr == USE_LEFT)
	  pv_map[i].ioaddr = devc->left_io;
	else
	  pv_map[i].ioaddr = devc->right_io;

      opl3_command (devc->right_io, OPL3_MODE_REGISTER, OPL3_ENABLE);
      opl3_command (devc->right_io, CONNECTION_SELECT_REGISTER, 0x00);
    }
  else
    {
      conf_printf2 ("Yamaha OPL2 FM", ioaddr, 0, -1, -1);
      devc->v_alloc->max_voice = devc->nr_voice = 9;
      devc->fm_info.nr_drums = 0;

      for (i = 0; i < 18; i++)
	pv_map[i].ioaddr = devc->left_io;
    };

  for (i = 0; i < SBFM_MAXINSTR; i++)
    devc->i_map[i].channel = -1;

}

#endif
