Open Sound System |
Do you have problems with sound/audio application development? Don't panic! Click here for help! |
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_trident_cfg.h" #include "oss_pci.h" #include "ac97.h" #if defined(sparc) #define MEM_MAPPED_REGISTERS #else #undef MEM_MAPPED_REGISTERS #endif #define TRIDENT_VENDOR_ID 0x1023 #define TRIDENT_4DWAVEDX_ID 0x2000 #define TRIDENT_4DWAVENX_ID 0x2001 #define TRIDENT_4DWAVECX_ID 0x2002 #define ALI_VENDOR_ID 0x10b9 #define ALI_5451_ID 0x5451 #define SIS_VENDOR_ID 0x1039 #define SIS_7018_ID 0x7018 /* DX/NX/CX IO Registers */ #define SP_CSO 0x24 #define SP_LBA 0x28 #define SP_ESO 0x2C #define ACR0 0x40 #define SP_STAT 0x64 #define TLBC 0x6C #define START_A 0x80 #define STOP_A 0x84 #define INT_A 0x98 #define INTEN_A 0xA4 #define VOL 0xA8 #define DELTA 0xAC #define MISCINT 0xB0 #define START_B 0xB4 #define STOP_B 0xB8 #define INT_B 0xD8 #define INTEN_B 0xDC #define CIR 0xA0 #define CSO 0xE0 #define LBA 0xE4 #define ESO 0xE8 #define FMC 0xEC #define TCTRL 0xF0 #define EBUF1 0xF4 #define EBUF2 0xF8 #define DMAR0 0x00 #define DMAR4 0x04 #define DMAR6 0x06 #define DMAR11 0x0b #define DMAR15 0x0f #define SBDELTA 0xac #define SBBL 0xc0 #define SBCTL 0xc4 /* SIS 7018 Define */ #define SECONDARY_ID 0x00004000 #define PCMOUT 0x00010000 #define SURROUT 0x00020000 #define CENTEROUT 0x00040000 #define LFEOUT 0x00080000 #define LINE1OUT 0x00100000 #define LINE2OUT 0x00200000 #define GPIOOUT 0x00400000 #define CHANNEL_PB 0x0000 #define CHANNEL_SPC_PB 0x4000 #define CHANNEL_REC 0x8000 #define CHANNEL_REC_PB 0xc000 #define MODEM_LINE1 0x0000 #define MODEM_LINE2 0x0400 #define PCM_LR 0x0800 #define HSET 0x0c00 #define I2S_LR 0x1000 #define CENTER_LFE 0x1400 #define SURR_LR 0x1800 #define SPDIF_LR 0x1c00 #define MIC 0x1400 #define MONO_LEFT 0x0000 #define MONO_RIGHT 0x0100 #define MONO_MIX 0x0200 #define SRC_ENABLE 0x0080 #define INTR(bank) ((bank==1)?INT_A:INT_B) #define INTEN(bank) ((bank==1)?INTEN_A:INTEN_B) #define STOP(bank) ((bank==1)?STOP_A:STOP_B) #define START(bank) ((bank==1)?START_A:START_B) /* ALI5451 definitions */ #define ALI_5451_V02 0x2 #define SIS_7018_V02 0x2 extern int trident_mpu_ioaddr; #define MAX_PORTC 8 typedef struct trident_portc { int speed, bits, channels; int open_mode; int audio_enabled; int trigger_bits; int audiodev; int port_type; #define DF_PCM 0 #define DF_SPDIF 1 unsigned char play_chan, play_intr_chan; unsigned char rec_chan, rec_intr_chan; int pbank, rbank; #define BANK_A 1 #define BANK_B 0 } trident_portc; typedef struct trident_devc { oss_device_t *osdev; char *chip_name; int chip_type; int revision; int irq; #ifdef MEM_MAPPED_REGISTERS unsigned int bar1addr; char *bar1virt; #else /* */ oss_native_word base; #endif /* */ volatile unsigned char intr_mask; oss_mutex_t mutex; oss_mutex_t low_mutex; /* Legacy */ int mpu_base, mpu_irq, mpu_attached; /* Mixer parameters */ ac97_devc ac97devc; int mixer_dev; /* Audio parameters */ trident_portc portc[MAX_PORTC]; int audio_opened; unsigned char sb_dma_flags; } trident_devc; #ifndef MEM_MAPPED_REGISTERS /* I/O mapped register access */ #define READL(o,a) INL(o, devc->base+(a)) #define READW(o,a) INW(o, devc->base+(a)) #define READB(o,a) INB(o, devc->base+(a)) #define WRITEL(o,d,a) OUTL(o, d, devc->base+(a)) #define WRITEW(o,d,a) OUTW(o, d, devc->base+(a)) #define WRITEB(o,d,a) OUTB(o, d, devc->base+(a)) #else /* Mem mapped I/O registers */ #define READL(o,a) *(volatile unsigned int*)(devc->bar1virt+(a)) #define READW(o,a) *(volatile unsigned short*)(devc->bar1virt+(a)) #define READB(o,a) *(volatile unsigned char*)(devc->bar1virt+(a)) #define WRITEL(o,d,a) *(volatile unsigned int*)(devc->bar1virt+(a))=d #define WRITEW(o,d,a) *(volatile unsigned short*)(devc->bar1virt+(a))=d #define WRITEB(o,d,a) *(volatile unsigned char*)(devc->bar1virt+(a))=d #endif static int ac97_read (void *devc_, int addr) { trident_devc *devc = devc_; int t, ret; oss_native_word data, reg = 0; oss_native_word rmask = 0; oss_native_word dmask = 0; oss_native_word wport = 0; oss_native_word rport = 0; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); t = 20000; switch (devc->chip_type) { case TRIDENT_4DWAVEDX_ID: case SIS_7018_ID: wport = 0x40; rport = 0x44; if (devc->revision == SIS_7018_V02) { rport = 0x40; } rmask = 0x00008000; dmask = 0x00008000; break; case TRIDENT_4DWAVENX_ID: wport = 0x44; rport = 0x48; rmask = 0x00000800; dmask = 0x00000400; break; case ALI_5451_ID: wport = 0x40; rport = 0x44; rmask = 0x00008000; dmask = 0x00008000; if (devc->revision == ALI_5451_V02) { rport = 0x40; } break; default: { static int already_done = 0; if (!already_done) cmn_err (CE_WARN, "Unknown codec interface\n"); already_done = 1; MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return OSS_EIO; } } /* Check to make sure No Writes are pending */ reg = READL (devc->osdev, wport); while ((reg & dmask) && --t) reg = READL (devc->osdev, wport); /* re-read status/data */ /* Check to make sure No reads are pending */ reg = READL (devc->osdev, rport); while ((reg & dmask) && --t) reg = READL (devc->osdev, rport); /* re-read status/data */ data = addr | rmask; WRITEL (devc->osdev, data, rport); /* select register for reading */ reg = READL (devc->osdev, rport); /* read status/data */ t = 20000; while ((reg & dmask) && --t) { /* busy reading */ reg = READL (devc->osdev, rport); /* re-read status/data */ } if (t) ret = (reg >> 16); else { DDB (cmn_err (CE_WARN, "AC97 mixer read timed out\n")); MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return OSS_EIO; } MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return ret; } static int ac97_write (void *devc_, int addr, int data) { trident_devc *devc = devc_; int t; oss_native_word wport = 0; oss_native_word reg = 0; unsigned int wmask = 0; unsigned int dmask = 0; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); t = 2000; switch (devc->chip_type) { case TRIDENT_4DWAVEDX_ID: case SIS_7018_ID: wport = 0x40; wmask = 0x00008000; dmask = 0x00008000; break; case TRIDENT_4DWAVENX_ID: wport = 0x44; wmask = 0x00000800; dmask = 0x00000800; break; case ALI_5451_ID: wport = 0x40; wmask = 0x00008000; dmask = 0x00008000; if (devc->revision == 2) dmask = 0x100; break; default: { static int already_done = 0; if (!already_done) cmn_err (CE_WARN, "Unknown codec interface\n"); already_done = 1; MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return OSS_EIO; } } /* Check to make sure No Writes are pending */ reg = READL (devc->osdev, wport); while ((reg & wmask) && --t) { reg = READL (devc->osdev, wport); /* re-read status/data */ } if (t) { reg = data << 16; reg |= (addr & 0xff); reg |= wmask | dmask; WRITEL (devc->osdev, reg, wport); } else DDB (cmn_err (CE_WARN, "AC97 mixer write timed out\n")); MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return 0; } static int tridentintr (oss_device_t * osdev) { trident_devc *devc = (trident_devc *) osdev->devc; int i; oss_native_word status, intstat; int serviced = 0; dmap_t *dmapin; oss_native_word flag; /* check the interrupt status register MISCINT */ status = READL (devc->osdev, MISCINT); serviced = 1; /* If it is a not 4DWave Playback or MIDI interrupt then return */ if (!(status & (0x00000020 | 0x00000008 | 0x4))) { WRITEL (devc->osdev, status, MISCINT); return 1; } #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.
/* Check if this is a MIDI interrupt */ if (status & 0x00000008) { uart401intr (INT_HANDLER_CALL (devc->irq)); serviced = 1; } #endif MUTEX_ENTER_IRQDISABLE (devc->mutex, flag); /* Check to see if this is our channel */ if (status & 0x20) for (i = 0; i < MAX_PORTC; i++) { trident_portc *portc = &devc->portc[i]; serviced = 1; /* Handle Playback Interrupts */ intstat = READL (devc->osdev, INTR (portc->pbank)); if (intstat & 1L << portc->play_intr_chan) { WRITEL (devc->osdev, READL (devc->osdev, INTR (portc->pbank)) | 1L << portc->play_intr_chan, INTR (portc->pbank)); oss_audio_outputintr (portc->audiodev, 1); } /* Handle Record Interrupts */ intstat = READL (devc->osdev, INTR (portc->rbank)); if (intstat & 1L << portc->rec_intr_chan) { WRITEL (devc->osdev, READL (devc->osdev, INTR (portc->rbank)) | 1L << portc->rec_intr_chan, INTR (portc->rbank)); if ((devc->chip_type == ALI_5451_ID) || (devc->chip_type == SIS_7018_ID)) { unsigned int ptr; int i; int chan; dmapin = audio_engines[portc->audiodev]->dmap_in; if (portc->rbank == BANK_A) chan = portc->rec_chan; else chan = portc->rec_chan + 32; WRITEL (devc->osdev, (READL (devc->osdev, CIR) & ~0x3F) | chan, CIR); ptr = READW (devc->osdev, CSO + 2); ptr++; ptr *= portc->channels * (portc->bits / 8); if (dmapin->bytes_in_use == 0 || dmapin->fragment_size == 0) { cmn_err(CE_WARN, "bytes_in_use=%d, fragment_size=%d\n", dmapin->bytes_in_use, dmapin->fragment_size); continue; } ptr %= dmapin->bytes_in_use; ptr /= dmapin->fragment_size; i = 0; while (dmap_get_qtail (dmapin) != ptr && i++ < dmapin->nfrags) oss_audio_inputintr (portc->audiodev, 0); } else oss_audio_inputintr (portc->audiodev, 0); } } if (status & 0x4) { serviced = 1; READB (devc->osdev, 0x1E); /*SB ESP ack */ READB (devc->osdev, 0x1F); /*SB IRQ ack */ } MUTEX_EXIT_IRQRESTORE (devc->mutex, flag); return serviced; } /***************************************************************************/ static int trident_audio_set_rate (int dev, int arg) { trident_portc *portc = audio_engines[dev]->portc; if (arg == 0) return portc->speed; if (arg > 48000) arg = 48000; if (arg < 5000) arg = 5000; portc->speed = arg; return portc->speed; } static short trident_audio_set_channels (int dev, short arg) { trident_portc *portc = audio_engines[dev]->portc; if ((arg != 1) && (arg != 2)) return portc->channels; portc->channels = arg; return portc->channels; } static unsigned int trident_audio_set_format (int dev, unsigned int arg) { trident_portc *portc = audio_engines[dev]->portc; if (arg == 0) return portc->bits; if (!(arg & (AFMT_U8 | AFMT_S16_LE | AFMT_AC3))) return portc->bits; portc->bits = arg; return portc->bits; } /*ARGSUSED*/ static int trident_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg) { return OSS_EINVAL; } static void trident_audio_trigger (int dev, int state); static void trident_audio_reset (int dev) { trident_audio_trigger (dev, 0); } static void trident_audio_reset_input (int dev) { trident_portc *portc = audio_engines[dev]->portc; trident_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT); } static void trident_audio_reset_output (int dev) { trident_portc *portc = audio_engines[dev]->portc; trident_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT); } /*ARGSUSED*/ static int trident_audio_open (int dev, int mode, int open_flags) { trident_portc *portc = audio_engines[dev]->portc; trident_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; } portc->open_mode = mode; portc->audio_enabled = ~mode; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } static void trident_audio_close (int dev, int mode) { trident_portc *portc = audio_engines[dev]->portc; trident_audio_reset (dev); portc->open_mode = 0; portc->audio_enabled &= ~mode; } /*ARGSUSED*/ static void trident_audio_output_block (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { trident_portc *portc = audio_engines[dev]->portc; portc->audio_enabled |= PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; } /*ARGSUSED*/ static void trident_audio_start_input (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { trident_portc *portc = audio_engines[dev]->portc; portc->audio_enabled |= PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; } static void trident_audio_trigger (int dev, int state) { trident_devc *devc = audio_engines[dev]->devc; trident_portc *portc = audio_engines[dev]->portc; oss_native_word ainten = 0, playstop = 0, recstop = 0; 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)) { /* Enable the channel interrupt */ WRITEL (devc->osdev, READL (devc->osdev, INTEN (portc->pbank)) | (1L << portc->play_intr_chan), INTEN (portc->pbank)); /* start playback and playback interrupt channels */ WRITEL (devc->osdev, READL (devc->osdev, START (portc->pbank)) | (1L << portc->play_chan) | (1L << portc->play_intr_chan), START (portc->pbank)); 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; /* disable playback interrupt channel */ ainten = READL (devc->osdev, INTEN (portc->pbank)); ainten = ainten & ~(1L << portc->play_intr_chan); WRITEL (devc->osdev, ainten, INTEN (portc->pbank)); /* stop playback and playback interrupt channels */ playstop = ((1L << portc->play_chan) | (1L << portc->play_intr_chan)); WRITEL (devc->osdev, playstop, STOP (portc->pbank)); } } } if (portc->open_mode & OPEN_READ) { if (state & PCM_ENABLE_INPUT) { if ((portc->audio_enabled & PCM_ENABLE_INPUT) && !(portc->trigger_bits & PCM_ENABLE_INPUT)) { /* Enable the channel interrupts */ WRITEL (devc->osdev, READL (devc->osdev, INTEN (portc->rbank)) | (1L << portc->rec_intr_chan), INTEN (portc->rbank)); if ((devc->chip_type != ALI_5451_ID) && (devc->chip_type != SIS_7018_ID)) { /* set recording flags */ WRITEB (devc->osdev, devc->sb_dma_flags, SBCTL); /* start record interrupt channel */ WRITEL (devc->osdev, 1L << portc->rec_intr_chan, START (portc->rbank)); } else { WRITEL (devc->osdev, READL (devc->osdev, START (portc->rbank)) | (1L << portc-> rec_chan) | (1L << portc-> rec_intr_chan), START (portc->rbank)); } 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; if ((devc->chip_type != ALI_5451_ID) && (devc->chip_type != SIS_7018_ID)) { /* disable the record interrrupt enable channel */ ainten = READL (devc->osdev, INTEN (portc->rbank)); ainten = ainten & ~(1L << portc->rec_intr_chan); WRITEL (devc->osdev, ainten, INTEN (portc->rbank)); /* stop DMA controller */ WRITEB (devc->osdev, 0x00, SBCTL); /* stop record interrupt channel */ recstop |= 1L << portc->rec_intr_chan; WRITEL (devc->osdev, recstop, STOP (portc->rbank)); } else { /* disable rec interrupt channel */ ainten = READL (devc->osdev, INTEN (portc->rbank)); ainten = ainten & ~(1L << portc->rec_intr_chan); WRITEL (devc->osdev, ainten, INTEN (portc->rbank)); /* stop playback and playback interrupt channels */ recstop = ((1L << portc->rec_chan) | (1L << portc->rec_intr_chan)); WRITEL (devc->osdev, recstop, STOP (portc->rbank)); } /* ack any interrupts on INT_A */ recstop = READL (devc->osdev, INTR (portc->rbank)); recstop |= 1L << portc->rec_intr_chan; WRITEL (devc->osdev, recstop, INTR (portc->rbank)); } } } MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } /*ARGSUSED*/ static int trident_audio_prepare_for_input (int dev, int bsize, int bcount) { trident_devc *devc = audio_engines[dev]->devc; trident_portc *portc = audio_engines[dev]->portc; dmap_t *dmap = audio_engines[dev]->dmap_in; oss_native_word bufsize, delta, eso; oss_native_word temp; unsigned int attribute = 0; unsigned char bValue; unsigned short wValue; oss_native_word dwValue; unsigned short wRecCodecSamples; int chan; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (devc->chip_type == TRIDENT_4DWAVEDX_ID) { bValue = READB (devc->osdev, 0x48); /*enable AC97 ADC */ WRITEB (devc->osdev, bValue | 0x48, 0x48); /*disable rec intr */ } if (devc->chip_type == TRIDENT_4DWAVENX_ID) { wValue = READW (devc->osdev, MISCINT); WRITEW (devc->osdev, wValue | 0x1000, MISCINT); } if ((devc->chip_type != ALI_5451_ID) && (devc->chip_type != SIS_7018_ID)) { /* Initialize legacy recording */ WRITEB (devc->osdev, 0, DMAR15); bValue = READB (devc->osdev, DMAR11) & 0x03; WRITEB (devc->osdev, bValue | 0x54, DMAR11); /* Set base address in DMAR0-DMAR3 */ WRITEL (devc->osdev, dmap->dmabuf_phys, DMAR0); /* Set count in DMAR4/DMAR5 */ eso = dmap->bytes_in_use; WRITEW (devc->osdev, eso - 1, DMAR4); /* Set speed in SBDelta */ dwValue = (48000 << 12) / portc->speed; WRITEW (devc->osdev, (unsigned short) dwValue, SBDELTA); /*Set channel interrupt blk length */ if ((portc->bits == 16) || (portc->channels == 2)) wRecCodecSamples = (unsigned short) ((eso >> 1) - 1); else wRecCodecSamples = (unsigned short) (eso - 1); dwValue = wRecCodecSamples << 16; dwValue |= wRecCodecSamples & 0x0000ffff; WRITEL (devc->osdev, dwValue, SBBL); /*set format */ devc->sb_dma_flags |= 0x19; if (portc->bits == 16) devc->sb_dma_flags |= 0xA0; if (portc->channels == 2) devc->sb_dma_flags |= 0x40; } else { #if !defined(sparc) if (devc->chip_type == ALI_5451_ID) WRITEL (devc->osdev, READL (devc->osdev, 0xd4) | ((unsigned int) 1 << 31), 0xd4); #endif /* for SiS 7018: Rec with PCM_LR, MONO_MIX, SRC_EN */ if (devc->chip_type == SIS_7018_ID) attribute = 0x8880; /* PCM->IN */ delta = (48000 << 12) / portc->speed; bufsize = dmap->bytes_in_use; if (portc->bits == 16) bufsize /= 2; if (portc->channels == 2) bufsize /= 2; bufsize--; if (portc->rbank == BANK_A) chan = portc->rec_chan; else chan = portc->rec_chan + 32; WRITEL (devc->osdev, (READL (devc->osdev, CIR) & ~0x3F) | chan, CIR); /*select current chan */ /* Now set the buffer address pointer */ WRITEL (devc->osdev, dmap->dmabuf_phys, LBA); /* Set the Size and Sampling Rate */ eso = (bufsize << 16) | (delta & 0x0000FFFF); WRITEL (devc->osdev, eso, ESO); temp = 0x80000000; /*enable gvsel */ if (portc->channels == 2) temp |= 0x00004000; /*stereo/mono */ if (portc->bits == 16) temp |= 0x0000A000; /*unsigned 8/signed 16bit */ temp |= 0x00001000; /*enable loop */ temp |= 0x003F0000; /*set pan to off */ temp |= 0x00000FFF; /*set vol to off */ WRITEL (devc->osdev, temp, TCTRL); /*set format */ WRITEL (devc->osdev, attribute << 16, FMC); WRITEL (devc->osdev, 0, EBUF1); WRITEL (devc->osdev, 0, EBUF2); /* WRITEL (devc->osdev, 0xFFFF, VOL); *//*Music/WaveVol set to max */ WRITEL (devc->osdev, 0, CSO); /*set CSO and Alpha to 0 */ } /* Now prepare the Record Interrupt Channel */ delta = (48000 << 12) / portc->speed; bufsize = dmap->fragment_size; if (portc->bits == 16) bufsize /= 2; if (portc->channels == 2) bufsize /= 2; bufsize--; if (portc->rbank == BANK_A) chan = portc->rec_intr_chan; else chan = portc->rec_intr_chan + 32; WRITEL (devc->osdev, (READL (devc->osdev, CIR) & ~0x3F) | chan, CIR); /* Now set the buffer address pointer */ WRITEL (devc->osdev, dmap->dmabuf_phys, LBA); /* Set the Size and Sampling Rate */ if ((devc->chip_type == TRIDENT_4DWAVEDX_ID) || (devc->chip_type == SIS_7018_ID) || (devc->chip_type == ALI_5451_ID)) { eso = (bufsize << 16) | (delta & 0x0000FFFF); WRITEL (devc->osdev, eso, ESO); } else { eso = ((delta << 16) & 0xff000000) | (bufsize & 0x00ffffff); WRITEL (devc->osdev, eso, ESO); } temp = 0x80000000; /*enable gvsel */ if (portc->channels == 2) temp |= 0x00004000; /*stereo/mono */ if (portc->bits == 16) temp |= 0x0000A000; /*8 unsigned/16bit signed data */ temp |= 0x00001000; /*enable loop */ temp |= 0x003F0000; /*set pan to off */ temp |= 0x00000FFF; /*set vol to off */ WRITEL (devc->osdev, temp, TCTRL); /*set format */
set for SIS7018
WRITEL (devc->osdev, 0, FMC); WRITEL (devc->osdev, 0, EBUF1); WRITEL (devc->osdev, 0, EBUF2); /* WRITEL (devc->osdev, 0xFFFF, VOL); *//*Music/WaveVol set to min */ if ((devc->chip_type == TRIDENT_4DWAVEDX_ID) || (devc->chip_type == SIS_7018_ID) || (devc->chip_type == ALI_5451_ID)) { WRITEL (devc->osdev, 0, CSO); /*set CSO and Alpha to 0 */ } else { WRITEL (devc->osdev, (delta << 24) | (0 & 0x00ffffff), CSO); } /* Now set the mode on portc to record */ portc->audio_enabled &= ~PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } /*ARGSUSED*/ static int trident_audio_prepare_for_output (int dev, int bsize, int bcount) { trident_devc *devc = audio_engines[dev]->devc; trident_portc *portc = audio_engines[dev]->portc; dmap_t *dmap = audio_engines[dev]->dmap_out; oss_native_word bufsize, delta, eso; oss_native_word temp; int chan, spdif_rate, spdif_chan; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); /* prepare the main playback channel */ delta = (portc->speed * 4096) / 48000; bufsize = dmap->bytes_in_use; if (portc->bits == 16) bufsize /= 2; if (portc->channels == 2) bufsize /= 2; bufsize--; if ((devc->chip_type == ALI_5451_ID) && (devc->revision == ALI_5451_V02) && (portc->port_type == DF_SPDIF)) { ac97_spdifout_ctl (devc->mixer_dev, SPDIFOUT_AUDIO, SNDCTL_MIX_WRITE, 0); portc->play_chan = 15; portc->play_intr_chan = 14; if (portc->bits == AFMT_AC3) { ac97_spdif_setup (devc->mixer_dev, portc->speed, portc->bits); portc->bits = 16; portc->channels = 2; portc->speed = 48000; /* set non-pcm (AC3) mode on SPDIF control */ WRITEW (devc->osdev, (READW (devc->osdev, 0x70) & ~0x2) | 0x2, 0x70); } else /* set pcm mode on SPDIF control */ WRITEW (devc->osdev, READW (devc->osdev, 0x70) & ~0x2, 0x70); /*set up the sampling rate */ switch (portc->speed) { case 44100: spdif_rate = 0; break; case 32000: spdif_rate = 0x300; break; case 48000: default: spdif_rate = 0x200; break; } spdif_chan = READB (devc->osdev, 0x74) & 0xbf; /*select spdif_out */ spdif_chan |= 0x80; /*select right */ WRITEB (devc->osdev, spdif_chan, 0x74); WRITEB (devc->osdev, spdif_rate | 0x20, 0x72); spdif_chan &= (~0x80); /*select left */ WRITEB (devc->osdev, spdif_chan, 0x74); WRITEB (devc->osdev, spdif_rate | 0x10, 0x72); } if (portc->pbank == BANK_A) chan = portc->play_chan; else chan = portc->play_chan + 32; WRITEL (devc->osdev, (READL (devc->osdev, CIR) & ~0x3F) | chan, CIR); /*select current chan */ /* Now set the buffer address pointer */ WRITEL (devc->osdev, dmap->dmabuf_phys, LBA); /* Set the Size and Sampling Rate */ if ((devc->chip_type == TRIDENT_4DWAVEDX_ID) || (devc->chip_type == SIS_7018_ID) || (devc->chip_type == ALI_5451_ID)) { eso = (bufsize << 16) | (delta & 0x0000FFFF); WRITEL (devc->osdev, eso, ESO); } else { eso = ((delta << 16) & 0xff000000) | (bufsize & 0x00ffffff); WRITEL (devc->osdev, eso, ESO); } temp = 0x80000000; /*enable gvsel */ if (portc->channels == 2) temp |= 0x00004000; /*stereo/mono */ if (portc->bits == 16) temp |= 0x0000A000; /*8 unsigned/16bit signed data */ temp |= 0x00001000; /*enable loop */ WRITEL (devc->osdev, temp, TCTRL); /*set format */ WRITEL (devc->osdev, 0, FMC); WRITEL (devc->osdev, 0, EBUF1); WRITEL (devc->osdev, 0, EBUF2); WRITEL (devc->osdev, 0, VOL); /*Music/WaveVol set to max */ if ((devc->chip_type == TRIDENT_4DWAVEDX_ID) || (devc->chip_type == SIS_7018_ID) || (devc->chip_type == ALI_5451_ID)) { WRITEL (devc->osdev, 0, CSO); /*set CSO and Alpha to 0 */ } else { WRITEL (devc->osdev, (delta << 24) | (0 & 0x00ffffff), CSO); } /* Now prepare the playback interrupt channel */ delta = (portc->speed * 4096) / 48000; bufsize = dmap->fragment_size; if (portc->bits == 16) bufsize /= 2; if (portc->channels == 2) bufsize /= 2; bufsize--; if (portc->pbank == BANK_A) chan = portc->play_intr_chan; else chan = portc->play_intr_chan + 32; WRITEL (devc->osdev, (READL (devc->osdev, CIR) & ~0x3F) | chan, CIR); /*select current chan */ /* Now set the buffer address pointer */ WRITEL (devc->osdev, dmap->dmabuf_phys, LBA); /* Set the Size and Sampling Rate */ if ((devc->chip_type == TRIDENT_4DWAVEDX_ID) || (devc->chip_type == SIS_7018_ID) || (devc->chip_type == ALI_5451_ID)) { eso = (bufsize << 16) | (delta & 0x0000FFFF); WRITEL (devc->osdev, eso, ESO); } else { eso = ((delta << 16) & 0xff000000) | (bufsize & 0x00ffffff); WRITEL (devc->osdev, eso, ESO); } temp = 0x80000000; /*enable gvsel */ if (portc->channels == 2) temp |= 0x00004000; /*stereo/mono */ if (portc->bits == 16) temp |= 0x0000A000; /*8 unsigned/16bit signed data */ temp |= 0x00001000; /*enable loop */ temp |= 0xFFF; /* no vol */ temp |= 0x003F0000; /*set volume to off */ WRITEL (devc->osdev, temp, TCTRL); /*set format */ WRITEL (devc->osdev, 0, FMC); WRITEL (devc->osdev, 0, EBUF1); WRITEL (devc->osdev, 0, EBUF2); WRITEL (devc->osdev, 0, VOL); /*Music/WaveVol set to max */ if ((devc->chip_type == TRIDENT_4DWAVEDX_ID) || (devc->chip_type == SIS_7018_ID) || (devc->chip_type == ALI_5451_ID)) { WRITEL (devc->osdev, 0, CSO); /*set CSO and Alpha to 0 */ } else { WRITEL (devc->osdev, (delta << 24) | (0 & 0x00ffffff), CSO); } /* Now set the mode on portc to playback */ portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } #if 0 static int trident_get_buffer_pointer (int dev, dmap_t * dmap, int direction) { trident_devc *devc = audio_engines[dev]->devc; trident_portc *portc = audio_engines[dev]->portc; oss_native_word flags; int ptr = 0; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (direction == PCM_ENABLE_INPUT) { WRITEB (devc->osdev, (READB (devc->osdev, CIR) & ~0x7F) | portc->rec_chan, CIR); if ((devc->chip_type == TRIDENT_4DWAVEDX_ID) || (devc->chip_type == SIS_7018_ID) || (devc->chip_type == ALI_5451_ID)) ptr = READW (devc->osdev, CSO + 2); else /* ID_4DWAVE_NX */ { ptr = READW (devc->osdev, SBBL) & 0xFFFF; if (portc->channels > 1) ptr >>= 1; if (ptr > 0) ptr = dmap->bytes_in_use - ptr; } } if (direction == PCM_ENABLE_OUTPUT) { WRITEB (devc->osdev, (READB (devc->osdev, CIR) & ~0x7F) | portc->play_chan, CIR); if ((devc->chip_type == TRIDENT_4DWAVEDX_ID) || (devc->chip_type == SIS_7018_ID) || (devc->chip_type == ALI_5451_ID)) { ptr = READW (devc->osdev, CSO + 2); } else { ptr = READL (devc->osdev, CSO) & 0x00ffffff; } if (ptr > dmap->bytes_in_use) ptr = 0; } MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return ptr; } #endif #if !defined(sparc) static int trident_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) { cmn_err (CE_WARN, "Failed to allocate a DMA buffer.\n"); return err; } if (dmap->dmabuf_phys & 0x80000000) { cmn_err (CE_WARN, "Got DMA buffer address beyond 2G limit.\n"); oss_free_dmabuf (dev, dmap); dmap->dmabuf = NULL; return OSS_ENOSPC; } return 0; } #endif static audiodrv_t trident_audio_driver = { trident_audio_open, trident_audio_close, trident_audio_output_block, trident_audio_start_input, trident_audio_ioctl, trident_audio_prepare_for_input, trident_audio_prepare_for_output, trident_audio_reset, NULL, NULL, trident_audio_reset_input, trident_audio_reset_output, trident_audio_trigger, trident_audio_set_rate, trident_audio_set_format, trident_audio_set_channels, NULL, NULL, NULL, /* trident_check_input, */ NULL, /* trident_check_output, */ #if !defined(sparc) trident_alloc_buffer, #else NULL, #endif NULL, /* trident_free_buffer, */ NULL, NULL, NULL /* trident_get_buffer_pointer */ }; #ifdef OBSOLETED_STUFF static void attach_mpu (trident_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 = devc->chip_name; 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 (!probe_uart401 (&hw_config)) { cmn_err (CE_WARN, "MPU-401 was not detected\n"); return; } devc->mpu_attached = 1; attach_uart401 (&hw_config); } static void unload_mpu (trident_devc * devc) { struct address_info hw_config; hw_config.io_base = devc->mpu_base; hw_config.irq = devc->mpu_irq; hw_config.dma = -1; hw_config.dma2 = -1; hw_config.always_detect = 0; hw_config.name = devc->chip_name; 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; devc->mpu_attached = 0; unload_uart401 (&hw_config); } #endif static int init_trident (trident_devc * devc) { int my_mixer, i; oss_device_t *osdev = devc->osdev; unsigned char legacy; oss_native_word global_control; int adev; unsigned int dwVal; int first_dev = 0;
Legacy I/O setup
devc->mpu_attached = 0; legacy = 0xA0; /* Enable MPU, GP, FM */ switch (devc->mpu_base) { case 0x330: legacy |= 0x00; break; case 0x300: legacy |= 0x40; break; default: devc->mpu_base = 0; } pci_write_config_byte (osdev, 0x44, legacy); /*setup legacy devs */ /* pci_write_config_byte (osdev, 0x45, 0x12); *//*setup DDMA */ /* pci_write_config_byte (osdev, 0x46, 0x02); *//*setup device r/w */ /* Now reset AC97 and unmute the codec channels */ if (devc->chip_type == TRIDENT_4DWAVEDX_ID) { WRITEB (devc->osdev, 0x09, 0x40); /*set ddma */ WRITEL (devc->osdev, 0x2, 0x48); /*enable the AC97 */ } if (devc->chip_type == SIS_7018_ID) { WRITEL (devc->osdev, 0, 0x4c); WRITEL (devc->osdev, 0xF0000, 0x48); /* enable ac97 link */ } if (devc->chip_type == TRIDENT_4DWAVENX_ID) { WRITEL (devc->osdev, 0x2, 0x40); /* Setup 48KHz S/PDIF output on 4DWave NX */ WRITEL (devc->osdev, INL (devc->osdev, SP_STAT) | 4, SP_STAT); WRITEB (devc->osdev, 0x38, SP_CSO + 3); } if (devc->chip_type == ALI_5451_ID) { #ifndef sparc pci_read_config_dword (osdev, 0x7c, &dwVal); pci_write_config_dword (osdev, 0x7c, dwVal | 0x08000000); oss_udelay (5000); pci_read_config_dword (osdev, 0x7c, &dwVal); pci_write_config_dword (osdev, 0x7c, dwVal & 0xf7ffffff); oss_udelay (5000); pci_read_config_dword (osdev, 0x44, &dwVal); pci_write_config_dword (osdev, 0x44, dwVal | 0x000c0000); oss_udelay (500); pci_read_config_dword (osdev, 0x44, &dwVal); pci_write_config_dword (osdev, 0x44, dwVal & 0xfffbffff); #endif /* enable full 32bit and disable DDMA */ pci_write_config_dword (osdev, 0x40, dwVal & 0x00000008); oss_udelay (5000); } my_mixer = ac97_install (&devc->ac97devc, devc->chip_name, ac97_read, ac97_write, devc, devc->osdev); if (my_mixer >= 0) { devc->mixer_dev = my_mixer; mixer_devs[my_mixer]->priority = 9; } else return 0; #ifdef OBSOLETED_STUFF if (devc->mpu_base > 0) attach_mpu (devc); #endif for (i = 0; i < 2; i++) { char tmp_name[1024]; trident_portc *portc = &devc->portc[i]; int caps = ADEV_AUTOMODE /* | ADEV_HWMIX */ ; int porttype = DF_PCM; sprintf (tmp_name, "%s (rev %d)", devc->chip_name, devc->revision); if (i == 0) { caps |= ADEV_DUPLEX; } else { caps |= ADEV_NOINPUT; if (i > 1) caps |= ADEV_SHADOW; } #if 0 /* This will not work any more */ if ((devc->chip_type == ALI_5451_ID) && (devc->revision == ALI_5451_V02) && (i == 7)) { /* need to set SPDIF output in PCI southbridge chip 0x1533 */ while ((osdev = (pci_find_class (0x601 << 8, osdev)))) { unsigned short vendorid, deviceid; unsigned char temp; pci_read_config_word (osdev, PCI_VENDOR_ID, &vendorid); pci_read_config_word (osdev, PCI_DEVICE_ID, &deviceid); if (vendorid != 0x10b9 || deviceid != 0x1533) continue; pci_read_config_byte (osdev, 0x61, &temp); temp |= 0x40; pci_write_config_byte (osdev, 0x61, temp); pci_read_config_byte (osdev, 0x7d, &temp); temp |= 0x01; pci_write_config_byte (osdev, 0x7d, temp); pci_read_config_byte (osdev, 0x7e, &temp); temp &= (~0x20); temp |= 0x10; pci_write_config_byte (osdev, 0x7e, temp); /* SPDIF Magic!!! */ pci_read_config_byte (osdev, 0x63, &temp); temp |= 0x3; /* enable SPDIF OUT bit0=out bit1=in */ pci_write_config_byte (osdev, 0x63, temp); /* SPDIF Output in SERIAL Config reg */ temp = READL (devc->osdev, 0x48); WRITEB (devc->osdev, temp | 0x20, 0x48); temp = READL (devc->osdev, 0x74); WRITEB (devc->osdev, temp & 0xbf, 0x74); /* Enable SPDIF on play_channel 15 and setup as SPDIFOUT */ WRITEW (devc->osdev, READW (devc->osdev, 0xd4) | 0x8000, 0xd4); WRITEW (devc->osdev, READW (devc->osdev, 0xd4) & ~0x0400, 0xd4); porttype = DF_SPDIF; caps |= ADEV_SPECIAL | ADEV_FIXEDRATE; sprintf (tmp_name, "%s (S/PDIF Output)", devc->chip_name); } } #endif if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, devc->osdev, devc->osdev, tmp_name, &trident_audio_driver, sizeof (audiodrv_t), caps, AFMT_U8 | AFMT_S16_LE | AFMT_AC3, devc, -1)) < 0) { adev = -1; return 0; } else { if (i == 0) first_dev = adev; audio_engines[adev]->portc = portc; audio_engines[adev]->mixer_dev = my_mixer; audio_engines[adev]->rate_source = first_dev; audio_engines[adev]->min_rate = 8000; audio_engines[adev]->max_rate = 48000; audio_engines[adev]->caps |= PCM_CAP_FREERATE; #if !defined(sparc)
Only 31 bit memory addresses are supported except under Sparc where the on-board audio chip supports the top half of 32 bit PCI address space.
audio_engines[adev]->dmabuf_maxaddr = MEMLIMIT_31BITS; #endif portc->audiodev = adev; portc->open_mode = 0; portc->audio_enabled = 0; if (audio_engines[adev]->flags & ADEV_FIXEDRATE) { audio_engines[adev]->fixed_rate = 48000; audio_engines[adev]->min_rate = 48000; audio_engines[adev]->max_rate = 48000; } portc->port_type = porttype; portc->play_chan = 3 + (2 * i); portc->play_intr_chan = 2 + (2 * i); portc->pbank = BANK_A; portc->rec_chan = 1; portc->rec_intr_chan = 0; portc->rbank = BANK_A; if (devc->chip_type == ALI_5451_ID) { portc->rec_chan = 31; portc->rec_intr_chan = 30; portc->rbank = BANK_A; } if (devc->chip_type == SIS_7018_ID) { portc->pbank = BANK_B; portc->rec_chan = 1; portc->rec_intr_chan = 0; portc->rbank = BANK_B; WRITEL (devc->osdev, READL (devc->osdev, CIR) | 0x10000, CIR); } #ifdef CONFIG_OSS_VMIX if (i == 0) vmix_attach_audiodev(devc->osdev, adev, -1, 0); #endif } devc->audio_opened = 0; /* set the global control and enable end interrupt condition */ global_control = READL (devc->osdev, CIR); global_control |= (1 << 12); /* control end intr */ WRITEL (devc->osdev, global_control, CIR); } return 1; } int oss_trident_attach (oss_device_t * osdev) { unsigned char pci_irq_line, pci_revision /*, pci_latency */ ; unsigned short pci_command, vendor, device; unsigned int pci_ioaddr; trident_devc *devc; DDB (cmn_err (CE_WARN, "Entered Trident 4DWAVE probe routine\n")); oss_pci_byteswap (osdev, 1); pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor); pci_read_config_word (osdev, PCI_DEVICE_ID, &device); if ((vendor != TRIDENT_VENDOR_ID && vendor != ALI_VENDOR_ID && vendor != SIS_VENDOR_ID) || (device != TRIDENT_4DWAVEDX_ID && device != TRIDENT_4DWAVENX_ID && device != TRIDENT_4DWAVECX_ID && device != ALI_5451_ID && device != SIS_7018_ID)) 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->revision = pci_revision; devc->irq = pci_irq_line; switch (device) { case TRIDENT_4DWAVEDX_ID: devc->chip_name = "Trident 4DWAVEDX"; devc->chip_type = TRIDENT_4DWAVEDX_ID; break; case TRIDENT_4DWAVENX_ID: devc->chip_name = "Trident 4DWAVENX"; devc->chip_type = TRIDENT_4DWAVENX_ID; break; case TRIDENT_4DWAVECX_ID: devc->chip_name = "Trident 4DWAVECX"; devc->chip_type = TRIDENT_4DWAVECX_ID; break; case ALI_5451_ID: devc->chip_name = "ALI M5451"; devc->chip_type = ALI_5451_ID; break; case SIS_7018_ID: devc->chip_name = "SiS 7018"; devc->chip_type = SIS_7018_ID; break; default: devc->chip_name = "Trident 4DWAVE"; devc->chip_type = TRIDENT_4DWAVEDX_ID; } #ifdef MEM_MAPPED_REGISTERS pci_read_config_dword (osdev, PCI_MEM_BASE_ADDRESS_1, &devc->bar1addr); devc->bar1virt = (char *) MAP_PCI_MEM (devc->osdev, 1, devc->bar1addr, 1024 * 1024); pci_command |= PCI_COMMAND_MEMORY; #else devc->base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr); /* Remove I/O space marker in bit 0. */ devc->base &= ~0x03; pci_command |= PCI_COMMAND_IO; #endif devc->mpu_base = trident_mpu_ioaddr; devc->mpu_irq = devc->irq; /* activate the device */ pci_command |= PCI_COMMAND_MASTER; pci_write_config_word (osdev, PCI_COMMAND, pci_command); 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, tridentintr, NULL) < 0) { cmn_err (CE_WARN, "Can't allocate IRQ%d\n", pci_irq_line); return 0; } return init_trident (devc); /*Detected */ } int oss_trident_detach (oss_device_t * osdev) { trident_devc *devc = (trident_devc *) osdev->devc; if (oss_disable_device (osdev) < 0) return 0; WRITEL (devc->osdev, 0, CIR); if (devc->chip_type == TRIDENT_4DWAVENX_ID) WRITEL (devc->osdev, 0x0, SP_CSO); /*disable S/PDIF on NX */ #ifdef OBSOLETED_STUFF if (devc->mpu_attached) { unload_mpu (devc); devc->mpu_attached = 0; } #endif oss_unregister_interrupts (devc->osdev); MUTEX_CLEANUP (devc->mutex); MUTEX_CLEANUP (devc->low_mutex); #ifdef MEM_MAPPED_REGISTERS UNMAP_PCI_MEM(osdev, 1, devc->bar1addr, devc->bar1virt, 1024x1024); #else UNMAP_PCI_IOADDR (devc->osdev, 0); #endif oss_unregister_device (devc->osdev); return 1; }