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