diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
commit | 1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch) | |
tree | 4495d23e7b54ab5700e3839081e797c1eafe0db9 /attic/drv/oss_als4k | |
download | oss4-upstream.tar.gz |
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'attic/drv/oss_als4k')
-rw-r--r-- | attic/drv/oss_als4k/.config | 1 | ||||
-rw-r--r-- | attic/drv/oss_als4k/.devices | 1 | ||||
-rw-r--r-- | attic/drv/oss_als4k/.name | 1 | ||||
-rw-r--r-- | attic/drv/oss_als4k/.params | 11 | ||||
-rwxr-xr-x | attic/drv/oss_als4k/oss_als4k.c | 1153 | ||||
-rw-r--r-- | attic/drv/oss_als4k/oss_als4k.man | 23 |
6 files changed, 1190 insertions, 0 deletions
diff --git a/attic/drv/oss_als4k/.config b/attic/drv/oss_als4k/.config new file mode 100644 index 0000000..5280084 --- /dev/null +++ b/attic/drv/oss_als4k/.config @@ -0,0 +1 @@ +platform=i86pc diff --git a/attic/drv/oss_als4k/.devices b/attic/drv/oss_als4k/.devices new file mode 100644 index 0000000..99d4af6 --- /dev/null +++ b/attic/drv/oss_als4k/.devices @@ -0,0 +1 @@ +oss_als4k pci4005,4000 Avance Logic ALS4000 diff --git a/attic/drv/oss_als4k/.name b/attic/drv/oss_als4k/.name new file mode 100644 index 0000000..df310ec --- /dev/null +++ b/attic/drv/oss_als4k/.name @@ -0,0 +1 @@ +Avance Logic ALS4000 diff --git a/attic/drv/oss_als4k/.params b/attic/drv/oss_als4k/.params new file mode 100644 index 0000000..337be40 --- /dev/null +++ b/attic/drv/oss_als4k/.params @@ -0,0 +1,11 @@ +int als4000_mpu_ioaddr=0; +/* + * MPU 401 I/O Address + * Values: 0x300, 0x310, 0x320, 0x330, 0x340, 0x350, 0x360, 0x370 Default: 0x330 + */ + +int als4000_mpu_irq=0; +/* + * MPU 401 IRQ + * Values: 5, 7, 9, 10, 11 Default: 0 + */ diff --git a/attic/drv/oss_als4k/oss_als4k.c b/attic/drv/oss_als4k/oss_als4k.c new file mode 100755 index 0000000..f5f9222 --- /dev/null +++ b/attic/drv/oss_als4k/oss_als4k.c @@ -0,0 +1,1153 @@ +/* + * Purpose: Driver for ALS ALS4000 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_als4k_cfg.h" +#include "oss_pci.h" + +#define ALS_VENDOR_ID 0x4005 +#define ALS_4000 0x4000 + +#define DSP_RESET (devc->base + 0x16) +#define DSP_READ (devc->base + 0x1A) +#define DSP_WRITE (devc->base + 0x1C) +#define DSP_COMMAND (devc->base + 0x1C) +#define DSP_STATUS (devc->base + 0x1C) +#define DSP_DATA_AVAIL (devc->base + 0x1E) +#define DSP_DATA_AVL16 (devc->base + 0x1F) +#define MIXER_ADDR (devc->base + 0x14) +#define MIXER_DATA (devc->base + 0x15) + +#define DSP_CMD_SPKON 0xD1 +#define DSP_CMD_SPKOFF 0xD3 + +#define ALS_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) +#define ALS_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | \ + SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_BASS | SOUND_MASK_TREBLE | \ + SOUND_MASK_IGAIN | SOUND_MASK_CD | \ + SOUND_MASK_VOLUME) +#define ALS4000_RECORDING_DEVICES (ALS_RECORDING_DEVICES) +#define ALS4000_MIXER_DEVICES (ALS_MIXER_DEVICES|SOUND_MASK_LINE2|SOUND_MASK_SPEAKER|SOUND_MASK_RECLEV) + +#define LEFT_CHN 0 +#define RIGHT_CHN 1 + +#define VOC_VOL 0x04 +#define MIC_VOL 0x0A +#define MIC_MIX 0x0A +#define RECORD_SRC 0x0C +#define IN_FILTER 0x0C +#define OUT_FILTER 0x0E +#define MASTER_VOL 0x22 +#define FM_VOL 0x26 +#define CD_VOL 0x28 +#define LINE_VOL 0x2E +#define IRQ_NR 0x80 +#define DMA_NR 0x81 +#define IRQ_STAT 0x82 +#define OPSW 0x3c + +static 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 */ +}; + +#define MAX_PORTC 2 + +typedef struct als4000_portc +{ + int speed, bits, channels; + int open_mode; + int audio_enabled; + int trigger_bits; + int audiodev; +} +als4000_portc; + +typedef struct als4000_devc +{ + oss_device_t *osdev; + oss_native_word base, mpu_base; + int sb_attached, mpu_attached, fm_attached; + int irq, mpu_irq; + int play_rate_lock, rec_rate_lock; + + char *chip_name; + oss_mutex_t mutex; + oss_mutex_t low_mutex; + + /* Audio parameters */ + int open_mode; + als4000_portc portc[MAX_PORTC]; + oss_native_word srcode; + + /* Mixer parameters */ + int mixer_dev; + int *levels; + int recmask; +} +als4000_devc; + +extern int als4000_mpu_ioaddr; +extern int als4000_mpu_irq; + +void +als4000_gcr_writel (als4000_devc * devc, unsigned char index, + oss_native_word data) +{ + OUTB (devc->osdev, index, devc->base + 0x0c); + OUTL (devc->osdev, data, devc->base + 0x08); +} + + +oss_native_word +als4000_gcr_readl (als4000_devc * devc, unsigned char index) +{ + oss_native_word bufl; + + OUTB (devc->osdev, index, devc->base + 0x0c); + bufl = INL (devc->osdev, devc->base + 0x08); + return (bufl); +} + +static void +als4000_write (als4000_devc * devc, unsigned char val) +{ + int i; + + for (i = 0; i < 100000; i++) + if ((INB (devc->osdev, DSP_STATUS) & 0x80) == 0) + { + OUTB (devc->osdev, val, DSP_COMMAND); + return; + } + + cmn_err (CE_WARN, "Write Command(%x) timed out.\n", val); + return; +} + +static unsigned char +als4000_read (als4000_devc * devc) +{ + int i; + + for (i = 0; i < 100000; i++) + if (INB (devc->osdev, DSP_DATA_AVAIL) & 0x80) + { + return INB (devc->osdev, DSP_READ); + } + + cmn_err (CE_WARN, "Read Command timed out.\n"); + return 0xff; +} + +static void +als4000_setmixer (als4000_devc * devc, unsigned char port, + unsigned char value) +{ + OUTB (devc->osdev, port, MIXER_ADDR); + oss_udelay (20); + OUTB (devc->osdev, value, MIXER_DATA); + //oss_udelay (20); +} + +static unsigned int +als4000_getmixer (als4000_devc * devc, unsigned char port) +{ + unsigned char val; + + OUTB (devc->osdev, port, MIXER_ADDR); + oss_udelay (20); + val = INB (devc->osdev, MIXER_DATA); + //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 als4000_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_ALTPCM, 0x00, 0, 0, 0x00, 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 (als4000_devc * devc, unsigned char *regval, int dev, int chn, + int newval) +{ + unsigned char mask; + int shift; + + mask = (1 << als4000_mix[dev][chn].nbits) - 1; + newval = (int) ((newval * mask) + 50) / 100; /* Scale */ + + shift = + als4000_mix[dev][chn].bitoffs - als4000_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 (als4000_devc * devc, int mask); + +static int +als4000_mixer_set (als4000_devc * devc, int dev, 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 (dev > 31) + return OSS_EINVAL; + + if (!(ALS4000_MIXER_DEVICES & (1 << dev))) /* + * Not supported + */ + return OSS_EINVAL; + + regoffs = als4000_mix[dev][LEFT_CHN].regno; + + if (regoffs == 0) + return OSS_EINVAL; + + val = als4000_getmixer (devc, regoffs); + change_bits (devc, &val, dev, LEFT_CHN, left); + + devc->levels[dev] = left | (left << 8); + + if (als4000_mix[dev][RIGHT_CHN].regno != regoffs) /* + * Change register + */ + { + als4000_setmixer (devc, regoffs, val); /* + * Save the old one + */ + regoffs = als4000_mix[dev][RIGHT_CHN].regno; + + if (regoffs == 0) + return left | (left << 8); /* + * Just left channel present + */ + + val = als4000_getmixer (devc, regoffs); /* + * Read the new one + */ + } + + change_bits (devc, &val, dev, RIGHT_CHN, right); + + als4000_setmixer (devc, regoffs, val); + + devc->levels[dev] = left | (right << 8); + return left | (right << 8); +} + +/*ARGSUSED*/ +static int +als4000_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg) +{ + als4000_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; + return *arg = set_recmask (devc, val); + break; + + default: + + val = *arg; + return *arg = als4000_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 = ALS4000_MIXER_DEVICES; + break; + + case SOUND_MIXER_STEREODEVS: + return *arg = ALS4000_MIXER_DEVICES & + ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); + break; + + case SOUND_MIXER_RECMASK: + return *arg = ALS4000_RECORDING_DEVICES; + break; + + case SOUND_MIXER_CAPS: + return *arg = SOUND_CAP_EXCL_INPUT; + break; + + + default: + return *arg = devc->levels[cmd & 0x1f]; + } + } + else + return OSS_EINVAL; +} + + +static void +set_recsrc (als4000_devc * devc, int src) +{ + als4000_setmixer (devc, RECORD_SRC, + (als4000_getmixer (devc, RECORD_SRC) & ~7) | (src & 0x7)); +} + +static int +set_recmask (als4000_devc * devc, int mask) +{ + int devmask = mask & ALS4000_RECORDING_DEVICES; + + if (devmask != SOUND_MASK_MIC && + devmask != SOUND_MASK_LINE && devmask != SOUND_MASK_CD) + { /* + * More than one devices selected. Drop the * + * previous selection + */ + devmask &= ~devc->recmask; + } + + if (devmask != SOUND_MASK_MIC && + devmask != SOUND_MASK_LINE && devmask != SOUND_MASK_CD) + { /* + * More than one devices selected. Default to + * * mic + */ + devmask = SOUND_MASK_MIC; + } + + + if (devmask ^ devc->recmask) /* + * Input source changed + */ + { + switch (devmask) + { + + case SOUND_MASK_MIC: + set_recsrc (devc, 0); + break; + + case SOUND_MASK_LINE: + set_recsrc (devc, 6); + break; + + case SOUND_MASK_CD: + set_recsrc (devc, 2); + break; + + + default: + set_recsrc (devc, 0); + } + } + + devc->recmask = devmask; + return devc->recmask; +} + +static void +als4000_mixer_reset (als4000_devc * devc) +{ + char name[32]; + int i; + + sprintf (name, "ALS4000"); + + devc->levels = load_mixer_volumes (name, default_levels, 1); + devc->recmask = 0; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + als4000_mixer_set (devc, i, devc->levels[i]); + + set_recmask (devc, SOUND_MASK_MIC); +} + + +static mixer_driver_t als4000_mixer_driver = { + als4000_mixer_ioctl +}; + +static int +als4000intr (oss_device_t * osdev) +{ + als4000_devc *devc = (als4000_devc *) osdev->devc; + unsigned int status, i; + unsigned char mxstat; + int serviced = 0; + + + status = INB (devc->osdev, devc->base + 0x0e); + + for (i = 0; i < MAX_PORTC; i++) + { + als4000_portc *portc = &devc->portc[i]; + + if ((status & 0x80) && (portc->trigger_bits & PCM_ENABLE_OUTPUT)) + { + serviced = 1; + oss_audio_outputintr (portc->audiodev, 1); + } + if ((status & 0x40) && (portc->trigger_bits & PCM_ENABLE_INPUT)) + { + serviced = 1; + oss_audio_inputintr (portc->audiodev, 0); + } + } + + OUTB (devc->osdev, status, devc->base + 0x0e); /* acknowledge interrupt */ + +#if 0 + if (status & 0x10) + { + serviced = 1; + uart401intr (INT_HANDLER_CALL (devc->mpu_irq)); + } +#endif + mxstat = als4000_getmixer (devc, 0x82); + switch (mxstat) + { + case 0x1: + INB (devc->osdev, devc->base + 0x1e); + break; + case 0x2: + INB (devc->osdev, devc->base + 0x1f); + break; + case 0x4: + INB (devc->osdev, devc->base + 0x30); + break; + case 0x20: + INB (devc->osdev, devc->base + 0x16); + break; + } + return serviced; +} + +static int +als4000_audio_set_rate (int dev, int arg) +{ + als4000_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 +als4000_audio_set_channels (int dev, short arg) +{ + als4000_portc *portc = audio_engines[dev]->portc; + + if ((arg != 1) && (arg != 2)) + return portc->channels; + portc->channels = arg; + + return portc->channels; +} + +static unsigned int +als4000_audio_set_format (int dev, unsigned int arg) +{ + als4000_portc *portc = audio_engines[dev]->portc; + + if (arg == 0) + return portc->bits; + + if (!(arg & (AFMT_U8 | AFMT_S16_LE))) + return portc->bits; + portc->bits = arg; + + return portc->bits; +} + +static void +als4000_speed (als4000_devc * devc, int speed) +{ + if (!(devc->play_rate_lock | devc->rec_rate_lock)) + { + als4000_write (devc, 0x41); + als4000_write (devc, (speed >> 8) & 0xFF); + als4000_write (devc, speed & 0xFF); + } +} + +/*ARGSUSED*/ +static int +als4000_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg) +{ + return OSS_EINVAL; +} + +static int +als4000_reset (als4000_devc * devc) +{ + int i; + unsigned char byte; + DDB (cmn_err (CE_WARN, "Entered als4000_reset()\n")); + + OUTB (devc->osdev, 1, DSP_RESET); + oss_udelay (20); + OUTB (devc->osdev, 0, DSP_RESET); + + byte = als4000_read (devc); + for (i = 0; i < 10000; i++) + if (byte != 0xAA) + { + DDB (cmn_err (CE_WARN, "No response to RESET\n")); + return 0; /* Sorry */ + } + + DDB (cmn_err (CE_WARN, "als4000_reset() OK\n")); + return 1; +} + +static void als4000_audio_trigger (int dev, int state); + +static void +als4000_audio_reset (int dev) +{ + als4000_audio_trigger (dev, 0); +} + +static void +als4000_audio_reset_input (int dev) +{ + als4000_portc *portc = audio_engines[dev]->portc; + als4000_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT); +} + +static void +als4000_audio_reset_output (int dev) +{ + als4000_portc *portc = audio_engines[dev]->portc; + als4000_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT); +} + +/*ARGSUSED*/ +static int +als4000_audio_open (int dev, int mode, int open_flags) +{ + als4000_portc *portc = audio_engines[dev]->portc; + als4000_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->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 +als4000_audio_close (int dev, int mode) +{ + als4000_portc *portc = audio_engines[dev]->portc; + als4000_devc *devc = audio_engines[dev]->devc; + + als4000_audio_reset (dev); + als4000_reset (devc); + portc->open_mode = 0; + devc->open_mode &= ~mode; + portc->audio_enabled &= ~mode; +} + +/*ARGSUSED*/ +static void +als4000_audio_output_block (int dev, oss_native_word buf, int count, + int fragsize, int intrflag) +{ + als4000_portc *portc = audio_engines[dev]->portc; + + portc->audio_enabled |= PCM_ENABLE_OUTPUT; + portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; +} + +/*ARGSUSED*/ +static void +als4000_audio_start_input (int dev, oss_native_word buf, int count, + int fragsize, int intrflag) +{ + als4000_portc *portc = audio_engines[dev]->portc; + + portc->audio_enabled |= PCM_ENABLE_INPUT; + portc->trigger_bits &= ~PCM_ENABLE_INPUT; +} + +static void +als4000_audio_trigger (int dev, int state) +{ + als4000_portc *portc = audio_engines[dev]->portc; + als4000_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->bits == AFMT_U8) + als4000_write (devc, 0xd6); /*start 8bit dma */ + else + als4000_write (devc, 0xd4); /*start 16bit dma */ + devc->play_rate_lock = 1; + 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->bits == AFMT_U8) + als4000_write (devc, 0xd0); /*exit 8bit dma */ + else + als4000_write (devc, 0xd5); /*exit 16bit dma */ + devc->play_rate_lock = 0; + } + } + } + + if (portc->open_mode & OPEN_READ) + { + if (state & PCM_ENABLE_INPUT) + { + if ((portc->audio_enabled & PCM_ENABLE_INPUT) && + !(portc->trigger_bits & PCM_ENABLE_INPUT)) + { + /* start recording */ + als4000_setmixer (devc, 0xde, + als4000_getmixer (devc, 0xde) | 0x80); + devc->rec_rate_lock = 1; + portc->trigger_bits |= PCM_ENABLE_INPUT; + } + } + else + { + if ((portc->audio_enabled & PCM_ENABLE_INPUT) && + (portc->trigger_bits & PCM_ENABLE_INPUT)) + { + portc->audio_enabled &= ~PCM_ENABLE_INPUT; + portc->trigger_bits &= ~PCM_ENABLE_INPUT; + /* stop recording */ + als4000_setmixer (devc, 0xde, + als4000_getmixer (devc, 0xde) & ~0x80); + devc->rec_rate_lock = 0; + } + } + } + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); +} + +/*ARGSUSED*/ +static int +als4000_audio_prepare_for_input (int dev, int bsize, int bcount) +{ + als4000_devc *devc = audio_engines[dev]->devc; + als4000_portc *portc = audio_engines[dev]->portc; + dmap_t *dmap = audio_engines[dev]->dmap_in; + oss_native_word flags; + int cnt, bits; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + /* set speed */ + als4000_speed (devc, portc->speed); + + /* set mode */ + if (portc->bits == AFMT_S16_LE) + bits = 0x10; /*signed 16bit */ + else + bits = 0x04; /*unsigned 8bit */ + if (portc->channels == 2) + bits |= 0x20; /*stereo */ + als4000_setmixer (devc, 0xde, bits); + + /* set size and buffer address */ + als4000_gcr_writel (devc, 0xa2, dmap->dmabuf_phys); + als4000_gcr_writel (devc, 0xa3, dmap->bytes_in_use - 1); + + /* set tranfersize */ + cnt = dmap->fragment_size; + if (portc->bits == AFMT_S16_LE) + cnt >>= 1; + cnt--; + + als4000_setmixer (devc, 0xdc, cnt & 0xFF); + als4000_setmixer (devc, 0xdd, (cnt >> 8) & 0xFF); + + portc->audio_enabled &= ~PCM_ENABLE_INPUT; + portc->trigger_bits &= ~PCM_ENABLE_INPUT; + + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return 0; +} + +/*ARGSUSED*/ +static int +als4000_audio_prepare_for_output (int dev, int bsize, int bcount) +{ + als4000_devc *devc = audio_engines[dev]->devc; + als4000_portc *portc = audio_engines[dev]->portc; + dmap_t *dmap = audio_engines[dev]->dmap_out; + oss_native_word flags; + int cnt; + + MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); + /* set speed */ + als4000_speed (devc, portc->speed); + + /* set mode */ + als4000_write (devc, (portc->bits == AFMT_S16_LE ? 0xb6 : 0xc6)); + als4000_write (devc, ((portc->channels == 2 ? 0x20 : 0) + + (portc->bits == AFMT_S16_LE ? 0x10 : 0))); + + + /* set size and buffer address */ + als4000_gcr_writel (devc, 0x91, dmap->dmabuf_phys); + als4000_gcr_writel (devc, 0x92, (dmap->bytes_in_use - 1) | 0x00180000); + + /* set transfer size */ + cnt = dmap->fragment_size; + if (portc->bits == AFMT_S16_LE) + cnt >>= 1; + cnt--; + + als4000_write (devc, cnt & 0xff); /*length low */ + als4000_write (devc, (cnt >> 8)); /*length high */ + + als4000_write (devc, 0xd1); /* turn on speaker */ + + portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; + portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; + + MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); + return 0; +} + +static int +als4000_get_buffer_pointer (int dev, dmap_t * dmap, int direction) +{ + als4000_devc *devc = audio_engines[dev]->devc; + unsigned int ptr; + + if (direction == PCM_ENABLE_OUTPUT) + ptr = als4000_gcr_readl (devc, 0xa0); + else + ptr = als4000_gcr_readl (devc, 0xa4); + + return (ptr - dmap->dmabuf_phys) % dmap->bytes_in_use; +} + + +static audiodrv_t als4000_audio_driver = { + als4000_audio_open, + als4000_audio_close, + als4000_audio_output_block, + als4000_audio_start_input, + als4000_audio_ioctl, + als4000_audio_prepare_for_input, + als4000_audio_prepare_for_output, + als4000_audio_reset, + NULL, + NULL, + als4000_audio_reset_input, + als4000_audio_reset_output, + als4000_audio_trigger, + als4000_audio_set_rate, + als4000_audio_set_format, + als4000_audio_set_channels, + NULL, + NULL, + NULL, + NULL, + NULL, /* als4000_alloc_buffer, */ + NULL, /* als4000_free_buffer, */ + NULL, + NULL, + als4000_get_buffer_pointer +}; + +#ifdef OBSOLETED_STUFF +/* + * This device has "ISA style" MIDI and FM subsystems. Such devices don't + * use PCI config space for the I/O ports and interrupts. Instead the driver + * needs to allocate proper resources itself. This functionality is no longer + * possible. For this reason the MIDI and FM parts are not accessible. + */ +static void +attach_fm (als4000_devc * devc) +{ + if (!opl3_detect (0x388, devc->osdev)) + return; + opl3_init (0x388, devc->osdev); + devc->fm_attached = 1; +} + +static void +attach_mpu (als4000_devc * devc) +{ + struct address_info hw_config; + + hw_config.io_base = devc->mpu_base; + hw_config.irq = devc->mpu_irq; + hw_config.dma = -1; + hw_config.dma2 = -1; + hw_config.always_detect = 0; + hw_config.name = "Avance Logic ALS4000 MPU"; + hw_config.driver_use_1 = 0; + hw_config.driver_use_2 = 0; + hw_config.osdev = devc->osdev; +#ifdef CREATE_OSP + CREATE_OSP (hw_config.osdev); +#endif + hw_config.card_subtype = 0; + + if (!probe_uart401 (&hw_config)) + { + cmn_err (CE_WARN, "MPU-401 was not detected\n"); + return; + } + devc->mpu_attached = 1; + attach_uart401 (&hw_config); +} + +static void +unload_mpu (als4000_devc * devc) +{ + struct address_info hw_config; + + hw_config.io_base = devc->mpu_base; + hw_config.irq = devc->mpu_irq; + hw_config.dma = -1; + hw_config.dma2 = -1; + hw_config.always_detect = 0; + hw_config.name = "Avance Logic ALS4000 MPU"; + hw_config.driver_use_1 = 0; + hw_config.driver_use_2 = 0; + hw_config.osdev = devc->osdev; +#ifdef CREATE_OSP + CREATE_OSP (hw_config.osdev); +#endif + hw_config.card_subtype = 0; + + devc->mpu_attached = 0; + unload_uart401 (&hw_config); +} +#endif + +void +setup_als4000 (als4000_devc * devc) +{ + unsigned char byte, i; + oss_native_word dwTemp; + + dwTemp = ((oss_native_word) 0x200 << 16) | (0x388); + dwTemp |= 0x00010000; + dwTemp |= 0x00000001; + als4000_gcr_writel (devc, 0xa8, dwTemp); + dwTemp = ((oss_native_word) devc->mpu_base << 16); + dwTemp |= 0x00010000; + als4000_gcr_writel (devc, 0xa9, dwTemp); + + byte = als4000_getmixer (devc, 0xc0); + als4000_setmixer (devc, 0xc0, byte | 0x80); /*disable M80/M81 write protect */ + als4000_setmixer (devc, 0x81, 0x01); + als4000_setmixer (devc, 0xc0, (byte & 0x7F)); /*set M80/M81 write protect */ + + dwTemp = als4000_gcr_readl (devc, 0x8c); + als4000_gcr_writel (devc, 0x8c, dwTemp | 0x00028000); /*enable INTA */ + + for (i = 0x91; i < 0xa7; i++) + als4000_gcr_writel (devc, i, 0x00); + dwTemp = als4000_gcr_readl (devc, 0x99); + als4000_gcr_writel (devc, 0x99, dwTemp | 0x100); +} + + +static int +init_als4000 (als4000_devc * devc) +{ + int my_mixer; + int first_dev = 0; + unsigned char byte; + int i; + int adev; + + devc->mpu_attached = devc->fm_attached = 0; + + setup_als4000 (devc); + +#ifdef OBSOLETED_STUFF + attach_fm (devc); + if (devc->mpu_base > 0) + attach_mpu (devc); +#endif + + /* check ESP */ + als4000_write (devc, 0xE4); + als4000_write (devc, 0xaa); + als4000_write (devc, 0xE8); + byte = als4000_read (devc); + if (byte != 0xaa) + { + DDB (cmn_err (CE_WARN, "ESP Not OK\n")); + return 0; + } + + /* reset the DSP */ + als4000_reset (devc); + + if ((my_mixer = oss_install_mixer (OSS_MIXER_DRIVER_VERSION, + devc->osdev, + devc->osdev, + "Avance Logic ALS4000", + &als4000_mixer_driver, + sizeof (mixer_driver_t), devc)) < 0) + { + return 0; + } + als4000_mixer_reset (devc); + + for (i = 0; i < MAX_PORTC; i++) + { + char tmp_name[100]; + als4000_portc *portc = &devc->portc[i]; + int caps = ADEV_AUTOMODE; + + if (i == 0) + { + strcpy (tmp_name, devc->chip_name); + caps |= ADEV_DUPLEX; + } + else + { + strcpy (tmp_name, devc->chip_name); + caps |= ADEV_DUPLEX | ADEV_SHADOW; + } + + if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, + devc->osdev, + devc->osdev, + tmp_name, + &als4000_audio_driver, + sizeof (audiodrv_t), + caps, + AFMT_U8 | AFMT_S16_LE, devc, -1)) < 0) + { + adev = -1; + return 0; + } + else + { + if (i == 0) + first_dev = adev; + audio_engines[adev]->portc = portc; + audio_engines[adev]->rate_source = first_dev; + audio_engines[adev]->mixer_dev = my_mixer; + audio_engines[adev]->min_rate = 5000; + audio_engines[adev]->max_rate = 48000; + audio_engines[adev]->dmabuf_maxaddr = MEMLIMIT_ISA; + portc->open_mode = 0; + portc->audiodev = adev; + portc->audio_enabled = 0; +#ifdef CONFIG_OSS_VMIX + if (i == 0) + vmix_attach_audiodev(devc->osdev, adev, -1, 0); +#endif + } + } + return 1; +} + +int +oss_als4k_attach (oss_device_t * osdev) +{ + + unsigned char pci_irq_line, pci_revision; + unsigned short pci_command, vendor, device; + unsigned int pci_ioaddr; + int err; + als4000_devc *devc; + + DDB (cmn_err (CE_WARN, "Entered ALS ALS4000 probe routine\n")); + + pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor); + pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision); + pci_read_config_word (osdev, PCI_COMMAND, &pci_command); + pci_read_config_word (osdev, PCI_DEVICE_ID, &device); + pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line); + pci_read_config_dword (osdev, PCI_BASE_ADDRESS_0, &pci_ioaddr); + + if (vendor != ALS_VENDOR_ID || device != ALS_4000) + return 0; + + DDB (cmn_err (CE_WARN, "ALS4000 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; + devc->chip_name = "Avance Logic ALS4000"; + devc->mpu_base = als4000_mpu_ioaddr; + devc->mpu_irq = als4000_mpu_irq; + + + devc->base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr); + /* Remove I/O space marker in bit 0. */ + devc->base &= ~3; + + pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; + pci_write_config_word (osdev, PCI_COMMAND, pci_command); + + 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, als4000intr, NULL)) < 0) + { + cmn_err (CE_WARN, "Error installing interrupt handler: %x\n", err); + return 0; + } + + return init_als4000 (devc); /* Detected */ +} + + +int +oss_als4k_detach (oss_device_t * osdev) +{ + als4000_devc *devc = (als4000_devc *) osdev->devc; + + if (oss_disable_device (osdev) < 0) + return 0; + + als4000_gcr_writel (devc, 0x8c, 0x0); + +#ifdef OBSOLETED_STUFF + if (devc->mpu_attached) + unload_mpu (devc); +#endif + + oss_unregister_interrupts (devc->osdev); + + MUTEX_CLEANUP (devc->mutex); + MUTEX_CLEANUP (devc->low_mutex); + UNMAP_PCI_IOADDR (devc->osdev, 0); + + oss_unregister_device (devc->osdev); + return 1; +} diff --git a/attic/drv/oss_als4k/oss_als4k.man b/attic/drv/oss_als4k/oss_als4k.man new file mode 100644 index 0000000..ace289f --- /dev/null +++ b/attic/drv/oss_als4k/oss_als4k.man @@ -0,0 +1,23 @@ +NAME +oss_als4k - ALS4000 audio driver. + +DESCRIPTION +Open Sound System driver for Avance Logic ALS4000 based soundcards. +ALS4000 characteristics: + o 8/16 bit + o mono/stereo + o 8Khz - 48Khz sampling rates + o FM Synthesis + o MIDI UART401 + +The ALS4000 has a Sound Blaster compatible mixer device. + +OPTIONS +None + +FILES +CONFIGFILEPATH/oss_als4k.conf + +AUTHOR +4Front Technologies + |