OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [drivers/] [sound/] [mad16.c] - Rev 1777

Go to most recent revision | Compare with Previous | Blame | View Log

/*
 * 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>
 
/*
 * sound/mad16.c
 *
 * Initialization code for OPTi MAD16 compatible audio chips. Including
 *
 *      OPTi 82C928     MAD16           (replaced by C929)
 *      OAK OTI-601D    Mozart
 *      OPTi 82C929     MAD16 Pro
 *      OPTi 82C930
 *
 * These audio interface chips don't produce sound themselves. They just
 * connect some other components (OPL-[234] and a WSS compatible codec)
 * to the PC bus and perform I/O, DMA and IRQ address decoding. There is
 * also a UART for the MPU-401 mode (not 82C928/Mozart).
 * The Mozart chip appears to be compatible with the 82C928 (can anybody
 * confirm this?).
 *
 * NOTE! If you want to set CD-ROM address and/or joystick enable, define
 *       MAD16_CONF in local.h as combination of the following bits:
 *
 *      0x01    - joystick disabled
 *
 *      CD-ROM type selection (select just one):
 *      0x00    - none
 *      0x02    - Sony 31A
 *      0x04    - Mitsumi
 *      0x06    - Panasonic (type "LaserMate", not "Sound Blaster")
 *      0x08    - Secondary IDE (address 0x170)
 *      0x0a    - Primary IDE (address 0x1F0)
 *      
 *      For example Mitsumi with joystick disabled = 0x04|0x01 = 0x05
 *      For example LaserMate (for use with sbpcd) plus joystick = 0x06
 *      
 *    MAD16_CDSEL:
 *      This defaults to CD I/O 0x340, no IRQ and DMA3 
 *      (DMA5 with Mitsumi or IDE). If you like to change these, define
 *      MAD16_CDSEL with the following bits:
 *
 *      CD-ROM port: 0x00=340, 0x40=330, 0x80=360 or 0xc0=320
 *      OPL4 select: 0x20=OPL4, 0x00=OPL3
 *      CD-ROM irq: 0x00=disabled, 0x04=IRQ5, 0x08=IRQ7, 0x0a=IRQ3, 0x10=IRQ9,
 *                  0x14=IRQ10 and 0x18=IRQ11.
 *
 *      CD-ROM DMA (Sony or Panasonic): 0x00=DMA3, 0x01=DMA2, 0x02=DMA1 or 0x03=disabled
 *   or
 *      CD-ROM DMA (Mitsumi or IDE):    0x00=DMA5, 0x01=DMA6, 0x02=DMA7 or 0x03=disabled
 *
 *      For use with sbpcd, address 0x340, set MAD16_CDSEL to 0x03 or 0x23.
 */
 
#include "sound_config.h"
 
#if defined(CONFIG_MAD16)
 
#include "sb.h"
 
static int      already_initialized = 0;
 
#define C928	1
#define MOZART	2
#define C929	3
#define C930	4
 
/*
 *    Registers
 *
 *      The MAD16 occupies I/O ports 0xf8d to 0xf93 (fixed locations).
 *      All ports are inactive by default. They can be activated by
 *      writing 0xE2 or 0xE3 to the password register. The password is valid
 *      only until the next I/O read or write.
 *
 *      82C930 uses 0xE4 as the password and indirect addressing to access
 *      the config registers.
 */
 
#define MC0_PORT	0xf8c	/* Dummy port */
#define MC1_PORT	0xf8d	/* SB address, CD-ROM interface type, joystick */
#define MC2_PORT	0xf8e	/* CD-ROM address, IRQ, DMA, plus OPL4 bit */
#define MC3_PORT	0xf8f
#define PASSWD_REG	0xf8f
#define MC4_PORT	0xf90
#define MC5_PORT	0xf91
#define MC6_PORT	0xf92
#define MC7_PORT	0xf93
#define MC8_PORT	0xf94
#define MC9_PORT	0xf95
#define MC10_PORT	0xf96
#define MC11_PORT	0xf97
#define MC12_PORT	0xf98
 
static int      board_type = C928;
 
static int     *mad16_osp;
 
#ifndef DDB
#define DDB(x)
#endif
 
static unsigned char
mad_read (int port)
{
  unsigned long   flags;
  unsigned char   tmp;
 
  save_flags (flags);
  cli ();
 
  switch (board_type)		/* Output password */
    {
    case C928:
    case MOZART:
      outb (0xE2, PASSWD_REG);
      break;
 
    case C929:
      outb (0xE3, PASSWD_REG);
      break;
 
    case C930:
      /* outb( 0xE4,  PASSWD_REG); */
      break;
    }
 
  if (board_type == C930)
    {
      outb (port - MC0_PORT, 0xe0e);	/* Write to index reg */
      tmp = inb (0xe0f);	/* Read from data reg */
    }
  else
    tmp = inb (port);
  restore_flags (flags);
 
  return tmp;
}
 
static void
mad_write (int port, int value)
{
  unsigned long   flags;
 
  save_flags (flags);
  cli ();
 
  switch (board_type)		/* Output password */
    {
    case C928:
    case MOZART:
      outb (0xE2, PASSWD_REG);
      break;
 
    case C929:
      outb (0xE3, PASSWD_REG);
      break;
 
    case C930:
      /* outb( 0xE4,  PASSWD_REG); */
      break;
    }
 
  if (board_type == C930)
    {
      outb (port - MC0_PORT, 0xe0e);	/* Write to index reg */
      outb ((unsigned char) (value & 0xff), 0xe0f);
    }
  else
    outb ((unsigned char) (value & 0xff), port);
  restore_flags (flags);
}
 
static int
detect_c930 (void)
{
  unsigned char   tmp = mad_read (MC1_PORT);
 
  if ((tmp & 0x06) != 0x06)
    {
      DDB (printk ("Wrong C930 signature (%x)\n", tmp));
      /* return 0; */
    }
 
  mad_write (MC1_PORT, 0);
 
  if (mad_read (MC1_PORT) != 0x06)
    {
      DDB (printk ("Wrong C930 signature2 (%x)\n", tmp));
      /* return 0; */
    }
 
  mad_write (MC1_PORT, tmp);	/* Restore bits */
 
  mad_write (MC7_PORT, 0);
  if ((tmp = mad_read (MC7_PORT)) != 0)
    {
      DDB (printk ("MC7 not writable (%x)\n", tmp));
      return 0;
    }
 
  mad_write (MC7_PORT, 0xcb);
  if ((tmp = mad_read (MC7_PORT)) != 0xcb)
    {
      DDB (printk ("MC7 not writable2 (%x)\n", tmp));
      return 0;
    }
 
  return 1;
}
 
static int
detect_mad16 (void)
{
  unsigned char   tmp, tmp2;
  int             i;
 
/*
 * Check that reading a register doesn't return bus float (0xff)
 * when the card is accessed using password. This may fail in case
 * the card is in low power mode. Normally at least the power saving mode
 * bit should be 0.
 */
  if ((tmp = mad_read (MC1_PORT)) == 0xff)
    {
      DDB (printk ("MC1_PORT returned 0xff\n"));
      return 0;
    }
 
  for (i = 0xf8d; i <= 0xf98; i++)
    DDB (printk ("Port %0x (init value) = %0x\n", i, mad_read (i)));
 
  if (board_type == C930)
    return detect_c930 ();
/*
 * Now check that the gate is closed on first I/O after writing
 * the password. (This is how a MAD16 compatible card works).
 */
 
  if ((tmp2 = inb (MC1_PORT)) == tmp)	/* It didn't close */
    {
      DDB (printk ("MC1_PORT didn't close after read (0x%02x)\n", tmp2));
      return 0;
    }
 
  mad_write (MC1_PORT, tmp ^ 0x80);	/* Toggle a bit */
  if ((tmp2 = mad_read (MC1_PORT)) != (tmp ^ 0x80))	/* Compare the bit */
    {
      mad_write (MC1_PORT, tmp);	/* Restore */
      DDB (printk ("Bit revert test failed (0x%02x, 0x%02x)\n", tmp, tmp2));
      return 0;
    }
 
  mad_write (MC1_PORT, tmp);	/* Restore */
  return 1;			/* Bingo */
 
}
 
static int
wss_init (struct address_info *hw_config)
{
  int             ad_flags = 0;
 
/*
 *    Verify the WSS parameters
 */
 
  if (check_region (hw_config->io_base, 8))
    {
      printk ("MSS: I/O port conflict\n");
      return 0;
    }
 
  if (!ad1848_detect (hw_config->io_base + 4, &ad_flags, mad16_osp))
    return 0;
  /*
     * Check if the IO port returns valid signature. The original MS Sound
     * system returns 0x04 while some cards (AudioTrix Pro for example)
     * return 0x00.
   */
 
  if ((inb (hw_config->io_base + 3) & 0x3f) != 0x04 &&
      (inb (hw_config->io_base + 3) & 0x3f) != 0x00)
    {
      DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n",
		   hw_config->io_base, inb (hw_config->io_base + 3)));
      return 0;
    }
 
  if (hw_config->irq > 11)
    {
      printk ("MSS: Bad IRQ %d\n", hw_config->irq);
      return 0;
    }
 
  if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
    {
      printk ("MSS: Bad DMA %d\n", hw_config->dma);
      return 0;
    }
 
  /*
     * Check that DMA0 is not in use with a 8 bit board.
   */
 
  if (hw_config->dma == 0 && inb (hw_config->io_base + 3) & 0x80)
    {
      printk ("MSS: Can't use DMA0 with a 8 bit card/slot\n");
      return 0;
    }
 
  if (hw_config->irq > 7 && hw_config->irq != 9 && inb (hw_config->io_base + 3) & 0x80)
    {
      printk ("MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq);
    }
 
  return 1;
}
 
static int
init_c930 (struct address_info *hw_config)
{
  unsigned char   cfg;
 
  cfg = (mad_read (MC1_PORT) & ~0x30);
  /* mad_write(MC1_PORT, 0); */
 
  switch (hw_config->io_base)
    {
    case 0x530:
      cfg |= 0x00;
      break;
    case 0xe80:
      cfg |= 0x10;
      break;
    case 0xf40:
      cfg |= 0x20;
      break;
    case 0x604:
      cfg |= 0x30;
      break;
 
    default:
      printk ("MAD16: Invalid codec port %x\n", hw_config->io_base);
      return 0;
    }
  mad_write (MC1_PORT, cfg);
 
  /* MC2 is CD configuration. Don't touch it. */
 
  mad_write (MC3_PORT, 0);	/* Disable SB mode IRQ and DMA */
 
  mad_write (MC4_PORT, 0x52);	/* ??? */
  mad_write (MC5_PORT, 0x3D);	/* Init it into mode2 */
  mad_write (MC6_PORT, 0x02);	/* Enable WSS, Disable MPU and SB */
  mad_write (MC7_PORT, 0xCB);
  mad_write (MC10_PORT, 0x11);
 
  if (!wss_init (hw_config))
    return 0;
 
/*
 * A temporary kludge which drops the device back to mode1.
 * This removes problems with interrupts but disables full duplex.
 * A better solution should be introduced later.
 */
  mad_write (MC5_PORT, 0x1D);	/* Disable mode2 */
  return wss_init (hw_config);
}
 
int
probe_mad16 (struct address_info *hw_config)
{
  int             i;
  static int      valid_ports[] =
  {0x530, 0xe80, 0xf40, 0x604};
  unsigned char   tmp;
  unsigned char   cs4231_mode = 0;
 
  int             ad_flags = 0;
 
  if (already_initialized)
    return 0;
 
  mad16_osp = hw_config->osp;
/*
 *    Check that all ports return 0xff (bus float) when no password
 *      is written to the password register.
 */
 
  DDB (printk ("--- Detecting MAD16 / Mozart ---\n"));
 
 
/*
 *    Then try to detect with the old password
 */
  board_type = C928;
 
  DDB (printk ("Detect using password = 0xE2\n"));
 
  if (!detect_mad16 ())		/* No luck. Try different model */
    {
      board_type = C929;
 
      DDB (printk ("Detect using password = 0xE3\n"));
 
      if (!detect_mad16 ())
	{
	  if (inb (PASSWD_REG) != 0xff)
	    return 0;
 
/*
 * First relocate MC# registers to 0xe0e/0xe0f, disable password 
 */
 
	  outb (0xE4, PASSWD_REG);
	  outb (0x80, PASSWD_REG);
 
	  board_type = C930;
 
	  DDB (printk ("Detect using password = 0xE4\n"));
 
	  for (i = 0xf8d; i <= 0xf93; i++)
	    DDB (printk ("port %03x = %02x\n", i, mad_read (i)));
 
	  if (!detect_mad16 ())
	    return 0;
 
	  DDB (printk ("mad16.c: 82C930 detected\n"));
	  return init_c930 (hw_config);
	}
      else
	{
	  DDB (printk ("mad16.c: 82C929 detected\n"));
	}
    }
  else
    {
      unsigned char   model;
 
      if (((model = mad_read (MC3_PORT)) & 0x03) == 0x03)
	{
	  DDB (printk ("mad16.c: Mozart detected\n"));
	  board_type = MOZART;
	}
      else
	{
	  DDB (printk ("mad16.c: 82C928 detected???\n"));
	  board_type = C928;
	}
    }
 
  for (i = 0xf8d; i <= 0xf93; i++)
    DDB (printk ("port %03x = %02x\n", i, mad_read (i)));
 
/*
 * Set the WSS address
 */
 
  tmp = 0x80;			/* Enable WSS, Disable SB */
 
  for (i = 0; i < 5; i++)
    {
      if (i > 3)		/* Not a valid port */
	{
	  printk ("MAD16/Mozart: Bad WSS base address 0x%x\n", hw_config->io_base);
	  return 0;
	}
 
      if (valid_ports[i] == hw_config->io_base)
	{
	  tmp |= i << 4;	/* WSS port select bits */
	  break;
	}
    }
 
/*
 * Set optional CD-ROM and joystick settings.
 */
 
#ifdef MAD16_CONF
  tmp |= ((MAD16_CONF) & 0x0f);	/* CD-ROM and joystick bits */
#endif
  mad_write (MC1_PORT, tmp);
 
#if defined(MAD16_CONF) && defined(MAD16_CDSEL)
  tmp = MAD16_CDSEL;
#else
  tmp = 0x03;
#endif
 
#ifdef MAD16_OPL4
  tmp |= 0x20;			/* Enable OPL4 access */
#endif
 
  mad_write (MC2_PORT, tmp);
  mad_write (MC3_PORT, 0xf0);	/* Disable SB */
 
  if (!ad1848_detect (hw_config->io_base + 4, &ad_flags, mad16_osp))
    return 0;
 
  if (ad_flags & (AD_F_CS4231 | AD_F_CS4248))
    cs4231_mode = 0x02;		/* CS4248/CS4231 sync delay switch */
 
  if (board_type == C929)
    {
      mad_write (MC4_PORT, 0xa2);
      mad_write (MC5_PORT, 0xA5 | cs4231_mode);
      mad_write (MC6_PORT, 0x03);	/* Disable MPU401 */
    }
  else
    {
      mad_write (MC4_PORT, 0x02);
      mad_write (MC5_PORT, 0x30 | cs4231_mode);
    }
 
  for (i = 0xf8d; i <= 0xf93; i++)
    DDB (printk ("port %03x after init = %02x\n", i, mad_read (i)));
 
  wss_init (hw_config);
 
  return 1;
}
 
void
attach_mad16 (struct address_info *hw_config)
{
 
  static char     interrupt_bits[12] =
  {
    -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20
  };
  char            bits;
 
  static char     dma_bits[4] =
  {
    1, 2, 0, 3
  };
 
  int             config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
  int             ad_flags = 0, dma = hw_config->dma, dma2 = hw_config->dma2;
  unsigned char   dma2_bit = 0;
 
  already_initialized = 1;
 
  if (!ad1848_detect (hw_config->io_base + 4, &ad_flags, mad16_osp))
    return;
 
  /*
     * Set the IRQ and DMA addresses.
   */
 
  bits = interrupt_bits[hw_config->irq];
  if (bits == -1)
    return;
 
  outb (bits | 0x40, config_port);
  if ((inb (version_port) & 0x40) == 0)
    printk ("[IRQ Conflict?]");
 
/*
 * Handle the capture DMA channel
 */
 
  if (ad_flags & AD_F_CS4231 && dma2 != -1 && dma2 != dma)
    {
      if (!((dma == 0 && dma2 == 1) ||
	    (dma == 1 && dma2 == 0) ||
	    (dma == 3 && dma2 == 0)))
	{			/* Unsupported combination. Try to swap channels */
	  int             tmp = dma;
 
	  dma = dma2;
	  dma2 = tmp;
	}
 
      if ((dma == 0 && dma2 == 1) ||
	  (dma == 1 && dma2 == 0) ||
	  (dma == 3 && dma2 == 0))
	{
	  dma2_bit = 0x04;	/* Enable capture DMA */
	}
      else
	{
	  printk ("MAD16: Invalid capture DMA\n");
	  dma2 = dma;
	}
    }
  else
    dma2 = dma;
 
  outb (bits | dma_bits[dma] | dma2_bit, config_port);	/* Write IRQ+DMA setup */
 
  ad1848_init ("MAD16 WSS", hw_config->io_base + 4,
	       hw_config->irq,
	       dma,
	       dma2, 0,
	       hw_config->osp);
  request_region (hw_config->io_base, 4, "MAD16 WSS config");
}
 
void
attach_mad16_mpu (struct address_info *hw_config)
{
  if (board_type < C929)	/* Early chip. No MPU support. Just SB MIDI */
    {
#ifdef CONFIG_MIDI
 
      if (mad_read (MC1_PORT) & 0x20)
	hw_config->io_base = 0x240;
      else
	hw_config->io_base = 0x220;
 
      hw_config->name = "Mad16/Mozart";
      sb_dsp_init (hw_config);
#endif
 
      return;
    }
 
#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
  if (!already_initialized)
    return;
 
  hw_config->driver_use_1 = SB_MIDI_ONLY;
  hw_config->name = "Mad16/Mozart";
  attach_uart401 (hw_config);
#endif
}
 
int
probe_mad16_mpu (struct address_info *hw_config)
{
#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
  static int      mpu_attached = 0;
  static int      valid_ports[] =
  {0x330, 0x320, 0x310, 0x300};
  static short    valid_irqs[] =
  {9, 10, 5, 7};
  unsigned char   tmp;
 
  int             i;		/* A variable with secret power */
 
  if (!already_initialized)	/* The MSS port must be initialized first */
    return 0;
 
  if (mpu_attached)		/* Don't let them call this twice */
    return 0;
  mpu_attached = 1;
 
  if (board_type < C929)	/* Early chip. No MPU support. Just SB MIDI */
    {
 
#ifdef CONFIG_MIDI
      unsigned char   tmp;
 
      tmp = mad_read (MC3_PORT);
 
      /* 
       * MAD16 SB base is defined by the WSS base. It cannot be changed 
       * alone.
       * Ignore configured I/O base. Use the active setting. 
       */
 
      if (mad_read (MC1_PORT) & 0x20)
	hw_config->io_base = 0x240;
      else
	hw_config->io_base = 0x220;
 
      switch (hw_config->irq)
	{
	case 5:
	  tmp = (tmp & 0x3f) | 0x80;
	  break;
	case 7:
	  tmp = (tmp & 0x3f);
	  break;
	case 11:
	  tmp = (tmp & 0x3f) | 0x40;
	  break;
	default:
	  printk ("mad16/Mozart: Invalid MIDI IRQ\n");
	  return 0;
	}
 
      mad_write (MC3_PORT, tmp | 0x04);
      hw_config->driver_use_1 = SB_MIDI_ONLY;
      return sb_dsp_detect (hw_config);
#else
      return 0;
#endif
    }
 
  tmp = mad_read (MC6_PORT) & 0x83;
  tmp |= 0x80;			/* MPU-401 enable */
 
/*
 * Set the MPU base bits
 */
 
  for (i = 0; i < 5; i++)
    {
      if (i > 3)		/* Out of array bounds */
	{
	  printk ("MAD16 / Mozart: Invalid MIDI port 0x%x\n", hw_config->io_base);
	  return 0;
	}
 
      if (valid_ports[i] == hw_config->io_base)
	{
	  tmp |= i << 5;
	  break;
	}
    }
 
/*
 * Set the MPU IRQ bits
 */
 
  for (i = 0; i < 5; i++)
    {
      if (i > 3)		/* Out of array bounds */
	{
	  printk ("MAD16 / Mozart: Invalid MIDI IRQ %d\n", hw_config->irq);
	  return 0;
	}
 
      if (valid_irqs[i] == hw_config->irq)
	{
	  tmp |= i << 3;
	  break;
	}
    }
  mad_write (MC6_PORT, tmp);	/* Write MPU401 config */
 
  return probe_uart401 (hw_config);
#else
  return 0;
#endif
}
 
void
unload_mad16 (struct address_info *hw_config)
{
  ad1848_unload (hw_config->io_base + 4,
		 hw_config->irq,
		 hw_config->dma,
		 hw_config->dma2, 0);
  release_region (hw_config->io_base, 4);
 
}
 
void
unload_mad16_mpu (struct address_info *hw_config)
{
#ifdef CONFIG_MIDI
  if (board_type < C929)	/* Early chip. No MPU support. Just SB MIDI */
    {
      sb_dsp_unload (hw_config);
      return;
    }
#endif
 
#if (defined(CONFIG_UART401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
  unload_uart401 (hw_config);
#endif
}
 
/* That's all folks */
#endif
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.