patch-2.2.14 linux/drivers/sound/es1370.c
Next file: linux/drivers/sound/es1371.c
Previous file: linux/drivers/sound/dmasound.c
Back to the patch index
Back to the overall index
- Lines: 333
- Date:
Tue Jan 4 10:12:21 2000
- Orig file:
v2.2.13/linux/drivers/sound/es1370.c
- Orig date:
Mon Aug 9 16:05:57 1999
diff -u --recursive --new-file v2.2.13/linux/drivers/sound/es1370.c linux/drivers/sound/es1370.c
@@ -101,6 +101,17 @@
* 15.06.99 0.23 Fix bad allocation bug.
* Thanks to Deti Fliegl <fliegl@in.tum.de>
* 28.06.99 0.24 Add pci_set_master
+ * 02.08.99 0.25 Added workaround for the "phantom write" bug first
+ * documented by Dave Sharpless from Anchor Games
+ * 03.08.99 0.26 adapt to Linus' new __setup/__initcall
+ * added kernel command line option "es1370=joystick[,lineout[,micbias]]"
+ * removed CONFIG_SOUND_ES1370_JOYPORT_BOOT kludge
+ * 12.08.99 0.27 module_init/__setup fixes
+ * 19.08.99 0.28 SOUND_MIXER_IMIX fixes, reported by Gianluca <gialluca@mail.tiscalinet.it>
+ * 31.08.99 0.29 add spin_lock_init
+ * __initlocaldata to fix gcc 2.7.x problems
+ * 03.09.99 0.30 change read semantics for MIDI to match
+ * OSS more closely; remove possible wakeup race
*
* some important things missing in Ensoniq documentation:
*
@@ -123,7 +134,6 @@
/*****************************************************************************/
-#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/string.h>
@@ -175,12 +185,14 @@
#define ES1370_REG_DAC2_SCOUNT 0x28
#define ES1370_REG_ADC_SCOUNT 0x2c
-#define ES1370_REG_DAC1_FRAMEADR 0xc30
-#define ES1370_REG_DAC1_FRAMECNT 0xc34
-#define ES1370_REG_DAC2_FRAMEADR 0xc38
-#define ES1370_REG_DAC2_FRAMECNT 0xc3c
-#define ES1370_REG_ADC_FRAMEADR 0xd30
-#define ES1370_REG_ADC_FRAMECNT 0xd34
+#define ES1370_REG_DAC1_FRAMEADR 0xc30
+#define ES1370_REG_DAC1_FRAMECNT 0xc34
+#define ES1370_REG_DAC2_FRAMEADR 0xc38
+#define ES1370_REG_DAC2_FRAMECNT 0xc3c
+#define ES1370_REG_ADC_FRAMEADR 0xd30
+#define ES1370_REG_ADC_FRAMECNT 0xd34
+#define ES1370_REG_PHANTOM_FRAMEADR 0xd38
+#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c
#define ES1370_FMT_U8_MONO 0
#define ES1370_FMT_U8_STEREO 1
@@ -360,6 +372,13 @@
static struct es1370_state *devs = NULL;
+/*
+ * The following buffer is used to point the phantom write channel to,
+ * so that it cannot wreak havoc. The attribute makes sure it doesn't
+ * cross a page boundary and ensures dword alignment for the DMA engine
+ */
+static unsigned char bugbuf[16] __attribute__ ((aligned (16)));
+
/* --------------------------------------------------------------------- */
extern inline unsigned ld2(unsigned int x)
@@ -779,10 +798,36 @@
[SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } /* mono out */
};
+static void set_recsrc(struct es1370_state *s, unsigned int val)
+{
+ unsigned int i, j;
+
+ for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+ if (!(val & (1 << i)))
+ continue;
+ if (!mixtable[i].recmask) {
+ val &= ~(1 << i);
+ continue;
+ }
+ j |= mixtable[i].recmask;
+ }
+ s->mix.recsrc = val;
+ wrcodec(s, 0x12, j & 0xd5);
+ wrcodec(s, 0x13, j & 0xaa);
+ wrcodec(s, 0x14, (j >> 8) & 0x17);
+ wrcodec(s, 0x15, (j >> 8) & 0x0f);
+ i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60;
+ if (!s->mix.imix) {
+ i &= 0xff60; /* mute record and line monitor */
+ }
+ wrcodec(s, 0x10, i);
+ wrcodec(s, 0x11, i >> 8);
+}
+
static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long arg)
{
unsigned long flags;
- int i, val, j;
+ int i, val;
unsigned char l, r, rl, rr;
VALIDATE_STATE(s);
@@ -887,34 +932,13 @@
switch (_IOC_NR(cmd)) {
case SOUND_MIXER_IMIX:
- if (arg == 0)
- return -EFAULT;
- get_user_ret(s->mix.imix,(int *)arg, -EFAULT);
- val = s->mix.recsrc;
- /* fall through */
+ get_user_ret(s->mix.imix, (int *)arg, -EFAULT);
+ set_recsrc(s, s->mix.recsrc);
+ return 0;
case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
get_user_ret(val, (int *)arg, -EFAULT);
- for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if (!(val & (1 << i)))
- continue;
- if (!mixtable[i].recmask) {
- val &= ~(1 << i);
- continue;
- }
- j |= mixtable[i].recmask;
- }
- s->mix.recsrc = val;
- wrcodec(s, 0x12, j & 0xd5);
- wrcodec(s, 0x13, j & 0xaa);
- wrcodec(s, 0x14, (j >> 8) & 0x17);
- wrcodec(s, 0x15, (j >> 8) & 0x0f);
- i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60;
- if (!s->mix.imix) {
- i &= 0xff60; /* mute record and line monitor */
- }
- wrcodec(s, 0x10, i);
- wrcodec(s, 0x11, i >> 8);
+ set_recsrc(s, val);
return 0;
default:
@@ -1033,7 +1057,7 @@
static int drain_dac1(struct es1370_state *s, int nonblock)
{
- struct wait_queue wait = { current, NULL };
+ struct wait_queue wait = { current, NULL };
unsigned long flags;
int count, tmo;
@@ -1054,9 +1078,10 @@
current->state = TASK_RUNNING;
return -EBUSY;
}
- tmo = (count * HZ) / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
+ tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2
+ / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
- if (!schedule_timeout(tmo ? : 1) && tmo)
+ if (!schedule_timeout(tmo + 1))
DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");)
}
remove_wait_queue(&s->dma_dac1.wait, &wait);
@@ -1089,9 +1114,10 @@
current->state = TASK_RUNNING;
return -EBUSY;
}
- tmo = (count * HZ) / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV);
+ tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2
+ / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV);
tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
- if (!schedule_timeout(tmo ? : 1) && tmo)
+ if (!schedule_timeout(tmo + 1))
DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");)
}
remove_wait_queue(&s->dma_dac2.wait, &wait);
@@ -2049,6 +2075,7 @@
static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
struct es1370_state *s = (struct es1370_state *)file->private_data;
+ struct wait_queue wait = { current, NULL };
ssize_t ret;
unsigned long flags;
unsigned ptr;
@@ -2059,7 +2086,10 @@
return -ESPIPE;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
+ if (count == 0)
+ return 0;
ret = 0;
+ add_wait_queue(&s->midi.iwait, &wait);
while (count > 0) {
spin_lock_irqsave(&s->lock, flags);
ptr = s->midi.ird;
@@ -2070,15 +2100,25 @@
if (cnt > count)
cnt = count;
if (cnt <= 0) {
- if (file->f_flags & O_NONBLOCK)
- return ret ? ret : -EAGAIN;
- interruptible_sleep_on(&s->midi.iwait);
- if (signal_pending(current))
- return ret ? ret : -ERESTARTSYS;
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ break;
+ }
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (signal_pending(current)) {
+ if (!ret)
+ ret = -ERESTARTSYS;
+ break;
+ }
continue;
}
- if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt))
- return ret ? ret : -EFAULT;
+ if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) {
+ if (!ret)
+ ret = -EFAULT;
+ break;
+ }
ptr = (ptr + cnt) % MIDIINBUF;
spin_lock_irqsave(&s->lock, flags);
s->midi.ird = ptr;
@@ -2087,13 +2127,17 @@
count -= cnt;
buffer += cnt;
ret += cnt;
+ break;
}
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&s->midi.iwait, &wait);
return ret;
}
static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
struct es1370_state *s = (struct es1370_state *)file->private_data;
+ struct wait_queue wait = { current, NULL };
ssize_t ret;
unsigned long flags;
unsigned ptr;
@@ -2104,7 +2148,10 @@
return -ESPIPE;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
+ if (count == 0)
+ return 0;
ret = 0;
+ add_wait_queue(&s->midi.owait, &wait);
while (count > 0) {
spin_lock_irqsave(&s->lock, flags);
ptr = s->midi.owr;
@@ -2117,15 +2164,25 @@
if (cnt > count)
cnt = count;
if (cnt <= 0) {
- if (file->f_flags & O_NONBLOCK)
- return ret ? ret : -EAGAIN;
- interruptible_sleep_on(&s->midi.owait);
- if (signal_pending(current))
- return ret ? ret : -ERESTARTSYS;
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ break;
+ }
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (signal_pending(current)) {
+ if (!ret)
+ ret = -ERESTARTSYS;
+ break;
+ }
continue;
}
- if (copy_from_user(s->midi.obuf + ptr, buffer, cnt))
- return ret ? ret : -EFAULT;
+ if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) {
+ if (!ret)
+ ret = -EFAULT;
+ break;
+ }
ptr = (ptr + cnt) % MIDIOUTBUF;
spin_lock_irqsave(&s->lock, flags);
s->midi.owr = ptr;
@@ -2138,6 +2195,8 @@
es1370_handle_midi(s);
spin_unlock_irqrestore(&s->lock, flags);
}
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&s->midi.owait, &wait);
return ret;
}
@@ -2217,7 +2276,7 @@
static int es1370_midi_release(struct inode *inode, struct file *file)
{
struct es1370_state *s = (struct es1370_state *)file->private_data;
- struct wait_queue wait = { current, NULL };
+ struct wait_queue wait = { current, NULL };
unsigned long flags;
unsigned count, tmo;
@@ -2321,7 +2380,7 @@
if (!pci_present()) /* No PCI bus in this machine! */
return -ENODEV;
- printk(KERN_INFO "es1370: version v0.24 time " __TIME__ " " __DATE__ "\n");
+ printk(KERN_INFO "es1370: version v0.30 time " __TIME__ " " __DATE__ "\n");
while (index < NR_DEVICE &&
(pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) {
if (pcidev->base_address[0] == 0 ||
@@ -2341,6 +2400,7 @@
init_waitqueue(&s->midi.iwait);
init_waitqueue(&s->midi.owait);
s->open_sem = MUTEX;
+ spin_lock_init(&s->lock);
s->magic = ES1370_MAGIC;
s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
s->irq = pcidev->irq;
@@ -2385,6 +2445,10 @@
/* initialize the chips */
outl(s->ctrl, s->io+ES1370_REG_CONTROL);
outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+ /* point phantom write channel to "bugbuf" */
+ outl((ES1370_REG_PHANTOM_FRAMEADR >> 8) & 15, s->io+ES1370_REG_MEMPAGE);
+ outl(virt_to_bus(bugbuf), s->io+(ES1370_REG_PHANTOM_FRAMEADR & 0xff));
+ outl(0, s->io+(ES1370_REG_PHANTOM_FRAMECNT & 0xff));
pci_set_master(pcidev); /* enable bus mastering */
wrcodec(s, 0x16, 3); /* no RST, PD */
wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!! */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)