Open Sound System |
Do you have problems with sound/audio application development? Don't panic! Click here for help! |
The audio input and output devices implemented by this driver use additional layer of buffering for channel re-interleaving. The device itself uses 10/12 channel interleaved 32 bit format in hardware level. The re-interleaving engine splits these multi channel devices to several "stereo" devices.
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_envy24_cfg.h" #include <ac97.h> #include <oss_pci.h> #include "envy24.h" extern int envy24_skipdevs; extern int envy24_force_mono; extern int envy24_gain_sliders; int envy24_virtualout = 0; /* This used to be an config option */ extern int envy24_devmask; #define DMASK_ANALOGOUT 1 #define DMASK_ANALOGIN 2 #define DMASK_SPDIFOUT 4 #define DMASK_SPDIFIN 8 #define DMASK_MONITORIN 16 #define DMASK_RAWDEVS 32 extern envy24_auxdrv_t default_auxdrv; extern envy24_auxdrv_t ap2496_auxdrv; extern envy24_auxdrv_t d410_auxdrv; extern envy24_auxdrv_t d1010lt_auxdrv; extern envy24_auxdrv_t tdif_auxdrv; extern envy24_auxdrv_t ewx2496_auxdrv; extern envy24_auxdrv_t ews88d_auxdrv; extern envy24_auxdrv_t dmx6fire_auxdrv; static card_spec models[] = { {0xd6301412, "M Audio Delta 1010", 8, 8, MF_MAUDIO | MF_MIDI1 | MF_SPDIF | MF_WCLOCK | MF_MEEPROM}, {0xd6311412, "M Audio Delta DiO 2496", 2, 0, MF_MAUDIO | MF_SPDIF | MF_SPDSELECT | MF_MEEPROM}, {0xd6321412, "M Audio Delta 66", 4, 4, MF_MAUDIO | MF_SPDIF | MF_AKMCODEC | MF_MEEPROM}, {0xd6331412, "M Audio Delta 44", 4, 4, MF_MAUDIO | MF_AKMCODEC | MF_MEEPROM}, {0xd6341412, "M Audio Audiophile 2496", 2, 2, MF_AP | MF_SPDIF | MF_MIDI1 | MF_MEEPROM, &ap2496_auxdrv}, {0xd6381412, "M Audio Delta 410", 8, 2, MF_D410 | MF_SPDIF | MF_MEEPROM, &d410_auxdrv}, /* Delta 1010 rev E is based on 1010LT instead of the original 1010 design */ {0xd63014ff, "M Audio Delta 1010 rev E", 8, 8, MF_MIDI1 | MF_SPDIF | MF_MEEPROM | MF_WCLOCK, &d1010lt_auxdrv}, {0xd63b1412, "M Audio Delta 1010LT", 8, 8, MF_MIDI1 | MF_SPDIF | MF_MEEPROM | MF_WCLOCK, &d1010lt_auxdrv}, {0xd6351412, "M Audio Delta TDIF", 8, 8, MF_SPDIF | MF_MEEPROM | MF_WCLOCK, &tdif_auxdrv}, {0x1115153b, "Terratec EWS88MT", 8, 8, MF_MIDI1 | MF_SPDIF | MF_EWS88 | MF_AC97}, {0x112b153b, "Terratec EWS88D", 8, 8, MF_MIDI1 | MF_MIDI2 | MF_SPDIF | MF_AC97 | MF_WCLOCK, &ews88d_auxdrv}, {0x1130153b, "Terratec EWX 24/96", 2, 2, MF_SPDIF | MF_EWX2496, &ewx2496_auxdrv}, {0x1138153b, "Terratec DMX6fire 24/96", 6, 6, MF_MIDI1 | MF_MIDI2 | MF_SPDIF, &dmx6fire_auxdrv}, {0x17121412, "Generic Envy24 based card", 8, 8, MF_SPDIF | MF_MIDI1 | MF_CONSUMER | MF_HOONTECH}, {0} }; static struct speed_sel speed_tab[] = { { 8000, 0x06} , { 9600, 0x03} , { 11025, 0x0a} , { 12000, 0x02} , { 16000, 0x05} , { 22050, 0x09} , { 24000, 0x01} , { 32000, 0x04} , { 44100, 0x08} , { 48000, 0x00} , /* {64000, 0x0f}, doesn't work */ { 88200, 0x0b} , { 96000, 0x07} , { -1, 0x10} , }; int envy24_read_cci (envy24_devc * devc, int pos) { OUTB (devc->osdev, pos, devc->ccs_base + 0x03); return INB (devc->osdev, devc->ccs_base + 0x04); } void envy24_write_cci (envy24_devc * devc, int pos, int data) { OUTB (devc->osdev, pos, devc->ccs_base + 0x03); OUTB (devc->osdev, data, devc->ccs_base + 0x04); } static int eeprom_read (envy24_devc * devc, int pos) { int i, status; for (i = 0; i < 0x10000; i++) { status = INB (devc->osdev, devc->ccs_base + 0x13); if (!(status & 1)) break; } OUTB (devc->osdev, 0xa0, devc->ccs_base + 0x10); /* EEPROM read */ OUTB (devc->osdev, pos, devc->ccs_base + 0x11); /* Offset */ for (i = 0; i < 2000; i++) { status = INB (devc->osdev, devc->ccs_base + 0x13); if (!(status & 1)) break; } oss_udelay (1); return INB (devc->osdev, devc->ccs_base + 0x12); } static int load_eeprom (envy24_devc * devc, int subid) { int status, i, check; status = INB (devc->osdev, devc->ccs_base + 0x13); if (!(status & 0x80)) return 0; /* No EEPROM */ for (i = 0; i < 32; i++) { devc->eeprom[i] = eeprom_read (devc, i); devc->eeprom[i] = eeprom_read (devc, i); } DDB (cmn_err (CE_CONT, "EEPROM=")); for (i = 0; i < 10; i++) DDB (cmn_err (CE_CONT, "0x%02x, ", devc->eeprom[i])); DDB (cmn_err (CE_CONT, "\n")); check = 0; for (i = 0; i < 4; i++) { check <<= 8; check |= devc->eeprom[i]; } if (check != subid) cmn_err (CE_CONT, "Envy24 WARNING: Possible EEPROM read error %08x != %08x\n", check, subid); return 1; } static void handle_playdev (envy24_devc * devc, envy24_portc * portc, int this_frag) { int sample, nsamples, nbytes, ch; dmap_t *dmap = audio_engines[portc->dev]->dmap_out; if (!(portc->trigger_bits & PCM_ENABLE_OUTPUT) && devc->playback_started) return; nsamples = devc->hw_fragsamples; /* Number of 32 bit samples */ nbytes = nsamples * portc->channels; if (audio_engines[portc->dev]->dmap_out->flags & DMAP_POST) { if (portc->pcm_qlen > 0) portc->pcm_qlen--; } else { if (portc->pcm_qlen < devc->writeahead) portc->pcm_qlen++; } if (portc->bits & (AFMT_S16_LE | AFMT_S16_BE | AFMT_AC3)) nbytes *= 2; else if (portc-> bits & (AFMT_S32_LE | AFMT_S32_BE | AFMT_S24_LE | AFMT_S24_BE)) nbytes *= 4; if (nbytes != dmap->fragment_size) return; /* Fragment size mismatch */ switch (portc->bits) { case AFMT_U8: { unsigned char *ip; int *op; ip = audio_engines[portc->dev]->dmap_out->dmabuf; ip += (dmap_get_qhead (dmap) * dmap->fragment_size); op = (int *) (devc->playbuf + devc->hw_pfragsize * this_frag); VMEM_CHECK (ip, nsamples * sizeof (*ip)); VMEM_CHECK (op, nsamples * sizeof (*op)); for (sample = 0; sample < nsamples; sample++) { int *p = &op[sample * 10 + portc->chnum]; for (ch = 0; ch < portc->channels; ch++) { *p++ = (*ip++ ^ 0x80) << 24; } } } break; case AFMT_AC3: case AFMT_S16_LE: { short *ip; int *op; #ifdef DO_TIMINGS oss_timing_printf ("Envy24: Copy out %d, %d", dmap_get_qhead (dmap) * dmap->fragment_size, nbytes); #endif ip = (short *) (dmap->dmabuf + (dmap_get_qhead (dmap) * dmap->fragment_size)); op = (int *) (devc->playbuf + devc->hw_pfragsize * this_frag); VMEM_CHECK (ip, nsamples * sizeof (*ip)); VMEM_CHECK (op, nsamples * sizeof (*op)); for (sample = 0; sample < nsamples; sample++) { int *p = &op[sample * 10 + portc->chnum]; for (ch = 0; ch < portc->channels; ch++) { *p++ = *ip++ << 16; } } } break; case AFMT_S16_BE: { short *ip; int *op; ip = (short *) (audio_engines[portc->dev]->dmap_out->dmabuf + (dmap_get_qhead (dmap) * dmap->fragment_size)); op = (int *) (devc->playbuf + devc->hw_pfragsize * this_frag); VMEM_CHECK (ip, nsamples * sizeof (*ip)); VMEM_CHECK (op, nsamples * sizeof (*op)); for (sample = 0; sample < nsamples; sample++) { int *p = &op[sample * 10 + portc->chnum]; for (ch = 0; ch < portc->channels; ch++) { short s = (short) (((*(unsigned short *) ip & 0xff) << 8) | ((*(unsigned short *) ip & 0xff00) >> 8)); ip++; *p++ = s << 16; } } } break; case AFMT_S24_LE: { int *ip; int *op; ip = (int *) (audio_engines[portc->dev]->dmap_out->dmabuf + (dmap_get_qhead (dmap) * dmap->fragment_size)); op = (int *) (devc->playbuf + devc->hw_pfragsize * this_frag); VMEM_CHECK (ip, nsamples * sizeof (*ip)); VMEM_CHECK (op, nsamples * sizeof (*op)); for (sample = 0; sample < nsamples; sample++) { int *p = &op[sample * 10 + portc->chnum]; for (ch = 0; ch < portc->channels; ch++) { *p++ = *ip++ << 8; } } } break; case AFMT_S32_LE: { int *ip; int *op; ip = (int *) (audio_engines[portc->dev]->dmap_out->dmabuf + (dmap_get_qhead (dmap) * dmap->fragment_size)); op = (int *) (devc->playbuf + devc->hw_pfragsize * this_frag); VMEM_CHECK (ip, nsamples * sizeof (*ip)); VMEM_CHECK (op, nsamples * sizeof (*op)); for (sample = 0; sample < nsamples; sample++) { int *p = &op[sample * 10 + portc->chnum]; for (ch = 0; ch < portc->channels; ch++) { *p++ = *ip++; } } } break; } oss_audio_outputintr (portc->dev, 1); } #ifdef DO_RIAA static __inline__ int32_t _riaa_sat31 (register int32_t a, register int32_t b) { register int64_t v = (((int64_t) a) * b) + (1 << 30); return (int32_t) (v >> 31); } #endif static void handle_recdev (envy24_devc * devc, envy24_portc * portc) { int sample, nsamples, nbytes, ch; dmap_t *dmap = audio_engines[portc->dev]->dmap_in; if (portc->trigger_bits == 0 && devc->recording_started) return; nsamples = devc->hw_fragsamples; /* Number of 32 bit samples */ nbytes = nsamples * portc->channels; if (portc->bits & (AFMT_S16_LE | AFMT_S16_BE | AFMT_AC3)) nbytes *= 2; else if (portc->bits & (AFMT_S32_LE | AFMT_S24_LE)) nbytes *= 4; if (nbytes != dmap->fragment_size) { return; /* Fragment size mismatch */ } switch (portc->bits) { case AFMT_U8: { unsigned char *ip; int *op; ip = audio_engines[portc->dev]->dmap_in->dmabuf; ip += (dmap_get_qtail (dmap) * dmap->fragment_size); op = (int *) (devc->recbuf + devc->hw_rfragsize * devc->hw_recfrag); VMEM_CHECK (ip, nsamples * sizeof (*ip)); VMEM_CHECK (op, nsamples * sizeof (*op)); for (sample = 0; sample < nsamples; sample++) { int *p = &op[sample * 12 + portc->chnum]; for (ch = 0; ch < portc->channels; ch++) { *ip++ = ((*p++) >> 24) ^ 0x80; } } } break; case AFMT_S16_LE: #ifdef DO_RIAA if (portc->riaa_filter) { /* RIAA filtered version */ short *ip; int *op; ip = (short *) (audio_engines[portc->dev]->dmap_in->dmabuf + (dmap_get_qtail (dmap) * dmap->fragment_size)); op = (int *) (devc->recbuf + devc->hw_rfragsize * devc->hw_recfrag); VMEM_CHECK (ip, nsamples * sizeof (*ip)); VMEM_CHECK (op, nsamples * sizeof (*op)); for (ch = 0; ch < portc->channels; ch++) { int *p = &op[portc->chnum + ch]; short *p2 = &ip[ch]; riaa_t *ff = &portc->riaa_parms[ch]; int32_t x1 = ff->x1, x2 = ff->x2, x3 = ff->x3, y1 = ff->y1, y2 = ff->y2, y3 = ff->y3, x0, y0; for (sample = 0; sample < nsamples; sample++) { int tmp = *p; p += 12; x0 = _riaa_sat31 (tmp, 0x4C30C30C); y0 = _riaa_sat31 (x0, 0xF38FB92F) + _riaa_sat31 (x1, 0xF2492994) + _riaa_sat31 (x2, 0x1AB82385) + _riaa_sat31 (x3, 0x023FB0F8) + (_riaa_sat31 (y1, 0x574DB88C) << 1) + _riaa_sat31 (y2, 0xF650F27D) + _riaa_sat31 (y3, 0xDACB84B9); x3 = x2; x2 = x1; x1 = x0; y3 = y2; y2 = y1; y1 = y0; tmp = -y0; *p2 = tmp >> 16; p2 += portc->channels; } ff->x1 = x1; ff->x2 = x2; ff->x3 = x3; ff->y1 = y1; ff->y2 = y2; ff->y3 = y3; } /* RIAA filtered version */ } else #endif { short *ip; int *op; ip = (short *) (audio_engines[portc->dev]->dmap_in->dmabuf + (dmap_get_qtail (dmap) * dmap->fragment_size)); op = (int *) (devc->recbuf + devc->hw_rfragsize * devc->hw_recfrag); VMEM_CHECK (ip, nsamples * sizeof (*ip)); VMEM_CHECK (op, nsamples * sizeof (*op)); for (sample = 0; sample < nsamples; sample++) { int *p = &op[sample * 12 + portc->chnum]; for (ch = 0; ch < portc->channels; ch++) { *ip++ = (*p++) >> 16; } } } break; case AFMT_S32_LE: { int *ip; int *op; ip = (int *) (audio_engines[portc->dev]->dmap_in->dmabuf + (dmap_get_qtail (dmap) * dmap->fragment_size)); op = (int *) (devc->recbuf + devc->hw_rfragsize * devc->hw_recfrag); VMEM_CHECK (ip, nsamples * sizeof (*ip)); VMEM_CHECK (op, nsamples * sizeof (*op)); for (sample = 0; sample < nsamples; sample++) { int *p = &op[sample * 12 + portc->chnum]; for (ch = 0; ch < portc->channels; ch++) { *ip++ = *p++; } } } break; case AFMT_S24_LE: { int *ip; int *op; ip = (int *) (audio_engines[portc->dev]->dmap_in->dmabuf + (dmap_get_qtail (dmap) * dmap->fragment_size)); op = (int *) (devc->recbuf + devc->hw_rfragsize * devc->hw_recfrag); VMEM_CHECK (ip, nsamples * sizeof (*ip)); VMEM_CHECK (op, nsamples * sizeof (*op)); for (sample = 0; sample < nsamples; sample++) { int *p = &op[sample * 12 + portc->chnum]; for (ch = 0; ch < portc->channels; ch++) { *ip++ = *p++ >> 8; } } } break; } oss_audio_inputintr (portc->dev, 0); } static void tank_playback_data (envy24_devc * devc) { int i, nc = devc->nr_outdevs; envy24_portc *portc; unsigned char *p; p = devc->playbuf + devc->hw_playfrag * devc->hw_pfragsize; VMEM_CHECK (p, devc->hw_pfragsize); memset (p, 0, devc->hw_pfragsize); /* Cleanup the fragment */ for (i = 0; i < nc; i++) { portc = &devc->play_portc[i]; if (!portc->open_mode) /* Not opened */ continue; handle_playdev (devc, portc, devc->hw_playfrag); } devc->hw_playfrag = (devc->hw_playfrag + 1) % devc->hw_nfrags; } static void handle_recording (envy24_devc * devc) { int i; envy24_portc *portc; /* oss_native_word flags; */
TODO: Fix mutexes and move the inputintr/outputintr calls outside the mutex block.
/* MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); */ for (i = 0; i < devc->nr_indevs; i++) { portc = &devc->rec_portc[i]; if (!portc->open_mode) /* Not opened */ continue; handle_recdev (devc, portc); } devc->hw_recfrag = (devc->hw_recfrag + 1) % devc->hw_nfrags; /* MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); */ } extern int envy24d_get_buffer_pointer (int dev, dmap_t * dmap, int direction); static void mt_audio_intr (envy24_devc * devc) { int status; #ifdef DO_TIMINGS oss_timing_enter (DF_INTERRUPT); oss_do_timing2 (DFLAG_PROFILE, "Envy24_audio_intr"); #endif status = INB (devc->osdev, devc->mt_base + 0x00); if (devc->playback_started && (status & 0x01)) /* Playback interrupt */ { /* cmn_err(CE_CONT, "%d\n", GET_JIFFIES()); */ if (devc->direct_audio_opened & OPEN_WRITE) { envy24d_playintr (devc); } else { int ptr, qlen, i; ptr = INW (devc->osdev, devc->mt_base + 0x14); ptr = (devc->playbuffsize - ((ptr + 1) * 4)) / devc->hw_pfragsize; /* Find the number of current fragments in the hardware level buffer */ qlen = 0; i = devc->hw_playfrag; while (qlen < 15 && i != ptr) { qlen++; i = (i + 1) % devc->hw_nfrags; } if (qlen != devc->writeahead) { tank_playback_data (devc); } if (devc->hw_playfrag == ptr) /* Out of sync */ { tank_playback_data (devc); /* Try to catch the hardware pointer */ } tank_playback_data (devc); } } if (devc->recording_started && (status & 0x02)) /* Record interrupt */ { if (devc->direct_audio_opened & OPEN_READ) envy24d_recintr (devc); else handle_recording (devc); } OUTB (devc->osdev, status, devc->mt_base + 0x00); #ifdef DO_TIMINGS oss_timing_leave (DF_INTERRUPT); oss_do_timing2 (DFLAG_PROFILE, "Envy24_audio_intr done"); #endif } static int envy24intr (oss_device_t * osdev) { int status; envy24_devc *devc; devc = osdev->devc; status = INB (devc->osdev, devc->ccs_base + 0x02); if (status == 0) return 0; if (status & 0x80) /* MIDI UART 1 */ if (devc->model_data->flags & MF_MIDI1) uart401_irq (&devc->uart401devc1); if (status & 0x20) /* MIDI UART 2 */ if (devc->model_data->flags & MF_MIDI2) uart401_irq (&devc->uart401devc2); if (status & 0x10) { /*cmn_err(CE_CONT, "%d/%d.", GET_JIFFIES(), envy24d_get_buffer_pointer(11, audio_engines[11]->dmap_out, DMODE_OUTPUT)); */ mt_audio_intr (devc); } OUTB (devc->osdev, status, devc->ccs_base + 0x02); /* ACK */ return 1; } static void envy24_setup_pro_speed (envy24_devc * devc); static void envy24_setup_consumer_speed (envy24_devc * devc); void envy24_prepare_play_engine (envy24_devc * devc) { int tmp, fragsize, buffsize; if (devc->playback_prepared) return; /* Set S/PDIF sample rate indication */ if (devc->spdif_cbits[0] & 0x01) envy24_setup_pro_speed (devc); else envy24_setup_consumer_speed (devc); if (devc->model_data->flags & MF_SPDIF) { tmp = 0x80; if (devc->ac3_mode) tmp |= 0x40; /* Audio mode off */ switch (devc->speed) { case 48000: tmp |= 0x01; break; case 44100: tmp |= 0x02; break; case 32000: tmp |= 0x03; break; } if (devc->model_data->auxdrv->spdif_set) devc->model_data->auxdrv->spdif_set (devc, tmp); } if (devc->model_data->auxdrv->set_rate) devc->model_data->auxdrv->set_rate (devc); else { tmp = devc->speedbits; if (devc->syncsource != SYNC_INTERNAL) { tmp |= 0x10; /* S/PDIF input clock select */ if (devc->model_data->flags & MF_WCLOCK) /* Has world clock too */ { int cmd = envy24_read_cci (devc, 0x20); cmd |= 0x10; /* S/PDIF */ if (devc->syncsource == SYNC_WCLOCK) cmd &= ~0x10; /* World clock */ envy24_write_cci (devc, 0x20, cmd); } } OUTB (devc->osdev, tmp, devc->mt_base + 0x01); } fragsize = devc->hw_pfragsize; buffsize = devc->playbuffsize / 4 - 1; PMEM_CHECK (devc->playbuf_phys, devc->playbuffsize); OUTL (devc->osdev, devc->playbuf_phys, devc->mt_base + 0x10); /* Base */ OUTW (devc->osdev, buffsize, devc->mt_base + 0x14); /* Count */ OUTL (devc->osdev, devc->playbuf_phys, devc->mt_base + 0x10); /* Base */ OUTW (devc->osdev, buffsize, devc->mt_base + 0x14); /* Count */ OUTL (devc->osdev, devc->playbuf_phys, devc->mt_base + 0x10); /* Base */ OUTW (devc->osdev, fragsize / 4 - 1, devc->mt_base + 0x16); /* Interrupt rate */ OUTL (devc->osdev, devc->playbuf_phys, devc->mt_base + 0x10); /* Base */ devc->playback_prepared = 1; mixer_devs[devc->mixer_dev]->modify_counter++; } void envy24_launch_play_engine (envy24_devc * devc) { /* Unmask playback interrupts */ OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x00) & ~0x40, devc->mt_base + 0x00); OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x00) & ~0x40, devc->mt_base + 0x00); /* Kick it */ OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) | 0x01, devc->mt_base + 0x18); devc->playback_started = 1; if (devc->model_data->auxdrv->set_rate) devc->model_data->auxdrv->set_rate (devc); } static void start_playback (envy24_devc * devc) { devc->hw_playfrag = 0; #ifdef DO_TIMINGS oss_do_timing ("Envy24: Start playback"); #endif tank_playback_data (devc); tank_playback_data (devc); if (devc->writeahead == 2) tank_playback_data (devc); envy24_prepare_play_engine (devc); envy24_launch_play_engine (devc); } void envy24_stop_playback (envy24_devc * devc) { #ifdef DO_TIMINGS oss_do_timing ("Envy24: Stop playback"); #endif memset (devc->playbuf, 0, devc->playbuffsize);
Give the engine time to eat some silent samples This makes the corresponding digital mixer inputs to drop to 0 which decreases noise in the monitor outputs.
OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) & ~0x01, devc->mt_base + 0x18); OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) & ~0x01, devc->mt_base + 0x18); /* Mask playback interrupts */ OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x00) | 0x40, devc->mt_base + 0x00); devc->playback_started = 0; devc->playback_prepared = 0; } void envy24_start_recording (envy24_devc * devc) { int tmp; devc->hw_recfrag = 0; OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) & ~0x04, devc->mt_base + 0x18); OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) & ~0x04, devc->mt_base + 0x18); oss_udelay (20); if (devc->model_data->flags & MF_SPDIF) { tmp = 0x80; switch (devc->speed) { case 48000: tmp |= 0x01; break; case 44100: tmp |= 0x02; break; case 32000: tmp |= 0x03; break; } if (devc->model_data->auxdrv->spdif_set) devc->model_data->auxdrv->spdif_set (devc, tmp); } tmp = devc->speedbits; if (devc->syncsource != SYNC_INTERNAL) { tmp |= 0x10; /* S/PDIF input clock select */ if (devc->model_data->flags & MF_WCLOCK) /* Has world clock too */ { int cmd = envy24_read_cci (devc, 0x20); cmd |= 0x10; /* S/PDIF */ if (devc->syncsource == SYNC_WCLOCK) cmd &= ~0x10; /* World clock */ envy24_write_cci (devc, 0x20, cmd); } } OUTB (devc->osdev, tmp, devc->mt_base + 0x01); if (devc->model_data->auxdrv->set_rate) devc->model_data->auxdrv->set_rate (devc); PMEM_CHECK (devc->recbuf_phys, devc->recbuffsize); OUTL (devc->osdev, devc->recbuf_phys, devc->mt_base + 0x20); /* Base */ oss_udelay (20); OUTL (devc->osdev, devc->recbuf_phys, devc->mt_base + 0x20); /* Base */ oss_udelay (20); OUTW (devc->osdev, devc->recbuffsize / 4 - 1, devc->mt_base + 0x24); /* Count */ OUTL (devc->osdev, devc->recbuf_phys, devc->mt_base + 0x20); /* Base */ oss_udelay (60); OUTW (devc->osdev, devc->hw_rfragsize / 4 - 1, devc->mt_base + 0x26); /* Interrupt rate */ oss_udelay (60); } void envy24_launch_recording (envy24_devc * devc) { #if 1 /* Unmask recording interrupts */ OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x00) & ~0x80, devc->mt_base + 0x00); #endif /* Kick it */ OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) | 0x04, devc->mt_base + 0x18); devc->recording_started = 1; mixer_devs[devc->mixer_dev]->modify_counter++; } void envy24_stop_recording (envy24_devc * devc) { OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) & ~0x04, devc->mt_base + 0x18); OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) & ~0x04, devc->mt_base + 0x18); /* Mask recording interrupts */ OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x00) | 0x80, devc->mt_base + 0x00); devc->recording_started = 0; memset (devc->recbuf, 0, devc->recbuffsize); }
Audio entrypoint routines
int envy24_audio_set_rate (int dev, int arg) { envy24_devc *devc = audio_engines[dev]->devc; #if 1 int i = 0, ix = -1, df, best = 0x7fffffff; oss_native_word flags; if (arg <= 0) return devc->speed; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (devc->recording_started || devc->playback_started) { DDB (cmn_err (CE_CONT, "Requested sampling rate(1) on device %d was %d, got %d\n", dev, arg, devc->speed)); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return devc->speed; } if ((devc->open_inputs + devc->open_outputs) > 1) { DDB (cmn_err (CE_CONT, "Requested sampling rate(2) on device %d was %d, got %d\n", dev, arg, devc->speed)); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return devc->speed; } if (devc->ratelock) { DDB (cmn_err (CE_CONT, "Requested sampling rate(3) on device %d was %d, got %d\n", dev, arg, devc->speed)); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return devc->speed; } /* This is the only open device file so change the speed */ i = 0; while (speed_tab[i].speed != -1) { df = arg - speed_tab[i].speed; if (df < 0) df = -df; if (df < best) { best = df; ix = i; if (df == 0) break; } i++; } if (ix == -1) /* No matching rate */ { DDB (cmn_err (CE_CONT, "Requested sampling rate(4) on device %d was %d, got %d\n", dev, arg, devc->speed)); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return devc->speed; } devc->speed = speed_tab[ix].speed; devc->speedbits = speed_tab[ix].speedbits; #endif if (devc->speed != arg) { DDB (cmn_err (CE_CONT, "Requested sampling rate(5) on device %d was %d, got %d\n", dev, arg, devc->speed)); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return devc->speed; } DDB (cmn_err (CE_CONT, "Sampling rate set to %d\n", devc->speed)); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return devc->speed; } static void update_fragments (envy24_portc * portc) { int nsamples, nbytes, dev = portc->dev; envy24_devc *devc = audio_engines[dev]->devc; nsamples = devc->hw_fragsamples; /* Number of 32 bit samples */ nbytes = nsamples * portc->channels; if (portc->bits & (AFMT_S16_LE | AFMT_S16_BE | AFMT_AC3)) { nbytes *= 2; } else if (portc->bits & (AFMT_S32_LE | AFMT_S24_LE)) nbytes *= 4; audio_engines[dev]->min_block = nbytes; audio_engines[dev]->max_block = nbytes; } static short envy24_audio_set_channels (int dev, short arg) { envy24_portc *portc = audio_engines[dev]->portc; envy24_devc *devc = audio_engines[dev]->devc; int i, nc = devc->nr_play_channels; oss_native_word flags; if (envy24_virtualout) nc = 10; if (arg <= portc->channels) return portc->channels; /* Force mono->stereo conversion if in skip=2 mode */ if (devc->skipdevs == 2 && arg < 2) arg = 2; if (envy24_force_mono) arg = 1; if (portc->direction == DIR_INPUT) { if ((portc->chnum + arg) > devc->nr_rec_channels) return portc->channels; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); for (i = portc->channels; i < arg; i++) if (devc->rec_channel_mask & (1 << (portc->chnum + i))) { MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return portc->channels; } for (i = portc->channels; i < arg; i++) devc->rec_channel_mask |= (1 << (portc->chnum + i)); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } else { if ((portc->chnum + arg) > nc) return portc->channels; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); for (i = portc->channels; i < arg; i++) if (devc->play_channel_mask & (1 << (portc->chnum + i))) { MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return portc->channels; } for (i = portc->channels; i < arg; i++) devc->play_channel_mask |= (1 << (portc->chnum + i)); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } portc->channels = arg; update_fragments (portc); return portc->channels; } static unsigned int envy24_audio_set_format (int dev, unsigned int arg) { envy24_portc *portc = audio_engines[dev]->portc; if (arg == 0) return portc->bits; if (!(arg & audio_engines[dev]->oformat_mask)) return portc->bits = AFMT_S16_LE; portc->bits = arg; if (arg == AFMT_AC3) { envy24_audio_set_channels (dev, 2); } update_fragments (portc); return portc->bits; } static int envy24_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg) { envy24_devc *devc = audio_engines[dev]->devc; envy24_portc *portc = audio_engines[dev]->portc; oss_native_word flags; int rt; if (arg == NULL) return OSS_EINVAL; switch (cmd) { case SNDCTL_DSP_GET_RECSRC: case SNDCTL_DSP_SET_RECSRC: case SNDCTL_DSP_GET_PLAYTGT: case SNDCTL_DSP_SET_PLAYTGT: return *arg = 0; break; case SNDCTL_DSP_GET_RECSRC_NAMES: return oss_encode_enum ((oss_mixer_enuminfo *) arg, portc->name, 0); break; case SNDCTL_DSP_GET_PLAYTGT_NAMES: return oss_encode_enum ((oss_mixer_enuminfo *) arg, portc->name, 0); break; case SNDCTL_DSP_GET_CHNORDER: *(oss_uint64_t *) arg = CHNORDER_UNDEF; return 0; } if (devc->model_data->auxdrv->spdif_ioctl == NULL) return OSS_EINVAL; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); rt = devc->model_data->auxdrv->spdif_ioctl (devc, dev, cmd, arg); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return rt; } static void envy24_audio_trigger (int dev, int state); static void envy24_audio_reset (int dev) { #ifdef DO_TIMINGS oss_do_timing ("Envy24: Reset audio"); #endif envy24_audio_trigger (dev, 0); } #define WriteCsByte(devc, b, v) (devc)->spdif_cbits[b]=(v) #define ReadCsByte(devc, b) (devc)->spdif_cbits[b] static __inline__ void WriteCsField (envy24_devc * devc, unsigned char bByteNum, unsigned short bMask, unsigned short bBits) { /* Get current reg value. */ unsigned char bTemp = ReadCsByte (devc, bByteNum); /* Clear field to be written. */ bTemp &= ~(bMask); /* Set new values. */ WriteCsByte (devc, bByteNum, (unsigned char) (bTemp | (bBits & bMask))); } static void envy24_setup_pro_speed (envy24_devc * devc) { switch (devc->speed) { case 32000: WriteCsField (devc, 0, 0xc0, 0xc0); break; case 44100: WriteCsField (devc, 0, 0xc0, 0x40); break; case 48000: WriteCsField (devc, 0, 0xc0, 0x80); break; default: WriteCsField (devc, 0, 0xc0, 0x00); break; } } static void setup_pro_mode (envy24_devc * devc) { devc->spdif_cbits[0] |= 0x01; /* Pro mode */ devc->spdif_cbits[2] |= 0x2c; /* 24-bit data word */ envy24_setup_pro_speed (devc); } static void envy24_setup_consumer_speed (envy24_devc * devc) {
Set the sampling rate indication
if (devc->ac3_mode) WriteCsField (devc, 0, 0x02, 0x02); /* 1:1 = 1 */ else WriteCsField (devc, 0, 0x02, 0x00); /* 1:1 = 0 */ switch (devc->speed) { case 22050L: WriteCsField (devc, 0, 0xC0, 0x00); /* 7:6 = 00 */ WriteCsField (devc, 3, 0x0F, 0x00); /* 3:0 = 0000 */ WriteCsField (devc, 4, 0x0F, 0x09); /* 3:0 = 1001 */ break; case 32000L: WriteCsField (devc, 0, 0xC0, 0xC0); /* 7:6 = 11 */ WriteCsField (devc, 3, 0x0F, 0x03); /* 3:0 = 0011 */ WriteCsField (devc, 4, 0x0F, 0x00); /* 3:0 = 0000 */ break; case 44100L: WriteCsField (devc, 0, 0xC0, 0x40); /* 7:6 = 01 */ WriteCsField (devc, 3, 0x0F, 0x00); /* 3:0 = 0000 */ WriteCsField (devc, 4, 0x0F, 0x00); /* 3:0 = 0000 */ break; case 48000L: WriteCsField (devc, 0, 0xC0, 0x80); /* 7:6 = 10 */ WriteCsField (devc, 3, 0x0F, 0x02); /* 3:0 = 0010 */ WriteCsField (devc, 4, 0x0F, 0x00); /* 3:0 = 0000 */ break; case 88200L: WriteCsField (devc, 0, 0xC0, 0x00); /* 7:6 = 00 */ WriteCsField (devc, 3, 0x0F, 0x00); /* 3:0 = 0000 */ WriteCsField (devc, 4, 0x0F, 0x05); /* 3:0 = 0101 */ break; case 96000L: WriteCsField (devc, 0, 0xC0, 0x00); /* 7:6 = 00 */ WriteCsField (devc, 3, 0x0F, 0x00); /* 3:0 = 0000 */ WriteCsField (devc, 4, 0x0F, 0x04); /* 3:0 = 0100 */ break; default: WriteCsField (devc, 0, 0xC0, 0x00); /* 7:6 = 00 */ WriteCsField (devc, 3, 0x0F, 0x00); /* 3:0 = 0000 */ WriteCsField (devc, 4, 0x0F, 0x00); /* 3:0 = 0000 */ break; } } static void setup_consumer_mode (envy24_devc * devc) { WriteCsByte (devc, 0, ReadCsByte (devc, 0) & ~(0x02)); /* Set audio mode */ WriteCsByte (devc, 0, ReadCsByte (devc, 0) & ~(0x38)); /* Set no emphasis */ WriteCsByte (devc, 0, ReadCsByte (devc, 0) & ~(0x04)); /* Set "original" */ WriteCsByte (devc, 1, ReadCsByte (devc, 1) | (0x80)); /* Set "original" */ envy24_setup_consumer_speed (devc); } static void setup_spdif_control (envy24_devc * devc) { /* unsigned char *cbits; */ memset (devc->spdif_cbits, 0, sizeof (devc->spdif_cbits)); /* cbits = devc->spdif_cbits; */ if (devc->spdif_pro_mode) { setup_pro_mode (devc); } else { setup_consumer_mode (devc); } } /*ARGSUSED*/ static int envy24_audio_open (int dev, int mode, int open_flags) { envy24_portc *portc = audio_engines[dev]->portc; envy24_devc *devc = audio_engines[dev]->devc; oss_native_word flags; mode |= ADEV_NOVIRTUAL; if (devc->playbuf == NULL || devc->recbuf == NULL) { cmn_err (CE_WARN, "No DMA buffer\n"); return OSS_ENOSPC; } MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (portc->open_mode != 0 || devc->direct_audio_opened != 0) { MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return OSS_EBUSY; } if (portc->direction == DIR_INPUT) { if (devc->rec_channel_mask & (1 << portc->chnum)) { MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return OSS_EBUSY; } devc->rec_channel_mask |= (1 << portc->chnum); } else { if (devc->play_channel_mask & (1 << portc->chnum)) { MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return OSS_EBUSY; } devc->play_channel_mask |= (1 << portc->chnum); } portc->open_mode = mode; portc->channels = 1; if (devc->skipdevs == 2) portc->channels = 2; portc->pcm_qlen = 0; if (portc->direction == DIR_INPUT) { if (devc->open_inputs++ == 0 && devc->open_outputs == 0) { devc->speed = speed_tab[devc->pending_speed_sel].speed; devc->speedbits = speed_tab[devc->pending_speed_sel].speedbits; } } else { if (devc->open_inputs == 0 && devc->open_outputs++ == 0) { if (portc->flags & PORTC_SPDOUT) { setup_spdif_control (devc); } devc->speed = speed_tab[devc->pending_speed_sel].speed; devc->speedbits = speed_tab[devc->pending_speed_sel].speedbits; } } #if 1 if (devc->use_src) { /* SRC stuff */ audio_engines[dev]->flags |= ADEV_FIXEDRATE; audio_engines[dev]->fixed_rate = devc->speed; audio_engines[dev]->min_rate = devc->speed; audio_engines[dev]->max_rate = devc->speed; } else { audio_engines[dev]->flags &= ~ADEV_FIXEDRATE; audio_engines[dev]->fixed_rate = 0; audio_engines[dev]->min_rate = 8000; audio_engines[dev]->max_rate = 96000; } #endif MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } /*ARGSUSED*/ static void envy24_audio_close (int dev, int mode) { envy24_devc *devc = audio_engines[dev]->devc; envy24_portc *portc = audio_engines[dev]->portc; oss_native_word flags; int i; envy24_audio_reset (dev); MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); portc->open_mode = 0; if (portc->flags & PORTC_SPDOUT) devc->ac3_mode = 0; if (portc->direction == DIR_INPUT) { devc->open_inputs--; for (i = 0; i < portc->channels; i++) devc->rec_channel_mask &= ~(1 << (portc->chnum + i)); } else { devc->open_outputs--; for (i = 0; i < portc->channels; i++) devc->play_channel_mask &= ~(1 << (portc->chnum + i)); } MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } /*ARGSUSED*/ static void envy24_audio_output_block (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { } /*ARGSUSED*/ static void envy24_audio_start_input (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { } static int envy24_sync_control (int dev, int event, int mode) { envy24_devc *devc = audio_engines[dev]->devc; envy24_portc *portc = audio_engines[dev]->portc; if (event == SYNC_PREPARE) { if (mode & PCM_ENABLE_OUTPUT) { if (!devc->playback_prepared) devc->hw_playfrag = 0; handle_playdev (devc, portc, devc->hw_playfrag); handle_playdev (devc, portc, devc->hw_playfrag + 1); if (devc->writeahead == 2) handle_playdev (devc, portc, devc->hw_playfrag + 2); envy24_prepare_play_engine (devc); portc->trigger_bits |= PCM_ENABLE_OUTPUT; } if (mode & PCM_ENABLE_INPUT) { if (devc->active_inputs == 0) { envy24_start_recording (devc); } portc->trigger_bits |= PCM_ENABLE_INPUT; } return 0; } if (event == SYNC_TRIGGER) { if (mode & PCM_ENABLE_OUTPUT) { envy24_prepare_play_engine (devc); /* Just to make sure */ devc->hw_playfrag = 1 + devc->writeahead; if (devc->active_outputs++ == 0) envy24_launch_play_engine (devc); } if (mode & PCM_ENABLE_INPUT) { if (devc->active_inputs++ == 0) { devc->hw_recfrag = 0; envy24_launch_recording (devc); } } return 0; } return OSS_EIO; } static void envy24_audio_trigger (int dev, int state) { int changed; oss_native_word flags; envy24_portc *portc = audio_engines[dev]->portc; envy24_devc *devc = audio_engines[dev]->devc; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); changed = state ^ portc->trigger_bits; if (portc->direction == DIR_OUTPUT && (changed & PCM_ENABLE_OUTPUT)) { if (state & PCM_ENABLE_OUTPUT) { #ifdef DO_TIMINGS oss_do_timing ("Envy24: Trigger start output"); #endif portc->trigger_bits = state; if (devc->active_outputs++ == 0) start_playback (devc); } else { #ifdef DO_TIMINGS oss_do_timing ("Envy24: Trigger stop output"); #endif portc->trigger_bits = state; if (--devc->active_outputs == 0) envy24_stop_playback (devc); } } if (portc->direction == DIR_INPUT && (changed & PCM_ENABLE_INPUT)) { if (state & PCM_ENABLE_INPUT) { portc->trigger_bits = state; if (devc->active_inputs++ == 0) { envy24_start_recording (devc); envy24_launch_recording (devc); } } else { if (--devc->active_inputs == 0) envy24_stop_recording (devc); portc->trigger_bits = state; } } portc->trigger_bits = state; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } /*ARGSUSED*/ static int envy24_audio_prepare_for_input (int dev, int bsize, int bcount) { int nsamples, nbytes; envy24_portc *portc = audio_engines[dev]->portc; envy24_devc *devc = audio_engines[dev]->devc; oss_native_word flags; if (audio_engines[dev]->flags & ADEV_NOINPUT) return OSS_EACCES; nsamples = devc->hw_fragsamples; /* Number of 32 bit samples */ nbytes = nsamples * portc->channels; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); #ifdef DO_RIAA memset (portc->riaa_parms, 0, sizeof (portc->riaa_parms)); if (portc->riaa_filter) cmn_err (CE_CONT, "oss: RIAA filter activated for /dev/dsp%d\n", dev); #endif if (portc->bits & (AFMT_S16_LE | AFMT_S16_BE | AFMT_AC3)) { nbytes *= 2; } else if (portc->bits & (AFMT_S32_LE | AFMT_S24_LE)) nbytes *= 4; if (nbytes != bsize) { dmap_p dmap = audio_engines[dev]->dmap_in; dmap->fragment_size = bsize = nbytes; dmap->bytes_in_use = dmap->fragment_size * dmap->nfrags; if (dmap->bytes_in_use > dmap->buffsize) { dmap->nfrags = dmap->buffsize / dmap->fragment_size; dmap->bytes_in_use = dmap->nfrags * dmap->fragment_size; } } MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } /*ARGSUSED*/ static int envy24_audio_prepare_for_output (int dev, int bsize, int bcount) { int nsamples, nbytes; oss_native_word flags; envy24_portc *portc = audio_engines[dev]->portc; envy24_devc *devc = audio_engines[dev]->devc; if (audio_engines[dev]->flags & ADEV_NOOUTPUT) return OSS_EACCES; nsamples = devc->hw_fragsamples; /* Number of 32 bit samples */ nbytes = nsamples * portc->channels; if (portc->flags & PORTC_SPDOUT) if (portc->bits == AFMT_AC3) devc->ac3_mode = 1; if (portc->bits & (AFMT_S16_LE | AFMT_S16_BE | AFMT_AC3)) { nbytes *= 2; } else if (portc->bits & (AFMT_S32_LE | AFMT_S32_BE)) nbytes *= 4; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (nbytes != bsize) { dmap_p dmap = audio_engines[dev]->dmap_out; cmn_err (CE_CONT, "Fragment size mismatch: hw=%d, sw=%d\n", nbytes, bsize); cmn_err (CE_NOTE, "Application bug detected. Fix ioctl() calling order\n"); oss_audio_set_error (dev, E_PLAY, OSSERR (1012, "Wrong ioctl call order"), 0);
Errordesc: The envy24 driver requires that number of channels, sample format and sampling rate are set before calling any ioctl call that may lock the fragment size prematurely. In such case the driver cannot change the fragment size to value that is suitable for the device.
Please use the recommended ioctl call order defined in http://manuals.opensound.com/developer/callorder.html.
dmap->fragment_size = bsize = nbytes; dmap->bytes_in_use = dmap->fragment_size * dmap->nfrags; if (dmap->bytes_in_use > dmap->buffsize) { dmap->nfrags = dmap->buffsize / dmap->fragment_size; dmap->bytes_in_use = dmap->nfrags * dmap->fragment_size; } MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return OSS_EIO; } MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } /*ARGSUSED*/ static int envy24_alloc_buffer (int dev, dmap_t * dmap, int direction) { envy24_devc *devc = audio_engines[dev]->devc; if (dmap->dmabuf != NULL) return 0; #if 0
Ignore the direction parameter since it's missleading. Instead use the ADEV_NOINPUT/ADEV_NOOUTPUT flag.
if (audio_engines[dev]->flags & ADEV_NOINPUT) direction = OPEN_WRITE; else direction = OPEN_READ; #endif dmap->buffsize = devc->skipdevs * DEV_BUFSIZE; dmap->dmabuf_phys = 0; dmap->dmabuf = KERNEL_MALLOC (dmap->buffsize); if (dmap->dmabuf == NULL) { cmn_err (CE_WARN, "Failed to allocate a DMA buffer\n"); return OSS_ENOSPC; } memset (dmap->dmabuf, 0, dmap->buffsize); return 0; } /*ARGSUSED*/ static int envy24_free_buffer (int dev, dmap_t * dmap, int direction) { if (dmap->dmabuf == NULL) return 0; #if 1 KERNEL_FREE (dmap->dmabuf); #endif dmap->dmabuf = NULL; return 0; } static int envy24_check_input (int dev) { envy24_devc *devc = audio_engines[dev]->devc; if (!devc->recording_started) return 0; cmn_err (CE_NOTE, "Input timed out.\n"); return OSS_EIO; } static int envy24_check_output (int dev) { envy24_devc *devc = audio_engines[dev]->devc; if (!devc->playback_started) return 0; cmn_err (CE_NOTE, "Output timed out\n"); return OSS_EIO; } static int envy24_local_qlen (int dev) { envy24_portc *portc = audio_engines[dev]->portc; return portc->pcm_qlen * audio_engines[dev]->dmap_out->fragment_size; } static const audiodrv_t envy24_audio_driver = { envy24_audio_open, envy24_audio_close, envy24_audio_output_block, envy24_audio_start_input, envy24_audio_ioctl, envy24_audio_prepare_for_input, envy24_audio_prepare_for_output, envy24_audio_reset, envy24_local_qlen, NULL, NULL, NULL, envy24_audio_trigger, envy24_audio_set_rate, envy24_audio_set_format, envy24_audio_set_channels, NULL, NULL, envy24_check_input, envy24_check_output, envy24_alloc_buffer, envy24_free_buffer, NULL, NULL, NULL, /* envy24_get_buffer_pointer */ NULL, /* calibrate_speed */ envy24_sync_control }; /*ARGSUSED*/ static int envy24_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg) { extern int envy24_realencoder_hack; if (!envy24_realencoder_hack) { if (cmd == SOUND_MIXER_READ_DEVMASK || cmd == SOUND_MIXER_READ_RECMASK || cmd == SOUND_MIXER_READ_RECSRC || cmd == SOUND_MIXER_READ_STEREODEVS) return *arg = 0; } if (cmd == SOUND_MIXER_READ_DEVMASK || cmd == SOUND_MIXER_READ_RECMASK || cmd == SOUND_MIXER_READ_RECSRC || cmd == SOUND_MIXER_READ_STEREODEVS) return *arg = SOUND_MASK_LINE | SOUND_MASK_PCM | SOUND_MASK_MIC | SOUND_MASK_VOLUME | SOUND_MASK_CD; if (cmd == SOUND_MIXER_READ_VOLUME || cmd == SOUND_MIXER_READ_PCM || cmd == SOUND_MIXER_READ_LINE || cmd == SOUND_MIXER_READ_MIC || cmd == SOUND_MIXER_READ_CD || cmd == MIXER_READ (SOUND_MIXER_DIGITAL1)) return *arg = 100 | (100 << 8); if (cmd == SOUND_MIXER_WRITE_VOLUME || cmd == SOUND_MIXER_WRITE_PCM || cmd == SOUND_MIXER_WRITE_LINE || cmd == SOUND_MIXER_READ_MIC || cmd == SOUND_MIXER_WRITE_CD || cmd == MIXER_WRITE (SOUND_MIXER_DIGITAL1)) return *arg = 100 | (100 << 8); if (cmd == SOUND_MIXER_READ_CAPS) return *arg = SOUND_CAP_EXCL_INPUT; if (cmd == SOUND_MIXER_PRIVATE1) return *arg = 0; return OSS_EINVAL; } static int envy24_set_control (int dev, int ctrl, unsigned int cmd, int value) { envy24_devc *devc = mixer_devs[dev]->devc; if (cmd == SNDCTL_MIX_READ) switch (ctrl) { case 1: return devc->pending_speed_sel; break; case 2: return devc->syncsource; break; case 3: return devc->use_src; break; case 4: { int tmp = envy24_read_cci (devc, 0x20); return !!(tmp & 0x10); } break; case 5: return devc->ratelock; break; case 6: return devc->speed; break; case 7: return devc->sync_locked = devc->model_data->auxdrv->get_locked_status (devc); default: return OSS_EIO; } if (cmd == SNDCTL_MIX_WRITE) switch (ctrl) { case 1: if (value < 0 || value > 12) return OSS_EIO; if (value != devc->pending_speed_sel) { if (devc->open_inputs == 0 && devc->open_outputs == 0) /* IDDLE */ OUTB (devc->osdev, value, devc->mt_base + 0x01); /* Make the change now */ } return devc->pending_speed_sel = value; break; case 2: if (value < 0 || value > 2) return OSS_EIO; return devc->syncsource = value; break; case 3: return devc->use_src = value; break; case 4: { int tmp = envy24_read_cci (devc, 0x20) & ~0x10; if (value) tmp |= 0x10; /* Optical */ envy24_write_cci (devc, 0x20, tmp); return !!(tmp & 0x10); } break; case 5: return devc->ratelock = value; break; case 6: return devc->speed; break; case 7: return devc->sync_locked = devc->model_data->auxdrv->get_locked_status (devc); break; default: return OSS_EIO; } return OSS_EINVAL; } static int read_mon (envy24_devc * devc, int ch, int is_right) { int tmp; if (ch >= 20) return 0; OUTB (devc->osdev, ch, devc->mt_base + 0x3a); tmp = INW (devc->osdev, devc->mt_base + 0x38); if (is_right) tmp >>= 8; tmp &= 0x7f; if (tmp > 0x60) /* Mute? */ return 0; tmp = (tmp * 15) / 10; return 144 - tmp; } static int mon_scale (int v) { if (v == 0) return 0x7f; /* Mute */ v = 144 - v; v = (10 * v) / 15; if (v > 0x60) v = 0x7f; return v; } static void mon_set (envy24_devc * devc, int ch, int left, int right) { left = mon_scale (left); right = mon_scale (right); OUTB (devc->osdev, 1, devc->mt_base + 0x3b); /* Volume change rate */ OUTB (devc->osdev, ch, devc->mt_base + 0x3a); OUTW (devc->osdev, left | (right << 8), devc->mt_base + 0x38); } static int read_peak (envy24_devc * devc, int ch) { int tmp; if (ch >= 22) return 0; OUTB (devc->osdev, ch, devc->mt_base + 0x3e); tmp = INB (devc->osdev, devc->mt_base + 0x3f); return tmp; } /*ARGSUSED*/ static int envy24_get_peak (int dev, int ctrl, unsigned int cmd, int value) { static const unsigned char peak_cnv[256] = { 0, 18, 29, 36, 42, 47, 51, 54, 57, 60, 62, 65, 67, 69, 71, 72, 74, 75, 77, 78, 79, 81, 82, 83, 84, 85, 86, 87, 88, 89, 89, 90, 91, 92, 93, 93, 94, 95, 95, 96, 97, 97, 98, 99, 99, 100, 100, 101, 101, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 108, 109, 109, 110, 110, 110, 111, 111, 111, 112, 112, 113, 113, 113, 114, 114, 114, 115, 115, 115, 115, 116, 116, 116, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, 121, 121, 121, 121, 122, 122, 122, 122, 122, 123, 123, 123, 123, 124, 124, 124, 124, 125, 125, 125, 125, 125, 126, 126, 126, 126, 126, 127, 127, 127, 127, 127, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 130, 130, 130, 130, 130, 130, 131, 131, 131, 131, 131, 131, 132, 132, 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 134, 134, 134, 134, 134, 134, 134, 135, 135, 135, 135, 135, 135, 135, 136, 136, 136, 136, 136, 136, 136, 137, 137, 137, 137, 137, 137, 137, 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, }; envy24_devc *devc = mixer_devs[dev]->devc; int i, orign, n = -1, left = 0, right = 0; for (i = 0; i < 12 && n == -1; i++) if (ctrl & (1 << i)) n = i; if (n == -1) return OSS_EINVAL; orign = n; if (ctrl & 0x80000000) n += 10; /* Recording stream */ if (cmd == SNDCTL_MIX_READ) { left = read_peak (devc, n); if (ctrl & (1 << (orign + 1))) /* Stereo mode? */ right = read_peak (devc, n + 1); else right = left; left = peak_cnv[left]; right = peak_cnv[right]; return left | (right << 8); } return OSS_EINVAL; } static int envy24_set_mon (int dev, int ctrl, unsigned int cmd, int value) { envy24_devc *devc = mixer_devs[dev]->devc; int i, orign, n = -1, left = 0, right = 0; for (i = 0; i < 10 && n == -1; i++) if (ctrl & (1 << i)) n = i; if (n == -1) return OSS_EINVAL; orign = n; if (ctrl & 0x80000000) n += 10; /* Recording stream */ if (cmd == SNDCTL_MIX_READ) { left = read_mon (devc, n, 0) * 10; if (ctrl & (1 << (orign + 1))) /* Stereo mode? */ right = read_mon (devc, n + 1, 1) * 10; else right = read_mon (devc, n, 1) * 10; return left | (right << 16); } else if (cmd == SNDCTL_MIX_WRITE) { left = value & 0xffff; right = (value >> 16) & 0xffff; if (right > 1440) right = 1440; if (left > 1440) left = 1440; if (ctrl & (1 << (orign + 1))) /* Stereo mode? */ { mon_set (devc, n, left / 10, 0); mon_set (devc, n + 1, 0, right / 10); } else { mon_set (devc, n, left / 10, right / 10); } return left | (right << 16); } return OSS_EINVAL; } static int get_loopback (envy24_devc * devc, int ch) { int tmp; tmp = INL (devc->osdev, devc->mt_base + 0x34); return (tmp >> (4 * ch)) & 0x07; } static int get_spdif_loopback (envy24_devc * devc, int ch) { int tmp; tmp = INL (devc->osdev, devc->mt_base + 0x34); return (tmp >> ((4 * ch) + 3)) & 0x01; } static void set_loopback (envy24_devc * devc, int ch, int val) { int tmp = INL (devc->osdev, devc->mt_base + 0x34); tmp &= ~(0x07 << (4 * ch)); tmp |= (val & 0x07) << (4 * ch); OUTL (devc->osdev, tmp, devc->mt_base + 0x34); } static void set_spdif_loopback (envy24_devc * devc, int ch, int val) { int tmp = INL (devc->osdev, devc->mt_base + 0x34); tmp &= ~(0x08 << (4 * ch)); tmp |= (val & 0x01) << ((4 * ch) + 3); OUTL (devc->osdev, tmp, devc->mt_base + 0x34); } static int envy24_set_outrout (int dev, int ctrl, unsigned int cmd, int value) { envy24_devc *devc = mixer_devs[dev]->devc; int tmp, i; if (cmd == SNDCTL_MIX_READ) { tmp = INW (devc->osdev, devc->mt_base + 0x30); for (i = 0; i < 8; i++) if (ctrl & (1 << i)) { tmp = (tmp >> (2 * i)) & 0x03; switch (tmp) { case 0: /* DMA */ return 0; break; case 1: /* Monitor */ return 1; break; case 2: /* Analog input loopback */ return 2 + get_loopback (devc, i); break; case 3: /* S/PDIF input loopback */ return 10 + get_spdif_loopback (devc, i); break; } } return OSS_EINVAL; } else if (cmd == SNDCTL_MIX_WRITE) { tmp = INW (devc->osdev, devc->mt_base + 0x30); for (i = 0; i < 8; i++) if (ctrl & (1 << i)) { int ch; ch = i / 2; if (i & 1) ch += 4; tmp &= ~(0x03 << (ch * 2)); /* Cleanup */ if (value == 0) /* DMA */ continue; if (value == 1) /* Monitor */ { tmp |= 1 << (ch * 2); continue; } if (value < 10) /* Analog inputs */ { tmp |= 2 << (ch * 2); set_loopback (devc, i, value - 2); continue; } tmp |= 3 << (ch * 2); set_spdif_loopback (devc, i, value - 10); } OUTW (devc->osdev, tmp, devc->mt_base + 0x30); return value; } return OSS_EINVAL; } static int envy24_set_stereo_outrout (int dev, int ctrl, unsigned int cmd, int value) { envy24_devc *devc = mixer_devs[dev]->devc; int tmp, i; if (cmd == SNDCTL_MIX_READ) { tmp = INW (devc->osdev, devc->mt_base + 0x30); for (i = 0; i < 8; i++) if (ctrl & (1 << i)) { int ch; ch = i / 2; if (i & 1) ch += 4; tmp = (tmp >> (2 * ch)) & 0x03; switch (tmp) { case 0: /* DMA */ return 0; break; case 1: /* Monitor */ return 1; break; case 2: /* Analog input loopback */ return 2 + get_loopback (devc, i) / 2; break; case 3: /* S/PDIF input loopback */ return 6; break; } } return OSS_EINVAL; } else if (cmd == SNDCTL_MIX_WRITE) { tmp = INW (devc->osdev, devc->mt_base + 0x30); for (i = 0; i < 8; i++) if (ctrl & (1 << i)) { int ch; ch = i / 2; if (i & 1) ch += 4; tmp &= ~(0x03 << (ch * 2)); /* Cleanup */ if (value == 0) /* DMA */ { continue; } if (value == 1) /* Monitor */ { tmp |= 1 << (ch * 2); continue; } if (value < 6) /* Analog inputs */ { tmp |= 2 << (ch * 2); set_loopback (devc, i, (value - 2) * 2 + (i & 1)); continue; } tmp |= 3 << (ch * 2); /* S/PDIF */ set_spdif_loopback (devc, i, (value - 10) + (i & 1)); continue; } OUTW (devc->osdev, tmp, devc->mt_base + 0x30); return value; } return OSS_EINVAL; } static int read_spdif_stereo (envy24_devc * devc) { int tmp; tmp = INL (devc->osdev, devc->mt_base + 0x32);
Look only at the left channel. Assume the same settings on right.
switch (tmp & 0x03) { case 0: /* From DMA */ return 0; break; case 1: /* From digital mixer */ return 1; break; case 2: /* Analog input # loopback */ return 2 + ((tmp >> 9) & 0x03); break; case 3: /* S/PDIF input loopback */ return 6; break; } return 0; } static int read_spdif_mono (envy24_devc * devc, int ch) { int tmp, v; tmp = INL (devc->osdev, devc->mt_base + 0x32); if (ch == 0) /* Left channel ? */ v = (tmp) & 0x03; else v = (tmp >> 2) & 0x03; switch (v) { case 0: /* DMA */ return 0; break; case 1: /* Monitor */ return 1; break; case 2: /* Analog input */ if (ch == 0) /* Left or right */ v = (tmp >> 8) & 0x07; else v = (tmp >> 12) & 0x07; return 2 + v; break; case 3: if (ch == 0) /* Left or right */ v = (tmp >> 11) & 0x01; else v = (tmp >> 15) & 0x01; return 10 + v; break; } return 0; } static int write_spdif_mono (envy24_devc * devc, int ch, int val) { int tmp = 0, v; tmp = INW (devc->osdev, devc->mt_base + 0x32); if (val == 0) /* DMA */ { if (ch == 0) /* Left */ tmp &= ~0x0003; else tmp &= ~0x000c; goto do_ne; } if (val == 1) /* Monitor */ { if (ch == 0) /* Left */ { tmp &= ~0x0003; tmp |= 0x0001; } else { tmp &= ~0x000c; tmp |= 0x0004; } goto do_ne; } if (val < 10) /* Analog inputs */ { v = (val - 2) & 0x07; if (ch == 0) /* Left */ { tmp &= ~(0x0003 | (0x07 << 8)); tmp |= 0x02 | (v << 8); } else { tmp &= ~(0x000c | (0x07 << 12)); tmp |= 0x08 | (v << 12); } goto do_ne; } /* Else S/PDIF */ if (ch == 0) /* Left */ { tmp &= ~(1 << 11); tmp |= 0x0003; if (val == 11) tmp |= 1 << 11; } else { tmp &= ~(1 << 15); tmp |= 0x000c; if (val == 11) tmp |= 1 << 15; } do_ne: OUTW (devc->osdev, tmp, devc->mt_base + 0x32); return val; } static int write_spdif_stereo (envy24_devc * devc, int val) { int tmp = 0, v; if (val == 0) /* DMA */ { tmp = 0x0000; goto do_ne; } if (val == 1) /* Monitor */ { tmp = 0x0005; goto do_ne; } if (val < 6) /* Analog inputs */ { tmp = 0x000a; v = (val - 2) * 2; tmp |= (v << 8); tmp |= ((v + 1) << 12); goto do_ne; } /* Else S/PDIF */ tmp = 0x800f; do_ne: OUTW (devc->osdev, tmp, devc->mt_base + 0x32); return val; } static int envy24_set_spdifrout (int dev, int ctrl, unsigned int cmd, int value) { envy24_devc *devc = mixer_devs[dev]->devc; if (cmd == SNDCTL_MIX_READ) { if (ctrl == 3) return read_spdif_stereo (devc); else return read_spdif_mono (devc, ctrl - 1); } else if (cmd == SNDCTL_MIX_WRITE) { if (ctrl == 3) return write_spdif_stereo (devc, value); else return write_spdif_mono (devc, ctrl - 1, value); } return OSS_EINVAL; } /*ARGSUSED*/ static int create_output_mixer (int dev, envy24_devc * devc, int root) { int i, mask = devc->outportmask, group, err, num, skip; char tmp[64]; int nc = devc->nr_play_channels; if (envy24_virtualout) { mask = 0; nc = 10; for (i = 0; i < nc; i++) mask |= (1 << i); } if ((group = mixer_ext_create_group (dev, 0, "ENVY24_OUTPUT")) < 0) return group; skip = devc->skipdevs; if (skip != 2) skip = 1; for (i = 0; i < nc; i += skip) { num = 1 << i; if (!(mask & num)) continue; /* Not present */ sprintf (tmp, "@pcm%d", devc->play_portc[i / 2].dev); num |= 1 << (i + 1); if ((err = mixer_ext_create_control (dev, group, num, envy24_set_mon, MIXT_STEREOSLIDER16, tmp, 1440, MIXF_MONVOL | MIXF_READABLE | MIXF_WRITEABLE | MIXF_CENTIBEL)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, num, envy24_get_peak, MIXT_STEREOPEAK, "-", 144, MIXF_READABLE | MIXF_DECIBEL)) < 0) return err; } return 0; } /*ARGSUSED*/ static int create_input_mixer (int dev, envy24_devc * devc, int root) { int i, mask = devc->inportmask, group, err, num, skip; char tmp[64]; if ((group = mixer_ext_create_group (dev, 0, "ENVY24_INPUT")) < 0) return group; skip = devc->skipdevs; if (skip != 2) skip = 1; for (i = 0; i < devc->nr_rec_channels && i < 10; i += skip) { num = (1 << i); if (!(mask & num)) continue; /* Not present */ num |= 0x80000000; /* Input flag */ if (i == 8) strcpy (tmp, "ENVY24_SPDIN"); else sprintf (tmp, "ENVY24_IN%d/%d", i + 1, i + 2); num |= (1 << (i + 1)); if ((err = mixer_ext_create_control (dev, group, num, envy24_set_mon, MIXT_STEREOSLIDER16, tmp, 1440, MIXF_MONVOL | MIXF_READABLE | MIXF_WRITEABLE | MIXF_CENTIBEL)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, num, envy24_get_peak, MIXT_STEREOPEAK, "-", 144, MIXF_READABLE | MIXF_DECIBEL)) < 0) return err; } num = (1 << 11); if ((err = mixer_ext_create_control (dev, group, num, envy24_get_peak, MIXT_STEREOPEAK, "MONITOR", 144, MIXF_READABLE | MIXF_DECIBEL)) < 0) return err; return 0; } /*ARGSUSED*/ static int create_mon_mixer (int dev, envy24_devc * devc, int root) { int i, mask = devc->outportmask, group, err, num, skip; char tmp[64]; int nc = devc->nr_play_channels; if (envy24_virtualout) { mask = 0; nc = 10; for (i = 0; i < nc; i++) mask |= (1 << i); } if ((group = mixer_ext_create_group (dev, 0, "ENVY24_MON")) < 0) return group; skip = devc->skipdevs; if (skip != 2) skip = 1; for (i = 0; i < nc; i += skip) { num = 1 << i; if (!(mask & num)) continue; /* Not present */ if (devc->skipdevs == 2) { if (i == 8) strcpy (tmp, "ENVY24_SPDOUT"); else sprintf (tmp, "ENVY24_OUT%d/%d", i + 1, i + 2); num |= 1 << (i + 1); if ((err = mixer_ext_create_control (dev, group, num, envy24_set_mon, MIXT_STEREOSLIDER16, tmp, 1440, MIXF_MONVOL | MIXF_READABLE | MIXF_WRITEABLE | MIXF_CENTIBEL)) < 0) return err; } else { if (i == 8) strcpy (tmp, "ENVY24_SPDOUTL"); else if (i == 9) strcpy (tmp, "ENVY24_SPDOUTR"); else sprintf (tmp, "ENVY24_OUT%d", i + 1); if ((err = mixer_ext_create_control (dev, group, num, envy24_set_mon, MIXT_STEREOSLIDER16, tmp, 1440, MIXF_MONVOL | MIXF_READABLE | MIXF_WRITEABLE | MIXF_CENTIBEL)) < 0) return err; } } mask = devc->inportmask; for (i = 0; i < devc->nr_rec_channels && i < 10; i += skip) { num = 1 << i; if (!(mask & num)) continue; /* Not present */ num |= 0x80000000; /* Input flag */ if (devc->skipdevs == 2) { if (i == 8) strcpy (tmp, "ENVY24_SPDIN"); else sprintf (tmp, "ENVY24_IN%d/%d", i + 1, i + 2); num |= 1 << (i + 1); if ((err = mixer_ext_create_control (dev, group, num, envy24_set_mon, MIXT_STEREOSLIDER16, tmp, 1440, MIXF_MONVOL | MIXF_READABLE | MIXF_WRITEABLE | MIXF_CENTIBEL)) < 0) return err; } else { if (i == 8) strcpy (tmp, "ENVY24_SPDINL"); else if (i == 9) strcpy (tmp, "ENVY24_SPDINR"); else sprintf (tmp, "ENVY24_IN%d", i + 1); if ((err = mixer_ext_create_control (dev, group, num, envy24_set_mon, MIXT_STEREOSLIDER16, tmp, 1440, MIXF_MONVOL | MIXF_READABLE | MIXF_WRITEABLE | MIXF_CENTIBEL)) < 0) return err; } } return 0; } /*ARGSUSED*/ static int create_peak_mixer (int dev, envy24_devc * devc, int root) { int i, mask = devc->outportmask, group, err, num, skip; int nc = devc->nr_play_channels; char tmp[64]; if ((group = mixer_ext_create_group (dev, 0, "ENVY24_PEAK")) < 0) return group; skip = 2; for (i = 0; i < nc; i += skip) { num = 1 << i; if (!(mask & num)) continue; /* Not present */ { if (i == 8) strcpy (tmp, "ENVY24_SPDOUT"); else sprintf (tmp, "ENVY24_OUT%d/%d", i + 1, i + 2); num |= 1 << (i + 1); if ((err = mixer_ext_create_control (dev, group, num, envy24_get_peak, MIXT_STEREOPEAK, tmp, 144, MIXF_READABLE | MIXF_DECIBEL)) < 0) return err; } } mask = devc->inportmask; for (i = 0; i < devc->nr_rec_channels; i += skip) { num = 1 << i; if (!(mask & num)) continue; /* Not present */ num |= 0x80000000; /* Input flag */ { if (i == 8) strcpy (tmp, "ENVY24_SPDIN"); else if (i == 10) strcpy (tmp, "ENVY24_MAIN"); else sprintf (tmp, "ENVY24_IN%d/%d", i + 1, i + 2); num |= 1 << (i + 1); if ((err = mixer_ext_create_control (dev, group, num, envy24_get_peak, MIXT_STEREOPEAK, tmp, 144, MIXF_READABLE | MIXF_DECIBEL)) < 0) return err; } } return 0; } void envy24_set_enum_mask (int dev, int ctl, oss_native_word mask) { oss_mixext *ext; int i; ext = mixer_find_ext (dev, ctl); if (ext == NULL) { cmn_err (CE_WARN, "Cannot locate the mixer extension\n"); return; } memset (ext->enum_present, 0, sizeof (ext->enum_present)); for (i = 0; i < 32; i++) if (mask & (1 << i)) ext->enum_present[i / 8] |= (1 << (i % 8)); } /*ARGSUSED*/ static int create_rout_mixer (int dev, envy24_devc * devc, int root) { int i, mask = devc->outportmask, group, err, skip, num, chnum; char tmp[64]; if ((group = mixer_ext_create_group_flags (dev, 0, "ENVY24_ROUTE", MIXF_FLAT)) < 0) return group; skip = devc->skipdevs; if (skip != 2) skip = 1; for (i = 0; i < 8; i += skip) { num = 1 << i; if (!(mask & num)) continue; /* Not present */ if (devc->skipdevs == 2) { oss_native_word tmpmask = 0x00000001; int j; if (i < 2) tmpmask |= 0x00000002; for (j = 0; j < 8; j++) if (mask & (1 << j)) tmpmask |= 1 << ((j / 2) + 2); if (devc->model_data->flags & MF_SPDIF) tmpmask |= 0x00000040; sprintf (tmp, "ENVY24_OUT%d/%d", i + 1, i + 2); chnum = i; num = (1 << chnum) | (1 << (chnum + 1)); if ((err = mixer_ext_create_control (dev, group, num, envy24_set_stereo_outrout, MIXT_ENUM, tmp, 7, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; envy24_set_enum_mask (dev, err, tmpmask); } else { oss_native_word tmpmask = 0x00000001; int j; sprintf (tmp, "ENVY24_OUT%d", i + 1); chnum = i; num = 1 << chnum; if (i < 2) tmpmask |= (1 << 1); for (j = 0; j < 8; j++) if (mask & (1 << j)) tmpmask |= 1 << (j + 2); if (devc->model_data->flags & MF_SPDIF) tmpmask |= (3 << 10); if ((err = mixer_ext_create_control (dev, group, num, envy24_set_outrout, MIXT_ENUM, tmp, 12, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; envy24_set_enum_mask (dev, err, tmpmask); } } mask = devc->inportmask; if (devc->model_data->flags & MF_SPDIF) { if (devc->skipdevs == 2) { oss_native_word tmpmask = 0x00000043; int j; for (j = 0; j < 8; j++) if (mask & (1 << j)) tmpmask |= (1 << ((j / 2) + 2)); if ((err = mixer_ext_create_control (dev, group, 3, envy24_set_spdifrout, MIXT_ENUM, "ENVY24_SPDIF", 7, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; envy24_set_enum_mask (dev, err, tmpmask); } else { oss_native_word tmpmask = 0x00000c03; int j; for (j = 0; j < 8; j++) if (mask & (1 << j)) tmpmask |= (1 << (j + 2)); if ((err = mixer_ext_create_control (dev, group, 1, envy24_set_spdifrout, MIXT_ENUM, "ENVY24_SPDIFL", 12, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; envy24_set_enum_mask (dev, err, tmpmask); if ((err = mixer_ext_create_control (dev, group, 2, envy24_set_spdifrout, MIXT_ENUM, "ENVY24_SPDIFR", 12, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; envy24_set_enum_mask (dev, err, tmpmask); } } #if 0 for (i = 0; i < devc->nr_rec_channels && i < 10; i += skip) { num = 1 << i; if (!(mask & num)) continue; /* Not present */ num |= 0x80000000; /* Input flag */ if (devc->skipdevs == 2) { sprintf (tmp, "ENVY24_IN%d/%d", i + 1, i + 2); num |= 1 << (i + 1); if ((err = mixer_ext_create_control (dev, group, num, envy24_set_mon, MIXT_STEREOSLIDER16, tmp, 1440, MIXF_READABLE | MIXF_WRITEABLE | MIXF_CENTIBEL)) < 0) return err; } else { sprintf (tmp, "ENVY24_IN%d", i + 1); if ((err = mixer_ext_create_control (dev, group, num, envy24_set_mon, MIXT_STEREOSLIDER16, tmp, 1440, MIXF_READABLE | MIXF_WRITEABLE | MIXF_CENTIBEL)) < 0) return err; } } #endif return 0; } static int envy24_mix_init (int dev) { envy24_devc *devc = mixer_devs[dev]->devc; int group, err, ctl; int n; extern int envy24_mixerstyle; if ((group = mixer_ext_create_group (dev, 0, "ENVY24")) < 0) return group; if (envy24_skipdevs == 2) switch (envy24_mixerstyle) { case 2: /* New style input and output mixer sections */ if ((err = create_output_mixer (dev, devc, group)) < 0) return err; if ((err = create_input_mixer (dev, devc, group)) < 0) return err; break; default: /* Traditional mixer (peak meters and montor gains separated) */ if ((err = create_peak_mixer (dev, devc, group)) < 0) return err; if ((err = create_mon_mixer (dev, devc, group)) < 0) return err; break; } if ((err = create_rout_mixer (dev, devc, group)) < 0) return err; if (devc->model_data->auxdrv->mixer_init) if ((err = devc->model_data->auxdrv->mixer_init (devc, dev, group)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, 1, envy24_set_control, MIXT_ENUM, "ENVY24_RATE", 12, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; mixer_ext_set_strings (dev, err, "8000 9600 11025 12000 16000 22050 24000 32000 44100 48000 88200 96000", 0); if (devc->model_data->flags & (MF_SPDIF | MF_WCLOCK)) { n = 2; if (devc->model_data->flags & MF_WCLOCK) n = 3; if ((err = mixer_ext_create_control (dev, group, 2, envy24_set_control, MIXT_ENUM, "ENVY24_SYNC", n, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; } if (devc->model_data->flags & MF_SPDSELECT) { if ((err = mixer_ext_create_control (dev, group, 4, envy24_set_control, MIXT_ENUM, "ENVY24_SPDIN", 2, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; } #if 0 /* Always on */ if ((err = mixer_ext_create_control (dev, group, 3, envy24_set_control, MIXT_ONOFF, "ENVY24_SRC", 2, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; #endif if ((err = mixer_ext_create_control (dev, group, 5, envy24_set_control, MIXT_ONOFF, "ENVY24_RATELOCK", 2, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((ctl = mixer_ext_create_control (dev, group, 6, envy24_set_control, MIXT_VALUE, "ENVY24_ACTRATE", 96000, MIXF_READABLE)) < 0) return ctl; mixer_ext_set_description(dev, ctl, "Sample rate currently used by the device"); #if 1 if (devc->model_data->auxdrv->get_locked_status) { devc->sync_locked = devc->model_data->auxdrv->get_locked_status (devc); if ((err = mixer_ext_create_control (dev, group, 7, envy24_set_control, MIXT_ONOFF, "ENVY24_LOCKED", 1, MIXF_READABLE)) < 0) return err; } #endif if (devc->model_data->auxdrv->spdif_mixer_init) if ((err = devc->model_data->auxdrv->spdif_mixer_init (devc, dev, group)) < 0) return err; return 0; } static mixer_driver_t envy24_mixer_driver = { envy24_mixer_ioctl }; static int install_adev (envy24_devc * devc, char *name, int flags, int skip, int portc_flags, char *port_id, char *devfile_name) { int dev, i; adev_p adev; int fmts = 0, last; extern int envy24_realencoder_hack; if (portc_flags & PORTC_SPDOUT) fmts |= AFMT_AC3; if ((dev = oss_install_audiodev_with_devname (OSS_AUDIO_DRIVER_VERSION, devc->osdev, devc->osdev, name, &envy24_audio_driver, sizeof (audiodrv_t), ADEV_AUTOMODE | ADEV_NOMMAP | flags | ADEV_NOVIRTUAL, fmts | AFMT_S16_LE | AFMT_S32_LE | AFMT_S24_LE, devc, -1, devfile_name)) < 0) { dev = -1; return 0; } else { envy24_portc *portc; adev = audio_engines[dev]; if (devc->first_dev == -1) devc->first_dev = dev; for (i = 0; speed_tab[i].speed != -1; i++) adev->rates[adev->nrates++] = speed_tab[i].speed; adev->vmix_flags = 0; if (flags == DIR_OUTPUT) { last = 10; audio_engines[dev]->port_number = devc->curr_outch; audio_engines[dev]->min_rate = 8000; audio_engines[dev]->max_rate = 96000; portc = &devc->play_portc[devc->nr_outdevs++]; portc->chnum = devc->curr_outch; strncpy (portc->name, port_id, sizeof (portc->name) - 1); portc->name[sizeof (portc->name) - 1] = 0; devc->curr_outch += skip; if (portc_flags & PORTC_SPDOUT) audio_engines[dev]->caps |= PCM_CAP_DIGITALOUT; if (portc_flags & PORTC_SPDIN) audio_engines[dev]->caps |= PCM_CAP_DIGITALIN; } else { last = 12; portc = &devc->rec_portc[devc->nr_indevs++]; audio_engines[dev]->port_number = devc->curr_inch + 10; portc->chnum = devc->curr_inch; strncpy (portc->name, port_id, sizeof (portc->name) - 1); portc->name[sizeof (portc->name) - 1] = 0; devc->curr_inch += skip; #ifdef DO_RIAA portc->riaa_filter = 0; #endif } portc->flags = portc_flags; audio_engines[dev]->devc = devc; audio_engines[dev]->portc = portc; audio_engines[dev]->rate_source = devc->first_dev; switch (skip) { case 1: audio_engines[dev]->caps |= DSP_CH_MONO; audio_engines[dev]->min_channels = 1; audio_engines[dev]->max_channels = (envy24_force_mono) ? 1 : last - portc->chnum; break; case 2: audio_engines[dev]->caps |= DSP_CH_STEREO; audio_engines[dev]->min_channels = 1; audio_engines[dev]->max_channels = last - portc->chnum; break; default: audio_engines[dev]->caps |= DSP_CH_MULTI; audio_engines[dev]->min_channels = 1; audio_engines[dev]->max_channels = last - portc->chnum; } audio_engines[dev]->mixer_dev = devc->mixer_dev; portc->dev = dev; portc->open_mode = 0; portc->is_active = 0; portc->direction = flags; portc->trigger_bits = 0; if (envy24_realencoder_hack && flags == DIR_INPUT && devc->nr_indevs > 1) if (oss_install_mixer (OSS_MIXER_DRIVER_VERSION, devc->osdev, devc->osdev, "Dummy mixer", &envy24_mixer_driver, sizeof (mixer_driver_t), devc) >= 0) { mixer_devs[devc->mixer_dev]->priority = -1; /* Don't use as the default mixer */ } } return 1; } static int install_output_devices (envy24_devc * devc, int mask) { char tmp[512], id[32]; int i, nc, portc_flags = 0; char *lr = "", *kind; nc = devc->nr_play_channels = MAX_ODEV; devc->nr_rec_channels = MAX_IDEV; if (devc->skipdevs < 1) devc->skipdevs = 1; for (i = 0; i < nc; i += devc->skipdevs) { char *devfile_name = ""; if (devc->skipdevs != 2) lr = (i & 1) ? "R" : "L"; kind = ""; if (!(mask & (1 << devc->curr_outch))) kind = "(virtual) "; switch (devc->curr_outch) { case 8: case 9: portc_flags = PORTC_SPDOUT; sprintf (tmp, "%s %sS/PDIF out %s", devc->model_data->product, kind, lr); sprintf (id, "SPDIF%s", lr); devfile_name = "spdout"; break; default: if (devc->skipdevs > 1) { sprintf (tmp, "%s %sout%d/%d", devc->model_data->product, kind, i + 1, i + devc->skipdevs); sprintf (id, "OUT%d/%d", i + 1, i + devc->skipdevs); } else { sprintf (tmp, "%s %sout%d", devc->model_data->product, kind, i + 1); sprintf (id, "OUT%d", i + 1); } } if (mask & (1 << devc->curr_outch)) install_adev (devc, tmp, ADEV_NOINPUT, devc->skipdevs, portc_flags, id, devfile_name); else devc->curr_outch += devc->skipdevs; } return 1; } /*ARGSUSED*/ static int install_virtual_output_devices (envy24_devc * devc, int mask) { #if 0 char tmp[512]; int i, nc; char *lr = ""; nc = devc->nr_play_channels = MAX_ODEV; devc->nr_rec_channels = MAX_IDEV; if (envy24_virtualout) { nc = 10; } if (devc->skipdevs < 1) devc->skipdevs = 1; for (i = 0; i < nc; i += devc->skipdevs) { if (devc->skipdevs != 2) lr = (i & 1) ? "R" : "L"; switch (devc->curr_outch) { case 8: case 9: sprintf (tmp, "%s virtual out %s", devc->model_data->product, lr); break; default: if (devc->skipdevs > 1) sprintf (tmp, "%s virtual out%d/%d", devc->model_data->product, i + 1, i + devc->skipdevs); else sprintf (tmp, "%s virtual out%d", devc->model_data->product, i + 1); } if (!(mask & (1 << devc->curr_outch))) /* Not done yet */ install_adev (devc, tmp, ADEV_NOINPUT, devc->skipdevs, 0, "virtual", ""); // TODO: Find better device file name else devc->curr_outch += devc->skipdevs; } #endif return 1; } static int install_input_devices (envy24_devc * devc, int mask) { char tmp[512], id[32]; int i, portc_flags = 0; char *lr = ""; devc->nr_play_channels = MAX_ODEV; devc->nr_rec_channels = MAX_IDEV; if (devc->skipdevs < 1) devc->skipdevs = 1; for (i = 0; i < devc->nr_rec_channels; i += devc->skipdevs) { char *devfile_name = ""; if (devc->skipdevs != 2) lr = (i & 1) ? "R" : "L"; switch (devc->curr_inch) { case 8: case 9: portc_flags = PORTC_SPDIN; sprintf (tmp, "%s S/PDIF in %s", devc->model_data->product, lr); sprintf (id, "SPDIF%s", lr); devfile_name = "spdin"; break; case 10: case 11: sprintf (tmp, "%s input from mon. mixer %s", devc->model_data->product, lr); sprintf (id, "MON%s", lr); devfile_name = "mon"; break; default: if (devc->skipdevs > 1) { sprintf (tmp, "%s in%d/%d", devc->model_data->product, i + 1, i + devc->skipdevs); sprintf (id, "IN%d/%d", i + 1, i + devc->skipdevs); } else { sprintf (tmp, "%s in%d", devc->model_data->product, i + 1); sprintf (id, "IN%d", i + 1); } } if (mask & (1 << devc->curr_inch)) install_adev (devc, tmp, ADEV_NOOUTPUT, devc->skipdevs, portc_flags, id, devfile_name); else devc->curr_inch += devc->skipdevs; } OUTL (devc->osdev, 0x00224466, devc->mt_base + 0x34); /* 1 to 1 input routing */ return 1; } static int install_audio_devices (envy24_devc * devc) { extern int envy24_swapdevs; int maskout, maskin, i; #define setmask(m, b) m|=(1<<(b)) maskout = maskin = 0; if (envy24_devmask == 0) envy24_devmask = 65535; if (envy24_devmask & DMASK_MONITORIN) { setmask (maskin, 10); /* Monitor input left */ setmask (maskin, 11); /* Monitor input right */ } if (devc->model_data->flags & MF_SPDIF) { if (envy24_devmask & DMASK_SPDIFIN) { setmask (maskin, 8); /* S/PDIF left */ setmask (maskin, 9); /* S/PDIF right */ } if (envy24_devmask & DMASK_SPDIFOUT) { setmask (maskout, 8); /* S/PDIF left */ setmask (maskout, 9); /* S/PDIF right */ } if (devc->model_data->auxdrv->spdif_set) devc->model_data->auxdrv->spdif_set (devc, 0x20); } if (envy24_devmask & DMASK_ANALOGOUT) for (i = 0; i < devc->model_data->nr_outs; i++) setmask (maskout, i); if (envy24_devmask & DMASK_ANALOGIN) for (i = 0; i < devc->model_data->nr_ins; i++) setmask (maskin, i); devc->inportmask = maskin; devc->outportmask = maskout; if (envy24_swapdevs) { install_input_devices (devc, maskin); install_output_devices (devc, maskout); install_virtual_output_devices (devc, maskout); } else { install_output_devices (devc, maskout); install_input_devices (devc, maskin); install_virtual_output_devices (devc, maskout); } for (i = 0; i < 10; i += devc->skipdevs) { int num = 1 << i; if (devc->skipdevs == 2) num |= 1 << (i + 1); if (maskout & num) envy24_set_mon (devc->mixer_dev, num, SNDCTL_MIX_WRITE, 1340 | (1340 << 16)); if (maskin & num) envy24_set_mon (devc->mixer_dev, 0x80000000 | num, SNDCTL_MIX_WRITE, 1340 | (1340 << 16)); } if (envy24_devmask & DMASK_RAWDEVS) envy24d_install (devc); return 1; } static int ac97_read (void *devc_, int reg) { envy24_devc *devc = devc_; int i, status; OUTB (devc->osdev, reg, devc->ccs_base + 0x08); OUTB (devc->osdev, reg, devc->ccs_base + 0x08); OUTB (devc->osdev, reg, devc->ccs_base + 0x08); OUTB (devc->osdev, 0x10, devc->ccs_base + 0x09); for (i = 0; i < 1000; i++) { status = INB (devc->osdev, devc->ccs_base + 0x09); if (!(status & 0x10)) { status = INW (devc->osdev, devc->ccs_base + 0x0a); return status; } } return 0xffff; } static int ac97_writereg (void *devc_, int reg, int data) { envy24_devc *devc = devc_; int i, status; OUTB (devc->osdev, reg, devc->ccs_base + 0x08); OUTB (devc->osdev, reg, devc->ccs_base + 0x08); OUTB (devc->osdev, reg, devc->ccs_base + 0x08); OUTB (devc->osdev, 0x20, devc->ccs_base + 0x09); for (i = 0; i < 1000; i++) { status = INB (devc->osdev, devc->ccs_base + 0x09); if (!(status & 0x20)) { OUTW (devc->osdev, data & 0xffff, devc->ccs_base + 0x0a); return 1; } } return 0; } static int ac97_write (void *devc, int reg, int data) { int ret; ac97_writereg (devc, reg, data); ac97_writereg (devc, reg, data); ret = ac97_writereg (devc, reg, data); return ret; } static void install_consumer_devices (envy24_devc * devc) { #if 1 int i, status; OUTB (devc->osdev, 0x80, devc->ccs_base + 0x09); /* Cold reset mixer */ oss_udelay (200); OUTB (devc->osdev, 0x00, devc->ccs_base + 0x09); /* Release reset */ oss_udelay (200); for (i = 0; i < 1000; i++) { status = INB (devc->osdev, devc->ccs_base + 0x09); if (status & 0x80) break; oss_udelay (1000); } if (i >= 1000) { } #endif devc->consumer_mixer_dev = ac97_install (&devc->ac97devc, "Envy24 consumer mixer", ac97_read, ac97_write, devc, devc->osdev); /* Route monitor output to consumer AC97 */ OUTB (devc->osdev, 0x01, devc->mt_base + 0x3c); /* Set consumer volumes to full */ envy24_write_cci (devc, 3, 0); envy24_write_cci (devc, 4, 0); } static int maudio_load_eeprom (envy24_devc * devc) { int status; status = INB (devc->osdev, devc->ccs_base + 0x13); if (!(status & 0x80)) return 0; /* No EEPROM */ envy24_write_cci (devc, 0x22, devc->eeprom[0xc]); /* GPIO direction */ envy24_write_cci (devc, 0x21, devc->eeprom[0xa]); /* GPIO write mask */ envy24_write_cci (devc, 0x20, devc->eeprom[0xb]); /* GPIO data */ return 1; } static int envy24_init (envy24_devc * devc) { extern int envy24_nfrags; oss_native_word phaddr; int err; /* Disable all interrupts */ OUTB (devc->osdev, 0xff, devc->ccs_base + 0x01); OUTB (devc->osdev, 0xff, devc->mt_base + 0x00); if ((err = oss_register_interrupts (devc->osdev, 0, envy24intr, NULL)) < 0) { cmn_err (CE_WARN, "Can't register interrupt handler, err=%d\n", err); return 0; } if (envy24_skipdevs < 1) envy24_skipdevs = 1; if (envy24_skipdevs > 2) envy24_skipdevs = 2; devc->skipdevs = envy24_skipdevs; if (envy24_skipdevs != 1) envy24_force_mono = 0; if (devc->model_data->flags & MF_MIDI1) { char name[128]; oss_native_word flags; sprintf (name, "%s #1", devc->model_data->product); MUTEX_ENTER (devc->mutex, flags); uart401_init (&devc->uart401devc1, devc->osdev, devc->ccs_base + 0x0c, name); MUTEX_EXIT (devc->mutex, flags); /* Enable UART1 interrupts */ OUTB (devc->osdev, INB (devc->osdev, devc->ccs_base + 0x01) & ~0x80, devc->ccs_base + 0x01); } if (devc->model_data->flags & MF_MIDI2) { char name[128]; oss_native_word flags; sprintf (name, "%s #2", devc->model_data->product); MUTEX_ENTER (devc->mutex, flags); uart401_init (&devc->uart401devc2, devc->osdev, devc->ccs_base + 0x1c, name); MUTEX_EXIT (devc->mutex, flags); /* Enable UART2 interrupts */ OUTB (devc->osdev, INB (devc->osdev, devc->ccs_base + 0x01) & ~0x20, devc->ccs_base + 0x01); } devc->speedbits = 0; devc->speed = 48000; devc->pending_speed_sel = 9; if (devc->model_data->flags & (MF_MEEPROM)) maudio_load_eeprom (devc); if (devc->model_data->auxdrv->card_init) devc->model_data->auxdrv->card_init (devc); if (devc->model_data->auxdrv->spdif_init) devc->model_data->auxdrv->spdif_init (devc); if ((devc->mixer_dev = oss_install_mixer (OSS_MIXER_DRIVER_VERSION, devc->osdev, devc->osdev, devc->model_data->product, &envy24_mixer_driver, sizeof (mixer_driver_t), devc)) >= 0) { int n = 50; if (devc->skipdevs == 1) n += 30; mixer_ext_set_init_fn (devc->mixer_dev, envy24_mix_init, n); } if (envy24_nfrags != 2 && envy24_nfrags != 4 && envy24_nfrags != 8 && envy24_nfrags != 16 && envy24_nfrags != 32 && envy24_nfrags != 64) envy24_nfrags = 16; devc->playbuffsize = HW_PLAYBUFFSIZE; devc->recbuffsize = HW_RECBUFFSIZE; devc->hw_nfrags = envy24_nfrags; devc->hw_pfragsize = devc->playbuffsize / devc->hw_nfrags; devc->hw_rfragsize = devc->recbuffsize / devc->hw_nfrags; devc->hw_fragsamples = devc->hw_pfragsize / 40; /* # of 32 bit samples/fragment/channel */ if (devc->hw_pfragsize % 40) cmn_err (CE_WARN, "Error! Bad per channel fragment size\n"); devc->hw_playfrag = 0; devc->hw_recfrag = 0; devc->playbuf = CONTIG_MALLOC (devc->osdev, HW_ALLOCSIZE, MEMLIMIT_28BITS, &phaddr, devc->playbuf_dma_handle); if (devc->playbuf == NULL) { cmn_err (CE_WARN, "Failed to allocate %d bytes of DMA buffer\n", HW_ALLOCSIZE); return 0; } devc->playbuf_phys = phaddr; if ((devc->playbuf_phys + HW_ALLOCSIZE) >= (256 * 1024 * 1024)) { cmn_err (CE_WARN, "Got DMA buffer beyond address 256M.\n"); cmn_err (CE_CONT, "Reboot and try again\n"); return 1; } OUTL (devc->osdev, devc->playbuf_phys, devc->mt_base + 0x10); /* Play base */ devc->recbuf = CONTIG_MALLOC (devc->osdev, HW_ALLOCSIZE, MEMLIMIT_28BITS, &phaddr, devc->recbuf_dma_handle); if (devc->recbuf == NULL) { cmn_err (CE_WARN, "Failed to allocate %d bytes of DMA buffer\n", HW_ALLOCSIZE); return 0; } devc->recbuf_phys = phaddr; if ((devc->recbuf_phys + HW_ALLOCSIZE) >= (256 * 1024 * 1024)) { cmn_err (CE_WARN, "Got DMA buffer beyond address 256M.\n"); cmn_err (CE_CONT, "Reboot and try again\n"); return 1; } OUTL (devc->osdev, devc->recbuf_phys, devc->mt_base + 0x20); /* Rec base */ devc->playback_started = 0; devc->recording_started = 0; devc->playback_prepared = 0; devc->recording_prepared = 0; devc->writeahead = 1; #ifdef __VXWORKS__ devc->ratelock = 0; #else devc->ratelock = 1; #endif memset (devc->playbuf, 0, HW_ALLOCSIZE); memset (devc->recbuf, 0, HW_ALLOCSIZE); install_audio_devices (devc); if (devc->consumer_ac97_present || (devc->model_data->flags & MF_CONSUMER)) install_consumer_devices (devc); /* Enable professional rec/play interrupts */ OUTB (devc->osdev, INB (devc->osdev, devc->ccs_base + 0x01) & ~0x10, devc->ccs_base + 0x01); setup_spdif_control (devc); if (devc->model_data->auxdrv->spdif_set) devc->model_data->auxdrv->spdif_set (devc, 0x20); #if 1 /* Make sure everything is initialized */ envy24_prepare_play_engine (devc); envy24_launch_play_engine (devc); oss_udelay (10000); envy24_stop_playback (devc); #endif #if 0 { char line[200], *s = line; *line = 0; for (i = 0; i < 0x20; i++) { if (!(i % 16)) { if (*line != 0) cmn_err (CE_CONT, "%s\n", line); s = line; sprintf (s, "CCS%02x: ", i); s = line + strlen (line); } sprintf (s, "%02x ", INB (devc->osdev, devc->ccs_base + i)); s = line + strlen (line); } *line = 0; for (i = 0; i < 0x40; i++) { if (!(i % 16)) { if (*line != 0) cmn_err (CE_CONT, "%s\n", line); s = line; sprintf (s, "MT%02x: ", i); s = line + strlen (line); } sprintf (s, "%02x ", INB (devc->osdev, devc->mt_base + i)); s = line + strlen (line); } cmn_err (CE_CONT, "%s\n", line); } #endif return 1; } int oss_envy24_attach (oss_device_t * osdev) { envy24_devc *devc; static int status; unsigned char pci_irq_line; unsigned short pci_command, vendor, device; unsigned int subvendor; unsigned int pci_ioaddr, pci_ioaddr3; int i; char *name = "Generic ENVY24"; DDB (cmn_err (CE_CONT, "Entered Envy24 probe routine\n")); 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_word (osdev, PCI_VENDOR_ID, &vendor); pci_read_config_word (osdev, PCI_DEVICE_ID, &device); if (vendor != ICENSEMBLE_VENDOR_ID || device != ICENSEMBLE_ENVY24_ID) return 0; pci_read_config_dword (osdev, PCI_BASE_ADDRESS_0, &pci_ioaddr); DDB (cmn_err (CE_CONT, "Device found at I/O %x\n", pci_ioaddr)); pci_read_config_dword (osdev, PCI_BASE_ADDRESS_3, &pci_ioaddr3); devc->active_inputs = 0; devc->active_outputs = 0; devc->sync_locked = 1; devc->first_dev = -1; 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, 0x2c, &subvendor); devc->ccs_base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr) & ~0x3; DDB (cmn_err (CE_CONT, "CCS base %x/%lx\n", pci_ioaddr, devc->ccs_base)); devc->mt_base = MAP_PCI_IOADDR (devc->osdev, 3, pci_ioaddr3) & ~0x3; DDB (cmn_err (CE_CONT, "MT base %x/%lx\n", pci_ioaddr3, devc->mt_base)); pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; pci_write_config_word (osdev, PCI_COMMAND, pci_command); /* Reset the chip */ OUTB (devc->osdev, 0x81, devc->ccs_base + 0x00); oss_udelay (200); /* Release reset */ OUTB (devc->osdev, 0x01, devc->ccs_base + 0x00); oss_udelay (200); devc->nr_outdevs = devc->nr_indevs = 0; devc->curr_outch = devc->curr_inch = 0; devc->playbuffsize = 0; devc->recbuffsize = 0; devc->playbuf = devc->recbuf = NULL; status = INB (devc->osdev, devc->ccs_base + 0x13); if (status & 0x80) /* EEPROM present */ { static char resol_tab[] = { 16, 18, 20, 24 }; unsigned char tmpbyte; load_eeprom (devc, subvendor); /* Fix bit 0x80 of EEPROM location 0x07. */ pci_read_config_byte (osdev, 0x61, &tmpbyte); tmpbyte &= 0x80; devc->eeprom[0x07] &= ~0x80; devc->eeprom[0x07] |= tmpbyte; #if 1 devc->eeprom[0x07] |= 0x80; #endif pci_write_config_byte (osdev, 0x60, devc->eeprom[0x06]); pci_write_config_byte (osdev, 0x61, devc->eeprom[0x07]); pci_write_config_byte (osdev, 0x62, devc->eeprom[0x08]); pci_write_config_byte (osdev, 0x63, devc->eeprom[0x09]); #if 1 if (devc->eeprom[0x06] & 0x20) DDB (cmn_err (CE_CONT, "Two MPU401 UARTs present.\n")); if (devc->eeprom[0x06] & 0x10) { DDB (cmn_err (CE_CONT, "Consumer AC97 not present.\n")); } else { DDB (cmn_err (CE_CONT, "Consumer AC97 present.\n")); } DDB (cmn_err (CE_CONT, "%d stereo ADC(s) available\n", ((devc->eeprom[0x06] >> 2) & 0x03) + 1)); DDB (cmn_err (CE_CONT, "%d stereo DAC(s) available\n", ((devc->eeprom[0x06] >> 0) & 0x03) + 1)); DDB (cmn_err (CE_CONT, "MT converter type %s\n", (devc->eeprom[0x07] & 0x80) ? "I2S" : "AC97")); if (devc->eeprom[0x08] & 0x80) { DDB (cmn_err (CE_CONT, "Has I2S volume control and mute\n")); } else { DDB (cmn_err (CE_CONT, "No I2S volume control and mute\n")); } if (devc->eeprom[0x08] & 0x20) DDB (cmn_err (CE_CONT, "Has 96kHz support\n")); DDB (cmn_err (CE_CONT, "Converter resolution %d bits\n", resol_tab[(devc->eeprom[0x08] >> 4) & 0x03])); if (devc->eeprom[0x09] & 0x02) DDB (cmn_err (CE_CONT, "Has S/PDIF in support\n")); if (devc->eeprom[0x09] & 0x01) DDB (cmn_err (CE_CONT, "Has S/PDIF out support\n")); #endif if (subvendor == 0xd6301412) /* Delta 1010 */ if (devc->eeprom[0xc] == 0x7b) /* Looks like Delta 1010 rev E */ subvendor = 0xd63014ff; /* Delta 1010E */ #if 1 if (!(devc->eeprom[0x07] & 0x80)) { cmn_err (CE_WARN, "Cards with AC97 codecs are not supported\n"); return 0; } #endif } i = 0; while (models[i].svid != 0) { if (models[i].svid == subvendor) { name = models[i].product; devc->model_data = &models[i]; if (devc->model_data->auxdrv == NULL) devc->model_data->auxdrv = &default_auxdrv; DDB (cmn_err (CE_CONT, "Card id '%s'\n", name)); break; } i++; } if (devc->model_data->flags & MF_AC97) devc->consumer_ac97_present = 1; if (models[i].svid == 0) { cmn_err (CE_NOTE, "Unknown device ID (%08x).\n", subvendor); cmn_err (CE_NOTE, "This card is not supported (yet).\n"); return 0; } MUTEX_INIT (osdev, devc->mutex, MH_DRV); oss_register_device (osdev, name); devc->irq = pci_irq_line; return envy24_init (devc); /* Detected */ } int oss_envy24_detach (oss_device_t * osdev) { envy24_devc *devc; devc = osdev->devc; if (oss_disable_device (osdev) < 0) return 0; /* Disable all interrupts */ OUTB (devc->osdev, 0xff, devc->ccs_base + 0x01); /* Stop playback */ OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) & ~0x01, devc->mt_base + 0x18); oss_udelay (100); OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) & ~0x01, devc->mt_base + 0x18); /* Stop recording */ OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) & ~0x04, devc->mt_base + 0x18); oss_udelay (100); OUTB (devc->osdev, INB (devc->osdev, devc->mt_base + 0x18) & ~0x04, devc->mt_base + 0x18); if (devc->model_data->flags & MF_MIDI1) uart401_disable (&devc->uart401devc1); if (devc->model_data->flags & MF_MIDI2) uart401_disable (&devc->uart401devc2); oss_unregister_interrupts (osdev); MUTEX_CLEANUP (devc->mutex); UNMAP_PCI_IOADDR (devc->osdev, 0); UNMAP_PCI_IOADDR (devc->osdev, 3); if (devc->playbuf != NULL) CONTIG_FREE (devc->osdev, devc->playbuf, HW_ALLOCSIZE, devc->playbuf_dma_handle); if (devc->recbuf != NULL) CONTIG_FREE (devc->osdev, devc->recbuf, HW_ALLOCSIZE, devc->recbuf_dma_handle); devc->playbuf = devc->recbuf = NULL; oss_unregister_device (osdev); return 1; }