Open Sound System |
Do you have problems with sound/audio application development? Don't panic! Click here for help! |
This file is part of Open Sound System.
Copyright (C) 4Front Technologies 1996-2008.
This this source file is released under GPL v2 license (no other versions). See the COPYING file included in the main directory of this source distribution for the license terms and conditions.
#include "oss_cmpci_cfg.h" #include "oss_pci.h" #include "uart401.h" #define CMEDIA_VENDOR_ID 0x13F6 #define CMEDIA_CM8738 0x0111 #define CMEDIA_CM8338A 0x0100 #define CMEDIA_CM8338B 0x0101
CM8338 registers definition
#define FUNCTRL0 (devc->base+0x00) #define FUNCTRL1 (devc->base+0x04) #define CHFORMAT (devc->base+0x08) #define INT_HLDCLR (devc->base+0x0C) #define INT_STATUS (devc->base+0x10) #define LEGACY_CTRL (devc->base+0x14) #define MISC_CTRL (devc->base+0x18) #define TDMA_POS (devc->base+0x1C) #define MIXER (devc->base+0x20) #define MIXER_DATA (devc->base+0x22) #define MIXER_ADDR (devc->base+0x23) #define MIXER1 (devc->base+0x24) #define MIXER2 (devc->base+0x25) #define AUX_VOL (devc->base+0x26) #define MIXER3 (devc->base+0x27) #define AC97 (devc->base+0x28) #define CH0_FRAME1 (devc->base+0x80) #define CH0_FRAME2 (devc->base+0x84) #define CH1_FRAME1 (devc->base+0x88) #define CH1_FRAME2 (devc->base+0x8C) #define SPDIF_STAT (devc->base+0x90) #define MISC2_CTRL (devc->base+0x92) #define MPU_MIRROR (devc->base+0x40) #define FM_MIRROR (devc->base+0x50) #define JOY_MIRROR (devc->base+0x60) #define DSP_MIX_DATARESETIDX (0x00) #define DSP_MIX_OUTMIXIDX (0x3c) #define CM_CH0_ENABLE 0x01 #define CM_CH1_ENABLE 0x02 #define CM_CH0_RESET 0x04 #define CM_CH1_RESET 0x08 #define CM_CH0_RECORD 0x01 #define CM_CH1_RECORD 0x02 #define CM_CH0_PLAY ~0x01 #define CM_CH1_PLAY ~0x02 #define CM_CH0_INT 1 #define CM_CH1_INT 2 #define CM_EXTENT_CODEC 0x100 #define CM_EXTENT_MIDI 0x2 #define CM_EXTENT_SYNTH 0x4 #define CM_CFMT_STEREO 0x01 #define CM_CFMT_16BIT 0x02 #define CM_CFMT_MASK 0x03 #define CM_CFMT_DACSHIFT 0 #define CM_CFMT_ADCSHIFT 2 #define MUTE_LINE 1 #define MUTE_CD 2 #define MUTE_MIC 3 #define MUTE_AUX 4 #define REAR2LINE 5 #define CEN2LINE 6 #define BASS2LINE 7 #define CEN2MIC 8 #define MODE_4SPK 9 #define DUALDAC 10 #define MICBOOST 11 #define SPDIF_PLAY 12 #define SPDIF_LOOP 13 #define SPDIF_REC 14 #define SPDIF_IMON 15 #define SPDIF_POL 16 #define SPDIF_COPY 17 #define SPDIF_OPT 18 #define SPDIF_AC3 19 #define AUX_REC 20 #define AUX_LEVEL 21 #define CHAN0 0x1 #define CHAN1 0x2 static struct { unsigned int rate; unsigned int lower; unsigned int upper; unsigned char freq; } rate_lookup[] = { { 5512, (0 + 5512) / 2, (5512 + 8000) / 2, 0} , { 8000, (5512 + 8000) / 2, (8000 + 11025) / 2, 4} , { 11025, (8000 + 11025) / 2, (11025 + 16000) / 2, 1} , { 16000, (11025 + 16000) / 2, (16000 + 22050) / 2, 5} , { 22050, (16000 + 22050) / 2, (22050 + 32000) / 2, 2} , { 32000, (22050 + 32000) / 2, (32000 + 44100) / 2, 6} , { 44100, (32000 + 44100) / 2, (44100 + 48000) / 2, 3} , { 48000, (48000 + 44100) / 2, (48000 + 96000) / 2, 7} }; static unsigned char cmpci_recmasks_L[SOUND_MIXER_NRDEVICES] = { 0x00, /* SOUND_MIXER_VOLUME */ 0x00, /* SOUND_MIXER_BASS */ 0x00, /* SOUND_MIXER_TREBLE */ 0x40, /* SOUND_MIXER_SYNTH */ 0x00, /* SOUND_MIXER_PCM */ 0x00, /* SOUND_MIXER_SPEAKER */ 0x10, /* SOUND_MIXER_LINE */ 0x01, /* SOUND_MIXER_MIC */ 0x04, /* SOUND_MIXER_CD */ 0x00, /* SOUND_MIXER_IMIX */ 0x00, /* SOUND_MIXER_LINE1 */ 0x00, /* SOUND_MIXER_RECLEV */ 0x00, /* SOUND_MIXER_IGAIN */ 0x00 /* SOUND_MIXER_OGAIN */ }; static unsigned char cmpci_recmasks_R[SOUND_MIXER_NRDEVICES] = { 0x00, /* SOUND_MIXER_VOLUME */ 0x00, /* SOUND_MIXER_BASS */ 0x00, /* SOUND_MIXER_TREBLE */ 0x20, /* SOUND_MIXER_SYNTH */ 0x00, /* SOUND_MIXER_PCM */ 0x00, /* SOUND_MIXER_SPEAKER */ 0x08, /* SOUND_MIXER_LINE */ 0x01, /* SOUND_MIXER_MIC */ 0x02, /* SOUND_MIXER_CD */ 0x00, /* SOUND_MIXER_IMIX */ 0x00, /* SOUND_MIXER_LINE1 */ 0x00, /* SOUND_MIXER_RECLEV */ 0x00, /* SOUND_MIXER_IGAIN */ 0x00 /* SOUND_MIXER_OGAIN */ }; #define CMEDIA_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | \ SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1) #define CMEDIA_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | \ SOUND_MASK_LINE | SOUND_MASK_MIC | \ SOUND_MASK_IGAIN | SOUND_MASK_CD | \ SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER |\ SOUND_MASK_LINE1|SOUND_MASK_RECLEV) #define LEFT_CHN 0 #define RIGHT_CHN 1
Mixer registers of CMPCI
#define CMPCI_IMASK_L 0x3d #define CMPCI_IMASK_R 0x3e int default_levels[32] = { 0x5a5a, /* Master Volume */ 0x4b4b, /* Bass */ 0x4b4b, /* Treble */ 0x4b4b, /* FM */ 0x4b4b, /* PCM */ 0x4b4b, /* PC Speaker */ 0x4b4b, /* Ext Line */ 0x2020, /* Mic */ 0x4b4b, /* CD */ 0x0000, /* Recording monitor */ 0x4b4b, /* SB PCM */ 0x4b4b, /* Recording level */ 0x4b4b, /* Input gain */ 0x4b4b, /* Output gain */ 0x4040, /* Line1 */ 0x4040, /* Line2 */ 0x1515 /* Line3 */ }; typedef struct cmpci_portc { int speed, bits, channels; int open_mode; int trigger_bits; int audio_enabled; int audiodev; int dacfmt, adcfmt; int chan0_play, chan0_rec; int chan1_play, chan1_rec; } cmpci_portc; #define MAX_PORTC 2 typedef struct cmpci_devc { oss_device_t *osdev; oss_native_word base; int fm_attached; int irq; int max_channels; volatile unsigned char intr_mask; int model; #define MDL_CM8738 1 #define MDL_CM8338A 2 #define MDL_CM8338B 3 #define MDL_CM8768 4 char *chip_name; int chiprev; int mode_4spk; int dev_mode; #define DEFAULT_MODE 1 #define DUALDAC_MODE 2 #define SPDIFIN_MODE 4 unsigned char spdif_control_bits[24]; /* Audio parameters */ oss_mutex_t mutex; oss_mutex_t low_mutex; int open_mode; int audio_initialized; cmpci_portc portc[MAX_PORTC]; /* spdif/ac3 stuff */ int spdif_enabled; int can_ac3; /* Mixer parameters */ int mixer_dev; int *levels; int recmask; /* uart401 */ int uart401_attached; uart401_devc uart401devc; } cmpci_devc; static void set_spdif_rate (cmpci_devc * devc, cmpci_portc * portc) { if (portc->speed == 48000) { /* setup MISC control reg */ OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 24) | (1 << 15), MISC_CTRL); } else { OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 15), MISC_CTRL); OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 24), MISC_CTRL); } } static void setup_ac3 (cmpci_devc * devc, cmpci_portc * portc, int value) { if (value && (portc->speed == 48000 || portc->speed == 44100)) { if (devc->chiprev == 37) { OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) | (1 << 20), CHFORMAT); } else { /* Enable AC3 */ OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 18), MISC_CTRL); } } else { if (devc->chiprev == 37) { OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~(1 << 20), CHFORMAT); } else { /* Disable AC3 */ OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 18), MISC_CTRL); } } } static void cmpci_setmixer (cmpci_devc * devc, unsigned int port, unsigned int value) { oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); OUTB (devc->osdev, (unsigned char) (port & 0xff), MIXER_ADDR); oss_udelay (20); OUTB (devc->osdev, (unsigned char) (value & 0xff), MIXER_DATA); MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); oss_udelay (20); } static unsigned int cmpci_getmixer (cmpci_devc * devc, unsigned int port) { unsigned int val; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); OUTB (devc->osdev, (unsigned char) (port & 0xff), MIXER_ADDR); oss_udelay (20); val = INB (devc->osdev, MIXER_DATA); MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); oss_udelay (20); return val; } struct mixer_def { unsigned int regno:8; unsigned int bitoffs:4; unsigned int nbits:4; }; typedef struct mixer_def mixer_tab[32][2]; typedef struct mixer_def mixer_ent; #define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \ {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}} static mixer_tab cmpci_mix = { MIX_ENT (SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5), MIX_ENT (SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4), MIX_ENT (SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4), MIX_ENT (SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5), MIX_ENT (SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5), MIX_ENT (SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0), MIX_ENT (SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5), MIX_ENT (SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0), MIX_ENT (SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5), MIX_ENT (SOUND_MIXER_IMIX, 0x3c, 0, 1, 0x00, 0, 0), MIX_ENT (SOUND_MIXER_LINE1, 0, 0, 0, 0, 0, 0), MIX_ENT (SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2), MIX_ENT (SOUND_MIXER_IGAIN, 0x3f, 7, 2, 0x40, 7, 2), MIX_ENT (SOUND_MIXER_OGAIN, 0x41, 7, 2, 0x42, 7, 2) }; /*ARGSUSED*/ static void change_bits (cmpci_devc * devc, unsigned char *regval, int dev, int chn, int newval) { unsigned char mask; int shift; mask = (1 << cmpci_mix[dev][chn].nbits) - 1; newval = (int) ((newval * mask) + 50) / 100; /* Scale */ shift = cmpci_mix[dev][chn].bitoffs - cmpci_mix[dev][LEFT_CHN].nbits + 1; *regval &= ~(mask << shift); /* Mask out previous value */ *regval |= (newval & mask) << shift; /* Set the new value */ } static int set_recmask (int dev, int mask); static int cmpci_mixer_set (cmpci_devc * devc, int chan, int value) { int left = value & 0x000000ff; int right = (value & 0x0000ff00) >> 8; int regoffs; unsigned char val; if (left > 100) left = 100; if (right > 100) right = 100; if (chan > 31) return OSS_EINVAL;
Not supported
return OSS_EINVAL; regoffs = cmpci_mix[chan][LEFT_CHN].regno; if (regoffs == 0) return OSS_EINVAL; val = cmpci_getmixer (devc, regoffs); change_bits (devc, &val, chan, LEFT_CHN, left); devc->levels[chan] = left | (left << 8);
Change register
{
Save the old one
regoffs = cmpci_mix[chan][RIGHT_CHN].regno; if (regoffs == 0)
Just left channel present
Read the new one
} change_bits (devc, &val, chan, RIGHT_CHN, right); cmpci_setmixer (devc, regoffs, val); devc->levels[chan] = left | (right << 8); return left | (right << 8); } static int cmpci_outsw (int dev, int ctrl, unsigned int cmd, int value); /*ARGSUSED*/ static int cmpci_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg) { cmpci_devc *devc = mixer_devs[dev]->devc; int val; if (((cmd >> 8) & 0xff) == 'M') { if (IOC_IS_OUTPUT (cmd)) switch (cmd & 0xff) { case SOUND_MIXER_RECSRC: val = *arg; if (val == SOUND_MASK_LINE1) *arg = cmpci_outsw (dev, AUX_REC, SNDCTL_MIX_WRITE, 1); else *arg = cmpci_outsw (dev, AUX_REC, SNDCTL_MIX_WRITE, 0); return *arg = set_recmask (dev, val); break; case SOUND_MIXER_LINE1: val = *arg; return *arg = cmpci_outsw (dev, AUX_LEVEL, SNDCTL_MIX_WRITE, val); break; default: val = *arg; return *arg = cmpci_mixer_set (devc, cmd & 0xff, val); } else switch (cmd & 0xff) { case SOUND_MIXER_RECSRC: return *arg = devc->recmask; break; case SOUND_MIXER_DEVMASK: return *arg = CMEDIA_MIXER_DEVICES; break; case SOUND_MIXER_STEREODEVS: return *arg = CMEDIA_MIXER_DEVICES & ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); break; case SOUND_MIXER_RECMASK: return *arg = CMEDIA_RECORDING_DEVICES; break; case SOUND_MIXER_LINE1: val = cmpci_outsw (dev, AUX_LEVEL, SNDCTL_MIX_READ, 0); return *arg = val; break; case SOUND_MIXER_CAPS: return *arg = SOUND_CAP_EXCL_INPUT; break; default: return *arg = devc->levels[cmd & 0x1f]; } } else return OSS_EINVAL; } static int set_recmask (int dev, int mask) { cmpci_devc *devc = mixer_devs[dev]->devc; int devmask = mask & CMEDIA_RECORDING_DEVICES; int i; unsigned char regimageL, regimageR; if (!devmask) devmask = SOUND_MASK_MIC; regimageL = regimageR = 0; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if ((1 << i) & devmask) { regimageL |= cmpci_recmasks_L[i]; regimageR |= cmpci_recmasks_R[i]; } cmpci_setmixer (devc, CMPCI_IMASK_L, regimageL); cmpci_setmixer (devc, CMPCI_IMASK_R, regimageR); devc->recmask = devmask; return devc->recmask; } static int cmpci_outsw (int dev, int ctrl, unsigned int cmd, int value) {
Access function for CMPCI mixer extension bits
cmpci_devc *devc = mixer_devs[dev]->devc; int left, right, tmp; if (cmd == SNDCTL_MIX_READ) { value = 0; switch (ctrl) { case MUTE_LINE: /* Left line in to output connection */ value = (cmpci_getmixer (devc, 0x3c) & 0x18) ? 0 : 1; break; case MUTE_CD: /* Cd in to output connection */ value = (cmpci_getmixer (devc, 0x3c) & 0x06) ? 0 : 1; break; case MUTE_MIC: /* Mic in to output connection */ value = (cmpci_getmixer (devc, 0x3c) & 0x01) ? 0 : 1; break; case MODE_4SPK: /* 4Speaker out */ value = INL (devc->osdev, MISC_CTRL) & (1 << 26) ? 1 : 0; break; case DUALDAC: /* dual dac */ value = devc->dev_mode & DUALDAC_MODE ? 1 : 0; break; case REAR2LINE: /* rear to line in */ value = INB (devc->osdev, MIXER1) & (1 << 5) ? 1 : 0; break; case CEN2LINE: /* center to line in */ value = INL (devc->osdev, LEGACY_CTRL) & (1 << 14) ? 1 : 0; break; case BASS2LINE: /* basss to line in */ value = INL (devc->osdev, LEGACY_CTRL) & (1 << 13) ? 1 : 0; break; case SPDIF_PLAY: /* spdif out */ value = INL (devc->osdev, LEGACY_CTRL) & (1 << 23) ? 1 : 0; break; case SPDIF_LOOP: /* S/PDIF I/O Loop */ value = (INL (devc->osdev, FUNCTRL1) & (1 << 7)) ? 1 : 0; break; case SPDIF_REC: /* spdif record mode */ value = devc->dev_mode & SPDIFIN_MODE ? 1 : 0; break; case SPDIF_IMON: /* spdif input monitor */ value = INB (devc->osdev, MIXER1) & 0x1 ? 1 : 0; break; case SPDIF_POL: /* spdif input reverse */ if (devc->chiprev < 39) value = INB (devc->osdev, MIXER3) & 0x06 ? 1 : 0; else value = INL (devc->osdev, CHFORMAT) & 0x80 ? 1 : 0; break; case SPDIF_AC3: /* ac3 */ value = INL (devc->osdev, MISC_CTRL) & (1 << 18) ? 1 : 0; break; case SPDIF_COPY: /* copy protect (indirect) */ value = INL (devc->osdev, LEGACY_CTRL) & (1 << 22) ? 1 : 0; break; case SPDIF_OPT: /* Coax/Optical Select */ value = INL (devc->osdev, MISC_CTRL) & (1 << 25) ? 1 : 0; break; case CEN2MIC: /* Center2MIC */ if (devc->chiprev >= 39) value = INB (devc->osdev, MIXER3) & 0x4 ? 1 : 0; break; case MICBOOST: /* MIC Boost */ value = INB (devc->osdev, MIXER2) & 0x1 ? 0 : 1; break; case AUX_LEVEL: value = devc->levels[SOUND_MIXER_LINE1]; break; case AUX_REC: /* set LINE1 as rec source - handled by set_recmask */ break; case MUTE_AUX: /* AUX mute */ value = INB (devc->osdev, MIXER2) & 0x30 ? 0 : 1; break; default: return OSS_EINVAL; } return value; } if (cmd == SNDCTL_MIX_WRITE) { switch (ctrl) { case MUTE_LINE: /* L/R line in to output connection */ if (!value) cmpci_setmixer (devc, 0x3c, cmpci_getmixer (devc, 0x3c) | 0x18); else cmpci_setmixer (devc, 0x3c, cmpci_getmixer (devc, 0x3c) & ~0x18); break; case MUTE_CD: /* Cd in to output connection */ if (!value) cmpci_setmixer (devc, 0x3c, cmpci_getmixer (devc, 0x3c) | 0x06); else cmpci_setmixer (devc, 0x3c, cmpci_getmixer (devc, 0x3c) & ~0x06); break; case MUTE_MIC: /* Mic in to output connection */ if (!value) cmpci_setmixer (devc, 0x3c, cmpci_getmixer (devc, 0x3c) | 0x01); else cmpci_setmixer (devc, 0x3c, cmpci_getmixer (devc, 0x3c) & ~0x01); break; case MODE_4SPK: /* 4Speaker out */ if (value) { OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 26), MISC_CTRL); devc->mode_4spk = 1; } else OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 26), MISC_CTRL); devc->mode_4spk = 0; break; case DUALDAC: /* DUAL DAC mode */ if (value) { OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 23), MISC_CTRL); /* Disable 4Speaker mode */ OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 26), MISC_CTRL); devc->dev_mode = DUALDAC_MODE; } else { OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 23), MISC_CTRL); /* enable back the 4Speaker mode */ OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | 1 << 26, MISC_CTRL); devc->dev_mode = DEFAULT_MODE; } break; case REAR2LINE: /* REAR TO LINEIN */ if (value) { OUTB (devc->osdev, (INB (devc->osdev, MIXER1) | (1 << 5)), MIXER1); } else OUTB (devc->osdev, (INB (devc->osdev, MIXER1) & ~(1 << 5)), MIXER1); break; case CEN2LINE: /* CENTER TO LINEIN */ if (value) { OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 14), LEGACY_CTRL); } else OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 14), LEGACY_CTRL); break; case BASS2LINE: /* BASS TO LINEIN */ if (value) { OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 13), LEGACY_CTRL); } else OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 13), LEGACY_CTRL); break; case SPDIF_PLAY: /* SPDIF ENABLE */ if (value) { OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 23), LEGACY_CTRL); /* enable wave/fm/midi to spdif OUT DAC2SPDO on rev 33/37 */ if (devc->chiprev < 39) OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 21), LEGACY_CTRL); devc->spdif_enabled = 1; } else { OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 23), LEGACY_CTRL); /* Disable wave/fm/midi to spdif OUT (DAC2SPDO) */ if (devc->chiprev < 39) OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 21), LEGACY_CTRL); devc->spdif_enabled = 0; } break; case SPDIF_LOOP: /* S/PDIF I/O Loop */ if (value) { OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) | (1 << 7), FUNCTRL1); } else OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) & ~(1 << 7), FUNCTRL1); break; case SPDIF_REC: /* SPDIF Record Mode */ if (value) { devc->dev_mode = SPDIFIN_MODE; } else devc->dev_mode = DEFAULT_MODE; break; case SPDIF_IMON: /* spdif monitor */ if (value) { OUTB (devc->osdev, INB (devc->osdev, MIXER1) | 0x0D, MIXER1); } else OUTB (devc->osdev, INB (devc->osdev, MIXER1) & ~0xD, MIXER1); break; case SPDIF_POL: /* spdif reverse */ if (value) { if (devc->chiprev < 39) { OUTB (devc->osdev, INB (devc->osdev, MIXER3) | 0x06, MIXER3); } else OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) | 0x80, CHFORMAT); } else { if (devc->chiprev < 39) { OUTB (devc->osdev, INB (devc->osdev, MIXER3) & ~0x06, MIXER3); } else OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~0x80, CHFORMAT); } break; case SPDIF_AC3: /* AC3 enabled on S/PDIF */ if (value) { OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 18), MISC_CTRL); } else OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 18), MISC_CTRL); break; case SPDIF_COPY: /* Copy protect */ if (value) { OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 22), LEGACY_CTRL); } else OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 22), LEGACY_CTRL); break; case SPDIF_OPT: /* Coax/Optical */ if (value) { OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 25), MISC_CTRL); } else OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 25), MISC_CTRL); break; case CEN2MIC: /* Center -> Mic OUT */ if (value) { if (devc->chiprev >= 39) { OUTB (devc->osdev, INB (devc->osdev, MIXER3) | 0x4, MIXER3); } } else if (devc->chiprev >= 39) { OUTB (devc->osdev, INB (devc->osdev, MIXER3) & ~0x4, MIXER3); } break; case MICBOOST: /* Mic Boost */ if (!value) { OUTB (devc->osdev, INB (devc->osdev, MIXER2) | 0x1, MIXER2); } else OUTB (devc->osdev, INB (devc->osdev, MIXER2) & ~0x1, MIXER2); break; case AUX_LEVEL: /* Aux levels */ left = value & 0xff; right = (value >> 8) & 0xff; if (left > 100) left = 100; if (right > 100) right = 100; value = left | (right << 8); left = mix_cvt[left]; right = mix_cvt[right]; tmp = ((right * ((1 << 4) - 1) / 100) << 4) | (left * ((1 << 4) - 1) / 100); OUTB (devc->osdev, tmp, AUX_VOL); devc->levels[SOUND_MIXER_LINE1] = value; break; case AUX_REC: /* line1 record select */ if (value) { OUTB (devc->osdev, INB (devc->osdev, MIXER2) | 0xc0, MIXER2); } else OUTB (devc->osdev, INB (devc->osdev, MIXER2) & ~0xc0, MIXER2); break; case MUTE_AUX: /* line1 mute control */ if (!value) { OUTB (devc->osdev, INB (devc->osdev, MIXER2) | 0x30, MIXER2); } else OUTB (devc->osdev, INB (devc->osdev, MIXER2) & ~0x30, MIXER2); break; default: return OSS_EINVAL; } return (value); } return OSS_EINVAL; } static int cmpci_mix_init (int dev) { int group, err; if ((group = mixer_ext_create_group (dev, 0, "CMPCI_MUTECTL")) < 0) return group; if ((err = mixer_ext_create_control (dev, group, MUTE_LINE, cmpci_outsw, MIXT_ONOFF, "CMPCI_LINEMUTE", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, MUTE_CD, cmpci_outsw, MIXT_ONOFF, "CMPCI_CDMUTE", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, MUTE_MIC, cmpci_outsw, MIXT_ONOFF, "CMPCI_MICMUTE", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, MUTE_AUX, cmpci_outsw, MIXT_ONOFF, "CMPCI_LINE1MUTE", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((group = mixer_ext_create_group (dev, 0, "CMPCI_JACKCTL")) < 0) return group; if ((err = mixer_ext_create_control (dev, group, REAR2LINE, cmpci_outsw, MIXT_ONOFF, "CMPCI_REAR2LINE", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, CEN2LINE, cmpci_outsw, MIXT_ONOFF, "CMPCI_CEN2LINE", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, BASS2LINE, cmpci_outsw, MIXT_ONOFF, "CMPCI_BASS2LINE", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, CEN2MIC, cmpci_outsw, MIXT_ONOFF, "CMPCI_CEN2MIC", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((group = mixer_ext_create_group (dev, 0, "CMPCI_MIXEXT")) < 0) return group; if ((err = mixer_ext_create_control (dev, group, MODE_4SPK, cmpci_outsw, MIXT_ENUM, "CMPCI_SPKMODE", 2, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, DUALDAC, cmpci_outsw, MIXT_ONOFF, "CMPCI_DUALDAC", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, MICBOOST, cmpci_outsw, MIXT_ONOFF, "CMPCI_MICBOOST", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; /* Create a new SPDIF group */ if ((group = mixer_ext_create_group (dev, 0, "CMPCI_SPDIF")) < 0) return group; if ((err = mixer_ext_create_control (dev, group, SPDIF_PLAY, cmpci_outsw, MIXT_ONOFF, "CMPCI_Play", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, SPDIF_LOOP, cmpci_outsw, MIXT_ONOFF, "CMPCI_LOOP", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; /* Having this in mixer doesn't make any sense */ if ((err = mixer_ext_create_control (dev, group, SPDIF_REC, cmpci_outsw, MIXT_ONOFF, "CMPCI_RECORD", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, SPDIF_IMON, cmpci_outsw, MIXT_ONOFF, "CMPCI_IMON", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, SPDIF_POL, cmpci_outsw, MIXT_ONOFF, "CMPCI_POLREV", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; #if 0 /* Having this in mixer doesn't make any sense */ if ((err = mixer_ext_create_control (dev, group, SPDIF_AC3, cmpci_outsw, MIXT_ONOFF, "CMPCI_AC3", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; #endif if ((err = mixer_ext_create_control (dev, group, SPDIF_COPY, cmpci_outsw, MIXT_ONOFF, "CMPCI_COPYPROT", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, SPDIF_OPT, cmpci_outsw, MIXT_ONOFF, "CMPCI_OPTICAL", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; return 0; } static void cmpci_mixer_reset (int dev) { int i; cmpci_devc *devc = mixer_devs[dev]->devc; devc->levels = load_mixer_volumes ("CM8738_Mixer", default_levels, 1); devc->recmask = 0; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) cmpci_mixer_set (devc, i, devc->levels[i]); set_recmask (dev, SOUND_MASK_MIC); } static mixer_driver_t cmpci_mixer_driver = { cmpci_mixer_ioctl }; static int cmpciintr (oss_device_t * osdev) { cmpci_devc *devc = (cmpci_devc *) osdev->devc; unsigned int intstat, intsrc; int i; int serviced = 0; /* see if this is our interrupt */ intsrc = INL (devc->osdev, INT_STATUS); if (intsrc & (CM_CH0_INT | CM_CH1_INT)) { /* Handle playback */ serviced = 1; intstat = INB (devc->osdev, INT_HLDCLR + 2); for (i = 0; i < MAX_PORTC; i++) { cmpci_portc *portc = &devc->portc[i]; if (intstat & CM_CH1_INT) { /* do chan1 playback */ if ((portc->chan1_play) && (portc->trigger_bits & PCM_ENABLE_OUTPUT)) { OUTB (devc->osdev, intstat & ~CM_CH1_INT, INT_HLDCLR + 2); OUTB (devc->osdev, intstat | CM_CH1_INT, INT_HLDCLR + 2); oss_audio_outputintr (portc->audiodev, 0); } /* do chan1 record */ if ((portc->chan1_rec) && (portc->trigger_bits & PCM_ENABLE_INPUT)) { OUTB (devc->osdev, intstat & ~CM_CH1_INT, INT_HLDCLR + 2); OUTB (devc->osdev, intstat | CM_CH1_INT, INT_HLDCLR + 2); oss_audio_inputintr (portc->audiodev, 0); } } if (intstat & CM_CH0_INT) { /* do chan0 playback */ if ((portc->chan0_play) && (portc->trigger_bits & PCM_ENABLE_OUTPUT)) { OUTB (devc->osdev, intstat & ~CM_CH0_INT, INT_HLDCLR + 2); OUTB (devc->osdev, intstat | CM_CH0_INT, INT_HLDCLR + 2); oss_audio_outputintr (portc->audiodev, 0); } /* do chan0 record */ if ((portc->chan0_rec) && (portc->trigger_bits & PCM_ENABLE_INPUT)) { OUTB (devc->osdev, intstat & ~CM_CH0_INT, INT_HLDCLR + 2); OUTB (devc->osdev, intstat | CM_CH0_INT, INT_HLDCLR + 2); oss_audio_inputintr (portc->audiodev, 0); } } } } if (intsrc & 0x10000) { serviced = 1; uart401_irq (&devc->uart401devc); } return serviced; } static int cmpci_audio_set_rate (int dev, int arg) { cmpci_portc *portc = audio_engines[dev]->portc; if (arg == 0) return portc->speed; if (arg > 48000) arg = 48000; if (arg < 5000) arg = 5000; portc->speed = arg; return portc->speed; } static short cmpci_audio_set_channels (int dev, short arg) { cmpci_devc *devc = audio_engines[dev]->devc; cmpci_portc *portc = audio_engines[dev]->portc; if (devc->model == MDL_CM8768) { if (arg>8) arg=8; if ((arg != 1) && (arg != 2) && (arg != 4) && (arg != 6) && (arg != 8)) return portc->channels; } else { if (arg>6) arg=6; if ((arg != 1) && (arg != 2) && (arg != 4) && (arg != 6)) return portc->channels; } portc->channels = arg; return portc->channels; } static unsigned int cmpci_audio_set_format (int dev, unsigned int arg) { cmpci_portc *portc = audio_engines[dev]->portc; if (arg == 0) return portc->bits; if (!(arg & (AFMT_U8 | AFMT_S16_LE | AFMT_AC3))) return portc->bits; portc->bits = arg; return portc->bits; } /*ARGSUSED*/ static int cmpci_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg) { return OSS_EINVAL; } static void cmpci_audio_trigger (int dev, int state); static void cmpci_audio_reset (int dev) { cmpci_audio_trigger (dev, 0); } static void cmpci_audio_reset_input (int dev) { cmpci_portc *portc = audio_engines[dev]->portc; cmpci_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT); } static void cmpci_audio_reset_output (int dev) { cmpci_portc *portc = audio_engines[dev]->portc; cmpci_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT); } /*ARGSUSED*/ static int cmpci_audio_open (int dev, int mode, int open_flags) { cmpci_portc *portc = audio_engines[dev]->portc; cmpci_devc *devc = audio_engines[dev]->devc; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (portc->open_mode) { MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return OSS_EBUSY; } if (!(devc->dev_mode & DUALDAC_MODE)) { 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 cmpci_audio_close (int dev, int mode) { cmpci_portc *portc = audio_engines[dev]->portc; cmpci_devc *devc = audio_engines[dev]->devc; cmpci_audio_reset (dev); portc->open_mode = 0; devc->open_mode &= ~mode; portc->audio_enabled &= ~mode; if ((devc->spdif_enabled) || (devc->dev_mode & SPDIFIN_MODE)) OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) & ~(1 << 9), FUNCTRL1); if (portc->chan0_play) portc->chan0_play = 0; if (portc->chan1_play) portc->chan1_play = 0; if (portc->chan0_rec) portc->chan0_rec = 0; if (portc->chan1_rec) portc->chan1_rec = 0; } /*ARGSUSED*/ static void cmpci_audio_output_block (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { cmpci_portc *portc = audio_engines[dev]->portc; portc->audio_enabled |= PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; } /*ARGSUSED*/ static void cmpci_audio_start_input (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { cmpci_portc *portc = audio_engines[dev]->portc; portc->audio_enabled |= PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; } static void cmpci_audio_trigger (int dev, int state) { cmpci_portc *portc = audio_engines[dev]->portc; cmpci_devc *devc = audio_engines[dev]->devc; 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)) { if (portc->chan0_play) { /* enable the channel0 */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) | CM_CH0_ENABLE, FUNCTRL0 + 2); OUTB (devc->osdev, INB (devc->osdev, INT_HLDCLR + 2) | CM_CH0_INT, INT_HLDCLR + 2); } if (portc->chan1_play) { /* enable the channel1 */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) | CM_CH1_ENABLE, FUNCTRL0 + 2); OUTB (devc->osdev, INB (devc->osdev, INT_HLDCLR + 2) | CM_CH1_INT, INT_HLDCLR + 2); } portc->trigger_bits |= PCM_ENABLE_OUTPUT; } } else { if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) && (portc->trigger_bits & PCM_ENABLE_OUTPUT)) { portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; if (portc->chan0_play) { /* disable interrupt */ OUTB (devc->osdev, INB (devc->osdev, INT_HLDCLR + 2) & ~CM_CH0_INT, INT_HLDCLR + 2); /* disable channel0 */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH0_ENABLE, FUNCTRL0 + 2); } if (portc->chan1_play) { /* disable interrupt */ OUTB (devc->osdev, INB (devc->osdev, INT_HLDCLR + 2) & ~CM_CH1_INT, INT_HLDCLR + 2); /* disable channel */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH1_ENABLE, FUNCTRL0 + 2); } } } } if (portc->open_mode & OPEN_READ) { if (state & PCM_ENABLE_INPUT) { if ((portc->audio_enabled & PCM_ENABLE_INPUT) && !(portc->trigger_bits & PCM_ENABLE_INPUT)) { if (portc->chan1_rec) { /* enable the channel1 */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) | CM_CH1_ENABLE, FUNCTRL0 + 2); OUTB (devc->osdev, INB (devc->osdev, INT_HLDCLR + 2) | CM_CH1_INT, INT_HLDCLR + 2); } if (portc->chan0_rec) { /* enable the channel0 */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) | CM_CH0_ENABLE, FUNCTRL0 + 2); OUTB (devc->osdev, INB (devc->osdev, INT_HLDCLR + 2) | CM_CH0_INT, INT_HLDCLR + 2); } 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; if (portc->chan1_rec) { /* disable interrupt */ OUTB (devc->osdev, INB (devc->osdev, INT_HLDCLR + 2) & ~CM_CH1_INT, INT_HLDCLR + 2); /* disable channel 1 */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH1_ENABLE, FUNCTRL0 + 2); } if (portc->chan0_rec) { /* disable interrupt */ OUTB (devc->osdev, INB (devc->osdev, INT_HLDCLR + 2) & ~CM_CH0_INT, INT_HLDCLR + 2); /* disable channel 0 */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH0_ENABLE, FUNCTRL0 + 2); } } } } MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } static void set_dac_rate (int dev, int chan_type) { cmpci_devc *devc = audio_engines[dev]->devc; cmpci_portc *portc = audio_engines[dev]->portc; unsigned char freq = 4, val; int i; int rate = portc->speed; if (rate > 48000) rate = 48000; if (rate < 5512) rate = 5512; for (i = 0; i < sizeof (rate_lookup) / sizeof (rate_lookup[0]); i++) { if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { rate = rate_lookup[i].rate; freq = rate_lookup[i].freq; break; } } if (chan_type == CHAN0) { val = INB (devc->osdev, FUNCTRL1 + 1) & ~0x1c; OUTB (devc->osdev, val | freq << 2, FUNCTRL1 + 1); } else { val = INB (devc->osdev, FUNCTRL1 + 1) & ~0xe0; OUTB (devc->osdev, val | freq << 5, FUNCTRL1 + 1); } if (devc->spdif_enabled) set_spdif_rate (devc, portc); } static void set_dac_fmt (int dev, int chan_type) { unsigned char val; cmpci_devc *devc = audio_engines[dev]->devc; cmpci_portc *portc = audio_engines[dev]->portc; int channels = portc->channels; int bits = portc->bits; if (chan_type == CHAN0) { /* Set the format on Channl 0 */ val = INB (devc->osdev, CHFORMAT) & ~0x3; if ((channels == 1) && (bits == 8)) { OUTB (devc->osdev, 0x00 | val, CHFORMAT); portc->dacfmt = 0; } if ((channels == 2) && (bits == 8)) { OUTB (devc->osdev, 0x01 | val, CHFORMAT); portc->dacfmt = 1; } if ((channels == 1) && (bits == 16)) { OUTB (devc->osdev, 0x02 | val, CHFORMAT); portc->dacfmt = 1; } if ((channels > 1) && (bits == 16)) { OUTB (devc->osdev, 0x03 | val, CHFORMAT); portc->dacfmt = 2; } } else { /* Set the format on Channel 1 */ val = INB (devc->osdev, CHFORMAT) & ~0xC; if ((channels == 1) && (bits == 8)) { OUTB (devc->osdev, 0x00 | val, CHFORMAT); portc->dacfmt = 0; } if ((channels == 2) && (bits == 8)) { OUTB (devc->osdev, 0x04 | val, CHFORMAT); portc->dacfmt = 1; } if ((channels == 1) && (bits == 16)) { OUTB (devc->osdev, 0x08 | val, CHFORMAT); portc->dacfmt = 1; } if ((channels > 1) && (bits == 16)) { OUTB (devc->osdev, 0x0C | val, CHFORMAT); portc->dacfmt = 2; } } } static void set_adc_rate (int dev, int chan_type) { cmpci_devc *devc = audio_engines[dev]->devc; cmpci_portc *portc = audio_engines[dev]->portc; unsigned char freq = 4, val; int i; int rate = portc->speed; if (rate > 48000) rate = 48000; if (rate < 5512) rate = 5512; for (i = 0; i < sizeof (rate_lookup) / sizeof (rate_lookup[0]); i++) { if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { rate = rate_lookup[i].rate; freq = rate_lookup[i].freq; break; } } if (chan_type == CHAN1) { val = INB (devc->osdev, FUNCTRL1 + 1) & ~0xe0; OUTB (devc->osdev, val | freq << 5, FUNCTRL1 + 1); } else { val = INB (devc->osdev, FUNCTRL1 + 1) & ~0x1c; OUTB (devc->osdev, val | freq << 2, FUNCTRL1 + 1); } if (devc->dev_mode & SPDIFIN_MODE) set_spdif_rate (devc, portc); } static void set_adc_fmt (int dev, int chan_type) { unsigned char val; cmpci_devc *devc = audio_engines[dev]->devc; cmpci_portc *portc = audio_engines[dev]->portc; int channels = portc->channels; int bits = portc->bits; if (chan_type == CHAN1) { /* Set the format on Channel 1 */ val = INB (devc->osdev, CHFORMAT) & ~0xC; if ((channels == 1) && (bits == 8)) { OUTB (devc->osdev, 0x00 | val, CHFORMAT); portc->adcfmt = 0; } if ((channels == 2) && (bits == 8)) { OUTB (devc->osdev, 0x04 | val, CHFORMAT); portc->adcfmt = 1; } if ((channels == 1) && (bits == 16)) { OUTB (devc->osdev, 0x08 | val, CHFORMAT); portc->adcfmt = 1; } if ((channels > 1) && (bits == 16)) { OUTB (devc->osdev, 0x0C | val, CHFORMAT); portc->adcfmt = 2; } } else { /* Set the format on Channl 0 */ val = INB (devc->osdev, CHFORMAT) & ~0x3; if ((channels == 1) && (bits == 8)) { OUTB (devc->osdev, 0x00 | val, CHFORMAT); portc->adcfmt = 0; } if ((channels == 2) && (bits == 8)) { OUTB (devc->osdev, 0x01 | val, CHFORMAT); portc->adcfmt = 1; } if ((channels == 1) && (bits == 16)) { OUTB (devc->osdev, 0x02 | val, CHFORMAT); portc->adcfmt = 1; } if ((channels == 2) && (bits == 16)) { OUTB (devc->osdev, 0x03 | val, CHFORMAT); portc->adcfmt = 2; } } } static void setup_record (int dev, int chan_type) { cmpci_devc *devc = audio_engines[dev]->devc; cmpci_portc *portc = audio_engines[dev]->portc; dmap_t *dmap = audio_engines[dev]->dmap_in; if (chan_type == CHAN1) /* SPDIF Record can only occur on CHAN1 */ { /* reset and disable channel */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) | CM_CH1_RESET, FUNCTRL0 + 2); oss_udelay (10); OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH1_RESET, FUNCTRL0 + 2); cmpci_outsw (devc->mixer_dev, SPDIF_PLAY, SNDCTL_MIX_WRITE, 0); OUTL (devc->osdev, dmap->dmabuf_phys, CH1_FRAME1); OUTW (devc->osdev, (dmap->bytes_in_use >> portc->adcfmt) - 1, CH1_FRAME2); OUTW (devc->osdev, (dmap->fragment_size >> portc->adcfmt), CH1_FRAME2 + 2); /* set channel 1 to record mode */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0) | CM_CH1_RECORD, FUNCTRL0); portc->chan1_rec = 1; /* setup SPDIF in on CHAN A */ OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) | (1 << 9), FUNCTRL1); } else /* Normal PCM record on Channel 0 */ { /* reset and disable channel */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) | CM_CH0_RESET, FUNCTRL0 + 2); oss_udelay (10); OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH0_RESET, FUNCTRL0 + 2); OUTL (devc->osdev, dmap->dmabuf_phys, CH0_FRAME1); OUTW (devc->osdev, (dmap->bytes_in_use >> portc->adcfmt) - 1, CH0_FRAME2); OUTW (devc->osdev, (dmap->fragment_size >> portc->adcfmt), CH0_FRAME2 + 2); /* set channel 0 to record mode */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0) | CM_CH0_RECORD, FUNCTRL0); portc->chan0_rec = 1; } } /*ARGSUSED*/ static int cmpci_audio_prepare_for_input (int dev, int bsize, int bcount) { cmpci_devc *devc = audio_engines[dev]->devc; cmpci_portc *portc = audio_engines[dev]->portc; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); switch (devc->dev_mode) { case DEFAULT_MODE: set_adc_rate (dev, CHAN0); set_adc_fmt (dev, CHAN0); setup_record (dev, CHAN0); break; case DUALDAC_MODE: cmn_err (CE_WARN, "Cannot record because DUALDAC mode is ON.\n"); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return OSS_EIO; case SPDIFIN_MODE: if (portc->speed < 44100) { cmn_err (CE_WARN, "Cannot record spdif at sampling rate less than 44.1Khz.\n"); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return OSS_EIO; } set_adc_rate (dev, CHAN1); set_adc_fmt (dev, CHAN1); setup_record (dev, CHAN1); break; } portc->audio_enabled &= ~PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } static void setup_play (int dev, int chan_type) { cmpci_devc *devc = audio_engines[dev]->devc; cmpci_portc *portc = audio_engines[dev]->portc; dmap_t *dmap = audio_engines[dev]->dmap_out; if (chan_type == CHAN0) { /* reset channel */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) | CM_CH0_RESET, FUNCTRL0 + 2); oss_udelay (10); OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH0_RESET, FUNCTRL0 + 2); oss_udelay (10); /* Now set the buffer address/sizes */ OUTL (devc->osdev, dmap->dmabuf_phys, CH0_FRAME1); OUTW (devc->osdev, (dmap->bytes_in_use >> portc->dacfmt) - 1, CH0_FRAME2); OUTW (devc->osdev, (dmap->fragment_size >> portc->dacfmt), CH0_FRAME2 + 2); /* set channel 0 to play mode */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0) & CM_CH0_PLAY, FUNCTRL0); portc->chan0_play = 1; /* setup spdif output on CHAN A , disable CHAN B spdif */ if (devc->spdif_enabled) { OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) | (1 << 8), FUNCTRL1); OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) & ~(1 << 9), FUNCTRL1); } } else { /* reset and disable channel */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) | CM_CH1_RESET, FUNCTRL0 + 2); oss_udelay (10); OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0 + 2) & ~CM_CH1_RESET, FUNCTRL0 + 2); oss_udelay (10); /* Handle 4/5/6 channel mode */ if (portc->channels < 4) { /* check if 4speaker mode is enabled from mixer or not */ if (devc->mode_4spk) OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 26), MISC_CTRL); /* disable 4channel mode on CHAN B */ OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~(1 << 29), CHFORMAT); /* disable 5 channel mode on CHAN B */ OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~(0x80000000), CHFORMAT); /* disable 6channel mode out CHAN B */ OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 15), LEGACY_CTRL); /* disable 8 channel decode on CHAN B - only for CMI8768 */ if (devc->model == MDL_CM8768) OUTB (devc->osdev, INB (devc->osdev, MISC2_CTRL) & ~0x20, MISC2_CTRL); /* Set NXCNG */ OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(0x80000000), LEGACY_CTRL); } if ((portc->channels == 4) && (devc->chiprev > 37)) { /* disable 4 speaker mode */ OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 26), MISC_CTRL); /* enable 4channel mode on CHAN B */ OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) | (1 << 29), CHFORMAT); /* disable 5 channel mode on CHAN B */ OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~(0x80000000), CHFORMAT); /* disable 6channel mode out CHAN B */ OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 15), LEGACY_CTRL); /* disable center/bass channel */ OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 7), MISC_CTRL); /* disable bass */ OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 12), LEGACY_CTRL); /* disable 8 channel decode on CHAN B - only for CMI8768 */ if (devc->model == MDL_CM8768) OUTB (devc->osdev, INB (devc->osdev, MISC2_CTRL) & ~0x20, MISC2_CTRL); /* Set NXCNG */ OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(0x80000000), LEGACY_CTRL); } if ((portc->channels == 6) && (devc->chiprev > 37)) { /* disable 4 speaker mode */ OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 26), MISC_CTRL); /* disable 4channel mode on CHAN B */ OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~(1 << 29), CHFORMAT); /* enable center channel */ OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 7), MISC_CTRL); /* enable bass */ OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 12), LEGACY_CTRL); /* enable 5 channel mode on CHAN B */ OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) | (0x80000000), CHFORMAT); /* enable 6 channel decode on CHAN B */ OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 15), LEGACY_CTRL); /* disable 8 channel decode on CHAN B - only for CMI8768 */ if (devc->model == MDL_CM8768) OUTB (devc->osdev, INB (devc->osdev, MISC2_CTRL) & ~0x20, MISC2_CTRL); /* Set NXCNG */ OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (0x80000000), LEGACY_CTRL); } if ((portc->channels == 8) && (devc->model == MDL_CM8768)) { /* disable 4 speaker mode */ OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) & ~(1 << 26), MISC_CTRL); /* disable 4channel mode on CHAN B */ OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~(1 << 29), CHFORMAT); /* enable center channel */ OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 7), MISC_CTRL); /* enable bass channel */ OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 12), LEGACY_CTRL); /* disable 5 channel mode on CHAN B */ OUTL (devc->osdev, INL (devc->osdev, CHFORMAT) & ~(0x80000000), CHFORMAT); /* disable 6 channel decode on CHAN B */ OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) & ~(1 << 15), LEGACY_CTRL); /* enable 8 channel decode on CHAN B */ OUTB (devc->osdev, INB (devc->osdev, MISC2_CTRL) | 0x20, MISC2_CTRL); /* Set NXCNG */ OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (0x80000000), LEGACY_CTRL); } /* Now set the buffer address/sizes */ OUTL (devc->osdev, dmap->dmabuf_phys, CH1_FRAME1); OUTW (devc->osdev, (dmap->bytes_in_use >> portc->dacfmt) - 1, CH1_FRAME2); OUTW (devc->osdev, (dmap->fragment_size >> portc->dacfmt), CH1_FRAME2 + 2); /* set channel 1 to play mode */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL0) & CM_CH1_PLAY, FUNCTRL0); portc->chan1_play = 1; /* setup spdif output on CHAN B , disable CHAN A spdif */ if (devc->spdif_enabled) { OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) | (1 << 9), FUNCTRL1); OUTL (devc->osdev, INL (devc->osdev, FUNCTRL1) & ~(1 << 8), FUNCTRL1); } } } /*ARGSUSED*/ static int cmpci_audio_prepare_for_output (int dev, int bsize, int bcount) { cmpci_devc *devc = audio_engines[dev]->devc; cmpci_portc *portc = audio_engines[dev]->portc; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if ((portc->bits == AFMT_AC3) && devc->can_ac3) { portc->bits = 16; portc->channels = 2; cmpci_outsw (devc->mixer_dev, SPDIF_PLAY, SNDCTL_MIX_WRITE, 1); setup_ac3 (devc, portc, 1); } else setup_ac3 (devc, portc, 0); switch (devc->dev_mode) { case DEFAULT_MODE: /* set speed */ set_dac_rate (dev, CHAN1); /* set format */ set_dac_fmt (dev, CHAN1); /* set buffer address/size and other setups */ setup_play (dev, CHAN1); break; case DUALDAC_MODE: if (dev == devc->portc[0].audiodev) { set_dac_rate (dev, CHAN1); set_dac_fmt (dev, CHAN1); setup_play (dev, CHAN1); setup_ac3 (devc, portc, 0); } if (dev == devc->portc[1].audiodev) { set_dac_rate (dev, CHAN0); set_dac_fmt (dev, CHAN0); setup_play (dev, CHAN0); setup_ac3 (devc, portc, 0); } break; case SPDIFIN_MODE: set_dac_rate (dev, CHAN0); set_dac_fmt (dev, CHAN0); setup_play (dev, CHAN0); setup_ac3 (devc, portc, 0); break; } portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } static int cmpci_get_buffer_pointer (int dev, dmap_t * dmap, int direction) { cmpci_devc *devc = audio_engines[dev]->devc; cmpci_portc *portc = audio_engines[dev]->portc; unsigned int ptr = 0; oss_native_word flags; if (!(portc->open_mode & direction)) return 0; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); if (direction == PCM_ENABLE_INPUT) { if (portc->chan0_rec) ptr = INW (devc->osdev, CH0_FRAME1); if (portc->chan1_rec) ptr = INW (devc->osdev, CH1_FRAME1); } if (direction == PCM_ENABLE_OUTPUT) { if (portc->chan0_play) ptr = INW (devc->osdev, CH0_FRAME1); if (portc->chan1_play) ptr = INW (devc->osdev, CH1_FRAME1); } MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return ptr % dmap->bytes_in_use; } static audiodrv_t cmpci_audio_driver = { cmpci_audio_open, cmpci_audio_close, cmpci_audio_output_block, cmpci_audio_start_input, cmpci_audio_ioctl, cmpci_audio_prepare_for_input, cmpci_audio_prepare_for_output, cmpci_audio_reset, NULL, NULL, cmpci_audio_reset_input, cmpci_audio_reset_output, cmpci_audio_trigger, cmpci_audio_set_rate, cmpci_audio_set_format, cmpci_audio_set_channels, NULL, NULL, NULL, NULL, NULL, /* cmpci_alloc_buffer, */ NULL, /* cmpci_free_buffer, */ NULL, NULL, cmpci_get_buffer_pointer }; #ifdef OBSOLETED_STUFF static void attach_mpu (cmpci_devc * devc) { unsigned int base; if (devc->chiprev == 33) base = 0x330; /* Chiprev033 doen't have bas+0x40 */ else base = MPU_MIRROR; /* base+0x40 is MPU PCI mirror */ uart401_init (&devc->uart401devc, devc->osdev, base, "Cmedia MIDI UART"); devc->uart401_attached = 1; } #endif static int init_cmpci (cmpci_devc * devc) { oss_native_word val; int first_dev = 0; int i; devc->fm_attached = 0;
Enable BusMasterMode and IOSpace Access
/* Check the model number of the chip */ val = INL (devc->osdev, INT_HLDCLR) & 0xff000000; if (!val) { val = INL (devc->osdev, CHFORMAT) & 0x1f000000; if (!val) { devc->chiprev = 33; devc->can_ac3 = 0; devc->max_channels = 6; } else { devc->chiprev = 37; devc->can_ac3 = 1; devc->max_channels = 6; } } else { if (val & 0x04000000) { devc->chiprev = 39; devc->can_ac3 = 1; devc->max_channels = 6; } if (val & 0x08000000) { devc->chiprev = 55; devc->can_ac3 = 1; devc->max_channels = 6; } if (val & 0x28000000) { devc->chiprev = 68; devc->can_ac3 = 1; devc->model = MDL_CM8768; devc->max_channels = 8; devc->chip_name = "CMedia CM8768"; } } /* enable uart, joystick in Function Control Reg1 */ OUTB (devc->osdev, INB (devc->osdev, FUNCTRL1) | 0x06, FUNCTRL1); /* enable FM */ OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 19), MISC_CTRL); OUTB (devc->osdev, 0, INT_HLDCLR + 2); /* disable ints */ OUTB (devc->osdev, 0, FUNCTRL0 + 2); /* reset channels */ #ifdef OBSOLETED_STUFF attach_mpu (devc); #endif /* install the CMPCI mixer */ if ((devc->mixer_dev = oss_install_mixer (OSS_MIXER_DRIVER_VERSION, devc->osdev, devc->osdev, "CMedia CMPCI", &cmpci_mixer_driver, sizeof (mixer_driver_t), devc)) < 0) { return 0; } mixer_devs[devc->mixer_dev]->hw_devc = devc; mixer_devs[devc->mixer_dev]->priority = 1; /* Possible default mixer candidate */ cmpci_mixer_reset (devc->mixer_dev); mixer_ext_set_init_fn (devc->mixer_dev, cmpci_mix_init, 25); OUTB (devc->osdev, 0xF, MIXER2); /* setup 4speaker output */ OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 26), MISC_CTRL); /* enable subwoofer/center channel */ OUTL (devc->osdev, INL (devc->osdev, LEGACY_CTRL) | (1 << 12), LEGACY_CTRL); OUTL (devc->osdev, INL (devc->osdev, MISC_CTRL) | (1 << 7), MISC_CTRL); for (i = 0; i < MAX_PORTC; i++) { char tmp_name[100]; cmpci_portc *portc = &devc->portc[i]; int caps = ADEV_AUTOMODE; if (i == 0) { sprintf (tmp_name, "%s (rev %0d)", devc->chip_name, devc->chiprev); caps |= ADEV_DUPLEX; } else { sprintf (tmp_name, "%s (playback only)", devc->chip_name); caps |= ADEV_NOINPUT; } if ((portc->audiodev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, devc->osdev, devc->osdev, tmp_name, &cmpci_audio_driver, sizeof (audiodrv_t), caps, AFMT_U8 | AFMT_S16_LE | AFMT_AC3, devc, -1)) < 0) { return 0; } else { if (i == 0) first_dev = portc->audiodev; audio_engines[portc->audiodev]->portc = portc; audio_engines[portc->audiodev]->rate_source = first_dev; audio_engines[portc->audiodev]->caps = PCM_CAP_ANALOGOUT | PCM_CAP_ANALOGIN | PCM_CAP_DIGITALOUT | PCM_CAP_DIGITALIN; audio_engines[portc->audiodev]->min_rate = 5000; audio_engines[portc->audiodev]->max_rate = 48000; audio_engines[portc->audiodev]->caps |= PCM_CAP_FREERATE; audio_engines[portc->audiodev]->min_channels = 2; audio_engines[portc->audiodev]->max_channels = devc->max_channels; audio_engines[portc->audiodev]->vmix_flags = VMIX_MULTIFRAG; audio_engines[portc->audiodev]->dmabuf_alloc_flags |= DMABUF_SIZE_16BITS; portc->open_mode = 0; portc->audio_enabled = 0; audio_engines[portc->audiodev]->mixer_dev = devc->mixer_dev; devc->dev_mode = DEFAULT_MODE; devc->spdif_enabled = 0; #ifdef CONFIG_OSS_VMIX if (i == 0) vmix_attach_audiodev(devc->osdev, portc->audiodev, -1, 0); #endif } } return 1; } int oss_cmpci_attach (oss_device_t * osdev) { unsigned char pci_irq_line, pci_revision; unsigned short pci_command, vendor, device; unsigned int pci_ioaddr; int err; cmpci_devc *devc; DDB (cmn_err (CE_CONT, "Entered CMEDIA CMPCI attach routine\n")); pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor); pci_read_config_word (osdev, PCI_DEVICE_ID, &device); if (vendor != CMEDIA_VENDOR_ID || ((device != CMEDIA_CM8738) && (device != CMEDIA_CM8338A) && (device != CMEDIA_CM8338B))) return 0; pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision); pci_read_config_word (osdev, PCI_COMMAND, &pci_command); pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line); pci_read_config_dword (osdev, PCI_BASE_ADDRESS_0, &pci_ioaddr); DDB (cmn_err (CE_WARN, "CMPCI I/O base %04x\n", pci_ioaddr)); if (pci_ioaddr == 0) { cmn_err (CE_WARN, "I/O address not assigned by BIOS.\n"); return 0; } if (pci_irq_line == 0) { cmn_err (CE_WARN, "IRQ not assigned by BIOS (%d).\n", pci_irq_line); return 0; } if ((devc = PMALLOC (osdev, sizeof (*devc))) == NULL) { cmn_err (CE_WARN, "Out of memory\n"); return 0; } devc->osdev = osdev; osdev->devc = devc; devc->irq = pci_irq_line; /* Map the IO Base address */ devc->base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr); /* Remove I/O space marker in bit 0. */ devc->base &= ~3; /* set the PCI_COMMAND register to master mode */ pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; pci_write_config_word (osdev, PCI_COMMAND, pci_command); switch (device) { case CMEDIA_CM8738: devc->model = MDL_CM8738; devc->chip_name = "CMedia CM8738"; devc->max_channels = 6; break; case CMEDIA_CM8338A: devc->model = MDL_CM8338A; devc->chip_name = "CMedia CM8338A"; devc->max_channels = 6; break; case CMEDIA_CM8338B: devc->model = MDL_CM8338B; devc->chip_name = "CMedia CM8338B"; devc->max_channels = 6; break; } MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV); MUTEX_INIT (devc->osdev, devc->low_mutex, MH_DRV + 1); oss_register_device (osdev, devc->chip_name); if ((err = oss_register_interrupts (devc->osdev, 0, cmpciintr, NULL)) < 0) { cmn_err (CE_WARN, "Can't allocate IRQ%d, err=%d\n", pci_irq_line, err); return 0; } return init_cmpci (devc); /* Detected */ } int oss_cmpci_detach (oss_device_t * osdev) { cmpci_devc *devc = (cmpci_devc *) osdev->devc; if (oss_disable_device (osdev) < 0) return 0; /* disable Interrupts */ OUTB (devc->osdev, 0, INT_HLDCLR + 2); /* disable channels */ OUTB (devc->osdev, 0, FUNCTRL0 + 2); /* uninstall UART401 */ if (devc->uart401_attached) uart401_disable (&devc->uart401devc); oss_unregister_interrupts (devc->osdev); MUTEX_CLEANUP (devc->mutex); MUTEX_CLEANUP (devc->low_mutex); UNMAP_PCI_IOADDR (devc->osdev, 0); oss_unregister_device (osdev); return 1; }