summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_cmpci
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drv/oss_cmpci')
-rw-r--r--kernel/drv/oss_cmpci/.devices5
-rw-r--r--kernel/drv/oss_cmpci/.name1
-rw-r--r--kernel/drv/oss_cmpci/oss_cmpci.c2379
-rw-r--r--kernel/drv/oss_cmpci/oss_cmpci.man56
4 files changed, 2441 insertions, 0 deletions
diff --git a/kernel/drv/oss_cmpci/.devices b/kernel/drv/oss_cmpci/.devices
new file mode 100644
index 0000000..9933503
--- /dev/null
+++ b/kernel/drv/oss_cmpci/.devices
@@ -0,0 +1,5 @@
+oss_cmpci pci13f6,100 C-Media CM8338A
+oss_cmpci pci13f6,100 MIDIMan DiO 2448
+oss_cmpci pci13f6,101 CMedia CM8338B
+oss_cmpci pci13f6,111 CMedia CM8738/CM8768
+oss_cmpci pci14af,20 Guillemot Maxi Sound MUSE
diff --git a/kernel/drv/oss_cmpci/.name b/kernel/drv/oss_cmpci/.name
new file mode 100644
index 0000000..0d987dc
--- /dev/null
+++ b/kernel/drv/oss_cmpci/.name
@@ -0,0 +1 @@
+C-Media CM833x audio chipset
diff --git a/kernel/drv/oss_cmpci/oss_cmpci.c b/kernel/drv/oss_cmpci/oss_cmpci.c
new file mode 100644
index 0000000..50ccf84
--- /dev/null
+++ b/kernel/drv/oss_cmpci/oss_cmpci.c
@@ -0,0 +1,2379 @@
+/*
+ * Purpose: Driver for CMEDIA CM8738 PCI audio controller.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include "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;
+
+ if (!(CMEDIA_MIXER_DEVICES & (1 << chan))) /*
+ * 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);
+
+ if (cmpci_mix[chan][RIGHT_CHN].regno != regoffs) /*
+ * Change register
+ */
+ {
+ cmpci_setmixer (devc, regoffs, val); /*
+ * Save the old one
+ */
+ regoffs = cmpci_mix[chan][RIGHT_CHN].regno;
+
+ if (regoffs == 0)
+ return left | (left << 8); /*
+ * Just left channel present
+ */
+
+ val = cmpci_getmixer (devc, regoffs); /*
+ * 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;
+}
diff --git a/kernel/drv/oss_cmpci/oss_cmpci.man b/kernel/drv/oss_cmpci/oss_cmpci.man
new file mode 100644
index 0000000..1d817b3
--- /dev/null
+++ b/kernel/drv/oss_cmpci/oss_cmpci.man
@@ -0,0 +1,56 @@
+NAME
+oss_cmpci - CMedia CMI8738/8768 audio driver.
+
+DESCRIPTION
+Open Sound System driver for CMedia Electronics CMI8738/8768 audio
+
+CMI87xx device characteristics:
+ o 8/16 bit playback/record
+ o mono/stereo/4ch/5.1ch playback
+ o 8KHz to 48Khz sample rate.
+
+ MIXER PANEL
+The CMedia chip provides some unique features that are set up
+by the Mixer chip. Running ossxmix will display the CMI8738 mixer
+panel.
+
+Most of the sliders and buttons are self evident. However there
+are some options that need explaining:
+
+Dual Dac: Enabling this button sets the CMPCI device as two
+separate output devices with /dev/dsp1 audio going to the front and
+/dev/dsp0 going to the rear outputs. Separate audio streams can
+be send to the device simultaneously.
+
+Speaker Mode: The audio can be sent just to the front speakers or
+it can be sent simultaneously to all speakers in the "Spread" mode.
+
+AC3 passthrough only works on Models 037 and higher. This is because of
+a hardware bug in the earlier models so check the model number
+(ossinfo -a).
+
+SPDIF:
+
+o Enable will enable SPDIF output.
+
+o Rec will allow you to record from the SPDIF device. Note that when
+you have SPDIF recording enabled, you cannot play 4/6 channel audio.
+
+o Polarity - certain models require you to flip the bit otherwise you
+ get distorted audio.
+
+o IMon - monitor input via SPDIF in.
+
+o Optical - sets the SPDIF to Optical (TOSLINK) or RCA Jacks interface.
+
+
+
+OPTIONS
+None
+
+FILES
+CONFIGFILEPATH/oss_cmpci.conf Device configuration file
+
+AUTHOR
+4Front Technologies
+