patch-2.2.17 linux/drivers/sound/trident.c
Next file: linux/drivers/sound/trident.h
Previous file: linux/drivers/sound/sound_core.c
Back to the patch index
Back to the overall index
- Lines: 578
- Date:
Mon Sep 4 18:39:21 2000
- Orig file:
v2.2.16/drivers/sound/trident.c
- Orig date:
Mon Sep 4 18:37:37 2000
diff -u --recursive --new-file v2.2.16/drivers/sound/trident.c linux/drivers/sound/trident.c
@@ -1,6 +1,6 @@
/*
*
- * Trident 4D-Wave/SiS 7018 OSS driver for Linux 2.2.x
+ * Trident 4D-Wave/SiS 7018/ALi 5451 OSS driver for Linux 2.2.x
*
* Driver: Alan Cox <alan@redhat.com>
*
@@ -12,6 +12,7 @@
* Hacked up by:
* Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
* Ollie Lho <ollie@sis.com.tw> SiS 7018 Audio Core Support
+ * Ching Ling Lee <cling-li@ali.com.tw> ALi 5451 Audio Core Support
*
*
* This program is free software; you can redistribute it and/or modify
@@ -112,9 +113,11 @@
#include <asm/hardirq.h>
#include <linux/bitops.h>
+#include "dm.h"
+
#include "trident.h"
-#define DRIVER_VERSION "0.14.5"
+#define DRIVER_VERSION "0.14.6a"
/* magic numbers to protect our data structures */
#define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */
@@ -137,6 +140,10 @@
/* minor number of /dev/swmodem (temporary, experimental) */
#define SND_DEV_SWMODEM 7
+/* Size of the MIDI buffer */
+#define MIDIINBUF 256
+#define MIDIOUTBUF 256
+
static const unsigned sample_size[] = { 1, 2, 2, 4 };
static const unsigned sample_shift[] = { 0, 1, 1, 2 };
@@ -155,6 +162,15 @@
{PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5451, "ALi Audio Accelerator"}
};
+struct midi_stuff{
+ unsigned ird, iwr, icnt;
+ unsigned ord, owr, ocnt;
+ struct wait_queue *iwait;
+ struct wait_queue *owait;
+ unsigned char ibuf[MIDIINBUF];
+ unsigned char obuf[MIDIOUTBUF];
+};
+
/* "software" or virtual channel, an instance of opened /dev/dsp */
struct trident_state {
unsigned int magic;
@@ -266,15 +282,24 @@
/* soundcore stuff */
int dev_audio;
-
+ int dev_midi;
+ int dev_dmfm;
+
+ /* Device locks */
+ int midi_on;
+ int dmfm_on;
+
/* structures for abstraction of hardware facilities, codecs, banks and channels*/
struct ac97_codec *ac97_codec[NR_AC97];
struct trident_pcm_bank banks[NR_BANKS];
struct trident_state *states[NR_HW_CH];
+ struct midi_stuff midi;
/* hardware resources */
unsigned long iobase;
u32 irq;
+ unsigned long midi_io;
+ unsigned long synth_io;
/* Function support */
struct trident_channel *(*alloc_pcm_channel)(struct trident_card *);
@@ -1268,6 +1293,35 @@
}
+static void trident_handle_midi(struct trident_card *card)
+{
+ unsigned char ch;
+ int wake = 0;
+
+ while (!(inb(card->iobase + ALI_MPUR1) & 0x80)) {
+ ch = inb(card->iobase + ALI_MPUR0);
+ if (card->midi.icnt < MIDIINBUF) {
+ card->midi.ibuf[card->midi.iwr] = ch;
+ card->midi.iwr = (card->midi.iwr + 1) % MIDIINBUF;
+ card->midi.icnt++;
+ }
+ wake = 1;
+ }
+ if (wake)
+ wake_up(&card->midi.iwait);
+ wake = 0;
+ while (!(inb(card->iobase + ALI_MPUR1) & 0x40) && card->midi.ocnt > 0) {
+ inb(card->iobase + ALI_MPUR0);
+ outb(card->midi.obuf[card->midi.ord], card->iobase + ALI_MPUR0);
+ card->midi.ord = (card->midi.ord + 1) % MIDIOUTBUF;
+ card->midi.ocnt--;
+ if (card->midi.ocnt < MIDIOUTBUF-16)
+ wake = 1;
+ }
+ if (wake)
+ wake_up(&card->midi.owait);
+}
+
static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct trident_card *card = (struct trident_card *)dev_id;
@@ -1283,7 +1337,11 @@
if (event & ADDRESS_IRQ) {
card->address_interrupt(card);
}
-
+
+ if(event & MPU401_IRQ) {
+ if(card->midi_on)
+ trident_handle_midi(card);
+ }
/* manually clear interrupt status, bad hardware design, blame T^2 */
outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW),
TRID_REG(card, T4D_MISCINT));
@@ -2017,6 +2075,385 @@
release: trident_release,
};
+/* --------------------------------------------------------------------- */
+
+/*
+ * MIDI and direct synth support for the ALI 5451. These functions
+ * are taken directly from Ching Ling Lee's original ALI only
+ * driver.
+ */
+
+static ssize_t trident_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+ struct trident_card *card = (struct trident_card *)file->private_data;
+ ssize_t ret;
+ unsigned long flags;
+ unsigned ptr;
+ int cnt;
+ struct midi_stuff *midi;
+
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+ if (!access_ok(VERIFY_WRITE, buffer, count))
+ return -EFAULT;
+
+ midi = &card->midi;
+
+ ret = 0;
+ while (count > 0) {
+ spin_lock_irqsave(&card->lock, flags);
+ ptr = midi->ird;
+ cnt = MIDIINBUF - ptr;
+ if (midi->icnt < cnt)
+ cnt = midi->icnt;
+ spin_unlock_irqrestore(&card->lock, flags);
+ if (cnt > count)
+ cnt = count;
+ if (cnt <= 0) {
+ if (file->f_flags & O_NONBLOCK)
+ return ret ? ret : EAGAIN;
+ interruptible_sleep_on(&midi->iwait);
+ if (signal_pending(current))
+ return ret ? ret : -ERESTARTSYS;
+ continue;
+ }
+ if (copy_to_user(buffer, midi->ibuf + ptr, cnt))
+ return ret ? ret : -EFAULT;
+ ptr = (ptr + cnt) % MIDIINBUF;
+ spin_lock_irqsave(&card->lock, flags);
+ midi->ird = ptr;
+ midi->icnt -= cnt;
+ spin_unlock_irqrestore(&card->lock, flags);
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+ }
+ return ret;
+}
+
+static ssize_t trident_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+ struct trident_card *card = (struct trident_card *)file->private_data;
+ ssize_t ret;
+ unsigned long flags;
+ unsigned ptr;
+ int cnt;
+ struct midi_stuff *midi;
+
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+ if (!access_ok(VERIFY_READ, buffer, count))
+ return -EFAULT;
+
+ midi = &card->midi;
+
+ ret = 0;
+ while (count > 0) {
+ spin_lock_irqsave(&card->lock, flags);
+ ptr = midi->owr;
+ cnt = MIDIOUTBUF - ptr;
+ if (midi->ocnt + cnt > MIDIOUTBUF)
+ cnt = MIDIOUTBUF - midi->ocnt;
+ if (cnt <= 0)
+ trident_handle_midi(card);
+ spin_unlock_irqrestore(&card->lock, flags);
+ if (cnt > count)
+ cnt = count;
+ if (cnt <= 0) {
+ if (file->f_flags & O_NONBLOCK)
+ return ret ? ret : -EAGAIN;
+ interruptible_sleep_on(&midi->owait);
+ if (signal_pending(current))
+ return ret ? ret : -ERESTARTSYS;
+ continue;
+ }
+ if (copy_from_user(midi->obuf + ptr, buffer, cnt))
+ return ret ? ret : -EFAULT;
+ ptr = (ptr + cnt) % MIDIOUTBUF;
+ spin_lock_irqsave(&card->lock, flags);
+ midi->owr = ptr;
+ midi->ocnt += cnt;
+ spin_unlock_irqrestore(&card->lock, flags);
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+ spin_lock_irqsave(&card->lock, flags);
+ trident_handle_midi(card);
+ spin_unlock_irqrestore(&card->lock, flags);
+ }
+ return ret;
+}
+
+
+static unsigned int trident_midi_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct trident_card *card = (struct trident_card *)file->private_data;
+ unsigned long flags;
+ unsigned int mask = 0;
+
+ if (file->f_flags & FMODE_WRITE)
+ poll_wait(file, &card->midi.owait, wait);
+ if (file->f_flags & FMODE_READ)
+ poll_wait(file, &card->midi.iwait, wait);
+ spin_lock_irqsave(&card->lock, flags);
+ if (file->f_flags & FMODE_READ) {
+ if (card->midi.icnt > 0)
+ mask |= POLLIN | POLLRDNORM;
+ }
+ if (file->f_flags & FMODE_WRITE) {
+ if (card->midi.ocnt < MIDIOUTBUF)
+ mask |= POLLOUT | POLLWRNORM;
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+ return mask;
+}
+
+static int trident_midi_open(struct inode *inode, struct file *file)
+{
+ struct trident_card *card = devs;
+ struct midi_stuff *midi;
+ unsigned long flags;
+
+ while(card!=NULL)
+ {
+ if(card->midi_on == 0 && card->dev_midi!=-1)
+ break;
+ card=card->next;
+ }
+ if(card==NULL)
+ return -EBUSY;
+
+ spin_lock_irqsave(&card->lock, flags);
+ card->midi_on = 1;
+ midi = &card->midi;
+ file->private_data = card;
+ if (file->f_mode & FMODE_READ) {
+ midi->ird = midi->iwr = midi->icnt = 0;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ midi->ord = midi->owr = midi->ocnt = 0;
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+ return 0;
+}
+
+static int trident_midi_release(struct inode *inode, struct file *file)
+{
+ struct trident_card *card = (struct trident_card *)file->private_data;
+ struct wait_queue wait = { current, NULL };
+ unsigned long flags;
+ unsigned count, tmo;
+
+ if (file->f_mode & FMODE_WRITE) {
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(&card->midi.owait, &wait);
+ for (;;) {
+ spin_lock_irqsave(&card->lock, flags);
+ count = card->midi.ocnt;
+ spin_unlock_irqrestore(&card->lock, flags);
+ if (count <= 0)
+ break;
+ if (signal_pending(current))
+ break;
+ if (file->f_flags & O_NONBLOCK) {
+ remove_wait_queue(&card->midi.owait, &wait);
+ current->state = TASK_RUNNING;
+ return -EBUSY;
+ }
+ tmo = (count * HZ) / 3100;
+ if (!schedule_timeout(tmo ? : 1) && tmo)
+ printk(KERN_DEBUG "trident: midi timed out??\n");
+ }
+ remove_wait_queue(&card->midi.owait, &wait);
+ current->state = TASK_RUNNING;
+ }
+ spin_lock_irqsave(&card->lock, flags);
+ outb(0xff, card->iobase + ALI_MPUR1);
+ card->midi_on = 0;
+ spin_unlock_irqrestore(&card->lock, flags);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static /*const*/ struct file_operations trident_midi_fops = {
+ &trident_llseek,
+ &trident_midi_read,
+ &trident_midi_write,
+ NULL, /* readdir */
+ &trident_midi_poll,
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ &trident_midi_open,
+ NULL, /* flush */
+ &trident_midi_release,
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+static int trident_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ static const unsigned char op_offset[18] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15
+ };
+ struct trident_card *card = (struct trident_card *)file->private_data;
+ struct dm_fm_voice v;
+ struct dm_fm_note n;
+ struct dm_fm_params p;
+ unsigned int io;
+ unsigned int regb;
+
+ switch (cmd) {
+ case FM_IOCTL_RESET:
+ for (regb = 0xb0; regb < 0xb9; regb++) {
+ outb(regb, card->synth_io);
+ outb(0, card->synth_io+1);
+ outb(regb, card->synth_io+2);
+ outb(0, card->synth_io+3);
+ }
+ return 0;
+
+ case FM_IOCTL_PLAY_NOTE:
+ if (copy_from_user(&n, (void *)arg, sizeof(n)))
+ return -EFAULT;
+ if (n.voice >= 18)
+ return -EINVAL;
+ if (n.voice >= 9) {
+ regb = n.voice - 9;
+ io = card->synth_io+2;
+ } else {
+ regb = n.voice;
+ io = card->synth_io;
+ }
+ outb(0xa0 + regb, io);
+ outb(n.fnum & 0xff, io+1);
+ outb(0xb0 + regb, io);
+ outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1);
+ return 0;
+
+ case FM_IOCTL_SET_VOICE:
+ if (copy_from_user(&v, (void *)arg, sizeof(v)))
+ return -EFAULT;
+ if (v.voice >= 18)
+ return -EINVAL;
+ regb = op_offset[v.voice];
+ io = card->synth_io + ((v.op & 1) << 1);
+ outb(0x20 + regb, io);
+ outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) |
+ ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1);
+ outb(0x40 + regb, io);
+ outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1);
+ outb(0x60 + regb, io);
+ outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1);
+ outb(0x80 + regb, io);
+ outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1);
+ outb(0xe0 + regb, io);
+ outb(v.waveform & 0x7, io+1);
+ if (n.voice >= 9) {
+ regb = n.voice - 9;
+ io = card->synth_io+2;
+ } else {
+ regb = n.voice;
+ io = card->synth_io;
+ }
+ outb(0xc0 + regb, io);
+ outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) |
+ (v.connection & 1), io+1);
+ return 0;
+
+ case FM_IOCTL_SET_PARAMS:
+ if (copy_from_user(&p, (void *)arg, sizeof(p)))
+ return -EFAULT;
+ outb(0x08, card->synth_io);
+ outb((p.kbd_split & 1) << 6, card->synth_io+1);
+ outb(0xbd, card->synth_io);
+ outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) |
+ ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), card->synth_io+1);
+ return 0;
+
+ case FM_IOCTL_SET_OPL:
+ outb(4, card->synth_io+2);
+ outb(arg, card->synth_io+3);
+ return 0;
+
+ case FM_IOCTL_SET_MODE:
+ outb(5, card->synth_io+2);
+ outb(arg & 1, card->synth_io+3);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int trident_dmfm_open(struct inode *inode, struct file *file)
+{
+ struct trident_card *card = devs;
+
+ file->private_data = card;
+ while(card!=NULL)
+ {
+ if(card->midi_on == 0 && card->dev_midi!=-1)
+ break;
+ card=card->next;
+ }
+ if(card==NULL)
+ return -EBUSY;
+ MOD_INC_USE_COUNT;
+ card->dmfm_on = 1;
+
+ /* init the stuff */
+ outb(1, card->synth_io);
+ outb(0x20, card->synth_io+1); /* enable waveforms */
+ outb(4, card->synth_io+2);
+ outb(0, card->synth_io+3); /* no 4op enabled */
+ outb(5, card->synth_io+2);
+ outb(1, card->synth_io+3); /* enable OPL3 */
+ return 0;
+}
+
+static int trident_dmfm_release(struct inode *inode, struct file *file)
+{
+ struct trident_card *card = (struct trident_card *)file->private_data;
+ unsigned int regb;
+
+ for (regb = 0xb0; regb < 0xb9; regb++) {
+ outb(regb, card->synth_io);
+ outb(0, card->synth_io+1);
+ outb(regb, card->synth_io+2);
+ outb(0, card->synth_io+3);
+ }
+ card->dmfm_on = 0;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static /*const*/ struct file_operations trident_dmfm_fops = {
+ &trident_llseek,
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ &trident_dmfm_ioctl,
+ NULL, /* mmap */
+ &trident_dmfm_open,
+ NULL, /* fl600
+ ush */
+ &trident_dmfm_release,
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
+
/* trident specific AC97 functions */
/* Write AC97 codec registers */
static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val)
@@ -2370,6 +2807,8 @@
memset(card, 0, sizeof(*card));
card->iobase = iobase;
+ card->midi_io = iobase + 0x20;
+ card->synth_io = iobase + 0x10;
card->pci_info = pci_info;
card->pci_id = pci_info->device;
card->irq = pci_dev->irq;
@@ -2423,6 +2862,31 @@
kfree(card);
return -ENODEV;
}
+
+ if ((card->dev_midi = register_sound_midi(&trident_midi_fops, -1))<0)
+ printk(KERN_WARNING "trident: Unable to register midi.\n");
+ else
+ {
+ struct midi_stuff *midi = &card->midi;
+ init_waitqueue(&midi->iwait);
+ init_waitqueue(&midi->owait);
+ midi->ird = midi->iwr = midi->icnt = 0;
+ midi->ord = midi->owr = midi->ocnt = 0;
+ outb(0xbc, card->iobase+ALI_MPUR2);
+
+ /* reset command */
+ while (inb(card->iobase + ALI_MPUR1) & 0x40);
+ outb(0xff, card->iobase + ALI_MPUR1);
+ while (inb(card->iobase + ALI_MPUR1) & 0x80);
+ while ((inb(card->iobase + ALI_MPUR0) != 0xfe) && (inb(card->iobase+ALI_MPUR1) != 0x10))
+ while (inb(card->iobase + ALI_MPUR1) & 0x80);
+
+ midi->ird = midi->iwr = midi->icnt = 0;
+ }
+
+ if ((card->dev_dmfm = register_sound_special(&trident_dmfm_fops, 15))<0)
+ printk(KERN_WARNING "trident: Unable to register FM.\n");
+
outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL));
if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
@@ -2454,7 +2918,7 @@
if (!pci_present()) /* No PCI bus in this machine! */
return -ENODEV;
- printk(KERN_INFO "Trident 4DWave/SiS 7018 PCI Audio, version "
+ printk(KERN_INFO "Trident 4DWave/SiS 7018/Ali 5451 PCI Audio, version "
DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n");
for (i = 0; i < sizeof (pci_audio_devices); i++) {
@@ -2496,6 +2960,10 @@
kfree (devs->ac97_codec[i]);
}
unregister_sound_dsp(devs->dev_audio);
+ if(devs->dev_midi!=-1)
+ unregister_sound_midi(devs->dev_midi);
+ if(devs->dev_dmfm!=-1)
+ unregister_sound_special(devs->dev_dmfm);
kfree(devs);
devs = devs->next;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)