patch-2.2.18 linux/drivers/sound/cs46xx.c
Next file: linux/drivers/sound/dmasound.c
Previous file: linux/drivers/sound/cs4281.c
Back to the patch index
Back to the overall index
- Lines: 334
- Date:
Thu Oct 19 01:30:12 2000
- Orig file:
v2.2.17/drivers/sound/cs46xx.c
- Orig date:
Sat Sep 9 18:42:42 2000
diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/drivers/sound/cs46xx.c linux/drivers/sound/cs46xx.c
@@ -22,6 +22,9 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
+ * Changes:
+ * 20000909 Changed cs_read, cs_write and drain_dac
+ * Nils Faerber <nils@kernelconcepts.de>
*/
#include <linux/module.h>
@@ -777,7 +780,7 @@
tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
tmo >>= sample_shift[dmabuf->fmt];
- tmo += (4096*HZ)/dmabuf->rate;
+ tmo += (2048*HZ)/dmabuf->rate;
if (!schedule_timeout(tmo ? tmo : 1) && tmo){
printk(KERN_ERR "cs461x: drain_dac, dma timeout? %d\n", count);
@@ -921,6 +924,7 @@
{
struct cs_state *state = (struct cs_state *)file->private_data;
struct dmabuf *dmabuf = &state->dmabuf;
+ DECLARE_WAITQUEUE(wait, current);
ssize_t ret;
unsigned long flags;
unsigned swptr;
@@ -940,6 +944,7 @@
return -EFAULT;
ret = 0;
+ add_wait_queue(&state->dmabuf.wait, &wait);
while (count > 0) {
spin_lock_irqsave(&state->card->lock, flags);
if (dmabuf->count > (signed) dmabuf->dmasize) {
@@ -952,6 +957,8 @@
cnt = dmabuf->dmasize - swptr;
if (dmabuf->count < cnt)
cnt = dmabuf->count;
+ if (cnt <= 0)
+ __set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irqrestore(&state->card->lock, flags);
if (cnt > count)
@@ -963,38 +970,20 @@
start_adc(state);
if (file->f_flags & O_NONBLOCK) {
if (!ret) ret = -EAGAIN;
- return ret;
- }
- /* This isnt strictly right for the 810 but it'll do */
- tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
- tmo >>= sample_shift[dmabuf->fmt];
- /* There are two situations when sleep_on_timeout returns, one is when
- the interrupt is serviced correctly and the process is waked up by
- ISR ON TIME. Another is when timeout is expired, which means that
- either interrupt is NOT serviced correctly (pending interrupt) or it
- is TOO LATE for the process to be scheduled to run (scheduler latency)
- which results in a (potential) buffer overrun. And worse, there is
- NOTHING we can do to prevent it. */
- if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) {
-#ifdef DEBUG
- printk(KERN_ERR "cs461x: recording schedule timeout, "
- "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
- dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
- dmabuf->hwptr, dmabuf->swptr);
-#endif
- /* a buffer overrun, we delay the recovery untill next time the
- while loop begin and we REALLY have space to record */
+ remove_wait_queue(&state->dmabuf.wait, &wait);
+ break;
}
+ schedule();
if (signal_pending(current)) {
ret = ret ? ret : -ERESTARTSYS;
- return ret;
+ break;
}
continue;
}
if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
if (!ret) ret = -EFAULT;
- return ret;
+ break;
}
swptr = (swptr + cnt) % dmabuf->dmasize;
@@ -1009,6 +998,8 @@
ret += cnt;
start_adc(state);
}
+ remove_wait_queue(&state->dmabuf.wait, &wait);
+ set_current_state(TASK_RUNNING);
return ret;
}
@@ -1017,8 +1008,9 @@
static ssize_t cs_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
struct cs_state *state = (struct cs_state *)file->private_data;
+ DECLARE_WAITQUEUE(wait, current);
struct dmabuf *dmabuf = &state->dmabuf;
- ssize_t ret;
+ ssize_t ret = 0;
unsigned long flags;
unsigned swptr;
int cnt;
@@ -1035,8 +1027,7 @@
return ret;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
- ret = 0;
-
+ add_wait_queue(&state->dmabuf.wait, &wait);
while (count > 0) {
spin_lock_irqsave(&state->card->lock, flags);
if (dmabuf->count < 0) {
@@ -1049,6 +1040,8 @@
cnt = dmabuf->dmasize - swptr;
if (dmabuf->count + cnt > dmabuf->dmasize)
cnt = dmabuf->dmasize - dmabuf->count;
+ if (cnt <= 0)
+ __set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irqrestore(&state->card->lock, flags);
if (cnt > count)
@@ -1060,37 +1053,18 @@
start_dac(state);
if (file->f_flags & O_NONBLOCK) {
if (!ret) ret = -EAGAIN;
- return ret;
- }
- /* Not strictly correct but works */
- tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
- tmo >>= sample_shift[dmabuf->fmt];
- /* There are two situations when sleep_on_timeout returns, one is when
- the interrupt is serviced correctly and the process is waked up by
- ISR ON TIME. Another is when timeout is expired, which means that
- either interrupt is NOT serviced correctly (pending interrupt) or it
- is TOO LATE for the process to be scheduled to run (scheduler latency)
- which results in a (potential) buffer underrun. And worse, there is
- NOTHING we can do to prevent it. */
- if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) {
-#ifdef DEBUG
- printk(KERN_ERR "cs461x: playback schedule timeout, "
- "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
- dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
- dmabuf->hwptr, dmabuf->swptr);
-#endif
- /* a buffer underrun, we delay the recovery untill next time the
- while loop begin and we REALLY have data to play */
+ break;
}
+ schedule();
if (signal_pending(current)) {
if (!ret) ret = -ERESTARTSYS;
- return ret;
+ break;
}
continue;
}
if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
if (!ret) ret = -EFAULT;
- return ret;
+ break;
}
swptr = (swptr + cnt) % dmabuf->dmasize;
@@ -1106,6 +1080,8 @@
ret += cnt;
start_dac(state);
}
+ remove_wait_queue(&state->dmabuf.wait, &wait);
+ set_current_state(TASK_RUNNING);
return ret;
}
@@ -1141,10 +1117,16 @@
return mask;
}
+/*
+ * We let users mmap the ring buffer. Its not the real DMA buffer but
+ * that side of the code is hidden in the IRQ handling. We do a software
+ * emulation of DMA from a 64K or so buffer into a 2K FIFO.
+ * (the hardware probably deserves a moan here but Crystal send me nice
+ * toys ;)).
+ */
+
static int cs_mmap(struct file *file, struct vm_area_struct *vma)
{
- return -EINVAL;
-#if 0
struct cs_state *state = (struct cs_state *)file->private_data;
struct dmabuf *dmabuf = &state->dmabuf;
int ret;
@@ -1171,7 +1153,6 @@
dmabuf->mapped = 1;
return 0;
-#endif
}
static int cs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
@@ -1822,34 +1803,15 @@
return cs461x_peekBA0(card, BA0_ACSDA);
}
-/*
- * Do we have the CD potentially enabled either left or right ?
- */
-
-static int cd_active(int r)
-{
- int l=(r>>8)&0x7;
- r&=7;
- if(l==1 || r==1)
- return 1; /* CD input */
- if(l==5 || r==5)
- return 1; /* Mixer input */
- if(l==6 || r==6)
- return 1; /* Mixer 16bit input */
- return 0;
-}
-
static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 val)
{
struct cs_card *card = dev->private_data;
int count;
int val2;
- int val3;
- if(reg==AC97_RECORD_SELECT || reg == AC97_CD_VOL)
+ if(reg == AC97_CD_VOL)
{
- val2 = cs_ac97_get(dev, AC97_RECORD_SELECT);
- val3 = cs_ac97_get(dev, AC97_CD_VOL);
+ val2 = cs_ac97_get(dev, AC97_CD_VOL);
}
/*
@@ -1907,35 +1869,31 @@
*
* When the CD mute changes we adjust the power level if the
* CD was a valid input.
+ *
+ * We also check for CD volume != 0, as the CD mute isn't
+ * normally tweaked from userspace.
*/
- if(reg==AC97_RECORD_SELECT && cd_active(val)!=cd_active(val2))
- {
- int n=-1;
- /* If we are turning on the port and it is not muted then
- bump the power level. If we are turning it off and its
- not muted drop the power level */
- if(cd_active(val))
- n=1;
- if(!(val3 & 0x8000))
- card->amplifier_ctrl(card, n);
- }
-
/* CD mute change ? */
if(reg==AC97_CD_VOL)
{
- if(cd_active(val2))
+ /* Mute bit change ? */
+ if((val2^val)&0x8000 || ((val2 == 0x1f1f || val == 0x1f1f) && val2 != val))
{
- /* Mute bit change ? */
- if((val3^val)&0x8000)
- {
- /* Mute on */
- if(val&0x8000)
- card->amplifier_ctrl(card, -1);
- else /* Mute off power on */
- card->amplifier_ctrl(card, 1);
- }
+ /* This is a hack but its cleaner than the alternatives.
+ Right now card->ac97_codec[0] might be NULL as we are
+ still doing codec setup. This does an early assignment
+ to avoid the problem if it occurs */
+
+ if(card->ac97_codec[0]==NULL)
+ card->ac97_codec[0]=dev;
+
+ /* Mute on */
+ if(val&0x8000 || val == 0x1f1f)
+ card->amplifier_ctrl(card, -1);
+ else /* Mute off power on */
+ card->amplifier_ctrl(card, 1);
}
}
}
@@ -2036,12 +1994,6 @@
card->ac97_features = eid;
- /* If the card has the CD enabled then bump the power to
- account for it */
-
- if(cd_active(cs_ac97_get(codec, AC97_RECORD_SELECT)))
- card->amplifier_ctrl(card, 1);
-
if ((codec->dev_mixer = register_sound_mixer(&cs_mixer_fops, -1)) < 0) {
printk(KERN_ERR "cs461x: couldn't register mixer!\n");
kfree(codec);
@@ -2343,7 +2295,7 @@
* generating bit clock (so we don't try to start the PLL without an
* input clock).
*/
- mdelay(5); /* 1 should be enough ?? */
+ mdelay(5); /* 1 should be enough ?? (and pigs might fly) */
/*
* Set the serial port timing configuration, so that
@@ -2548,7 +2500,7 @@
{PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", amp_none, clkrun_hack},
{PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL},
{0, 0, "Card without SSID set", NULL, NULL },
- {0, 0, NULL, NULL}
+ {0, 0, NULL, NULL, NULL}
};
static int __init cs_install(struct pci_dev *pci_dev)
@@ -2809,4 +2761,4 @@
MODULE_PARM(external_amp, "i");
MODULE_PARM(thinkpad, "i");
-#endif
\ No newline at end of file
+#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)