patch-2.2.15 linux/drivers/sound/cmpci.c
Next file: linux/drivers/sound/dmabuf.c
Previous file: linux/drivers/scsi/st.c
Back to the patch index
Back to the overall index
- Lines: 973
- Date:
Fri Apr 21 12:46:33 2000
- Orig file:
v2.2.14/drivers/sound/cmpci.c
- Orig date:
Wed Oct 20 01:12:48 1999
diff -u --new-file --recursive --exclude-from ../../exclude v2.2.14/drivers/sound/cmpci.c linux/drivers/sound/cmpci.c
@@ -21,7 +21,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * Special thanks to David C. Niemi
+ * Special thanks to David C. Niemi, Jan Pfeifer
*
*
* Module command line parameters:
@@ -57,9 +57,45 @@
* reported by Johan Maes <joma@telindus.be>
* 22.03.99 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK
* read/write cannot be executed
+ * 20 09 99 0.13 merged the generic changes in sonicvibes since this
+ * diverged.
+ * 18.08.99 1.5 Only deallocate DMA buffer when unloading.
+ * 02.09.99 1.6 Enable SPDIF LOOP
+ * Change the mixer read back
+ * 21.09.99 2.33 Use RCS version aas driver version.
+ * Add support for modem, S/PDIF loop and 4 channels.
+ * (8738 only)
+ * Fix bug cause x11amp cannot play.
+ * $Log: cmpci.c,v $
+ * Revision 2.41 1999/10/27 02:00:05 cltien
+ * Now the fragsize for modem is activated by parameter.
+ *
+ * Revision 2.40 1999/10/26 23:38:26 cltien
+ * Remove debugging message in cm_write which may cause module counter not 0.
+ *
+ * Revision 2.39 1999/10/26 21:52:50 cltien
+ * I forgor too adjust mic recording volume, as it should be moved to 5MUTEMONO.
+ * Change the DYNAMIC macro to FIXEDDMA, which means static DMA buffer.
+ *
+ * Revision 2.38 1999/10/08 21:59:03 cltien
+ * Set FLINKON and reset FLINKOFF for modem.
+ *
+ * Revision 2.37 1999/09/28 02:57:04 cltien
+ * Add set_bus_master() to make sure bus master enabled.
+ *
+ * Revision 2.36 1999/09/22 14:15:03 cltien
+ * Use open_sem to avoid multiple access to open_mode.
+ * Use wakeup in IntrClose to activate process in waiting queue.
+ *
+ * Revision 2.35 1999/09/22 13:20:53 cltien
+ * Use open_mode to check if DAC in used. Also more check in IntrWrite and IntrClose. Now the modem can access DAC safely.
+ *
+ * Revision 2.34 1999/09/22 03:29:57 cltien
+ * Use module count to decide which one to access the dac.
+ *
*
*/
-
+
/*****************************************************************************/
#include <linux/config.h>
@@ -236,6 +272,7 @@
struct semaphore open_sem;
mode_t open_mode;
struct wait_queue *open_wait;
+ struct wait_queue *poll_wait;
struct dmabuf {
void *rawbuf;
@@ -251,6 +288,7 @@
unsigned fragsize;
unsigned dmasize;
unsigned fragsamples;
+ unsigned dmasamples;
/* OSS stuff */
unsigned mapped:1;
unsigned ready:1;
@@ -266,6 +304,7 @@
unsigned ord, owr, ocnt;
struct wait_queue *iwait;
struct wait_queue *owait;
+ struct wait_queue *pollwait;
struct timer_list timer;
unsigned char ibuf[MIDIINBUF];
unsigned char obuf[MIDIOUTBUF];
@@ -275,6 +314,7 @@
/* --------------------------------------------------------------------- */
static struct cm_state *devs = NULL;
+static struct cm_state *devaudio = NULL;
static unsigned long wavetable_mem = 0;
/* --------------------------------------------------------------------- */
@@ -330,7 +370,7 @@
outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1);
outw(count, s->iobase + CODEC_CMI_CH0_FRAME2);
outb(inb(s->iobase + CODEC_CMI_FUNCTRL0) & ~1, s->iobase + CODEC_CMI_FUNCTRL0);
- outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 1, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+// outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 1, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
}
static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count)
@@ -339,13 +379,16 @@
outl(addr, s->iobase + CODEC_CMI_CH1_FRAME1);
outw(count, s->iobase + CODEC_CMI_CH1_FRAME2);
outb(inb(s->iobase + CODEC_CMI_FUNCTRL0) | 2, s->iobase + CODEC_CMI_FUNCTRL0);
- outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 2, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+// outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 2, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
}
extern __inline__ unsigned get_dmadac(struct cm_state *s)
{
unsigned int curr_addr;
+ if (!s->dma_dac.dmasize || !(s->enable & CM_CENABLE_PE))
+ return 0;
+
curr_addr = inl(s->iobase + CODEC_CMI_CH0_FRAME1);
curr_addr -= virt_to_bus(s->dma_dac.rawbuf);
curr_addr = s->dma_dac.dmasize - curr_addr;
@@ -357,6 +400,9 @@
{
unsigned int curr_addr;
+ if (!s->dma_adc.dmasize || !(s->enable & CM_CENABLE_RE))
+ return 0;
+
curr_addr = inl(s->iobase + CODEC_CMI_CH1_FRAME1);
curr_addr -= virt_to_bus(s->dma_adc.rawbuf);
curr_addr = s->dma_adc.dmasize - curr_addr;
@@ -420,7 +466,7 @@
{ 22050, (16000 + 22050) / 2, (22050 + 32000) / 2, 2 },
{ 32000, (22050 + 32000) / 2, (32000 + 44100) / 2, 6 },
{ 44100, (32000 + 44100) / 2, (44100 + 48000) / 2, 3 },
- { 48000, 48000, 48000, 7 }
+ { 48000, (44100 + 48000) /2, 48000, 7 }
};
static void set_dac_rate(struct cm_state *s, unsigned rate)
@@ -484,10 +530,12 @@
unsigned long flags;
spin_lock_irqsave(&s->lock, flags);
+ /* disable channel */
+ outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
s->enable &= ~CM_CENABLE_RE;
/* disable interrupt */
outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) & ~2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
- /* disable channel and reset */
+ /* reset */
outb(s->enable | CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
udelay(10);
outb(s->enable & ~CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
@@ -499,10 +547,12 @@
unsigned long flags;
spin_lock_irqsave(&s->lock, flags);
+ /* disable channel */
s->enable &= ~CM_CENABLE_PE;
+ outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
/* disable interrupt */
outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) & ~1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
- /* disable channel and reset */
+ /* reset */
outb(s->enable | CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
udelay(10);
outb(s->enable & ~CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
@@ -515,10 +565,10 @@
spin_lock_irqsave(&s->lock, flags);
if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) {
+ outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
s->enable |= CM_CENABLE_PE;
outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
}
- outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
spin_unlock_irqrestore(&s->lock, flags);
}
@@ -529,10 +579,10 @@
spin_lock_irqsave(&s->lock, flags);
if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize))
&& s->dma_adc.ready) {
+ outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
s->enable |= CM_CENABLE_RE;
outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
}
- outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
spin_unlock_irqrestore(&s->lock, flags);
}
@@ -585,16 +635,17 @@
db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
if (!db->rawbuf) {
db->ready = db->mapped = 0;
- for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--)
- db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order);
+ for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+ if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order)))
+ break;
if (!db->rawbuf)
return -ENOMEM;
db->buforder = order;
if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff)
- printk(KERN_DEBUG "cm: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n",
+ printk(KERN_DEBUG "cmpci: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n",
virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder);
if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff)
- printk(KERN_DEBUG "cm: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n",
+ printk(KERN_DEBUG "cmpci: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n",
virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder);
/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
@@ -621,17 +672,21 @@
db->fragsize = 1 << db->fragshift;
if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
db->numfrag = db->ossmaxfrags;
-#if 1
- /* to make fragsize >= 4096 */
- while (db->fragsize < 4096 && db->numfrag >= 4)
- {
- db->fragsize *= 2;
- db->fragshift++;
- db->numfrag /= 2;
+ /* to make fragsize >= 4096 */
+#if 0
+ if(s->modem)
+ {
+ while (db->fragsize < 4096 && db->numfrag >= 4)
+ {
+ db->fragsize *= 2;
+ db->fragshift++;
+ db->numfrag /= 2;
+ }
}
-#endif
+#endif
db->fragsamples = db->fragsize >> sample_shift[fmt];
db->dmasize = db->numfrag << db->fragshift;
+ db->dmasamples = db->dmasize >> sample_shift[fmt];
memset(db->rawbuf, (fmt & CM_CFMT_16BIT) ? 0 : 0x80, db->dmasize);
spin_lock_irqsave(&s->lock, flags);
if (rec) {
@@ -663,6 +718,7 @@
len -= x;
}
memset(buf + bptr, c, len);
+ outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
}
/* call with spinlock held! */
@@ -697,7 +753,10 @@
if (s->dma_dac.mapped) {
s->dma_dac.count += diff;
if (s->dma_dac.count >= (signed)s->dma_dac.fragsize)
+ {
wake_up(&s->dma_dac.wait);
+ wake_up(&s->poll_wait);
+ }
} else {
s->dma_dac.count -= diff;
if (s->dma_dac.count <= 0) {
@@ -709,7 +768,10 @@
s->dma_dac.endcleared = 1;
}
if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize)
+ {
wake_up(&s->dma_dac.wait);
+ wake_up(&s->poll_wait);
+ }
}
}
}
@@ -731,7 +793,10 @@
wake = 1;
}
if (wake)
+ {
wake_up(&s->midi.iwait);
+ wake_up(&s->midi.pollwait);
+ }
wake = 0;
while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) {
outb(s->midi.obuf[s->midi.ord], s->iomidi);
@@ -741,7 +806,10 @@
wake = 1;
}
if (wake)
+ {
wake_up(&s->midi.owait);
+ wake_up(&s->midi.pollwait);
+ }
}
static void cm_interrupt(int irq, void *dev_id, struct pt_regs *regs)
@@ -750,25 +818,26 @@
unsigned int intsrc, intstat;
/* fastpath out, to ease interrupt sharing */
- intsrc = inb(s->iobase + CODEC_CMI_INT_STATUS);
- if (!(intsrc & (CM_INT_CH0 | CM_INT_CH1)))
+ intsrc = inl(s->iobase + CODEC_CMI_INT_STATUS);
+ if (!(intsrc & 0x80000000))
return;
spin_lock(&s->lock);
intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2);
- /* disable interrupt */
+ /* acknowledge interrupt */
if (intsrc & CM_INT_CH0)
+ {
outb(intstat & ~1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+ udelay(10);
+ outb(intstat | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+ }
if (intsrc & CM_INT_CH1)
+ {
outb(intstat & ~2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+ udelay(10);
+ outb(intstat | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+ }
cm_update_ptr(s);
-#ifdef SOUND_CONFIG_CMPCI_MIDI
cm_handle_midi(s);
-#endif
- /* enable interrupt */
- if (intsrc & CM_INT_CH0)
- outb(intstat | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
- if (intsrc & CM_INT_CH1)
- outb(intstat | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
spin_unlock(&s->lock);
}
@@ -786,7 +855,7 @@
/* --------------------------------------------------------------------- */
-static const char invalid_magic[] = KERN_CRIT "cm: invalid magic value\n";
+static const char invalid_magic[] = KERN_CRIT "cmpci: invalid magic value\n";
#ifdef CONFIG_SOUND_CMPCI /* support multiple chips */
#define VALIDATE_STATE(s)
@@ -806,6 +875,7 @@
#define MT_5MUTE 2
#define MT_4MUTEMONO 3
#define MT_6MUTE 4
+#define MT_5MUTEMONO 5
static const struct {
unsigned left;
@@ -816,7 +886,7 @@
} mixtable[SOUND_MIXER_NRDEVICES] = {
[SOUND_MIXER_CD] = { DSP_MIX_CDVOLIDX_L, DSP_MIX_CDVOLIDX_R, MT_5MUTE, 0x04, 0x02 },
[SOUND_MIXER_LINE] = { DSP_MIX_LINEVOLIDX_L, DSP_MIX_LINEVOLIDX_R, MT_5MUTE, 0x10, 0x08 },
- [SOUND_MIXER_MIC] = { DSP_MIX_MICVOLIDX, CODEC_CMI_MIXER2, MT_4MUTEMONO, 0x01, 0x01 },
+ [SOUND_MIXER_MIC] = { DSP_MIX_MICVOLIDX, DSP_MIX_MICVOLIDX, MT_5MUTEMONO, 0x01, 0x01 },
[SOUND_MIXER_SYNTH] = { DSP_MIX_FMVOLIDX_L, DSP_MIX_FMVOLIDX_R, MT_5MUTE, 0x40, 0x00 },
[SOUND_MIXER_VOLUME] = { DSP_MIX_MASTERVOLIDX_L, DSP_MIX_MASTERVOLIDX_R, MT_5MUTE, 0x00, 0x00 },
[SOUND_MIXER_PCM] = { DSP_MIX_VOICEVOLIDX_L, DSP_MIX_VOICEVOLIDX_R, MT_5MUTE, 0x00, 0x00 }
@@ -849,10 +919,16 @@
r = l;
break;
+ case MT_5MUTEMONO:
+ r = l;
+ rl = 100 - 3 * ((l >> 3) & 31);
+ rr = rl;
+ break;
+
case MT_5MUTE:
default:
- rl = 100 - 3 * (l & 31);
- rr = 100 - 3 * (r & 31);
+ rl = 100 - 3 * ((l >> 3) & 31);
+ rr = 100 - 3 * ((r >> 3) & 31);
break;
case MT_6MUTE:
@@ -990,7 +1066,7 @@
}
spin_lock_irqsave(&s->lock, flags);
wrmixer(s, DSP_MIX_ADCMIXIDX_L, j);
- wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | j>>1);
+ wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | (j>>1));
spin_unlock_irqrestore(&s->lock, flags);
return 0;
@@ -1039,6 +1115,14 @@
outb((inb(s->iobase + CODEC_CMI_MIXER2) & ~0x0e) | rr<<1, s->iobase + CODEC_CMI_MIXER2);
break;
+ case MT_5MUTEMONO:
+ r = l;
+ rl = l < 4 ? 0 : (l - 5) / 3;
+ rr = rl >> 2;
+ wrmixer(s, mixtable[i].left, rl<<3);
+ outb((inb(s->iobase + CODEC_CMI_MIXER2) & ~0x0e) | rr<<1, s->iobase + CODEC_CMI_MIXER2);
+ break;
+
case MT_5MUTE:
rl = l < 4 ? 0 : (l - 5) / 3;
rr = r < 4 ? 0 : (r - 5) / 3;
@@ -1122,8 +1206,6 @@
&cm_release_mixdev,
NULL, /* fsync */
NULL, /* fasync */
- NULL, /* check_media_change */
- NULL, /* revalidate */
NULL, /* lock */
};
@@ -1131,7 +1213,7 @@
static int drain_dac(struct cm_state *s, int nonblock)
{
- struct wait_queue wait = { current, NULL };
+ struct wait_queue wait = { current, NULL} ;
unsigned long flags;
int count, tmo;
@@ -1155,7 +1237,7 @@
tmo = (count * HZ) / s->ratedac;
tmo >>= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK];
if (!schedule_timeout(tmo ? : 1) && tmo)
- printk(KERN_DEBUG "cm: dma timed out??\n");
+ printk(KERN_DEBUG "cmpci: dma timed out??\n");
}
remove_wait_queue(&s->dma_dac.wait, &wait);
current->state = TASK_RUNNING;
@@ -1202,7 +1284,18 @@
start_adc(s);
if (file->f_flags & O_NONBLOCK)
return ret ? ret : -EAGAIN;
- interruptible_sleep_on(&s->dma_adc.wait);
+ if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) {
+ printk(KERN_DEBUG "cmpci: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+ s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count,
+ s->dma_adc.hwptr, s->dma_adc.swptr);
+ stop_adc(s);
+ spin_lock_irqsave(&s->lock, flags);
+ set_dmaadc(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.dmasamples);
+ /* program sample counts */
+ outw(s->dma_adc.fragsamples-1, s->iobase + CODEC_CMI_CH1_FRAME2 + 2);
+ s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0;
+ spin_unlock_irqrestore(&s->lock, flags);
+ }
if (signal_pending(current))
return ret ? ret : -ERESTARTSYS;
continue;
@@ -1262,7 +1355,18 @@
start_dac(s);
if (file->f_flags & O_NONBLOCK)
return ret ? ret : -EAGAIN;
- interruptible_sleep_on(&s->dma_dac.wait);
+ if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) {
+ printk(KERN_DEBUG "cmpci: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+ s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count,
+ s->dma_dac.hwptr, s->dma_dac.swptr);
+ stop_dac(s);
+ spin_lock_irqsave(&s->lock, flags);
+ set_dmadac(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.dmasamples);
+ /* program sample counts */
+ outw(s->dma_dac.fragsamples-1, s->iobase + CODEC_CMI_CH0_FRAME2 + 2);
+ s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0;
+ spin_unlock_irqrestore(&s->lock, flags);
+ }
if (signal_pending(current))
return ret ? ret : -ERESTARTSYS;
continue;
@@ -1290,10 +1394,8 @@
unsigned int mask = 0;
VALIDATE_STATE(s);
- if (file->f_mode & FMODE_WRITE)
- poll_wait(file, &s->dma_dac.wait, wait);
- if (file->f_mode & FMODE_READ)
- poll_wait(file, &s->dma_adc.wait, wait);
+ if (file->f_mode & (FMODE_WRITE|FMODE_READ))
+ poll_wait(file, &s->poll_wait, wait);
spin_lock_irqsave(&s->lock, flags);
cm_update_ptr(s);
if (file->f_mode & FMODE_READ) {
@@ -1630,9 +1732,11 @@
case SOUND_PCM_READ_BITS:
return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? 16 : 8, (int *)arg);
+ case SOUND_PCM_READ_FILTER:
+ return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+
case SOUND_PCM_WRITE_FILTER:
case SNDCTL_DSP_SETSYNCRO:
- case SOUND_PCM_READ_FILTER:
return -EINVAL;
}
@@ -1721,17 +1825,15 @@
&cm_release,
NULL, /* fsync */
NULL, /* fasync */
- NULL, /* check_media_change */
- NULL, /* revalidate */
NULL, /* lock */
};
-#ifdef CONFIG_SOUND_CMPCI_MIDI
/* --------------------------------------------------------------------- */
static ssize_t cm_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
struct cm_state *s = (struct cm_state *)file->private_data;
+ struct wait_queue wait = { current , NULL };
ssize_t ret;
unsigned long flags;
unsigned ptr;
@@ -1742,7 +1844,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;
@@ -1753,15 +1858,27 @@
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;
@@ -1770,13 +1887,17 @@
count -= cnt;
buffer += cnt;
ret += cnt;
+ break;
}
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&s->midi.iwait, &wait);
return ret;
}
static ssize_t cm_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
struct cm_state *s = (struct cm_state *)file->private_data;
+ struct wait_queue wait = { current, NULL };
ssize_t ret;
unsigned long flags;
unsigned ptr;
@@ -1787,7 +1908,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;
@@ -1800,15 +1924,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;
@@ -1821,6 +1955,8 @@
cm_handle_midi(s);
spin_unlock_irqrestore(&s->lock, flags);
}
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&s->midi.owait, &wait);
return ret;
}
@@ -1831,10 +1967,8 @@
unsigned int mask = 0;
VALIDATE_STATE(s);
- if (file->f_mode & FMODE_WRITE)
- poll_wait(file, &s->midi.owait, wait);
- if (file->f_mode & FMODE_READ)
- poll_wait(file, &s->midi.iwait, wait);
+ if (file->f_mode & (FMODE_WRITE|FMODE_READ))
+ poll_wait(file, &s->midi.pollwait, wait);
spin_lock_irqsave(&s->lock, flags);
if (file->f_mode & FMODE_READ) {
if (s->midi.icnt > 0)
@@ -1908,14 +2042,15 @@
static int cm_midi_release(struct inode *inode, struct file *file)
{
struct cm_state *s = (struct cm_state *)file->private_data;
- struct wait_queue wait = { current, NULL };
+ struct wait_queue wait = { current, NULL};
+
unsigned long flags;
unsigned count, tmo;
VALIDATE_STATE(s);
if (file->f_mode & FMODE_WRITE) {
- current->state = TASK_INTERRUPTIBLE;
+ current->state = TASK_INTERRUPTIBLE;
add_wait_queue(&s->midi.owait, &wait);
for (;;) {
spin_lock_irqsave(&s->lock, flags);
@@ -1932,7 +2067,7 @@
}
tmo = (count * HZ) / 3100;
if (!schedule_timeout(tmo ? : 1) && tmo)
- printk(KERN_DEBUG "cm: midi timed out??\n");
+ printk(KERN_DEBUG "cmpci: midi timed out??\n");
}
remove_wait_queue(&s->midi.owait, &wait);
current->state = TASK_RUNNING;
@@ -1968,15 +2103,11 @@
&cm_midi_release,
NULL, /* fsync */
NULL, /* fasync */
- NULL, /* check_media_change */
- NULL, /* revalidate */
NULL, /* lock */
};
-#endif
/* --------------------------------------------------------------------- */
-#ifdef CONFIG_SOUND_CMPCI_FM
static int cm_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
static const unsigned char op_offset[18] = {
@@ -2144,11 +2275,8 @@
&cm_dmfm_release,
NULL, /* fsync */
NULL, /* fasync */
- NULL, /* check_media_change */
- NULL, /* revalidate */
NULL, /* lock */
};
-#endif /* CONFIG_SOUND_CMPCI_FM */
/* --------------------------------------------------------------------- */
@@ -2176,15 +2304,39 @@
};
#ifdef MODULE
-__initfunc(int init_module(void))
+static int spdif_loop = 0;
+static int four_ch = 0;
+static int rear_out = 0;
+MODULE_PARM(spdif_loop, "i");
+MODULE_PARM(four_ch, "i");
+MODULE_PARM(rear_out, "i");
+
+int __init init_module(void)
#else
-__initfunc(int init_cmpci(void))
+#ifdef CONFIG_SOUND_CMPCI_SPDIFLOOP
+static int spdif_loop = 1;
+#else
+static int spdif_loop = 0;
+#endif
+#ifdef CONFIG_SOUND_CMPCI_4CH
+static int four_ch = 1;
+#else
+static int four_ch = 0;
+#endif
+#ifdef CONFIG_SOUND_CMPCI_REAR
+static int rear_out = 1;
+#else
+static int rear_out = 0;
+#endif
+
+int __init init_cmpci(void)
#endif
{
struct cm_state *s;
struct pci_dev *pcidev = NULL;
mm_segment_t fs;
int i, val, index = 0;
+
struct {
unsigned short deviceid;
char *devicename;
@@ -2200,10 +2352,10 @@
if (!pci_present()) /* No PCI bus in this machine! */
#endif
return -ENODEV;
- printk(KERN_INFO "cm: version v1.1 time " __TIME__ " " __DATE__ "\n");
+ printk(KERN_INFO "cmpci: version v2.41-nomodem time " __TIME__ " " __DATE__ "\n");
#if 0
if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
- printk(KERN_INFO "cm: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
+ printk(KERN_INFO "cmpci: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
#endif
while (index < NR_DEVICE && pcidev == NULL && (
(pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, pcidev)) ||
@@ -2212,7 +2364,7 @@
if (pcidev->irq == 0)
continue;
if (!(s = kmalloc(sizeof(struct cm_state), GFP_KERNEL))) {
- printk(KERN_WARNING "cm: out of memory\n");
+ printk(KERN_WARNING "cmpci: out of memory\n");
continue;
}
/* search device name */
@@ -2226,72 +2378,69 @@
}
memset(s, 0, sizeof(struct cm_state));
init_waitqueue(&s->dma_adc.wait);
+ init_waitqueue(&s->poll_wait);
init_waitqueue(&s->dma_dac.wait);
init_waitqueue(&s->open_wait);
init_waitqueue(&s->midi.iwait);
init_waitqueue(&s->midi.owait);
+ init_waitqueue(&s->midi.pollwait);
s->open_sem = MUTEX;
s->magic = CM_MAGIC;
s->iobase = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
-#ifdef CONFIG_SOUND_CMPCI_FM
s->iosynth = 0x388;
-#endif
-#ifdef CONFIG_SOUND_CMPCI_MIDI
s->iomidi = 0x330;
-#endif
if (s->iobase == 0)
continue;
s->irq = pcidev->irq;
if (check_region(s->iobase, CM_EXTENT_CODEC)) {
- printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1);
+ printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1);
goto err_region5;
}
request_region(s->iobase, CM_EXTENT_CODEC, "cmpci");
-#ifdef CONFIG_SOUND_CMPCI_MIDI
if (check_region(s->iomidi, CM_EXTENT_MIDI)) {
- printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1);
- goto err_region4;
+ printk(KERN_WARNING "cmpci: io ports %#x-%#x in use, midi disabled.\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1);
+ s->iomidi = 0;
+ }
+ else
+ {
+ request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi");
+ /* set IO based at 0x330 */
+ outb(inb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3) & ~0x60, s->iobase + CODEC_CMI_LEGACY_CTRL + 3);
}
- request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi");
- /* set IO based at 0x330 */
- outb(inb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3) & ~0x60, s->iobase + CODEC_CMI_LEGACY_CTRL + 3);
-#endif
-#ifdef CONFIG_SOUND_CMPCI_FM
if (check_region(s->iosynth, CM_EXTENT_SYNTH)) {
- printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1);
- goto err_region1;
+ printk(KERN_WARNING "cmpci: io ports %#x-%#x in use, synth disabled.\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1);
+ s->iosynth = 0;
+ }
+ else
+ {
+ request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM");
+ /* enable FM */
+ outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 2) | 8, s->iobase + CODEC_CMI_MISC_CTRL);
}
- request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM");
- /* enable FM */
- outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 2) | 8, s->iobase + CODEC_CMI_MISC_CTRL);
-#endif
/* initialize codec registers */
outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */
- outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* reset channels */
+ outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */
/* reset mixer */
wrmixer(s, DSP_MIX_DATARESETIDX, 0);
/* request irq */
if (request_irq(s->irq, cm_interrupt, SA_SHIRQ, "cmpci", s)) {
- printk(KERN_ERR "cm: irq %u in use\n", s->irq);
+ printk(KERN_ERR "cmpci: irq %u in use\n", s->irq);
goto err_irq;
}
- printk(KERN_INFO "cm: found %s adapter at io %#06x irq %u\n",
+ printk(KERN_INFO "cmpci: found %s adapter at io %#06x irq %u\n",
devicename, s->iobase, s->irq);
/* register devices */
if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0)
goto err_dev1;
if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0)
goto err_dev2;
-#ifdef CONFIG_SOUND_CMPCI_MIDI
- if ((s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0)
+ if (s->iomidi && (s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0)
goto err_dev3;
-#endif
-#ifdef CONFIG_SOUND_CMPCI_FM
- if ((s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0)
+ if (s->iosynth && (s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0)
goto err_dev4;
-#endif
+ pci_set_master(pcidev);
/* initialize the chips */
fs = get_fs();
set_fs(KERNEL_DS);
@@ -2305,6 +2454,38 @@
mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
}
set_fs(fs);
+ if (pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738)
+ {
+ /* enable SPDIF loop */
+ if (spdif_loop)
+ {
+ /* turn on spdif-in to spdif-out */
+ outb(inb(s->iobase + CODEC_CMI_FUNCTRL1) | 0x80, s->iobase + CODEC_CMI_FUNCTRL1);
+ printk(KERN_INFO "cmpci: Enable SPDIF loop\n");
+ }
+ else
+ outb(inb(s->iobase + CODEC_CMI_FUNCTRL1) & ~0x80, s->iobase + CODEC_CMI_FUNCTRL1);
+ /* enable 4 channels mode */
+ if (four_ch)
+ {
+ /* 4 channel mode (analog duplicate) */
+ outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 3) | 0x04, s->iobase + CODEC_CMI_MISC_CTRL + 3);
+ printk(KERN_INFO "cmpci: Enable 4 channels mode\n");
+ /* has separate rear-out jack ? */
+ if (rear_out)
+ {
+ /* has separate rear out jack */
+ outb(inb(s->iobase + CODEC_CMI_MIXER1) & ~0x20, s->iobase + CODEC_CMI_MIXER1);
+ }
+ else
+ {
+ outb(inb(s->iobase + CODEC_CMI_MIXER1) | 0x20, s->iobase + CODEC_CMI_MIXER1);
+ printk(KERN_INFO "cmpci: line-in routed as rear-out\n");
+ }
+ }
+ else
+ outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 3) & ~0x04, s->iobase + CODEC_CMI_MISC_CTRL + 3);
+ }
/* queue it for later freeing */
s->next = devs;
devs = s;
@@ -2318,16 +2499,14 @@
err_dev2:
unregister_sound_dsp(s->dev_audio);
err_dev1:
- printk(KERN_ERR "cm: cannot register misc device\n");
+ printk(KERN_ERR "cmpci: cannot register misc device\n");
free_irq(s->irq, s);
err_irq:
-#ifdef CONFIG_SOUND_CMPCI_FM
- release_region(s->iosynth, CM_EXTENT_SYNTH);
+ if(s->iosynth)
+ release_region(s->iosynth, CM_EXTENT_SYNTH);
err_region1:
-#endif
-#ifdef CONFIG_SOUND_CMPCI_MIDI
- release_region(s->iomidi, CM_EXTENT_MIDI);
-#endif
+ if(s->iomidi)
+ release_region(s->iomidi, CM_EXTENT_MIDI);
err_region4:
release_region(s->iobase, CM_EXTENT_CODEC);
err_region5:
@@ -2345,11 +2524,6 @@
#ifdef MODULE
-#if 0
-MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i");
-MODULE_PARM_DESC(wavetable, "if 1 the wavetable synth is enabled");
-#endif
-
MODULE_AUTHOR("ChenLi Tien, cltien@home.com");
MODULE_DESCRIPTION("CMPCI Audio Driver");
@@ -2361,32 +2535,30 @@
devs = devs->next;
outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */
synchronize_irq();
- outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* reset channels */
+ outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */
free_irq(s->irq, s);
/* reset mixer */
wrmixer(s, DSP_MIX_DATARESETIDX, 0);
release_region(s->iobase, CM_EXTENT_CODEC);
-#ifdef CONFIG_SOUND_CMPCI_MIDI
- release_region(s->iomidi, CM_EXTENT_MIDI);
-#endif
-#ifdef CONFIG_SOUND_CMPCI_FM
- release_region(s->iosynth, CM_EXTENT_SYNTH);
-#endif
+ if(s->iomidi)
+ {
+ release_region(s->iomidi, CM_EXTENT_MIDI);
+ unregister_sound_midi(s->dev_midi);
+ }
+ if(s->iosynth)
+ {
+ release_region(s->iosynth, CM_EXTENT_SYNTH);
+ unregister_sound_special(s->dev_dmfm);
+ }
unregister_sound_dsp(s->dev_audio);
unregister_sound_mixer(s->dev_mixer);
-#ifdef CONFIG_SOUND_CMPCI_MIDI
- unregister_sound_midi(s->dev_midi);
-#endif
-#ifdef CONFIG_SOUND_CMPCI_FM
- unregister_sound_special(s->dev_dmfm);
-#endif
kfree_s(s, sizeof(struct cm_state));
}
if (wavetable_mem)
free_pages(wavetable_mem, 20-PAGE_SHIFT);
- printk(KERN_INFO "cm: unloading\n");
+ printk(KERN_INFO "cmpci: unloading\n");
}
#endif /* MODULE */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)