patch-2.4.21 linux-2.4.21/drivers/sound/forte.c
Next file: linux-2.4.21/drivers/sound/gus_midi.c
Previous file: linux-2.4.21/drivers/sound/esssolo1.c
Back to the patch index
Back to the overall index
- Lines: 678
- Date:
2003-06-13 07:51:36.000000000 -0700
- Orig file:
linux-2.4.20/drivers/sound/forte.c
- Orig date:
2002-11-28 15:53:14.000000000 -0800
diff -urN linux-2.4.20/drivers/sound/forte.c linux-2.4.21/drivers/sound/forte.c
@@ -3,6 +3,9 @@
*
* Written by Martin K. Petersen <mkp@mkp.net>
* Copyright (C) 2002 Hewlett-Packard Company
+ * Portions Copyright (C) 2003 Martin K. Petersen
+ *
+ * Latest version: http://mkp.net/forte/
*
* Based upon the ALSA FM801 driver by Jaroslav Kysela and OSS drivers
* by Thomas Sailer, Alan Cox, Zach Brown, and Jeff Garzik. Thanks
@@ -24,15 +27,6 @@
*
*/
-/*
- * TODO:
- * MMIO
- * Multichannelize
- * Multichipify
- * MPU401
- * M^Gameport
- */
-
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -53,9 +47,10 @@
#include <asm/uaccess.h>
#include <asm/hardirq.h>
+#include <asm/io.h>
#define DRIVER_NAME "forte"
-#define DRIVER_VERSION "$Id: forte.c,v 1.55 2002/10/02 00:01:42 mkp Exp $"
+#define DRIVER_VERSION "$Id: forte.c,v 1.63 2003/03/01 05:32:42 mkp Exp $"
#define PFX DRIVER_NAME ": "
#undef M_DEBUG
@@ -76,10 +71,11 @@
#define FORTE_MIN_FRAG_SIZE 256
#define FORTE_MAX_FRAG_SIZE PAGE_SIZE
#define FORTE_DEF_FRAG_SIZE 256
-#define FORTE_MIN_FRAGMENTS 16
+#define FORTE_MIN_FRAGMENTS 2
#define FORTE_MAX_FRAGMENTS 256
-#define FORTE_DEF_FRAGMENTS 16
-#define FORTE_MIN_BUF 16386
+#define FORTE_DEF_FRAGMENTS 2
+#define FORTE_MIN_BUF_MSECS 500
+#define FORTE_MAX_BUF_MSECS 1000
/* PCI BARs */
#define FORTE_PCM_VOL 0x00 /* PCM Output Volume */
@@ -168,6 +164,7 @@
unsigned int frag_sz; /* Current fragment size */
unsigned int frag_num; /* Current # of fragments */
+ unsigned int frag_msecs; /* Milliseconds per frag */
unsigned int buf_sz; /* Current buffer size */
unsigned int hwptr; /* Tail */
@@ -175,14 +172,13 @@
unsigned int filled_frags; /* Fragments currently full */
unsigned int next_buf; /* Index of next buffer */
- unsigned int blocked; /* Blocked on I/O */
- unsigned int drain; /* Drain queued buffers */
unsigned int active; /* Channel currently in use */
unsigned int mapped; /* mmap */
unsigned int buf_pages; /* Real size of buffer */
unsigned int nr_irqs; /* Number of interrupts */
unsigned int bytes; /* Total bytes */
+ unsigned int residue; /* Partial fragment */
};
@@ -404,12 +400,11 @@
channel->next_buf = 1;
channel->swptr = 0;
channel->filled_frags = 0;
- channel->blocked = 0;
- channel->drain = 0;
channel->active = 0;
channel->bytes = 0;
channel->nr_irqs = 0;
channel->mapped = 0;
+ channel->residue = 0;
}
@@ -423,12 +418,11 @@
static void inline
forte_channel_start (struct forte_channel *channel)
{
- if (!channel || !channel->iobase)
+ if (!channel || !channel->iobase || channel->active)
return;
- DPRINTK ("%s: channel = %s\n", __FUNCTION__, channel->name);
-
- channel->ctrl &= ~(FORTE_PAUSE | FORTE_BUF1_LAST | FORTE_BUF2_LAST);
+ channel->ctrl &= ~(FORTE_PAUSE | FORTE_BUF1_LAST | FORTE_BUF2_LAST
+ | FORTE_IMMED_STOP);
channel->ctrl |= FORTE_START;
channel->active = 1;
outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL);
@@ -448,9 +442,29 @@
if (!channel || !channel->iobase)
return;
- DPRINTK ("%s: channel = %s\n", __FUNCTION__, channel->name);
+ channel->ctrl &= ~(FORTE_START | FORTE_PAUSE);
+ channel->ctrl |= FORTE_IMMED_STOP;
+
+ channel->active = 0;
+ outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL);
+}
+
+
+/**
+ * forte_channel_pause:
+ * @channel: Channel to pause
+ *
+ * Locking: Must be called with lock held.
+ */
+
+static void inline
+forte_channel_pause (struct forte_channel *channel)
+{
+ if (!channel || !channel->iobase)
+ return;
+
+ channel->ctrl |= FORTE_PAUSE;
- channel->ctrl &= ~FORTE_START;
channel->active = 0;
outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL);
}
@@ -584,38 +598,61 @@
* @channel: Channel whose buffer to set up
*
* Locking: Must be called with lock held.
- *
- * FIXME: Buffer scaling dependent on rate/channels/bits
*/
static void
forte_channel_buffer (struct forte_channel *channel, int sz, int num)
{
+ unsigned int msecs, shift;
+
/* Go away, I'm busy */
if (channel->filled_frags || channel->bytes)
return;
- channel->frag_sz = sz;
- channel->frag_num = num;
+ /* Fragment size must be a power of 2 */
+ shift = 0; sz++;
+ while (sz >>= 1)
+ shift++;
+ channel->frag_sz = 1 << shift;
+ /* Round fragment size to something reasonable */
if (channel->frag_sz < FORTE_MIN_FRAG_SIZE)
channel->frag_sz = FORTE_MIN_FRAG_SIZE;
if (channel->frag_sz > FORTE_MAX_FRAG_SIZE)
channel->frag_sz = FORTE_MAX_FRAG_SIZE;
+ /* Find fragment length in milliseconds */
+ msecs = channel->frag_sz /
+ (channel->format == AFMT_S16_LE ? 2 : 1) /
+ (channel->stereo ? 2 : 1) /
+ (channel->rate / 1000);
+
+ channel->frag_msecs = msecs;
+
+ /* Pick a suitable number of fragments */
+ if (msecs * num < FORTE_MIN_BUF_MSECS)
+ num = FORTE_MIN_BUF_MSECS / msecs;
+
+ if (msecs * num > FORTE_MAX_BUF_MSECS)
+ num = FORTE_MAX_BUF_MSECS / msecs;
+
+ /* Fragment number must be a power of 2 */
+ shift = 0;
+ while (num >>= 1)
+ shift++;
+ channel->frag_num = 1 << (shift + 1);
+
+ /* Round fragment number to something reasonable */
if (channel->frag_num < FORTE_MIN_FRAGMENTS)
channel->frag_num = FORTE_MIN_FRAGMENTS;
if (channel->frag_num > FORTE_MAX_FRAGMENTS)
channel->frag_num = FORTE_MAX_FRAGMENTS;
- if (channel->frag_sz * channel->frag_num < FORTE_MIN_BUF)
- channel->frag_num = FORTE_MIN_BUF / channel->frag_sz;
-
channel->buf_sz = channel->frag_sz * channel->frag_num;
- DPRINTK ("%s: %s frag_sz = %d, frag_num = %d, buf_sz = %d\n",
+ DPRINTK ("%s: %s frag_sz = %d, frag_num = %d, buf_sz = %d\n",
__FUNCTION__, channel->name, channel->frag_sz,
channel->frag_num, channel->buf_sz);
}
@@ -637,6 +674,7 @@
if (channel->buf)
return;
+ forte_channel_buffer (channel, channel->frag_sz, channel->frag_num);
channel->buf_pages = channel->buf_sz >> PAGE_SHIFT;
if (channel->buf_sz % PAGE_SIZE)
@@ -688,8 +726,7 @@
DECLARE_WAITQUEUE (wait, current);
unsigned long flags;
- if (!channel->active)
- return 0;
+ DPRINTK ("%s\n", __FUNCTION__);
if (channel->mapped) {
spin_lock_irqsave (&forte->lock, flags);
@@ -698,24 +735,26 @@
return 0;
}
- channel->drain = 1;
+ spin_lock_irqsave (&forte->lock, flags);
add_wait_queue (&channel->wait, &wait);
for (;;) {
- spin_lock_irqsave (&forte->lock, flags);
-
- if (channel->active == 0 || channel->filled_frags < 1)
+ if (channel->active == 0 || channel->filled_frags == 1)
break;
spin_unlock_irqrestore (&forte->lock, flags);
+
__set_current_state (TASK_INTERRUPTIBLE);
schedule();
+
+ spin_lock_irqsave (&forte->lock, flags);
}
- channel->drain = 0;
- spin_unlock_irqrestore (&forte->lock, flags);
+ forte_channel_stop (channel);
+ forte_channel_reset (channel);
set_current_state (TASK_RUNNING);
remove_wait_queue (&channel->wait, &wait);
+ spin_unlock_irqrestore (&forte->lock, flags);
return 0;
}
@@ -765,8 +804,8 @@
forte_channel_stereo (channel, 1);
forte_channel_format (channel, AFMT_S16_LE);
forte_channel_rate (channel, 48000);
- forte_channel_buffer (channel, FORTE_DEF_FRAG_SIZE,
- FORTE_DEF_FRAGMENTS);
+ channel->frag_sz = FORTE_DEF_FRAG_SIZE;
+ channel->frag_num = FORTE_DEF_FRAGMENTS;
chip->trigger = 0;
spin_unlock_irq (&chip->lock);
@@ -967,12 +1006,8 @@
case SNDCTL_DSP_SYNC:
DPRINTK ("%s: SYNC\n", __FUNCTION__);
- if (wr) {
+ if (wr)
ret = forte_channel_drain (&chip->play);
- spin_lock_irq (&chip->lock);
- forte_channel_reset (&chip->play);
- spin_unlock_irq (&chip->lock);
- }
return 0;
@@ -981,7 +1016,10 @@
if (wr) {
spin_lock_irq (&chip->lock);
- forte_channel_reset (&chip->play);
+
+ if (chip->play.filled_frags)
+ forte_channel_start (&chip->play);
+
spin_unlock_irq (&chip->lock);
}
@@ -1059,7 +1097,7 @@
case SNDCTL_DSP_GETOSPACE:
if (!wr)
return -EINVAL;
-
+
spin_lock_irq (&chip->lock);
abi.fragstotal = chip->play.frag_num;
@@ -1072,7 +1110,12 @@
else {
abi.fragments = chip->play.frag_num -
chip->play.filled_frags;
- abi.bytes = abi.fragments * abi.fragsize;
+
+ if (chip->play.residue)
+ abi.fragments--;
+
+ abi.bytes = abi.fragments * abi.fragsize +
+ chip->play.residue;
}
spin_unlock_irq (&chip->lock);
@@ -1099,20 +1142,23 @@
return copy_to_user ((void *) arg, &cinfo, sizeof (cinfo));
case SNDCTL_DSP_GETODELAY:
- if (!chip->play.active)
- return 0;
-
if (!wr)
return -EINVAL;
spin_lock_irq (&chip->lock);
- if (chip->play.mapped) {
+ if (!chip->play.active) {
+ ival = 0;
+ }
+ else if (chip->play.mapped) {
count = inw (chip->play.iobase + FORTE_PLY_COUNT) + 1;
ival = chip->play.frag_sz - count;
}
else {
ival = chip->play.filled_frags * chip->play.frag_sz;
+
+ if (chip->play.residue)
+ ival += chip->play.frag_sz - chip->play.residue;
}
spin_unlock_irq (&chip->lock);
@@ -1170,13 +1216,18 @@
return put_user (chip->play.rate, (int *) arg);
case SOUND_PCM_READ_CHANNELS:
- DPRINTK ("%s: PCM_READ_CHANNELS\n", __FUNCTION__);
+ DPRINTK ("%s: PCM_READ_CHANNELS\n", __FUNCTION__);
return put_user (chip->play.stereo, (int *) arg);
case SOUND_PCM_READ_BITS:
DPRINTK ("%s: PCM_READ_BITS\n", __FUNCTION__);
return put_user (chip->play.format, (int *) arg);
+ case SNDCTL_DSP_NONBLOCK:
+ DPRINTK ("%s: DSP_NONBLOCK\n", __FUNCTION__);
+ file->f_flags |= O_NONBLOCK;
+ return 0;
+
default:
DPRINTK ("Unsupported ioctl: %x (%p)\n", cmd, (void *) arg);
break;
@@ -1195,14 +1246,22 @@
{
struct forte_chip *chip = forte; /* FIXME: HACK FROM HELL! */
- if (down_interruptible (&chip->open_sem)) {
- DPRINTK ("%s: returning -ERESTARTSYS\n", __FUNCTION__);
- return -ERESTARTSYS;
+ if (file->f_flags & O_NONBLOCK) {
+ if (down_trylock (&chip->open_sem)) {
+ DPRINTK ("%s: returning -EAGAIN\n", __FUNCTION__);
+ return -EAGAIN;
+ }
+ }
+ else {
+ if (down_interruptible (&chip->open_sem)) {
+ DPRINTK ("%s: returning -ERESTARTSYS\n", __FUNCTION__);
+ return -ERESTARTSYS;
+ }
}
file->private_data = forte;
- DPRINTK ("%s: chip @ %p\n", __FUNCTION__, file->private_data);
+ DPRINTK ("%s: dsp opened by %d\n", __FUNCTION__, current->pid);
if (file->f_mode & FMODE_WRITE)
forte_channel_init (forte, &forte->play);
@@ -1231,9 +1290,7 @@
spin_lock_irq (&chip->lock);
- chip->play.ctrl |= FORTE_IMMED_STOP;
- forte_channel_stop (&chip->play);
- forte_channel_free (chip, &chip->play);
+ forte_channel_free (chip, &chip->play);
spin_unlock_irq (&chip->lock);
}
@@ -1244,7 +1301,6 @@
spin_lock_irq (&chip->lock);
- chip->play.ctrl |= FORTE_IMMED_STOP;
forte_channel_stop (&chip->rec);
forte_channel_free (chip, &chip->rec);
@@ -1260,7 +1316,6 @@
/**
* forte_dsp_poll:
*
- * FIXME: Racy
*/
static unsigned int
@@ -1278,8 +1333,12 @@
if (channel->active)
poll_wait (file, &channel->wait, wait);
- if (channel->filled_frags)
+ spin_lock_irq (&chip->lock);
+
+ if (channel->frag_num - channel->filled_frags > 0)
mask |= POLLOUT | POLLWRNORM;
+
+ spin_unlock_irq (&chip->lock);
}
if (file->f_mode & FMODE_READ) {
@@ -1288,8 +1347,12 @@
if (channel->active)
poll_wait (file, &channel->wait, wait);
+ spin_lock_irq (&chip->lock);
+
if (channel->filled_frags > 0)
mask |= POLLIN | POLLRDNORM;
+
+ spin_unlock_irq (&chip->lock);
}
return mask;
@@ -1405,62 +1468,64 @@
if (channel->frag_num - channel->filled_frags == 0) {
DECLARE_WAITQUEUE (wait, current);
- /* For trigger mode operation, get out */
- if (chip->trigger) {
+ /* For trigger or non-blocking operation, get out */
+ if (chip->trigger || file->f_flags & O_NONBLOCK) {
spin_unlock_irqrestore (&chip->lock, flags);
return -EAGAIN;
}
/* Otherwise wait for buffers */
- channel->blocked = 1;
add_wait_queue (&channel->wait, &wait);
for (;;) {
- if (channel->active == 0)
- break;
-
- if (channel->frag_num - channel->filled_frags)
- break;
-
spin_unlock_irqrestore (&chip->lock, flags);
set_current_state (TASK_INTERRUPTIBLE);
- schedule();
+ schedule();
spin_lock_irqsave (&chip->lock, flags);
+
+ if (channel->frag_num - channel->filled_frags)
+ break;
}
- set_current_state (TASK_RUNNING);
remove_wait_queue (&channel->wait, &wait);
- channel->blocked = 0;
+ set_current_state (TASK_RUNNING);
+
+ if (signal_pending (current)) {
+ spin_unlock_irqrestore (&chip->lock, flags);
+ return -ERESTARTSYS;
+ }
}
- if (i > channel->frag_sz)
+ if (channel->residue)
+ sz = channel->residue;
+ else if (i > channel->frag_sz)
sz = channel->frag_sz;
else
sz = i;
spin_unlock_irqrestore (&chip->lock, flags);
- /* Clear the fragment so we don't get noise when copying
- * smaller buffers
- */
- memset ((void *) channel->buf + channel->swptr, 0x0, sz);
-
- if (copy_from_user ((void *) channel->buf + channel->swptr,
- buffer, sz)) {
+ if (copy_from_user ((void *) channel->buf + channel->swptr, buffer, sz))
return -EFAULT;
- }
spin_lock_irqsave (&chip->lock, flags);
/* Advance software pointer */
buffer += sz;
- channel->filled_frags++;
- channel->swptr += channel->frag_sz;
+ channel->swptr += sz;
channel->swptr %= channel->buf_sz;
i -= sz;
+ /* Only bump filled_frags if a full fragment has been written */
+ if (channel->swptr % channel->frag_sz == 0) {
+ channel->filled_frags++;
+ channel->residue = 0;
+ }
+ else
+ channel->residue = channel->frag_sz - sz;
+
/* If playback isn't active, start it */
if (channel->active == 0 && chip->trigger == 0)
forte_channel_start (channel);
@@ -1521,7 +1586,6 @@
return -EAGAIN;
}
- channel->blocked = 1;
add_wait_queue (&channel->wait, &wait);
for (;;) {
@@ -1541,7 +1605,6 @@
set_current_state (TASK_RUNNING);
remove_wait_queue (&channel->wait, &wait);
- channel->blocked = 0;
}
if (i > channel->frag_sz)
@@ -1560,7 +1623,8 @@
/* Advance software pointer */
buffer += sz;
- channel->filled_frags--;
+ if (channel->filled_frags > 0)
+ channel->filled_frags--;
channel->swptr += channel->frag_sz;
channel->swptr %= channel->buf_sz;
i -= sz;
@@ -1607,23 +1671,16 @@
if (status & FORTE_IRQ_PLAYBACK) {
channel = &chip->play;
- spin_lock (&chip->lock);
-
- /* Declare a fragment done */
- channel->filled_frags--;
- /* Get # of completed bytes */
- count = inw (channel->iobase + FORTE_PLY_COUNT) + 1;
- channel->bytes += count;
+ spin_lock (&chip->lock);
- if (count == 0) {
- DPRINTK ("%s: last, filled_frags = %d\n", __FUNCTION__,
- channel->filled_frags);
- channel->filled_frags = 0;
- forte_channel_stop (channel);
+ if (channel->frag_sz == 0)
goto pack;
- }
+ /* Declare a fragment done */
+ if (channel->filled_frags > 0)
+ channel->filled_frags--;
+ channel->bytes += channel->frag_sz;
channel->nr_irqs++;
/* Flip-flop between buffer I and II */
@@ -1639,17 +1696,18 @@
channel->iobase + FORTE_PLY_BUF1 :
channel->iobase + FORTE_PLY_BUF2);
- /* If the currently playing fragment is last, schedule stop */
- if (channel->filled_frags == 1)
- forte_channel_stop (channel);
+ /* If the currently playing fragment is last, schedule pause */
+ if (channel->filled_frags == 1)
+ forte_channel_pause (channel);
+
pack:
/* Acknowledge interrupt */
outw (FORTE_IRQ_PLAYBACK, chip->iobase + FORTE_IRQ_STATUS);
- spin_unlock (&chip->lock);
+ if (waitqueue_active (&channel->wait))
+ wake_up_all (&channel->wait);
- if (channel->blocked || channel->drain)
- wake_up_interruptible (&channel->wait);
+ spin_unlock (&chip->lock);
}
if (status & FORTE_IRQ_CAPTURE) {
@@ -1668,7 +1726,7 @@
channel->filled_frags = 0;
goto rack;
}
-
+
/* Buffer I or buffer II BAR */
outl (channel->buf_handle + channel->hwptr,
channel->next_buf == 0 ?
@@ -1691,7 +1749,7 @@
spin_unlock (&chip->lock);
- if (channel->blocked)
+ if (waitqueue_active (&channel->wait))
wake_up_all (&channel->wait);
}
@@ -1710,7 +1768,7 @@
int i = 0, p_rate, p_chan, r_rate;
unsigned short p_reg, r_reg;
- i += sprintf (page, "ForteMedia FM801 OSS Lite driver\n%s\n\n",
+ i += sprintf (page, "ForteMedia FM801 OSS Lite driver\n%s\n \n",
DRIVER_VERSION);
if (!forte->iobase)
@@ -1745,7 +1803,14 @@
"Rate : %-5d %-5d\n"
"Channels : %-5d -\n"
"16-bit : %-3s %-3s\n"
- "Stereo : %-3s %-3s\n",
+ "Stereo : %-3s %-3s\n"
+ " \n"
+ "Buffer Sz : %-6d %-6d\n"
+ "Frag Sz : %-6d %-6d\n"
+ "Frag Num : %-6d %-6d\n"
+ "Frag msecs : %-6d %-6d\n"
+ "Used Frags : %-6d %-6d\n"
+ "Mapped : %-3s %-3s\n",
p_reg & 1<<0 ? "yes" : "no",
r_reg & 1<<0 ? "yes" : "no",
p_reg & 1<<1 ? "yes" : "no",
@@ -1763,7 +1828,15 @@
p_reg & 1<<14 ? "yes" : "no",
r_reg & 1<<14 ? "yes" : "no",
p_reg & 1<<15 ? "yes" : "no",
- r_reg & 1<<15 ? "yes" : "no");
+ r_reg & 1<<15 ? "yes" : "no",
+ forte->play.buf_sz, forte->rec.buf_sz,
+ forte->play.frag_sz, forte->rec.frag_sz,
+ forte->play.frag_num, forte->rec.frag_num,
+ forte->play.frag_msecs, forte->rec.frag_msecs,
+ forte->play.filled_frags, forte->rec.filled_frags,
+ forte->play.mapped ? "yes" : "no",
+ forte->rec.mapped ? "yes" : "no"
+ );
return i;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)