Google
 

Open Sound System
The Hitchhiker's Guide to OSS 4.1 Internals

Do you have problems with sound/audio application development? Don't panic! Click here for help!

oss_via97/oss_via97.c

Driver for the VIA VT82C686A AC97 audio controller

Description




This file is part of Open Sound System.

Copyright (C) 4Front Technologies 1996-2008.

This this source file is released under GPL v2 license (no other versions). See the COPYING file included in the main directory of this source distribution for the license terms and conditions.


#include "oss_via97_cfg.h"
#include "oss_pci.h"
#include "ac97.h"

#define VIA_VENDOR_ID		0x1106
#define VIA_82C686			0x3058

#define CODEC_TIMEOUT_COUNT     500
#define AC97CODEC       0x80	/*Access AC97 Codec */
#define IN_CMD        0x01000000	/*busy in sending */
#define STA_VALID     0x02000000	/*1:status data is valid */
#define CODEC_RD      0x00800000	/*Read CODEC status */
#define CODEC_INDEX   0x007F0000	/*Index of command register to access */
#define CODEC_DATA    0x0000FFFF	/*AC97 status register data */

typedef struct
{
  unsigned int phaddr;
  unsigned int flags;
}
SGD_entry;

typedef struct
{
  int open_mode;
  int speed, bits, channels;
  int audiodev;
  int audio_enabled;
  int trigger_bits;
}
via97_portc;

typedef struct via97_devc
{
  oss_device_t *osdev;
  oss_native_word base;
  int irq;
  int open_mode;
  char *chip_name;

Mixer

  ac97_devc ac97devc;


MIDI


  int mpu_base;
  int mpu_attached;

  /* Audio parameters */
  via97_portc portc[2];
  oss_mutex_t mutex;		/* For normal locking */
  oss_mutex_t low_mutex;	/* For low level routines */

  /* Memory allocation and scatter/gather info */
#define BUFFER_SIZE		(128*1024)

/* NOTE! Check SGD_ALLOC if changing SGD_SIZE */
#define MIN_BLOCK		64
#define SGD_SIZE		(BUFFER_SIZE/MIN_BLOCK)
#define SGD_TOTAL_SIZE		(2*SGD_SIZE)
#define SGD_ALLOC		(SGD_TOTAL_SIZE*8)

  SGD_entry *SGD_table;
  oss_native_word SGD_table_phys;
  int play_sgd_ptr, rec_sgd_ptr;
  oss_dma_handle_t sgd_dma_handle;
  unsigned int play_sgd_phys, rec_sgd_phys;
}
via97_devc;

static int
ac97_read (void *devc_, int wIndex)
{
  oss_native_word dwWriteValue = 0, dwTmpValue;
  unsigned int i = 0;
  oss_native_word flags;
  via97_devc *devc = devc_;

  /* Index has only 7 bit */
  if (wIndex > 0x7F)
    return 0;
  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
  dwWriteValue = ((oss_native_word) wIndex << 16) + CODEC_RD;
  OUTL (devc->osdev, dwWriteValue, devc->base + AC97CODEC);
  oss_udelay (100);
  /* Check AC CODEC access time out */
  for (i = 0; i < CODEC_TIMEOUT_COUNT; i++)
    {
      /* if send command over, break */
      if (INL (devc->osdev, devc->base + AC97CODEC) & STA_VALID)
	break;
      oss_udelay (50);
    }
  if (i == CODEC_TIMEOUT_COUNT)
    {
      MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
      return 0;
    }

  /* Check if Index still ours? If yes, return data, else return FAIL */
  dwTmpValue = INL (devc->osdev, devc->base + AC97CODEC);
  OUTB (devc->osdev, 0x02, devc->base + AC97CODEC + 3);
  if (((dwTmpValue & CODEC_INDEX) >> 16) == wIndex)
    {
      MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
      return ((int) dwTmpValue & CODEC_DATA);
    }
  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
  return 0;

}

static int
ac97_write (void *devc_, int wIndex, int wData)
{
  oss_native_word dwWriteValue = 0;
  unsigned int i = 0;
  oss_native_word flags;
  via97_devc *devc = devc_;

  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
  dwWriteValue = ((oss_native_word) wIndex << 16) + wData;
  OUTL (devc->osdev, dwWriteValue, devc->base + AC97CODEC);
  oss_udelay (100);

  /* Check AC CODEC access time out */
  for (i = 0; i < CODEC_TIMEOUT_COUNT; i++)
    {
      /* if send command over, break */
      if (!(INL (devc->osdev, devc->base + AC97CODEC) & IN_CMD))
	break;
      oss_udelay (50);
    }
  if (i == CODEC_TIMEOUT_COUNT)
    {
      MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
      return (-1);
    }
  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
  return 1;
}

static int
via97intr (oss_device_t * osdev)
{
  via97_devc *devc = (via97_devc *) osdev->devc;
  via97_portc *portc;
  unsigned char status;
  int serviced = 0;
  int i;

#ifdef OBSOLETED_STUFF
  if (devc->mpu_attached)
    {
      if (uart401intr (INT_HANDLER_CALL (devc->irq)))
	serviced = 1;
    }
#endif

  /* Handle playback interrupt */
  status = INB (devc->osdev, devc->base + 0x00);

  if (status & 0x01)
    {
      serviced = 1;
      for (i = 0; i < 2; i++)
	{
	  portc = &devc->portc[i];
	  /* IOC Interrupt */
	  if ((portc->trigger_bits & PCM_ENABLE_OUTPUT) && (status & 0x01))
	    oss_audio_outputintr (portc->audiodev, 0);
	}
    }
  OUTB (devc->osdev, status | 0x01, devc->base + 0x00);


  /* Handle record interrupt */
  status = INB (devc->osdev, devc->base + 0x10);

  if (status & 0x01)
    {
      serviced = 1;
      for (i = 0; i < 2; i++)
	{
	  portc = &devc->portc[i];
	  /* IOC Interrupt */
	  if ((portc->trigger_bits & PCM_ENABLE_INPUT) && (status & 0x01))
	    oss_audio_inputintr (portc->audiodev, 0);
	}
    }

  OUTB (devc->osdev, status | 0x01, devc->base + 0x10);
  return serviced;
}


Audio routines


static int
via97_audio_set_rate (int dev, int arg)
{
  via97_portc *portc = audio_engines[dev]->portc;

  if (arg == 0)
    return portc->speed;

  if (audio_engines[dev]->flags & ADEV_FIXEDRATE)
    arg = 48000;

  if (arg > 48000)
    arg = 48000;
  if (arg < 5000)
    arg = 5000;
  portc->speed = arg;
  return portc->speed;
}

static short
via97_audio_set_channels (int dev, short arg)
{
  via97_portc *portc = audio_engines[dev]->portc;

  if (arg == 0)
    return portc->channels;


  if ((arg != 1) && (arg != 2))
    return portc->channels = 2;
  portc->channels = arg;

  return portc->channels;
}

static unsigned int
via97_audio_set_format (int dev, unsigned int arg)
{
  via97_portc *portc = audio_engines[dev]->portc;

  if (arg == 0)
    return portc->bits;

  if (!(arg & (AFMT_U8 | AFMT_S16_LE)))
    return portc->bits = AFMT_S16_LE;
  portc->bits = arg;

  return portc->bits;
}

/*ARGSUSED*/
static int
via97_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
{
  return OSS_EINVAL;
}

static void via97_audio_trigger (int dev, int state);

static void
via97_audio_reset (int dev)
{
  via97_audio_trigger (dev, 0);
}

static void
via97_audio_reset_input (int dev)
{
  via97_portc *portc = audio_engines[dev]->portc;
  via97_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT);
}

static void
via97_audio_reset_output (int dev)
{
  via97_portc *portc = audio_engines[dev]->portc;
  via97_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT);
}

/*ARGSUSED*/
static int
via97_audio_open (int dev, int mode, int open_flags)
{
  via97_portc *portc = audio_engines[dev]->portc;
  via97_devc *devc = audio_engines[dev]->devc;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
  if (portc->open_mode)
    {
      MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
      return OSS_EBUSY;
    }

  if (devc->open_mode & mode)
    {
      MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
      return OSS_EBUSY;
    }

  devc->open_mode |= mode;

  portc->open_mode = mode;
  portc->audio_enabled &= ~mode;
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);

  return 0;
}

static void
via97_audio_close (int dev, int mode)
{
  via97_portc *portc = audio_engines[dev]->portc;
  via97_devc *devc = audio_engines[dev]->devc;

  via97_audio_reset (dev);
  portc->open_mode = 0;
  devc->open_mode &= ~mode;
  portc->audio_enabled &= ~mode;
}

/*ARGSUSED*/
static void
via97_audio_output_block (int dev, oss_native_word buf, int count,
			  int fragsize, int intrflag)
{
  via97_portc *portc = audio_engines[dev]->portc;

  portc->audio_enabled |= PCM_ENABLE_OUTPUT;
  portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;

}

/*ARGSUSED*/
static void
via97_audio_start_input (int dev, oss_native_word buf, int count,
			 int fragsize, int intrflag)
{
  via97_portc *portc = audio_engines[dev]->portc;

  portc->audio_enabled |= PCM_ENABLE_INPUT;
  portc->trigger_bits &= ~PCM_ENABLE_INPUT;

}

static void
via97_audio_trigger (int dev, int state)
{
  via97_devc *devc = audio_engines[dev]->devc;
  via97_portc *portc = audio_engines[dev]->portc;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
  if (portc->open_mode & OPEN_WRITE)
    {
      if (state & PCM_ENABLE_OUTPUT)
	{
	  if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) &&
	      !(portc->trigger_bits & PCM_ENABLE_OUTPUT))
	    {
	      OUTB (devc->osdev, 0x80, devc->base + 0x01);
	      portc->trigger_bits |= PCM_ENABLE_OUTPUT;
	    }
	}
      else
	{
	  if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) &&
	      (portc->trigger_bits & PCM_ENABLE_OUTPUT))
	    {
	      portc->audio_enabled &= ~PCM_ENABLE_OUTPUT;
	      portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
	      OUTB (devc->osdev, 0x40, devc->base + 0x01);
	    }
	}
    }

  if (portc->open_mode & OPEN_READ)
    {
      if (state & PCM_ENABLE_INPUT)
	{
	  if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
	      !(portc->trigger_bits & PCM_ENABLE_INPUT))
	    {

	      OUTB (devc->osdev, 0x80, devc->base + 0x11);
	      portc->trigger_bits |= PCM_ENABLE_INPUT;
	    }
	}
      else
	{
	  if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
	      (portc->trigger_bits & PCM_ENABLE_INPUT))
	    {
	      portc->audio_enabled &= ~PCM_ENABLE_INPUT;
	      portc->trigger_bits &= ~PCM_ENABLE_INPUT;
	      OUTB (devc->osdev, 0x40, devc->base + 0x11);
	    }
	}
    }
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
}

/*ARGSUSED*/
static int
via97_audio_prepare_for_input (int dev, int bsize, int bcount)
{
  via97_devc *devc = audio_engines[dev]->devc;
  via97_portc *portc = audio_engines[dev]->portc;
  int i, sgd_ptr;
  dmap_t *dmap = audio_engines[dev]->dmap_in;
  unsigned char tmp;
  oss_native_word flags;

  if (audio_engines[dev]->dmap_in->dmabuf_phys == 0)
    return OSS_ENOSPC;

  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
  tmp = 0x81;			/* Auto start at EOL, interrupt on FLAG */

  if (portc->bits != AFMT_U8)
    tmp |= 0x20;
  if (portc->channels != 1)
    tmp |= 0x10;
  OUTB (devc->osdev, tmp, devc->base + 0x12);

  ac97_recrate (&devc->ac97devc, portc->speed);

  sgd_ptr = devc->rec_sgd_ptr;
  for (i = 0; i < dmap->nfrags; i++)
    {
      if (sgd_ptr >= SGD_TOTAL_SIZE)
	{
	  cmn_err (CE_WARN, "Out of Record SGD entries\n");
	  return OSS_ENOSPC;
	}

      devc->SGD_table[sgd_ptr].phaddr =
	dmap->dmabuf_phys + (i * dmap->fragment_size);
      devc->SGD_table[sgd_ptr].flags = 0x40000000 | dmap->fragment_size;

      sgd_ptr++;
    }

  devc->SGD_table[sgd_ptr - 1].flags |= 0x80000000;	/* EOL */
  devc->rec_sgd_phys =
    devc->SGD_table_phys + devc->rec_sgd_ptr * sizeof (SGD_entry);
  OUTL (devc->osdev, devc->rec_sgd_phys, devc->base + 0x14);
  portc->audio_enabled &= ~PCM_ENABLE_INPUT;
  portc->trigger_bits &= ~PCM_ENABLE_INPUT;
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
  return 0;
}

/*ARGSUSED*/
static int
via97_audio_prepare_for_output (int dev, int bsize, int bcount)
{
  via97_devc *devc = audio_engines[dev]->devc;
  via97_portc *portc = audio_engines[dev]->portc;
  int i, sgd_ptr;
  dmap_t *dmap = audio_engines[dev]->dmap_out;
  unsigned char tmp;
  oss_native_word flags;

  if (audio_engines[dev]->dmap_out->dmabuf_phys == 0)
    return OSS_ENOSPC;

  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
  tmp = 0x81;			/* Auto start at EOL, interrupt on FLAG */

  if (portc->bits != AFMT_U8)
    tmp |= 0x20;
  if (portc->channels != 1)
    tmp |= 0x10;
  OUTB (devc->osdev, tmp, devc->base + 0x02);

  ac97_playrate (&devc->ac97devc, portc->speed);

  sgd_ptr = devc->play_sgd_ptr;

  for (i = 0; i < dmap->nfrags; i++)
    {
      if (sgd_ptr >= SGD_TOTAL_SIZE)
	{
	  cmn_err (CE_WARN, "Out of Playback SGD entries\n");
	  return OSS_ENOSPC;
	}

      devc->SGD_table[sgd_ptr].phaddr =
	dmap->dmabuf_phys + (i * dmap->fragment_size);
      devc->SGD_table[sgd_ptr].flags = 0x40000000 | dmap->fragment_size;

      sgd_ptr++;
    }

  devc->SGD_table[sgd_ptr - 1].flags |= 0x80000000;	/* EOL */
  devc->play_sgd_phys =
    devc->SGD_table_phys + devc->play_sgd_ptr * sizeof (SGD_entry);
  OUTL (devc->osdev, devc->play_sgd_phys, devc->base + 0x04);

  portc->audio_enabled &= ~PCM_ENABLE_OUTPUT;
  portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
  return 0;
}

static int
via97_alloc_buffer (int dev, dmap_t * dmap, int direction)
{
  int err;

  if (dmap->dmabuf != NULL)
    return 0;
  if ((err = oss_alloc_dmabuf (dev, dmap, direction)) < 0)
    return err;

  if (dmap->buffsize > BUFFER_SIZE)
    {
      cmn_err (CE_WARN, "Too large DMA buffer (%d/%d) - truncated\n",
	       dmap->buffsize, BUFFER_SIZE);
      dmap->buffsize = BUFFER_SIZE;
    }

  return 0;
}

static int
via97_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
{

Unfortunately the VIA chip seems to raise interrupt about 32 bytes before the DMA pointer moves to a new fragment. This means that the bytes value returned by SNDCTL_DSP_GET[]PTR will be bogus during few samples before the pointer wraps back to the beginning of buffer.

If mmap() is not being used this routine will return 0 during this period.

  via97_devc *devc = audio_engines[dev]->devc;
  unsigned int ptr, sgd;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);

  if (direction == PCM_ENABLE_OUTPUT)
    {
      ptr = INL (devc->osdev, devc->base + 0x0c) & 0xffffff;
      sgd =
	((INL (devc->osdev, devc->base + 0x04) - devc->play_sgd_phys) / 8) -
	1;

      ptr = dmap->fragment_size - ptr;
      ptr = ptr + (sgd * dmap->fragment_size);
//cmn_err(CE_CONT, "%d/%d\n", sgd, ptr);
    }
  else
    {
      ptr = INL (devc->osdev, devc->base + 0x1c) & 0xffffff;
      sgd =
	((INL (devc->osdev, devc->base + 0x14) - devc->rec_sgd_phys) / 8) - 1;

      ptr = dmap->fragment_size - ptr;
      ptr = ptr + (sgd * dmap->fragment_size);
    }

  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
  return ptr;
}

static const audiodrv_t via97_audio_driver = {
  via97_audio_open,
  via97_audio_close,
  via97_audio_output_block,
  via97_audio_start_input,
  via97_audio_ioctl,
  via97_audio_prepare_for_input,
  via97_audio_prepare_for_output,
  via97_audio_reset,
  NULL,
  NULL,
  via97_audio_reset_input,
  via97_audio_reset_output,
  via97_audio_trigger,
  via97_audio_set_rate,
  via97_audio_set_format,
  via97_audio_set_channels,
  NULL,
  NULL,
  NULL,
  NULL,
  via97_alloc_buffer,
  NULL,				/* via97_free_buffer, */
  NULL,
  NULL,
  via97_get_buffer_pointer
};

#ifdef OBSOLETED_STUFF

This device has "ISA style" MIDI and FM subsystems. Such devices don't use PCI config space for the I/O ports and interrupts. Instead the driver needs to allocate proper resources itself. This functionality is no longer possible. For this reason the MIDI and FM parts are not accessible.

static void
attach_mpu (via97_devc * devc)
{
  struct address_info hw_config;

  hw_config.io_base = devc->mpu_base;
  hw_config.irq = -devc->irq;
  hw_config.dma = -1;
  hw_config.dma2 = -1;
  hw_config.always_detect = 0;
  hw_config.name = "VIA97 external MIDI";
  hw_config.driver_use_1 = 0;
  hw_config.driver_use_2 = 0;
  hw_config.osdev = devc->osdev;
#ifdef CREATE_OSP
  CREATE_OSP (hw_config.osdev);
#endif
  hw_config.card_subtype = 0;
#if 1
  if (!probe_uart401 (&hw_config))
    {
      cmn_err (CE_WARN, "MPU-401 was not detected\n");
      return;
    }
#endif
  DDB (cmn_err (CE_WARN, "MPU-401 detected - Good\n"));
  attach_uart401 (&hw_config);
  devc->mpu_attached = 1;
}

static void
unload_mpu (via97_devc * devc)
{
  struct address_info hw_config;

  if (devc == NULL || !devc->mpu_attached)
    return;

  devc->mpu_attached = 0;
  hw_config.io_base = devc->mpu_base;
  hw_config.irq = devc->irq;
  hw_config.dma = -1;
  hw_config.dma2 = -1;
  hw_config.always_detect = 0;
  hw_config.name = "VIA97";
  hw_config.driver_use_1 = 0;
  hw_config.driver_use_2 = 0;
  hw_config.osdev = devc->osdev;
#ifdef CREATE_OSP
  CREATE_OSP (hw_config.osdev);
#endif
  hw_config.card_subtype = 0;

  unload_uart401 (&hw_config);

}
#endif

static int
init_via97 (via97_devc * devc)
{
  int my_mixer, adev, opts, i;
  int first_dev = 0;
  oss_native_word phaddr;



Allocate the SGD buffers


  if (devc->SGD_table == NULL)
    {
      devc->SGD_table =
	CONTIG_MALLOC (devc->osdev, SGD_ALLOC, MEMLIMIT_32BITS, &phaddr, devc->sgd_dma_handle);

      if (devc->SGD_table == NULL)
	return OSS_ENOSPC;
      devc->SGD_table_phys = phaddr;
    }


Allocate SGD entries for recording and playback.

  devc->rec_sgd_ptr = 0;
  devc->play_sgd_ptr = SGD_SIZE;

Init mixer

  my_mixer =
    ac97_install (&devc->ac97devc, "VIA82C686 AC97 Mixer", ac97_read,
		  ac97_write, devc, devc->osdev);
  if (my_mixer == -1)
    return 0;			/* No mixer */

  mixer_devs[my_mixer]->priority = 10;	/* Known motherboard device */

  /* enable variable rate */
  ac97_write (devc, 0x2a, 0x01);

#ifdef OBSOLETED_STUFF
  DDB (cmn_err (CE_WARN, "Probing UART401 at 0x%x\n", devc->mpu_base));
  attach_mpu (devc);
#endif

  for (i = 0; i < 2; i++)
    {
      via97_portc *portc = &devc->portc[i];
      char tmp_name[100];

      opts = ADEV_AUTOMODE;

      if (!ac97_varrate (&devc->ac97devc))
	{
	  opts |= ADEV_FIXEDRATE;
	}

      if (i == 0)
	{
	  opts |= ADEV_DUPLEX;
	  strcpy (tmp_name, "VIA 82C686 AC97 Controller");
	}
      else
	{
	  opts |= ADEV_DUPLEX | ADEV_SHADOW;
	  strcpy (tmp_name, "VIA 82C686 AC97 Controller");
	}

      if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
					devc->osdev,
					devc->osdev,
					tmp_name,
					&via97_audio_driver,
					sizeof (audiodrv_t),
					opts,
					AFMT_U8 | AFMT_S16_LE, devc, -1)) < 0)
	{
	  adev = -1;
	  return 0;
	}
      else
	{
	  if (i == 0)
	    first_dev = adev;
	  audio_engines[adev]->portc = portc;
	  audio_engines[adev]->rate_source = first_dev;
	  audio_engines[adev]->mixer_dev = my_mixer;
	  audio_engines[adev]->min_rate = 8000;
	  audio_engines[adev]->max_rate = 48000;
	  audio_engines[adev]->caps |= PCM_CAP_FREERATE;
	  portc->open_mode = 0;
	  portc->audio_enabled = 0;
	  portc->audiodev = adev;
	  audio_engines[adev]->min_block = MIN_BLOCK;
	  audio_engines[adev]->max_block = 4 * 1024;
	  audio_engines[adev]->max_fragments = SGD_SIZE;
	  if (opts & ADEV_FIXEDRATE)
	    {
	      audio_engines[adev]->fixed_rate = 48000;
	      audio_engines[adev]->min_rate = 48000;
	      audio_engines[adev]->max_rate = 48000;
	    }
#ifdef CONFIG_OSS_VMIX
	  if (i == 0)
	     vmix_attach_audiodev(devc->osdev, adev, -1, 0);
#endif
	}
    }
  return 1;
}

int
oss_via97_attach (oss_device_t * osdev)
{
  unsigned char tmp, pci_irq_line, pci_revision /*, pci_latency */ ;
  unsigned short pci_command, vendor, device;
  unsigned int pci_ioaddr;
  int mpu_base;
  via97_devc *devc;

  DDB (cmn_err (CE_WARN, "Entered VT82C686 probe routine\n"));

  pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor);
  pci_read_config_word (osdev, PCI_DEVICE_ID, &device);

  if (vendor != VIA_VENDOR_ID || device != VIA_82C686)
    return 0;

  pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision);
  pci_read_config_word (osdev, PCI_COMMAND, &pci_command);
  pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line);
  pci_read_config_dword (osdev, PCI_BASE_ADDRESS_0, &pci_ioaddr);

  if (pci_ioaddr == 0)
    {
      cmn_err (CE_WARN, "I/O address not assigned by BIOS.\n");
      return 0;
    }

  if (pci_irq_line == 0)
    {
      cmn_err (CE_WARN, "IRQ not assigned by BIOS (%d).\n", pci_irq_line);
      return 0;
    }

  if ((devc = PMALLOC (osdev, sizeof (*devc))) == NULL)
    {
      cmn_err (CE_WARN, "Out of memory\n");
      return 0;
    }

  devc->osdev = osdev;
  osdev->devc = devc;
  devc->chip_name = "VIA VT82C686";

  /* Enable codec, etc */

  pci_write_config_byte (osdev, 0x41, 0xc0);
  oss_udelay (10);
  pci_read_config_byte (osdev, 0x41, &tmp);
  pci_write_config_byte (osdev, 0x41, tmp | 0x0c);
  oss_udelay (10);

  /* setup game port/MIDI */
  pci_write_config_byte (osdev, 0x42, 0x2a);
  /* disable FM io */
  pci_write_config_byte (osdev, 0x48, 0x00);

  /* Enable interrupt on FLAG and on EOL */
  tmp = INB (devc->osdev, devc->base + 0x22);
  OUTB (devc->osdev, tmp | 0x83, devc->base + 0x22);


  /* Enable MPU401 */
  pci_read_config_byte (osdev, 0x8, &tmp);
  if ((tmp & 0xff) >= 0x20)
    {
      pci_read_config_byte (osdev, 0x42, &tmp);
      pci_write_config_byte (osdev, 0x42, tmp & 0x3f);
    }

  pci_read_config_byte (osdev, 0x43, &tmp);
  switch ((tmp & 0x0c) >> 2)
    {
    case 0:
      mpu_base = 0x300;
      break;
    case 1:
      mpu_base = 0x310;
      break;
    case 2:
      mpu_base = 0x320;
      break;
    default:
      mpu_base = 0x330;
      break;
    }

  /* map PCI IO address space */
  devc->base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr);
  /* Remove I/O space marker in bit 0. */
  devc->base &= ~0x3;

  pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO;
  pci_write_config_word (osdev, PCI_COMMAND, pci_command);

  devc->irq = pci_irq_line;
  devc->mpu_base = mpu_base;
  devc->SGD_table = NULL;
  devc->mpu_attached = 0;
  devc->open_mode = 0;

  MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV);
  MUTEX_INIT (devc->osdev, devc->low_mutex, MH_DRV + 1);

  oss_register_device (osdev, devc->chip_name);

  if (oss_register_interrupts (devc->osdev, 0, via97intr, NULL) < 0)
    {
      cmn_err (CE_WARN, "Unable to register interrupts\n");
      return 0;
    }

  return init_via97 (devc);	/* Detected */
}


int
oss_via97_detach (oss_device_t * osdev)
{
  via97_devc *devc = (via97_devc *) osdev->devc;


  if (oss_disable_device (devc->osdev) < 0)
    return 0;

  OUTB (devc->osdev, 0x40, devc->base + 0x01);
  OUTB (devc->osdev, 0x40, devc->base + 0x11);
  OUTB (devc->osdev, 0, devc->base + 0x02);
  OUTB (devc->osdev, 0, devc->base + 0x12);
  OUTL (devc->osdev, 0, devc->base + 0x04);
  OUTL (devc->osdev, 0, devc->base + 0x14);
  OUTL (devc->osdev, 0, devc->base + 0x22);
  oss_udelay (30);

#ifdef OBSOLETED_STUFF
  if (devc->mpu_attached)
    unload_mpu (devc);
#endif

  oss_unregister_interrupts (devc->osdev);

  if (devc->SGD_table != NULL)
    {
      CONTIG_FREE (devc->osdev, devc->SGD_table, SGD_ALLOC, devc->sgd_dma_handle);
      devc->SGD_table = NULL;
    }

  MUTEX_CLEANUP (devc->mutex);
  MUTEX_CLEANUP (devc->low_mutex);
  UNMAP_PCI_IOADDR (devc->osdev, 0);

  oss_unregister_device (devc->osdev);
  return 1;
}

Copyright (C) 4Front Technologies, 2007. All rights reserved.

Back to index OSS web site


Copyright (C) 4Front Technologies, 2007. All rights reserved.
Back to index OSS web site