diff options
Diffstat (limited to 'attic/drv/oss_vortex/oss_vortex.c')
-rw-r--r-- | attic/drv/oss_vortex/oss_vortex.c | 1749 |
1 files changed, 1749 insertions, 0 deletions
diff --git a/attic/drv/oss_vortex/oss_vortex.c b/attic/drv/oss_vortex/oss_vortex.c new file mode 100644 index 0000000..d8c1738 --- /dev/null +++ b/attic/drv/oss_vortex/oss_vortex.c @@ -0,0 +1,1749 @@ +/* + * Purpose: Driver for Aureal Semiconductor Vortex PCI audio controller. + */ +/* + * + * 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 "vortex.h" + +#undef USE_SRC + +#define AUREAL_VENDOR_ID 0x12eb +#define AUREAL_VORTEX 0x0001 +#define AUREAL_VORTEX2 0x0002 + +#define global_base (0x12800) +# define GIRQSTAT global_base+0x0 +# define MODIRQST 0x00004000 +# define MIDIRQST 0x00002000 +# define TIMIRQST 0x00001000 +# define COIRQLST 0x00000800 +# define COIRQPST 0x00000400 +# define FRCIRQST 0x00000200 +# define SBIRQST 0x00000100 +# define SBMIRQST 0x00000080 +# define FMIRQST 0x00000040 +# define DMAENDIRQST 0x00000020 +# define DMABERRST 0x00000010 +# define FIFOERRST 0x00000008 +# define REGERRST 0x00000004 +# define MPARERRST 0x00000002 +# define MFATERRST 0x00000001 +# define GIRQCTL global_base+0x4 +# define GSTAT global_base+0x8 +# define GCTL global_base+0xc +# define SFTRST 0x00800000 +# define ARBRST 0x00400000 +# define EXTRST 0x00200000 +# define RBTOEN 0x00100000 +# define AINCEN 0x00080000 +# define TTXFLUSH 0x00040000 +# define TRVFLUSH 0x00020000 +# define MFIFOFLUSH 0x00010000 +# define FRCIRQ 0x00008000 +# define GIRQEN 0x00004000 + +#define dma_base (0x10000) + +#define midi_base (0x11000) +# define MIDIDAT midi_base+0x0 +# define MIDICMD midi_base+0x4 +# define MIDISTAT MIDICMD +# define MIDIVAL 0x00000080 +# define CMDOK 0x00000040 +# define MPUMODE 0x00000001 +# define GAMECTL midi_base+0xc +# define JOYMODE 0x00000040 +# define MIDITXFULL 0x00000008 +# define MIDIRXINIT 0x00000004 +# define MIDIRXOVFL 0x00000002 +# define MIDIDATVAL 0x00000001 + +#define fifo_base (0xe000) +#define adb_destinations 103 +#define adbarb_block_base (0x10800) +#define serial_block_base (0x11800) +# define CODSMPLTMR serial_block_base+0x19c +#define adbarb_wtdram_base_addr (adbarb_block_base + 0x000) +#define adbarb_wtdram_srhdr_base_addr (adbarb_block_base + (adb_destinations * 4)) +#define adbarb_sr_active_addr (adbarb_block_base + 0x200) +#define adbarb_srb_last_addr (adbarb_block_base + 0x204) +#define codec_control_reg_addr (serial_block_base + 0x0184) +#define cmd_status_reg_addr (serial_block_base + 0x0188) +#define channel_enable_reg_addr (serial_block_base + 0x0190) +#define serial_ram_reg_addr (serial_block_base + 0x00) + +#define fifo_chan0_src_addr 0x00 +#define fifo_chan1_src_addr 0x01 +#define fifo_chan2_dst_addr 0x02 +#define fifo_chan2a_dst_addr 0x022 +#define fifo_chan3_dst_addr 0x03 +#define fifo_chan3a_dst_addr 0x023 +#define fifo_chan4_dst_addr 0x04 +#define fifo_chan5_dst_addr 0x05 +#define fifo_chan6_dst_addr 0x06 +#define fifo_chan7_dst_addr 0x07 +#define fifo_chan8_dst_addr 0x08 +#define fifo_chan9_dst_addr 0x09 +#define fifo_chana_dst_addr 0x0a +#define fifo_chanb_dst_addr 0x0b +#define src_chan0_src_addr 0x10 +#define src_chan1_src_addr 0x11 +#define src_chan0_dst_addr 0x10 +#define src_chan1_dst_addr 0x11 +#define codec_chan0_src_addr 0x48 +#define codec_chan1_src_addr 0x49 +#define codec_chan0_dst_addr 0x58 +#define codec_chan1_dst_addr 0x59 + +#define src_base (0xc000) +#define src_input_fifo (src_base + 0x000) +#define src_output_double_buffer (src_base + 0x800) +#define src_next_channel (src_base + 0xc00) +#define src_sr_header (src_base + 0xc40) +#define src_active_sample_rate (src_base + 0xcc0) +#define src_throttle_source (src_base + 0xcc4) +#define src_throttle_count_size (src_base + 0xcc8) +#define src_channel_params (src_base + 0xe00) + +/* static int dst_routed[256] = { 0 }; */ + + +static unsigned int +ReadReg (vortex_devc * devc, oss_native_word addr) +{ + return READL (addr); +} + +static void +WriteReg (vortex_devc * devc, oss_native_word addr, unsigned int data) +{ + WRITEL (addr, data); +} + +static void +ReadCodecRegister (vortex_devc * devc, int cIndex, int *pdwData) +{ + int dwCmdStRegAddr; + int dwCmdStRegData; + int nCnt = 0; + + dwCmdStRegAddr = cIndex << 16; + WriteReg (devc, cmd_status_reg_addr, dwCmdStRegAddr); + do + { + dwCmdStRegData = ReadReg (devc, cmd_status_reg_addr); + if (nCnt++ > 10) + oss_udelay (10); + } + while ((dwCmdStRegData & 0x00FF0000) != ((1 << 23) | (dwCmdStRegAddr)) + && (nCnt < 100)); + if (nCnt >= 100) + { + *pdwData = dwCmdStRegData; + cmn_err (CE_WARN, + "timeout waiting for Status Valid bit in the Command Status Reg, index %x\n", + cIndex); + } + else + *pdwData = dwCmdStRegData & 0x0000ffff; +} + +static void +WriteCodecCommand (vortex_devc * devc, int cIndex, int wData) +{ + int dwData; + int nCnt = 0; + + do + { + dwData = ReadReg (devc, codec_control_reg_addr); + if (nCnt++ > 10) + oss_udelay (100); + } + while (!(dwData & 0x0100) && (nCnt < 100)); + if (nCnt >= 100) + cmn_err (CE_WARN, "timeout waiting for Codec Command Write OK bit.\n"); + + + do + { + dwData = ReadReg (devc, codec_control_reg_addr); + if (nCnt++ > 1) + oss_udelay (100); + } + while (!(dwData & 0x0100) && (nCnt < 100)); + if (nCnt >= 100) + cmn_err (CE_WARN, "timeout waiting for Codec Command Write OK bit.\n"); + + dwData = (cIndex << 16) | (1 << 23) | wData; + WriteReg (devc, cmd_status_reg_addr, dwData); + oss_udelay (1000); + + /* Read it back to make sure it got there */ + do + { + dwData = ReadReg (devc, codec_control_reg_addr); + if (nCnt++ > 1) + oss_udelay (1000); + } + while (!(dwData & 0x0100) && (nCnt < 100)); + if (nCnt >= 100) + cmn_err (CE_WARN, "timeout waiting for Codec Command Write OK bit.\n"); + ReadCodecRegister (devc, cIndex, &dwData); + if (dwData != (int) wData) + { + do + { + dwData = ReadReg (devc, codec_control_reg_addr); + if (nCnt++ > 1) + oss_udelay (1000); + } + while (!(dwData & 0x0100) && (nCnt < 100)); + if (nCnt >= 1000) + cmn_err (CE_WARN, + "timeout waiting for Codec Command Write OK bit.\n"); + dwData = (cIndex << 16) | (1 << 23) | wData; + WriteReg (devc, cmd_status_reg_addr, dwData); + do + { + dwData = ReadReg (devc, codec_control_reg_addr); + if (nCnt++ > 1) + oss_udelay (100); + } + while (!(dwData & 0x0100) && (nCnt < 50)); + if (nCnt >= 10) + cmn_err (CE_WARN, + "timeout waiting for Codec Command Write OK bit.\n"); +#if 0 + ReadCodecRegister (devc, cIndex, &dwData); + if (dwData != (int) wData) + { + cmn_err (CE_WARN, + "Vortex ERROR: Write to index %x failed (exp %04x, got %04x)\n", + cIndex, wData, dwData); + } +#endif + } +} + +static int +ac97_read (void *devc_, int addr) +{ + vortex_devc *devc = devc_; + int data; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + ReadCodecRegister (devc, addr, &data); + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + return data & 0xffff; +} + +static int +ac97_write (void *devc_, int addr, int data) +{ + vortex_devc *devc = devc_; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + WriteCodecCommand (devc, addr, data); + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + return 0; +} + +static void +ClearDataFifo (vortex_devc * devc, int nChannel) +{ + int j; + /* Clear out FIFO data */ + for (j = 0; j < 32; j++) + WriteReg (devc, fifo_base + (0x80 * nChannel) + (0x4 * j), 0x0); +} + +static int +vortexintr (oss_device_t * osdev) +{ + vortex_devc *devc = (vortex_devc *) osdev->devc; + int status; + int i; + int serviced = 0; + + /* + * TODO: Fix mutexes and move the inputintr/outputintr calls outside the + * mutex block. + */ + /* MUTEX_ENTER (devc->mutex, flags); */ + status = ReadReg (devc, GIRQSTAT); + +#if 0 + if (status & MFATERRST) + cmn_err (CE_WARN, "Master fatal error interrupt\n"); + if (status & MPARERRST) + cmn_err (CE_WARN, "Master parity error interrupt\n"); +#endif + + if (status & TIMIRQST) /* Timer interrupt */ + { + ReadReg (devc, CODSMPLTMR); /* Clear the interrupt */ + WriteReg (devc, CODSMPLTMR, 0x00001000); + serviced = 1; + } + + if (status & DMAENDIRQST) /* DMA end interrupt */ + { + for (i = 0; i < MAX_PORTC; i++) + { + vortex_portc *portc = &devc->portc[i]; + + if (portc->trigger_bits & PCM_ENABLE_OUTPUT) + { + dmap_t *dmap = audio_engines[portc->audiodev]->dmap_out; + + int pos = 0, n; + unsigned int dmastat; + + dmastat = ReadReg (devc, dma_base + 0x5c0); + pos = ((dmastat >> 12) & 0x03) * 4096 + (dmastat & 4095); + + pos /= dmap->fragment_size - 1; + if (pos < 0 || pos >= dmap->nfrags) + pos = 0; + + n = 0; + while (dmap_get_qhead (dmap) != pos && n++ < dmap->nfrags) + oss_audio_outputintr (portc->audiodev, 0); + } + + if (portc->trigger_bits & PCM_ENABLE_INPUT) + { + dmap_t *dmap = audio_engines[portc->audiodev]->dmap_in; + + int pos = 0, n; + unsigned int dmastat; + + dmastat = ReadReg (devc, dma_base + 0x5c8); + + pos = ((dmastat >> 12) & 0x03) * 4096 + (dmastat & 4095); + + pos /= dmap->fragment_size; + if (pos < 0 || pos >= dmap->nfrags) + pos = 0; + + n = 0; + while (dmap_get_qtail (dmap) != pos && n++ < dmap->nfrags) + oss_audio_inputintr (portc->audiodev, 0); + } + ReadReg (devc, dma_base + 0x600); /* Read Dma Status0 */ + ReadReg (devc, dma_base + 0x604); /* Read Dma Status1 */ + ReadReg (devc, dma_base + 0x608); /* Read Dma Status2 */ + serviced = 1; + } + } + + if (status & MIDIRQST) /* MIDI interrupt */ + { + int uart_stat = ReadReg (devc, MIDISTAT); + int n = 10; + + while (!(uart_stat & MIDIVAL) && n--) + { + int d; + d = ReadReg (devc, MIDIDAT) & 0xff; + + if (devc->midi_opened & OPEN_READ && devc->midi_input_intr) + devc->midi_input_intr (devc->midi_dev, d); + uart_stat = ReadReg (devc, MIDISTAT); + } + serviced = 1; + } + if (status != 0) + { + WriteReg (devc, GIRQSTAT, status & 0x7ff); /* Ack pulse interrupts */ + serviced = 1; + } + + /* MUTEX_EXIT (devc->mutex, flags); */ + return serviced; +} + +static void +cold_reset (vortex_devc * devc) +{ + int i; + int bSigmatelCodec = 0; + + for (i = 0; i < 32; i = i + 1) + { + WriteReg (devc, serial_ram_reg_addr + 0x80 + (i * 4), 0); + oss_udelay (100); + } + if (bSigmatelCodec) + { + /* Disable clock */ + WriteReg (devc, codec_control_reg_addr, 0x00a8); + oss_udelay (100); + /* Set Sync High */ + /* WriteReg(devc, codec_control_reg_addr, 0x40a8); */ + /* delay(100); */ + /* Place CODEC into reset */ + WriteReg (devc, codec_control_reg_addr, 0x80a8); + oss_udelay (100); + /* Give CODEC some Clocks with reset asserted */ + WriteReg (devc, codec_control_reg_addr, 0x80e8); + oss_udelay (100); + /* Turn off clocks */ + WriteReg (devc, codec_control_reg_addr, 0x80a8); + oss_udelay (100); + /* Take out of reset */ + /* WriteReg(devc, codec_control_reg_addr, 0x40a8); */ + /* oss_udelay(100); */ + /* Release reset */ + WriteReg (devc, codec_control_reg_addr, 0x00a8); + oss_udelay (100); + /* Turn on clocks */ + WriteReg (devc, codec_control_reg_addr, 0x00e8); + oss_udelay (100); + } + else + { + WriteReg (devc, codec_control_reg_addr, 0x8068); + oss_udelay (10); + WriteReg (devc, codec_control_reg_addr, 0x00e8); + oss_udelay (10); + } +} + +static void +InitCodec (vortex_devc * devc) +{ + int i; + cold_reset (devc); + for (i = 0; i < 32; i = i + 1) + { + WriteReg (devc, serial_ram_reg_addr + 0x80 + (i * 4), 0); + oss_udelay (10); + } + /* Set up the codec in AC97 mode */ + WriteReg (devc, codec_control_reg_addr, 0x00e8); + oss_udelay (10); + /* Clear the channel enable register */ + WriteReg (devc, channel_enable_reg_addr, 0); +} + +static void +SetupCodec (vortex_devc * devc) +{ + int dwData; + int count = 0; + int dwBit26 = 1 << 26; + + /* do the following only for ac97 codecs */ + /* Wait for Codec Ready (bit 26) */ + do + { + dwData = ReadReg (devc, codec_control_reg_addr); + if (count++ > 1) + oss_udelay (10); + } + while ((count < 100) && !(dwData & dwBit26)); + if (count >= 100) + { + cmn_err (CE_WARN, "Error: timeout waiting for Codec Ready bit.\n"); + cmn_err (CE_WARN, "Codec Interface Control Register is %08x\n", dwData); + } + /* Write interesting data to the Codec 97 Mixer registers */ + /* Master Volume 0dB Attunuation, Not muted. */ + WriteCodecCommand (devc, 0x02, 0x0f0f); + oss_udelay (10); + /* Master Volume mono muted. */ + WriteCodecCommand (devc, 0x06, 0x8000); + oss_udelay (10); + /* Mic Volume muted. */ + WriteCodecCommand (devc, 0x0e, 0x8000); + oss_udelay (10); + /* Line In Volume muted. */ + WriteCodecCommand (devc, 0x10, 0x8000); + oss_udelay (10); + /* CD Volume muted. */ + WriteCodecCommand (devc, 0x12, 0x8000); + oss_udelay (10); + /* Aux Volume muted. */ + WriteCodecCommand (devc, 0x16, 0x8000); + oss_udelay (10); + /* PCM out Volume 0 dB Gain, Not muted. */ + WriteCodecCommand (devc, 0x18, 0x0f0f); + oss_udelay (10); + /* Record select, select Mic for recording */ + WriteCodecCommand (devc, 0x1a, 0x0404); + oss_udelay (10); + /* Record Gain, 0dB */ + WriteCodecCommand (devc, 0x1c, 0x8000); + oss_udelay (10); + + /* Poll the Section Ready bits in the Status Register (index 0x26) */ + count = 0; + do + { + ReadCodecRegister (devc, 0x26, &dwData); + if (count++ > 1) + oss_udelay (10); + } + while (!(dwData & 0x02) && (count < 10)); + if (!(dwData & 0x02)) + cmn_err (CE_WARN, "DAC section ready bit is not set.\n"); + + /* Read and confirm the data in the Codec 97 Mixer registers. */ + /* just the PCM reg, as a sanity check */ + ReadCodecRegister (devc, 0x18, &dwData); + if ((dwData & 0x0000ffff) != 0xf0f) + { + cmn_err (CE_WARN, "PCM volume reg is %x, sb 0xf0f.\n", dwData); + } +} + +static void +InitAdb (vortex_devc * devc) +{ +/* parameter values for write_op */ +#define op_none 0 /* dst_op = x */ +#define op_tail 1 /* dst_op = x */ +#define op_add 2 /* dst_op = dst_addr being added */ +#define op_adds 3 /* dst_op = dst_addr being added */ +#define op_del 4 /* dst_op = dst_addr being deleted */ +#define op_dels 5 /* dst_op = dst_addr being deleted */ +#define op_inval 6 /* dst_op = x */ + + int i; +#if 0 + unsigned char /*reg [3:0] */ write_op; + unsigned char /*reg [6:0] */ dst_op; + unsigned char /*reg [6:0] */ src_op; + unsigned char /*reg [SS:0] */ sr_op; +#endif + /* misc */ + + devc->sr_active = 0; + /* the initial tail for each list is the header location */ + for (i = 0; i <= 31; i = i + 1) + devc->tail_index[i] = adb_destinations + i; + for (i = 0; i <= 127; i = i + 1) + devc->dst_index[i] = 0x7f; /*~('b0); */ + for (i = 0; i <= 127; i = i + 1) + devc->sr_list[i] = 0x1f; /*~('b0); */ +#if 0 + write_op = op_none; + dst_op = 0x7f; /* ~('b0); */ + src_op = 0x7f; /* ~('b0); */ + sr_op = 0x1f; /* ~('b0); */ +#endif + /* Disable any active sample rate */ + WriteReg (devc, 0x10800 + 0x200, 0); + /* Null out all the linked lists */ + for (i = 0; i < 0x1f0; i = i + 4) + { + WriteReg (devc, 0x10800 + i, 0xffffffff); + } +} + +static void +SetBit (unsigned int owData[], int nBit, unsigned char cVal) +{ + if (cVal) + owData[nBit / 32] |= (1 << (nBit % 32)); + else + owData[nBit / 32] &= ~(1 << (nBit % 32)); +} + +/*ARGSUSED*/ +static int +add_route (vortex_devc * devc, unsigned int sr, + unsigned int src_addr, unsigned int dst_addr, int verify) +{ + + unsigned int ram_hdr_addr; + unsigned int ram_dst_addr; + unsigned int ram_tail_addr; + unsigned int sr_ram_index; + + sr_ram_index = adb_destinations + sr; + ram_hdr_addr = adbarb_wtdram_srhdr_base_addr + (sr * 4); /* VHRDxx[sr] */ + ram_dst_addr = adbarb_wtdram_base_addr + (dst_addr * 4); /* VDSTxx[dst_addr] */ + ram_tail_addr = adbarb_wtdram_base_addr + (devc->tail_index[sr] * 4); + + /* since always add to end of list, ram[dst_addr] will be the new tail. */ + /* and, since we could be adding to an active list, the tail needs */ + /* to be NULLed before the new link is inserted */ + /* (since we need to check the current tail next, devc->tail_index is */ + /* updated a bit later below.) */ + WriteReg (devc, ram_dst_addr, 0xffffffff); + + /* check if this sr has a list started yet */ + if (devc->tail_index[sr] == (adb_destinations + sr)) + { + /* current tail for this sample rate indicates that list is empty, */ + /* thus this route will be head of list */ + WriteReg (devc, ram_hdr_addr, ((src_addr << 7) | dst_addr)); + devc->dst_index[dst_addr] = sr_ram_index; + } + else + { + /* add to end of list */ + WriteReg (devc, ram_tail_addr, ((src_addr << 7) | dst_addr)); + devc->dst_index[dst_addr] = devc->tail_index[sr]; + } + + /* keep track of the new tail */ + devc->tail_index[sr] = dst_addr; + + /* keep track of which sample rate list this dst_addr now belongs to */ + devc->sr_list[dst_addr] = sr; + + /* mark dst_addr as routed */ + /* dst_routed[dst_addr] = 1; */ + SetBit ((unsigned int *) &devc->dst_routed, dst_addr, 1); + + return 0; +} + +/*ARGSUSED*/ +static void +del_route (vortex_devc * devc, unsigned char dst_addr, int verify) +{ + + unsigned int data, ram_dst_addr, ram_rtd_addr; + unsigned char sr; + + ram_dst_addr = adbarb_wtdram_base_addr + (dst_addr * 4); + ram_rtd_addr = adbarb_wtdram_base_addr + (devc->dst_index[dst_addr] * 4); + + /* get the sr list that this dst_addr belongs to */ + sr = devc->sr_list[dst_addr]; + + /* mark dst as no longer routed */ + SetBit ((unsigned int *) &devc->dst_routed, dst_addr, 0); + + /* remove the dst from the sr_list then check to see if this was the */ + /* last route in the list. if so, disable wtd for this sample rate */ + devc->sr_list[dst_addr] = 0x1f; /*~('b0); */ + + /* find out what's linked to us so we can reroute it. if we are the */ + /* tail, this will be NULL and will get relinked as such */ + data = ReadReg (devc, ram_dst_addr); + + /* now update the list by writing what was linked to us to where we */ + /* were once at in the list */ + WriteReg (devc, ram_rtd_addr, data); + + /* update the devc->dst_index for this reroute */ + /* devc->dst_index[data[6:0]] = devc->dst_index[dst_addr]; */ + devc->dst_index[data & 0x7f] = devc->dst_index[dst_addr]; + + /* Invalidate the now deleted route. Data for dst_addr = 7e; */ + /* NOTE: the adbarb_monitor needs this write to track properly! */ + WriteReg (devc, ram_dst_addr, 0xfffffffe); + + /* if we are removing the tail, reset the tail pointer */ + if (devc->tail_index[sr] == dst_addr) + { + devc->tail_index[sr] = devc->dst_index[dst_addr]; + } + + /* clean up all data elements used to track this dst_addr */ + /* XXX check field size below */ + devc->dst_index[dst_addr] = 0x7f; /* ~('b0); */ +} + +static void +SetupRoutes (vortex_devc * devc) +{ + /* First add the record routes */ + add_route (devc, 17, codec_chan0_src_addr, fifo_chan2_dst_addr, 0); + add_route (devc, 17, codec_chan1_src_addr, fifo_chan3_dst_addr, 0); + add_route (devc, 17, codec_chan0_src_addr, fifo_chan2a_dst_addr, 0); + add_route (devc, 17, codec_chan1_src_addr, fifo_chan3a_dst_addr, 0); + + /* Now add the playback routes */ + add_route (devc, 17, fifo_chan0_src_addr, codec_chan0_dst_addr, 0); + add_route (devc, 17, fifo_chan0_src_addr, codec_chan1_dst_addr, 0); +} + +static void +EnableCodecChannel (vortex_devc * devc, unsigned char channel) +{ + unsigned int data; + + data = ReadReg (devc, channel_enable_reg_addr); + data = data | (1 << (8 + channel)); /*(1'b1 << (8+channel)); */ + WriteReg (devc, channel_enable_reg_addr, data); +} + +static void +DisableCodecChannel (vortex_devc * devc, unsigned char channel) +{ + unsigned int data; + + data = ReadReg (devc, channel_enable_reg_addr); + data = data & ~(1 << (8 + channel)); /*~(1'b1 << (8+channel)); */ + WriteReg (devc, channel_enable_reg_addr, data); +} + +static void +EnableAdbWtd (vortex_devc * devc, int sr) +{ + unsigned int dwData; + + dwData = ReadReg (devc, adbarb_sr_active_addr); + dwData |= (1 << sr); + WriteReg (devc, adbarb_sr_active_addr, dwData); + devc->sr_active |= (1 << sr); +} + +static void +DisableAdbWtd (vortex_devc * devc, int sr) +{ + unsigned int dwData; + + dwData = ReadReg (devc, adbarb_sr_active_addr); + dwData &= ~(1 << sr); + WriteReg (devc, adbarb_sr_active_addr, dwData); + devc->sr_active &= ~(1 << sr); +} + +/*ARGSUSED*/ +static int +vortex_midi_open (int dev, int mode, oss_midi_inputbyte_t inputbyte, + oss_midi_inputbuf_t inputbuf, + oss_midi_outputintr_t outputintr) +{ + vortex_devc *devc = (vortex_devc *) midi_devs[dev]->devc; + + if (devc->midi_opened) + { + return OSS_EBUSY; + } + + devc->midi_input_intr = inputbyte; + devc->midi_opened = mode; + + if (mode & OPEN_READ) + { + int tmp = ReadReg (devc, GIRQCTL); + WriteReg (devc, GIRQCTL, tmp | MIDIRQST); /* Enable MIDI interrupts */ + tmp = ReadReg (devc, GIRQCTL); + } + + WriteReg (devc, MIDICMD, 0x000000ff); /* Reset MIDI */ + WriteReg (devc, MIDICMD, 0x0000003f); /* Enter UART mode */ + if ((ReadReg (devc, MIDIDAT) & 0xff) != 0xfe) + cmn_err (CE_WARN, "Vortex warning! MIDI init not acknowledged\n"); + return 0; +} + +/*ARGSUSED*/ +static void +vortex_midi_close (int dev, int mode) +{ + vortex_devc *devc = (vortex_devc *) midi_devs[dev]->devc; + + int tmp = ReadReg (devc, GIRQCTL); + WriteReg (devc, GIRQCTL, tmp & ~MIDIRQST); /* Disable MIDI interrupts */ + WriteReg (devc, MIDICMD, 0x000000ff); /* Reset MIDI */ + + devc->midi_opened = 0; +} + +static int +vortex_midi_out (int dev, unsigned char midi_byte) +{ + int n = 10; + vortex_devc *devc = (vortex_devc *) midi_devs[dev]->devc; + + while ((ReadReg (devc, MIDISTAT) & CMDOK) && n--); + if (ReadReg (devc, MIDISTAT) & CMDOK) + return 0; + WriteReg (devc, MIDIDAT, midi_byte); + return 1; +} + +/*ARGSUSED*/ +static int +vortex_midi_ioctl (int dev, unsigned cmd, ioctl_arg arg) +{ + return OSS_EINVAL; +} + +static midi_driver_t vortex_midi_driver = { + vortex_midi_open, + vortex_midi_close, + vortex_midi_ioctl, + vortex_midi_out +}; + +static void +vortex_midi_init (vortex_devc * devc) +{ + /* Derive the MIDI baud rate from 49.152 MHz clock */ + WriteReg (devc, GAMECTL, 0x00006100); + WriteReg (devc, MIDICMD, 0x000000ff); /* Reset MIDI */ + WriteReg (devc, MIDICMD, 0x0000003f); /* Enter UART mode */ + + /* All commands should return 0xfe as an acknowledgement */ + if ((ReadReg (devc, MIDIDAT) & 0xff) != 0xfe) + cmn_err (CE_WARN, "Vortex warning! MIDI init not acknowledged\n"); + WriteReg (devc, MIDICMD, 0x000000ff); /* Reset MIDI */ +} + +/*********************************** + * Audio routines + ***********************************/ + +static int +vortex_set_rate (int dev, int arg) +{ + vortex_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 +vortex_set_channels (int dev, short arg) +{ + vortex_portc *portc = audio_engines[dev]->portc; + + if (audio_engines[dev]->flags & ADEV_STEREOONLY) + arg = 2; + + if ((arg != 1) && (arg != 2)) + return portc->channels; + portc->channels = arg; + + return portc->channels; +} + +static unsigned int +vortex_set_format (int dev, unsigned int arg) +{ + vortex_portc *portc = audio_engines[dev]->portc; + + if (audio_engines[dev]->flags & ADEV_16BITONLY) + arg = 16; + + if (!(arg & (AFMT_U8 | AFMT_S16_LE))) + return portc->bits; + portc->bits = arg; + + return portc->bits; +} + +/*ARGSUSED*/ +static int +vortex_ioctl (int dev, unsigned int cmd, ioctl_arg arg) +{ + return OSS_EINVAL; +} + +#ifdef USE_SRC +static void cleanup_src (int dev); +#endif + +static void vortex_trigger (int dev, int state); + +static void +vortex_reset (int dev) +{ + vortex_trigger (dev, 0); + +#ifdef USE_SRC + cleanup_src (dev); + del_route (devc, src_chan0_dst_addr, 0); + del_route (devc, src_chan1_dst_addr, 0); +#endif +} + +static void +vortex_reset_input (int dev) +{ + vortex_portc *portc = audio_engines[dev]->portc; + vortex_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT); +} + +static void +vortex_reset_output (int dev) +{ + vortex_portc *portc = audio_engines[dev]->portc; + vortex_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT); +} + +/*ARGSUSED*/ +static int +vortex_open (int dev, int mode, int open_flags) +{ + oss_native_word flags; + vortex_portc *portc = audio_engines[dev]->portc; + vortex_devc *devc = audio_engines[dev]->devc; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + if (portc->open_mode != 0) + { + 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 +vortex_close (int dev, int mode) +{ + vortex_portc *portc = audio_engines[dev]->portc; + vortex_devc *devc = audio_engines[dev]->devc; + + vortex_reset (dev); + portc->open_mode = 0; + devc->open_mode &= ~mode; + portc->audio_enabled &= ~mode; +} + +/* + * InitFifos + */ +static void +init_fifos (vortex_devc * devc) +{ + int i; + + /* Zero Out the FIFO Pointers */ + for (i = 0; i < 48; i++) + { + WriteReg (devc, fifo_base + 0x1800 + (4 * i), 0x00000020); + } + for (i = 2; i < 6; i++) + { + WriteReg (devc, fifo_base + 0x1800 + (4 * i), 0x00000000); + } + WriteReg (devc, fifo_base + 0x18c0, 0x00000843); + /* Clear out FIFO data */ + for (i = 0; i < 4; i++) + ClearDataFifo (devc, i); + + /* Set up the DMA engine to grab DMA memory */ + WriteReg (devc, dma_base + 0x61c, 0); /* Clear Dma Status0 */ + WriteReg (devc, dma_base + 0x620, 0); /* Clear Dma Status1 */ + WriteReg (devc, dma_base + 0x624, 0); /* Clear Dma Status2 */ +} + +#ifdef USE_SRC +static void +setup_src (int dev) +{ + vortex_portc *portc = audio_engines[dev]->portc; + vortex_devc *devc = audio_engines[dev]->devc; + + int i, j, chn; + + for (j = 0; j < 2; j++) + { + unsigned int tmp, ratio, link; + chn = portc->voice_chn + j; + + for (i = 0; i < 128; i += 4) + WriteReg (devc, src_input_fifo + (128 * chn) + i, 0); + for (i = 0; i < 8; i += 4) + WriteReg (devc, src_output_double_buffer + (chn * 8) + i, 0); + + ratio = 48000 / portc->speed; + tmp = 0; + tmp |= chn & 0xf; /* Correlated channel */ + if (ratio > 4) + tmp |= ((17 - ratio - 1) << 4); + else + tmp |= (12 << 4); /* Zero crossing */ + WriteReg (devc, src_channel_params + 0xe00 + 4 * chn, tmp); /* [0] */ + + ratio = (48000 << 14) / portc->speed; + WriteReg (devc, src_channel_params + 0xe40 * 4 * chn, ratio); /* [1] */ + + WriteReg (devc, src_channel_params + 0xe80 + 4 * chn, 0); /* [2] */ + WriteReg (devc, src_channel_params + 0xec0 + 4 * chn, 0); /* [3] */ + WriteReg (devc, src_channel_params + 0xf00 + 4 * chn, 0); /* [4] */ + WriteReg (devc, src_channel_params + 0xf40 + 4 * chn, 1); /* [5] */ + + ratio = 48000 / portc->speed; + tmp = 0x3000f; /* Throttle in, FIFO depth=15 */ + WriteReg (devc, src_channel_params + 0xf80 + 4 * chn, tmp); /* [6] */ + + link = ReadReg (devc, src_sr_header + 0); + WriteReg (devc, src_next_channel + chn * 4, link); + WriteReg (devc, src_sr_header + 0, 0x10 | chn); + + link = ReadReg (devc, src_throttle_source); + link |= (1 << chn); + WriteReg (devc, src_throttle_source, link); + + link = ReadReg (devc, src_active_sample_rate); + link |= (1 << 0); + WriteReg (devc, src_active_sample_rate, link); + + } +} + +static void +cleanup_src (int dev) +{ + vortex_portc *portc = audio_engines[dev]->portc; + vortex_devc *devc = audio_engines[dev]->devc; + + int i, j, chn; + + for (j = 0; j < 2; j++) + { + chn = portc->voice_chn + j; + + for (i = 0; i < 128; i += 4) + WriteReg (devc, src_input_fifo + (128 * chn) + i, 0); + for (i = 0; i < 8; i += 4) + WriteReg (devc, src_output_double_buffer + (chn * 8) + i, 0); + WriteReg (devc, src_next_channel + chn * 4, 0); + WriteReg (devc, src_sr_header + 0, 0); + + WriteReg (devc, src_active_sample_rate, 0); + + } +} +#endif + +/*ARGSUSED*/ +static void +vortex_output_block (int dev, oss_native_word buf, int count, int fragsize, + int intrflag) +{ + vortex_portc *portc = audio_engines[dev]->portc; + + portc->audio_enabled |= PCM_ENABLE_OUTPUT; + portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; +} + +/*ARGSUSED*/ +static void +vortex_start_input (int dev, oss_native_word buf, int count, int fragsize, + int intrflag) +{ + vortex_portc *portc = audio_engines[dev]->portc; + + portc->audio_enabled |= PCM_ENABLE_INPUT; + portc->trigger_bits &= ~PCM_ENABLE_INPUT; +} + +static void +vortex_trigger (int dev, int state) +{ + vortex_devc *devc = audio_engines[dev]->devc; + vortex_portc *portc = audio_engines[dev]->portc; + unsigned int fifo_mode; + 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)) + { + /* Start the FIFOs */ + fifo_mode = 0x30030; + if (portc->channels == 2) + { + fifo_mode |= 0x00000002; + WriteReg (devc, fifo_base + 0x1800, fifo_mode); /* Left Pb */ + } + else + { + WriteReg (devc, fifo_base + 0x1800, fifo_mode); /* Left Pb */ + WriteReg (devc, fifo_base + 0x1804, fifo_mode); /* Right Pb */ + } + + WriteReg (devc, CODSMPLTMR, 0x1000); /* start timer */ + portc->trigger_bits |= PCM_ENABLE_OUTPUT; + } + } + else + { + if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) && + (portc->trigger_bits & PCM_ENABLE_OUTPUT)) + { + portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; + portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; + + WriteReg (devc, CODSMPLTMR, 0x0); /* stop timer */ + WriteReg (devc, fifo_base + 0x1800, 0); /* Left Playback */ + WriteReg (devc, fifo_base + 0x1804, 0); /* Right Playback */ + + ClearDataFifo (devc, 0); + ClearDataFifo (devc, 1); + } + } + } + if (portc->open_mode & OPEN_READ) + { + if (state & PCM_ENABLE_INPUT) + { + if ((portc->audio_enabled & PCM_ENABLE_INPUT) && + !(portc->trigger_bits & PCM_ENABLE_INPUT)) + { + /* Start the FIFOs */ + fifo_mode = 0x30010; + if (portc->channels == 2) + fifo_mode |= 0x00000002; + WriteReg (devc, fifo_base + 0x1808, fifo_mode); /* Left Rec */ + WriteReg (devc, fifo_base + 0x180c, fifo_mode); /* Right Rec */ + WriteReg (devc, CODSMPLTMR, 0x1000); /* start timer */ + portc->trigger_bits |= PCM_ENABLE_INPUT; + } + } + else + { + if ((portc->audio_enabled & PCM_ENABLE_INPUT) && + (portc->trigger_bits & PCM_ENABLE_INPUT)) + { + portc->trigger_bits &= ~PCM_ENABLE_INPUT; + portc->audio_enabled &= ~PCM_ENABLE_INPUT; + + WriteReg (devc, CODSMPLTMR, 0x0); /* stop timer */ + + WriteReg (devc, fifo_base + 0x1808, 0); /* Left Record */ + WriteReg (devc, fifo_base + 0x180c, 0); /* Right Record */ + + ClearDataFifo (devc, 2); + ClearDataFifo (devc, 3); + } + } + } + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); +} + +/*ARGSUSED*/ +static int +vortex_prepare_for_input (int dev, int bsize, int bcount) +{ + unsigned int nBufSize, ChSizeGotoReg0, ChSizeGotoReg1, ch_mode; + dmap_t *dmap = audio_engines[dev]->dmap_in; + vortex_devc *devc = audio_engines[dev]->devc; + vortex_portc *portc = audio_engines[dev]->portc; + oss_native_word flags; + int SAMPLES = 1024; + + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + +#ifdef USE_SRC + setup_src (dev); + add_route (devc, 17, src_chan0_src_addr, codec_chan0_dst_addr, 0); + add_route (devc, 17, src_chan1_src_addr, codec_chan1_dst_addr, 0); + + add_route (devc, 0, fifo_chan0_src_addr, src_chan0_dst_addr, 0); + if (portc->channels == 2) + add_route (devc, 0, fifo_chan0_src_addr, src_chan1_dst_addr, 0); + else + add_route (devc, 0, fifo_chan1_src_addr, src_chan1_dst_addr, 0); +#endif + + nBufSize = (SAMPLES * 2 * 2) - 1; + + ch_mode = 0x00001000; + + switch (portc->bits) + { + case AFMT_U8: + ch_mode |= 0x00004000; + break; + + case AFMT_S16_LE: + ch_mode |= 0x00020000; + break; + + case AFMT_MU_LAW: + ch_mode |= 0x00008000; + break; + + case AFMT_A_LAW: + ch_mode |= 0x0000c000; + break; + + } + + /* Left Record Channel VDB Chan 2 */ + WriteReg (devc, dma_base + 0x220, dmap->dmabuf_phys); /* Set Chan0 Address */ + WriteReg (devc, dma_base + 0x224, dmap->dmabuf_phys + 4096); /* Set Chan0 Address */ + WriteReg (devc, dma_base + 0x228, dmap->dmabuf_phys + 2 * 4096); /* Set Chan0 Address */ + WriteReg (devc, dma_base + 0x22c, dmap->dmabuf_phys + 3 * 4096); /* Set Chan0 Address */ + + ChSizeGotoReg0 = (0xde000000) | (nBufSize << 12) | (nBufSize); + ChSizeGotoReg1 = (0xfc000000) | (nBufSize << 12) | (nBufSize); + WriteReg (devc, dma_base + 0x410, ChSizeGotoReg0); /* Set Chan0 Size to SAMPLES*2*2, Loop over 0 */ + WriteReg (devc, dma_base + 0x414, ChSizeGotoReg1); /* Set Chan0 Size to SAMPLES*2*2, Loop over 0 */ + WriteReg (devc, dma_base + 0x588, ch_mode); /* Set Chan0 Mode */ + + /* Right Record Channel VDB Chan 3 */ + WriteReg (devc, dma_base + 0x230, dmap->dmabuf_phys); /* Set Chan1 Address */ + WriteReg (devc, dma_base + 0x234, dmap->dmabuf_phys + 4096); /* Set Chan0 Address */ + WriteReg (devc, dma_base + 0x238, dmap->dmabuf_phys + 2 * 4096); /* Set Chan0 Address */ + WriteReg (devc, dma_base + 0x23c, dmap->dmabuf_phys + 3 * 4096); /* Set Chan0 Address */ + + ChSizeGotoReg0 = (0x56000000) | (nBufSize << 12) | (nBufSize); + ChSizeGotoReg1 = (0x74000000) | (nBufSize << 12) | (nBufSize); + WriteReg (devc, dma_base + 0x418, ChSizeGotoReg0); + WriteReg (devc, dma_base + 0x41c, ChSizeGotoReg1); + WriteReg (devc, dma_base + 0x58c, ch_mode); /* Set Chan1 Mode */ + + portc->audio_enabled &= ~PCM_ENABLE_INPUT; + portc->trigger_bits &= ~PCM_ENABLE_INPUT; + + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return 0; +} + +/*ARGSUSED*/ +static int +vortex_prepare_for_output (int dev, int bsize, int bcount) +{ + unsigned int nBufSize, ChSizeGotoReg0, ChSizeGotoReg1, ch_mode; + int SAMPLES = 1024; + dmap_t *dmap = audio_engines[dev]->dmap_out; + vortex_devc *devc = audio_engines[dev]->devc; + vortex_portc *portc = audio_engines[dev]->portc; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + +#ifdef USE_SRC + setup_src (dev); + add_route (devc, 17, src_chan0_src_addr, codec_chan0_dst_addr, 0); + add_route (devc, 17, src_chan1_src_addr, codec_chan1_dst_addr, 0); + + add_route (devc, 0, fifo_chan0_src_addr, src_chan0_dst_addr, 0); + if (portc->channels == 2) + add_route (devc, 0, fifo_chan0_src_addr, src_chan1_dst_addr, 0); + else + add_route (devc, 0, fifo_chan1_src_addr, src_chan1_dst_addr, 0); +#endif + + nBufSize = (SAMPLES * 2 * 2) - 1; + + ch_mode = 0x00003000; + + switch (portc->bits) + { + case AFMT_U8: + ch_mode |= 0x00004000; + break; + + case AFMT_S16_LE: + ch_mode |= 0x00020000; + break; + + case AFMT_MU_LAW: + ch_mode |= 0x00008000; + break; + + case AFMT_A_LAW: + ch_mode |= 0x0000c000; + break; + + } + + /* Left Playback Channel VDB Chan 0 */ + WriteReg (devc, dma_base + 0x200, dmap->dmabuf_phys); /* Set Chan0 Address */ + WriteReg (devc, dma_base + 0x204, dmap->dmabuf_phys + 4096); /* Set Chan0 Address */ + WriteReg (devc, dma_base + 0x208, dmap->dmabuf_phys + 2 * 4096); /* Set Chan0 Address */ + WriteReg (devc, dma_base + 0x20c, dmap->dmabuf_phys + 3 * 4096); /* Set Chan0 Address */ + + ChSizeGotoReg0 = (0xde000000) | (nBufSize << 12) | (nBufSize); + ChSizeGotoReg1 = (0xfc000000) | (nBufSize << 12) | (nBufSize); + WriteReg (devc, dma_base + 0x400, ChSizeGotoReg0); /* Set Chan0 Size to SAMPLES*2*2, Loop over 0 */ + WriteReg (devc, dma_base + 0x404, ChSizeGotoReg1); /* Set Chan0 Size to SAMPLES*2*2, Loop over 0 */ + WriteReg (devc, dma_base + 0x580, ch_mode); /* Set Chan0 Mode */ + + /* Right Playback Channel VDB Chan 1 */ + WriteReg (devc, dma_base + 0x210, dmap->dmabuf_phys); /* Set Chan1 Address */ + WriteReg (devc, dma_base + 0x214, dmap->dmabuf_phys + 4096); /* Set Chan1 Address */ + WriteReg (devc, dma_base + 0x218, dmap->dmabuf_phys + 2 * 4096); /* Set Chan1 Address */ + WriteReg (devc, dma_base + 0x21c, dmap->dmabuf_phys + 3 * 4096); /* Set Chan1 Address */ + + ChSizeGotoReg0 = (0x56000000) | (nBufSize << 12) | (nBufSize); + ChSizeGotoReg1 = (0x74000000) | (nBufSize << 12) | (nBufSize); + WriteReg (devc, dma_base + 0x408, ChSizeGotoReg0); + WriteReg (devc, dma_base + 0x40c, ChSizeGotoReg1); + + WriteReg (devc, dma_base + 0x584, ch_mode); /* Set Chan1 Mode */ + + portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; + portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; + + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return 0; +} + +/*ARGSUSED*/ +static int +vortex_free_buffer (int dev, dmap_t * dmap, int direction) +{ + vortex_devc *devc = audio_engines[dev]->devc; + + if (dmap->dmabuf == NULL) + return 0; +#if 1 + CONTIG_FREE (devc->osdev, dmap->dmabuf, dmap->buffsize, TODO); +#ifdef linux + oss_unreserve_pages ((oss_native_word) dmap->dmabuf, + (oss_native_word) dmap->dmabuf + 4 * 4096 - 1); +#endif +#else + dmap->buffsize += 4096; /* Return the stolen page back */ + oss_free_dmabuf (dev, dmap); +#endif + + dmap->dmabuf = NULL; + return 0; +} + +/*ARGSUSED*/ +static int +vortex_alloc_buffer (int dev, dmap_t * dmap, int direction) +{ + vortex_devc *devc = audio_engines[dev]->devc; + oss_native_word phaddr; + + if (dmap->dmabuf != NULL) + return 0; +#if 1 + dmap->buffsize = 4 * 4096; /* 4 subbuffers */ + dmap->dmabuf = + CONTIG_MALLOC (devc->osdev, dmap->buffsize, MEMLIMIT_32BITS, &phaddr, TODO); + dmap->dmabuf_phys = phaddr; +#ifdef linux + oss_reserve_pages ((oss_native_word) dmap->dmabuf, + (oss_native_word) dmap->dmabuf + 4 * 4096 - 1); +#endif +#else + if ((err = oss_alloc_dmabuf (dev, dmap, direction)) < 0) + return err; + + /* Reserve the last page for internal use (channel buffers) */ + p = dmap->buffsize - 4096; + dmap->buffsize = p; + + if (dmap->buffsize < (4 * 1024)) + { + cmn_err (CE_WARN, "Allocated DMA buffer smaller than 8k\n"); + vortex_free_buffer (dev, dmap, direction); + return OSS_ENOSPC; + } +#endif + + return 0; +} + +/*ARGSUSED*/ +static int +vortex_get_buffer_pointer (int dev, dmap_t * dmap, int direction) +{ + vortex_devc *devc = audio_engines[dev]->devc; + oss_native_word flags, dmastat = 0; + int ptr = 0; + + MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); + if (direction == PCM_ENABLE_OUTPUT) + { + dmastat = ReadReg (devc, dma_base + 0x5c0); + } + if (direction == PCM_ENABLE_INPUT) + { + dmastat = ReadReg (devc, dma_base + 0x5c8); + } + ptr = ((dmastat >> 12) & 0x03) * 4096 + (dmastat & 4095); + MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); + return ptr; +} + +static audiodrv_t vortex_driver = { + vortex_open, + vortex_close, + vortex_output_block, + vortex_start_input, + vortex_ioctl, + vortex_prepare_for_input, + vortex_prepare_for_output, + vortex_reset, + NULL, + NULL, + vortex_reset_input, + vortex_reset_output, + vortex_trigger, + vortex_set_rate, + vortex_set_format, + vortex_set_channels, + NULL, + NULL, + NULL, /* vortex_check_input, */ + NULL, /* vortex_check_output, */ + vortex_alloc_buffer, + vortex_free_buffer, + NULL, + NULL, + vortex_get_buffer_pointer +}; + +static void +attach_channel (vortex_devc * devc, int my_mixer) +{ + int adev, i; + int first_dev = 0; + + for (i = 0; i < MAX_PORTC; i++) + { + char tmp_name[100]; + vortex_portc *portc = &devc->portc[i]; + int caps = + ADEV_FIXEDRATE | ADEV_AUTOMODE | ADEV_STEREOONLY | ADEV_16BITONLY; + + if (i == 0) + { + sprintf (tmp_name, "Aureal Vortex (%s)", devc->name); + caps |= ADEV_DUPLEX; + } + else + { + sprintf (tmp_name, "Aureal Vortex (%s) (shadow)", devc->name); + caps |= ADEV_DUPLEX | ADEV_SHADOW; + } + if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, + devc->osdev, + devc->osdev, + tmp_name, + &vortex_driver, + sizeof (audiodrv_t), + caps, + AFMT_U8 | AFMT_S16_LE, devc, -1)) < 0) + { + adev = -1; + return; + } + else + { + if (i == 0) + first_dev = adev; + audio_engines[adev]->portc = portc; + audio_engines[adev]->vmix_flags = VMIX_MULTIFRAG; + audio_engines[adev]->rate_source = first_dev; + audio_engines[adev]->fixed_rate = 48000; + audio_engines[adev]->min_rate = 48000; + audio_engines[adev]->max_rate = 48000; +#if 0 + audio_engines[adev]->min_block = 4096; + audio_engines[adev]->max_block = 4096; +#endif + audio_engines[adev]->mixer_dev = my_mixer; + portc->voice_chn = 0; + portc->open_mode = 0; + portc->audiodev = adev; + portc->audio_enabled = 0; +#ifdef CONFIG_OSS_VMIX + if (i == 0) + vmix_attach_audiodev(devc->osdev, adev, -1, 0); +#endif + } + } + return; +} + +#ifdef USE_SRC +static void +init_src (vortex_devc * devc) +{ + int i; + + for (i = 0; i < 0x800; i += 4) + WriteReg (devc, src_input_fifo + i, 0); + for (i = 0; i < 0x080; i += 4) + WriteReg (devc, src_output_double_buffer + i, 0); + for (i = 0; i < 0x040; i += 4) + WriteReg (devc, src_next_channel + i, 0); + for (i = 0; i < 0x054; i += 4) + WriteReg (devc, src_sr_header + i, 0); + + WriteReg (devc, src_active_sample_rate, 0); + WriteReg (devc, src_throttle_source, 0); + WriteReg (devc, src_throttle_count_size, 511); + + for (i = 0; i < 0x1bf; i += 4) + WriteReg (devc, src_channel_params + i, 0); +} +#endif + +int init_vortex2 (vortex_devc * devc, int is_mx300); +int vortex2intr (oss_device_t * osdev); + +static int +init_vortex (vortex_devc * devc) +{ + int my_mixer; + int i; + +/* + * Reset Vortex + */ + WriteReg (devc, GCTL, 0xffffffff); + oss_udelay (1000); + + InitCodec (devc); + SetupCodec (devc); + InitAdb (devc); + EnableCodecChannel (devc, 0); + EnableCodecChannel (devc, 1); + init_fifos (devc); + EnableAdbWtd (devc, 17); + SetupRoutes (devc); +#ifdef USE_SRC + init_src (devc); +#endif + + /* + * DMA controller memory is supposed to contain 0xdeadbeef after + * reset. + */ + if (ReadReg (devc, 0x10000 + 0x7c0) != 0xdeadbeef) + cmn_err (CE_WARN, + "DMA memory check returned unexpected result %08x\n", + ReadReg (devc, 0x10000 + 0x7c0)); + + my_mixer = ac97_install (&devc->ac97devc, "AC97 Mixer", ac97_read, + ac97_write, devc, devc->osdev); + if (my_mixer >= 0) + devc->mixer_dev = my_mixer; + else + return 0; + + WriteReg (devc, GCTL, ReadReg (devc, GCTL) | GIRQEN); /* Enable IRQ */ + WriteReg (devc, CODSMPLTMR, 0x0); + WriteReg (devc, GIRQCTL, DMAENDIRQST | DMABERRST | TIMIRQST); + + for (i = 0; i < 2; i++) + devc->dst_routed[i] = 0; + + /* Turn on vortex serial interface outputs to codec */ + EnableCodecChannel (devc, 0); + EnableCodecChannel (devc, 1); + + /* Zero Out the FIFO Pointers */ + for (i = 0; i < 48; i++) + { + WriteReg (devc, fifo_base + 0x1800 + (4 * i), 0x00000020); + } + for (i = 2; i < 6; i++) + { + WriteReg (devc, fifo_base + 0x1800 + (4 * i), 0x00000000); + } + WriteReg (devc, fifo_base + 0x18c0, 0x00000843); + /* Clear out FIFO data */ + for (i = 0; i < 4; i++) + ClearDataFifo (devc, i); + + /* Set up the DMA engine to grab DMA memory */ + WriteReg (devc, dma_base + 0x61c, 0); /* Clear Dma Status0 */ + WriteReg (devc, dma_base + 0x620, 0); /* Clear Dma Status1 */ + WriteReg (devc, dma_base + 0x624, 0); /* Clear Dma Status2 */ + + attach_channel (devc, my_mixer); + + devc->midi_dev = oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "VORTEX", + "Aureal Vortex UART", + &vortex_midi_driver, + sizeof (midi_driver_t), + /* &std_midi_synth, */ NULL, + 0, devc, devc->osdev); + devc->midi_opened = 0; + vortex_midi_init (devc); + + return 1; +} + +int +oss_vortex_attach (oss_device_t * osdev) +{ + unsigned char pci_irq_line, pci_revision /*, pci_latency */ ; + unsigned short pci_command, vendor, device; + unsigned short subsystemid, subvendor; + int is_mx300 = 0; + vortex_devc *devc; + + DDB (cmn_err (CE_WARN, "Entered Aureal Vortex 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 != AUREAL_VENDOR_ID || (device != AUREAL_VORTEX && + device != AUREAL_VORTEX2)) + return 0; + + pci_read_config_word (osdev, PCI_COMMAND, &pci_command); + pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision); + pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line); + + pci_read_config_word (osdev, PCI_SUBSYSTEM_ID, &subsystemid); + pci_read_config_word (osdev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor); + + 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; + + pci_read_config_dword (osdev, PCI_MEM_BASE_ADDRESS_0, &devc->bar0addr); + + if (device == AUREAL_VORTEX2) + { + devc->name = "Aureal Vortex AU8830"; + devc->id = AUREAL_VORTEX2; + devc->bar0_size = 256 * 1024; + } + else + { + devc->name = "Aureal Vortex AU8820"; + devc->id = AUREAL_VORTEX; + devc->bar0_size = 128 * 1024; + } + + pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY); + pci_write_config_word (osdev, PCI_COMMAND, pci_command); + + if (subvendor == 0x1092 && subsystemid == 0x3001) + is_mx300 = 1; + + /* Prgram IORDY for VIA chipset failure */ + pci_write_config_byte (osdev, 0x40, 0xff); + + /* Map the shared memory area */ + devc->bar0virt = + (unsigned int *) MAP_PCI_MEM (devc->osdev, 0, devc->bar0addr, + devc->bar0_size); + devc->dwRegister = devc->bar0virt; + + /* Disable all interrupts */ + WriteReg (devc, GIRQCTL, 0x00000000); + + oss_register_device (osdev, devc->name); + + if (devc->id == AUREAL_VORTEX) + if (oss_register_interrupts (devc->osdev, 0, vortexintr, NULL) < 0) + { + cmn_err (CE_WARN, "Can't allocate IRQ%d\n", pci_irq_line); + return 0; + } + + if (devc->id == AUREAL_VORTEX2) + if (oss_register_interrupts (devc->osdev, 0, vortex2intr, NULL) < 0) + { + cmn_err (CE_WARN, "Can't allocate IRQ%d\n", pci_irq_line); + return 0; + } + + + MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV); + MUTEX_INIT (devc->osdev, devc->low_mutex, MH_DRV + 1); + + if (devc->id == AUREAL_VORTEX2) + return init_vortex2 (devc, is_mx300); + else + return init_vortex (devc); /* Detected */ +} + + +extern void unload_vortex2 (oss_device_t * osdev); + +int +oss_vortex_detach (oss_device_t * osdev) +{ + vortex_devc *devc = (vortex_devc *) osdev->devc; + + if (oss_disable_device (osdev) < 0) + return 0; + + if (devc->id == AUREAL_VORTEX2) + unload_vortex2 (osdev); + else + { + DisableCodecChannel (devc, 0); + DisableCodecChannel (devc, 1); + DisableAdbWtd (devc, 17); + + /* Disable routes */ + del_route (devc, codec_chan0_dst_addr, 0); + del_route (devc, codec_chan1_dst_addr, 0); + del_route (devc, fifo_chan2_dst_addr, 0); + del_route (devc, fifo_chan3_dst_addr, 0); + del_route (devc, fifo_chan2a_dst_addr, 0); + del_route (devc, fifo_chan3a_dst_addr, 0); + + /* Disable all interrupts */ + WriteReg (devc, GIRQCTL, 0x00000000); + } + + oss_unregister_interrupts (devc->osdev); + + MUTEX_CLEANUP (devc->mutex); + MUTEX_CLEANUP (devc->low_mutex); + + UNMAP_PCI_MEM (devc->osdev, 0, devc->bar0addr, devc->bar0virt, + devc->bar0_size); + + oss_unregister_device (devc->osdev); + return 1; +} |