patch-2.4.3 linux/drivers/sound/i810_audio.c
Next file: linux/drivers/sound/maestro.c
Previous file: linux/drivers/sound/gus_midi.c
Back to the patch index
Back to the overall index
- Lines: 975
- Date:
Sun Mar 25 18:24:31 2001
- Orig file:
v2.4.2/linux/drivers/sound/i810_audio.c
- Orig date:
Wed Feb 21 18:20:34 2001
diff -u --recursive --new-file v2.4.2/linux/drivers/sound/i810_audio.c linux/drivers/sound/i810_audio.c
@@ -194,8 +194,12 @@
/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
#define NR_AC97 2
+/* Please note that an 8bit mono stream is not valid on this card, you must have a 16bit */
+/* stream at a minimum for this card to be happy */
static const unsigned sample_size[] = { 1, 2, 2, 4 };
-static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+/* Samples are 16bit values, so we are shifting to a word, not to a byte, hence shift */
+/* values are one less than might be expected */
+static const unsigned sample_shift[] = { -1, 0, 0, 1 };
enum {
ICH82801AA = 0,
@@ -246,7 +250,8 @@
unsigned char fmt, enable;
/* hardware channel */
- struct i810_channel *channel;
+ struct i810_channel *read_channel;
+ struct i810_channel *write_channel;
/* OSS buffer management stuff */
void *rawbuf;
@@ -258,7 +263,7 @@
/* our buffer acts like a circular ring */
unsigned hwptr; /* where dma last started, updated by update_ptr */
unsigned swptr; /* where driver last clear/filled, updated by read/write */
- int count; /* bytes to be comsumed or been generated by dma machine */
+ int count; /* bytes to be consumed or been generated by dma machine */
unsigned total_bytes; /* total bytes dmaed by hardware */
unsigned error; /* number of over/underruns */
@@ -313,6 +318,7 @@
/* Function support */
struct i810_channel *(*alloc_pcm_channel)(struct i810_card *);
struct i810_channel *(*alloc_rec_pcm_channel)(struct i810_card *);
+ struct i810_channel *(*alloc_rec_mic_channel)(struct i810_card *);
void (*free_pcm_channel)(struct i810_card *, int chan);
};
@@ -356,9 +362,6 @@
if(card->channel[1].used==1)
return NULL;
card->channel[1].used=1;
- card->channel[1].offset = 0;
- card->channel[1].port = 0x10;
- card->channel[1].num=1;
return &card->channel[1];
}
@@ -367,12 +370,17 @@
if(card->channel[0].used==1)
return NULL;
card->channel[0].used=1;
- card->channel[0].offset = 0;
- card->channel[0].port = 0x00;
- card->channel[1].num=0;
return &card->channel[0];
}
+static struct i810_channel *i810_alloc_rec_mic_channel(struct i810_card *card)
+{
+ if(card->channel[2].used==1)
+ return NULL;
+ card->channel[2].used=1;
+ return &card->channel[2];
+}
+
static void i810_free_pcm_channel(struct i810_card *card, int channel)
{
card->channel[channel].used=0;
@@ -382,7 +390,7 @@
static unsigned int i810_set_dac_rate(struct i810_state * state, unsigned int rate)
{
struct dmabuf *dmabuf = &state->dmabuf;
- u32 dacp;
+ u32 dacp, new_rate;
struct ac97_codec *codec=state->card->ac97_codec[0];
if(!(state->card->ac97_features&0x0001))
@@ -395,6 +403,7 @@
rate = 48000;
if (rate < 8000)
rate = 8000;
+ dmabuf->rate = rate;
/*
* Adjust for misclocked crap
@@ -402,12 +411,6 @@
rate = ( rate * clocking)/48000;
- /* Analog codecs can go lower via magic registers but others
- might not */
-
- if(rate < 8000)
- rate = 8000;
-
if(rate != i810_ac97_get(codec, AC97_PCM_FRONT_DAC_RATE))
{
/* Power down the DAC */
@@ -415,24 +418,25 @@
i810_ac97_set(codec, AC97_POWER_CONTROL, dacp|0x0200);
/* Load the rate and read the effective rate */
i810_ac97_set(codec, AC97_PCM_FRONT_DAC_RATE, rate);
- rate=i810_ac97_get(codec, AC97_PCM_FRONT_DAC_RATE);
+ new_rate=i810_ac97_get(codec, AC97_PCM_FRONT_DAC_RATE);
/* Power it back up */
i810_ac97_set(codec, AC97_POWER_CONTROL, dacp);
+ if(new_rate != rate) {
+ dmabuf->rate = (new_rate * 48000)/clocking;
+ rate = new_rate;
+ }
}
- rate=(rate * 48000) / clocking;
- dmabuf->rate = rate;
#ifdef DEBUG
- printk("i810_audio: called i810_set_dac_rate : rate = %d\n", rate);
+ printk("i810_audio: called i810_set_dac_rate : rate = %d/%d\n", dmabuf->rate, rate);
#endif
-
- return rate;
+ return dmabuf->rate;
}
/* set recording sample rate */
static unsigned int i810_set_adc_rate(struct i810_state * state, unsigned int rate)
{
struct dmabuf *dmabuf = &state->dmabuf;
- u32 dacp;
+ u32 dacp, new_rate;
struct ac97_codec *codec=state->card->ac97_codec[0];
if(!(state->card->ac97_features&0x0001))
@@ -445,6 +449,7 @@
rate = 48000;
if (rate < 8000)
rate = 8000;
+ dmabuf->rate = rate;
/*
* Adjust for misclocked crap
@@ -452,12 +457,6 @@
rate = ( rate * clocking)/48000;
- /* Analog codecs can go lower via magic registers but others
- might not */
-
- if(rate < 8000)
- rate = 8000;
-
if(rate != i810_ac97_get(codec, AC97_PCM_LR_DAC_RATE))
{
/* Power down the ADC */
@@ -465,16 +464,18 @@
i810_ac97_set(codec, AC97_POWER_CONTROL, dacp|0x0100);
/* Load the rate and read the effective rate */
i810_ac97_set(codec, AC97_PCM_LR_DAC_RATE, rate);
- rate=i810_ac97_get(codec, AC97_PCM_LR_DAC_RATE);
+ new_rate=i810_ac97_get(codec, AC97_PCM_LR_DAC_RATE);
/* Power it back up */
i810_ac97_set(codec, AC97_POWER_CONTROL, dacp);
+ if(new_rate != rate) {
+ dmabuf->rate = (new_rate * 48000)/clocking;
+ rate = new_rate;
+ }
}
- rate = (rate * 48000) / clocking;
- dmabuf->rate = rate;
#ifdef DEBUG
- printk("i810_audio: called i810_set_adc_rate : rate = %d\n", rate);
+ printk("i810_audio: called i810_set_adc_rate : rate = %d/%d\n", dmabuf->rate, rate);
#endif
- return rate;
+ return dmabuf->rate;
}
/* prepare channel attributes for playback */
@@ -508,10 +509,18 @@
{
struct dmabuf *dmabuf = &state->dmabuf;
unsigned int civ, offset;
- struct i810_channel *c = dmabuf->channel;
+ struct i810_channel *c;
if (!dmabuf->enable)
return 0;
+ if (dmabuf->enable & DAC_RUNNING)
+ c = dmabuf->write_channel;
+ else if (dmabuf->enable & ADC_RUNNING)
+ c = dmabuf->read_channel;
+ else {
+ printk("i810_audio: invalid dmabuf->enable state in get_dma_addr\n");
+ return 0;
+ }
do {
civ = inb(state->card->iobase+c->port+OFF_CIV);
offset = (civ + 1) * (dmabuf->dmasize/SG_LEN) -
@@ -523,12 +532,17 @@
return offset;
}
-static void resync_dma_ptrs(struct i810_state *state)
+static void resync_dma_ptrs(struct i810_state *state, int rec)
{
struct dmabuf *dmabuf = &state->dmabuf;
- struct i810_channel *c = dmabuf->channel;
+ struct i810_channel *c;
int offset;
-
+
+ if(rec) {
+ c = dmabuf->read_channel;
+ } else {
+ c = dmabuf->write_channel;
+ }
offset = inb(state->card->iobase+c->port+OFF_CIV);
offset *= (dmabuf->dmasize/SG_LEN);
@@ -597,23 +611,20 @@
spin_lock_irqsave(&card->lock, flags);
if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) {
- if(!(dmabuf->enable&DAC_RUNNING))
- {
- dmabuf->enable |= DAC_RUNNING;
- outb((1<<4) | 1<<2 | 1, card->iobase + PO_CR);
- }
+ dmabuf->enable |= DAC_RUNNING;
+ outb((1<<4) | 1<<2 | 1, card->iobase + PO_CR);
}
spin_unlock_irqrestore(&card->lock, flags);
}
-#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)
+#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)
#define DMABUF_MINORDER 1
/* allocate DMA buffer, playback and recording buffer should be allocated seperately */
static int alloc_dmabuf(struct i810_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
- void *rawbuf;
+ void *rawbuf= NULL;
int order;
struct page *page, *pend;
@@ -664,6 +675,7 @@
static int prog_dmabuf(struct i810_state *state, unsigned rec)
{
struct dmabuf *dmabuf = &state->dmabuf;
+ struct i810_channel *c;
struct sg_item *sg;
unsigned bytepersec;
unsigned bufsize;
@@ -673,7 +685,7 @@
int i;
spin_lock_irqsave(&state->card->lock, flags);
- resync_dma_ptrs(state);
+ resync_dma_ptrs(state, rec);
dmabuf->total_bytes = 0;
dmabuf->count = dmabuf->error = 0;
spin_unlock_irqrestore(&state->card->lock, flags);
@@ -684,7 +696,8 @@
return ret;
/* FIXME: figure out all this OSS fragment stuff */
- bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt];
+ /* sample_shift is for 16 byte samples, add an extra shift for bytes */
+ bytepersec = dmabuf->rate << (sample_shift[dmabuf->fmt] + 1);
bufsize = PAGE_SIZE << dmabuf->buforder;
if (dmabuf->ossfragshift) {
if ((1000 << dmabuf->ossfragshift) < bytepersec)
@@ -709,44 +722,52 @@
memset(dmabuf->rawbuf, (dmabuf->fmt & I810_FMT_16BIT) ? 0 : 0x80,
dmabuf->dmasize);
- /*
- * Now set up the ring
- */
-
- sg=&dmabuf->channel->sg[0];
fragsize = bufsize / SG_LEN;
-
/*
- * Load up 32 sg entries and take an interrupt at half
- * way (we might want more interrupts later..)
+ * Now set up the ring
*/
+ if(dmabuf->read_channel)
+ c = dmabuf->read_channel;
+ else
+ c = dmabuf->write_channel;
+ while(c != NULL) {
+ sg=&c->sg[0];
+ /*
+ * Load up 32 sg entries and take an interrupt at half
+ * way (we might want more interrupts later..)
+ */
- for(i=0;i<32;i++)
- {
- sg->busaddr=virt_to_bus(dmabuf->rawbuf+fragsize*i);
- sg->control=(fragsize>>1);
- sg->control|=CON_IOC;
- sg++;
- }
+ for(i=0;i<32;i++)
+ {
+ sg->busaddr=virt_to_bus(dmabuf->rawbuf+fragsize*i);
+ sg->control=(fragsize>>sample_shift[dmabuf->fmt]);
+ sg->control|=CON_IOC;
+ sg++;
+ }
+ spin_lock_irqsave(&state->card->lock, flags);
+ outb(2, state->card->iobase+c->port+OFF_CR); /* reset DMA machine */
+ outl(virt_to_bus(&c->sg[0]), state->card->iobase+c->port+OFF_BDBAR);
+ outb(31, state->card->iobase+c->port+OFF_LVI);
+ outb(0, state->card->iobase+c->port+OFF_CIV);
- spin_lock_irqsave(&state->card->lock, flags);
- outb(2, state->card->iobase+dmabuf->channel->port+OFF_CR); /* reset DMA machine */
- outl(virt_to_bus(&dmabuf->channel->sg[0]), state->card->iobase+dmabuf->channel->port+OFF_BDBAR);
- outb(16, state->card->iobase+dmabuf->channel->port+OFF_LVI);
- outb(0, state->card->iobase+dmabuf->channel->port+OFF_CIV);
+ if (c == dmabuf->read_channel) {
+ i810_rec_setup(state);
+ } else {
+ i810_play_setup(state);
+ }
+ spin_unlock_irqrestore(&state->card->lock, flags);
- if (rec) {
- i810_rec_setup(state);
- } else {
- i810_play_setup(state);
+ if(c != dmabuf->write_channel)
+ c = dmabuf->write_channel;
+ else
+ c = NULL;
}
- spin_unlock_irqrestore(&state->card->lock, flags);
-
+
/* set the ready flag for the dma buffer */
dmabuf->ready = 1;
#ifdef DEBUG
- printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d, numfrag = %d, "
+ printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d,\n\tnumfrag = %d, "
"fragsize = %d dmasize = %d\n",
dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
dmabuf->fragsize, dmabuf->dmasize);
@@ -912,7 +933,6 @@
{
int i;
-// printk("CHANNEL IRQ .. ");
for(i=0;i<NR_HW_CH;i++)
{
struct i810_state *state = card->states[i];
@@ -924,38 +944,35 @@
continue;
if(!state->dmabuf.ready)
continue;
- c=state->dmabuf.channel;
+ if(state->dmabuf.enable & DAC_RUNNING)
+ c=state->dmabuf.write_channel;
+ else
+ c=state->dmabuf.read_channel;
port+=c->port;
-// printk("PORT %lX (", port);
-
status = inw(port + OFF_SR);
-// printk("ST%d ", status);
-
- if(status & DMA_INT_LVI)
- {
- /* Back to the start */
-// printk("LVI - STOP");
- outb((inb(port+OFF_CIV)-1)&31, port+OFF_LVI);
- i810_update_ptr(state);
- outb(0, port + OFF_CR);
- }
if(status & DMA_INT_COMPLETE)
{
int x;
/* Keep the card chasing its tail */
outb(x=((inb(port+OFF_CIV)-1)&31), port+OFF_LVI);
i810_update_ptr(state);
-// printk("COMP%d ",x);
}
-// printk(")");
+ if(status & DMA_INT_LVI)
+ {
+ /* Back to the start */
+ i810_update_ptr(state);
+ outb(0, port + OFF_CR);
+ }
outw(status & DMA_INT_MASK, port + OFF_SR);
}
-// printk("\n");
}
+static u32 jiff = 0;
+static u32 jiff_count = 0;
+
static void i810_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct i810_card *card = (struct i810_card *)dev_id;
@@ -964,13 +981,13 @@
spin_lock(&card->lock);
status = inl(card->iobase + GLOB_STA);
+
if(!(status & INT_MASK))
{
spin_unlock(&card->lock);
return; /* not for us */
}
-// printk("Interrupt %X: ", status);
if(status & (INT_PO|INT_PI|INT_MC))
i810_channel_interrupt(card);
@@ -1003,6 +1020,15 @@
return -ESPIPE;
if (dmabuf->mapped)
return -ENXIO;
+ if (dmabuf->enable & DAC_RUNNING)
+ return -ENODEV;
+ if (!dmabuf->read_channel) {
+ dmabuf->ready = 0;
+ dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card);
+ if (!dmabuf->read_channel) {
+ return -ENODEV;
+ }
+ }
if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
return ret;
if (!access_ok(VERIFY_WRITE, buffer, count))
@@ -1100,6 +1126,14 @@
return -ESPIPE;
if (dmabuf->mapped)
return -ENXIO;
+ if (dmabuf->enable & ADC_RUNNING)
+ return -ENODEV;
+ if (!dmabuf->write_channel) {
+ dmabuf->ready = 0;
+ dmabuf->write_channel = state->card->alloc_pcm_channel(state->card);
+ if(!dmabuf->write_channel)
+ return -ENODEV;
+ }
if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
return ret;
if (!access_ok(VERIFY_READ, buffer, count))
@@ -1132,8 +1166,8 @@
return ret;
}
/* Not strictly correct but works */
- tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
- tmo >>= sample_shift[dmabuf->fmt];
+ tmo = dmabuf->rate << (sample_shift[dmabuf->fmt] + 1);
+ tmo = dmabuf->dmasize * HZ / tmo;
/* 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
@@ -1187,22 +1221,27 @@
unsigned int mask = 0;
if (file->f_mode & FMODE_WRITE) {
+ if (!dmabuf->write_channel)
+ return 0;
if (!dmabuf->ready && prog_dmabuf(state, 0))
return 0;
poll_wait(file, &dmabuf->wait, wait);
- }
- if (file->f_mode & FMODE_READ) {
+ } else {
+ // don't do both read and write paths or we won't get woke up properly
+ // when we have a file with both permissions
+ if (!dmabuf->read_channel)
+ return 0;
if (!dmabuf->ready && prog_dmabuf(state, 1))
return 0;
poll_wait(file, &dmabuf->wait, wait);
}
spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
- if (file->f_mode & FMODE_READ) {
+ if (file->f_mode & FMODE_READ && dmabuf->enable & ADC_RUNNING) {
if (dmabuf->count >= (signed)dmabuf->fragsize)
mask |= POLLIN | POLLRDNORM;
}
- if (file->f_mode & FMODE_WRITE) {
+ if (file->f_mode & FMODE_WRITE && dmabuf->enable & DAC_RUNNING) {
if (dmabuf->mapped) {
if (dmabuf->count >= (signed)dmabuf->fragsize)
mask |= POLLOUT | POLLWRNORM;
@@ -1223,11 +1262,19 @@
int ret = -EINVAL;
unsigned long size;
+ /*
+ * Until we figure out a few problems
+ */
+
lock_kernel();
if (vma->vm_flags & VM_WRITE) {
+ if (!dmabuf->write_channel && (dmabuf->write_channel = state->card->alloc_pcm_channel(state->card)) == NULL)
+ goto out;
if ((ret = prog_dmabuf(state, 0)) != 0)
goto out;
} else if (vma->vm_flags & VM_READ) {
+ if (!dmabuf->read_channel && (dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card)) == NULL)
+ goto out;
if ((ret = prog_dmabuf(state, 1)) != 0)
goto out;
} else
@@ -1245,6 +1292,9 @@
goto out;
dmabuf->mapped = 1;
ret = 0;
+#ifdef DEBUG
+ printk("i810_audio: mmap'ed %d bytes of data space\n", size);
+#endif
out:
unlock_kernel();
return ret;
@@ -1277,14 +1327,14 @@
stop_dac(state);
synchronize_irq();
dmabuf->ready = 0;
- resync_dma_ptrs(state);
+ resync_dma_ptrs(state, 0);
dmabuf->swptr = dmabuf->hwptr = 0;
dmabuf->count = dmabuf->total_bytes = 0;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
synchronize_irq();
- resync_dma_ptrs(state);
+ resync_dma_ptrs(state, 1);
dmabuf->ready = 0;
dmabuf->swptr = dmabuf->hwptr = 0;
dmabuf->count = dmabuf->total_bytes = 0;
@@ -1325,23 +1375,23 @@
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
dmabuf->ready = 0;
- dmabuf->fmt = I810_FMT_STEREO;
+ dmabuf->fmt |= I810_FMT_STEREO;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
dmabuf->ready = 0;
- dmabuf->fmt = I810_FMT_STEREO;
+ dmabuf->fmt |= I810_FMT_STEREO;
}
return 0;
case SNDCTL_DSP_GETBLKSIZE:
if (file->f_mode & FMODE_WRITE) {
- if ((val = prog_dmabuf(state, 0)))
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 0)))
return val;
return put_user(dmabuf->fragsize, (int *)arg);
}
if (file->f_mode & FMODE_READ) {
- if ((val = prog_dmabuf(state, 1)))
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 1)))
return val;
return put_user(dmabuf->fragsize, (int *)arg);
}
@@ -1356,10 +1406,12 @@
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
dmabuf->ready = 0;
+ dmabuf->fmt |= I810_FMT_16BIT;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
dmabuf->ready = 0;
+ dmabuf->fmt |= I810_FMT_16BIT;
}
}
return put_user(AFMT_S16_LE, (int *)arg);
@@ -1391,6 +1443,7 @@
if (val != 1 && val != 2 && val != 4)
return -EINVAL;
dmabuf->subdivision = val;
+ dmabuf->ready = 0;
return 0;
case SNDCTL_DSP_SETFRAGMENT:
@@ -1405,13 +1458,14 @@
dmabuf->ossfragshift = 15;
if (dmabuf->ossmaxfrags < 4)
dmabuf->ossmaxfrags = 4;
+ dmabuf->ready = 0;
return 0;
case SNDCTL_DSP_GETOSPACE:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
- if (!dmabuf->enable && (val = prog_dmabuf(state, 0)) != 0)
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
return val;
spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
@@ -1425,7 +1479,7 @@
case SNDCTL_DSP_GETISPACE:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
- if (!dmabuf->enable && (val = prog_dmabuf(state, 1)) != 0)
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
return val;
spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
@@ -1446,30 +1500,40 @@
case SNDCTL_DSP_GETTRIGGER:
val = 0;
- if (file->f_mode & FMODE_READ && dmabuf->enable)
+ if (file->f_mode & FMODE_READ && dmabuf->enable & ADC_RUNNING)
val |= PCM_ENABLE_INPUT;
- if (file->f_mode & FMODE_WRITE && dmabuf->enable)
+ if (file->f_mode & FMODE_WRITE && dmabuf->enable & DAC_RUNNING)
val |= PCM_ENABLE_OUTPUT;
return put_user(val, (int *)arg);
case SNDCTL_DSP_SETTRIGGER:
if (get_user(val, (int *)arg))
return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- if (val & PCM_ENABLE_INPUT) {
- if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
- return ret;
- start_adc(state);
- } else
- stop_adc(state);
+ if (file->f_mode & FMODE_READ && val & PCM_ENABLE_INPUT) {
+ if (dmabuf->enable & DAC_RUNNING)
+ return -ENODEV;
+ if (!dmabuf->read_channel) {
+ dmabuf->ready = 0;
+ dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card);
+ if (!dmabuf->read_channel)
+ return -ENODEV;
+ }
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
+ return ret;
+ start_adc(state);
}
- if (file->f_mode & FMODE_WRITE) {
- if (val & PCM_ENABLE_OUTPUT) {
- if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
- return ret;
- start_dac(state);
- } else
- stop_dac(state);
+ if (file->f_mode & FMODE_WRITE && val & PCM_ENABLE_OUTPUT) {
+ if (dmabuf->enable & ADC_RUNNING)
+ return -ENODEV;
+ if (!dmabuf->write_channel) {
+ dmabuf->ready = 0;
+ dmabuf->write_channel = state->card->alloc_pcm_channel(state->card);
+ if (!dmabuf->write_channel)
+ return -ENODEV;
+ }
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
+ return ret;
+ start_dac(state);
}
return 0;
@@ -1558,18 +1622,6 @@
return -ENODEV;
found_virt:
- /* found a free virtual channel, allocate hardware channels */
- if(file->f_mode & FMODE_READ)
- dmabuf->channel = card->alloc_rec_pcm_channel(card);
- else
- dmabuf->channel = card->alloc_pcm_channel(card);
-
- if (dmabuf->channel == NULL) {
- kfree (card->states[i]);
- card->states[i] = NULL;;
- return -ENODEV;
- }
-
/* initialize the virtual channel */
state->virt = i;
state->card = card;
@@ -1578,28 +1630,34 @@
init_MUTEX(&state->open_sem);
file->private_data = state;
+ /* allocate hardware channels */
+ if(file->f_mode & FMODE_READ) {
+ if((dmabuf->read_channel = card->alloc_rec_pcm_channel(card)) == NULL) {
+ kfree (card->states[i]);
+ card->states[i] = NULL;;
+ return -ENODEV;
+ }
+ i810_set_adc_rate(state, 48000);
+ }
+ if(file->f_mode & FMODE_WRITE) {
+ if((dmabuf->write_channel = card->alloc_pcm_channel(card)) == NULL) {
+ kfree (card->states[i]);
+ card->states[i] = NULL;;
+ return -ENODEV;
+ }
+ i810_set_dac_rate(state, 48000);
+ }
+
down(&state->open_sem);
/* set default sample format. According to OSS Programmer's Guide /dev/dsp
should be default to unsigned 8-bits, mono, with sample rate 8kHz and
/dev/dspW will accept 16-bits sample */
- if (file->f_mode & FMODE_WRITE) {
- dmabuf->fmt &= ~I810_FMT_MASK;
- dmabuf->fmt |= I810_FMT_16BIT;
- dmabuf->ossfragshift = 0;
- dmabuf->ossmaxfrags = 0;
- dmabuf->subdivision = 0;
- i810_set_dac_rate(state, 48000);
- }
-
- if (file->f_mode & FMODE_READ) {
- dmabuf->fmt &= ~I810_FMT_MASK;
- dmabuf->fmt |= I810_FMT_16BIT;
- dmabuf->ossfragshift = 0;
- dmabuf->ossmaxfrags = 0;
- dmabuf->subdivision = 0;
- i810_set_adc_rate(state, 48000);
- }
+ dmabuf->fmt &= ~I810_FMT_MASK;
+ dmabuf->fmt |= I810_FMT_16BIT;
+ dmabuf->ossfragshift = 0;
+ dmabuf->ossmaxfrags = 0;
+ dmabuf->subdivision = 0;
state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
up(&state->open_sem);
@@ -1614,29 +1672,33 @@
lock_kernel();
if (file->f_mode & FMODE_WRITE) {
- i810_clear_tail(state);
- drain_dac(state, file->f_flags & O_NONBLOCK);
}
/* stop DMA state machine and free DMA buffers/channels */
down(&state->open_sem);
- if (file->f_mode & FMODE_WRITE) {
+ if (dmabuf->enable & DAC_RUNNING) {
+ i810_clear_tail(state);
+ drain_dac(state, file->f_flags & O_NONBLOCK);
stop_dac(state);
dealloc_dmabuf(state);
- state->card->free_pcm_channel(state->card, dmabuf->channel->num);
}
- if (file->f_mode & FMODE_READ) {
+ if(dmabuf->enable & ADC_RUNNING) {
stop_adc(state);
dealloc_dmabuf(state);
- state->card->free_pcm_channel(state->card, dmabuf->channel->num);
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ state->card->free_pcm_channel(state->card, dmabuf->write_channel->num);
+ }
+ if (file->f_mode & FMODE_READ) {
+ state->card->free_pcm_channel(state->card, dmabuf->read_channel->num);
}
/* we're covered by the open_sem */
up(&state->open_sem);
- kfree(state->card->states[state->virt]);
state->card->states[state->virt] = NULL;
+ kfree(state);
unlock_kernel();
return 0;
@@ -1688,16 +1750,11 @@
for (card = devs; card != NULL; card = card->next)
for (i = 0; i < NR_AC97; i++)
if (card->ac97_codec[i] != NULL &&
- card->ac97_codec[i]->dev_mixer == minor)
- goto match;
-
- if (!card)
- return -ENODEV;
-
- match:
- file->private_data = card->ac97_codec[i];
-
- return 0;
+ card->ac97_codec[i]->dev_mixer == minor) {
+ file->private_data = card->ac97_codec[i];
+ return 0;
+ }
+ return -ENODEV;
}
static int i810_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
@@ -1788,10 +1845,6 @@
printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n");
else
{
- /* Enable variable rate mode */
- i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9);
- i810_ac97_set(codec,AC97_EXTENDED_STATUS,
- i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800);
/* power up everything, modify this when implementing power saving */
i810_ac97_set(codec, AC97_POWER_CONTROL,
i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00);
@@ -1804,6 +1857,11 @@
schedule_timeout(HZ/20);
}
+ /* Enable variable rate mode */
+ i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9);
+ i810_ac97_set(codec,AC97_EXTENDED_STATUS,
+ i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800);
+
if(!(i810_ac97_get(codec, AC97_EXTENDED_STATUS)&1))
{
printk(KERN_WARNING "i810_audio: Codec refused to allow VRA, using 48Khz only.\n");
@@ -1833,14 +1891,15 @@
{
struct i810_card *card;
- if (!pci_dma_supported(pci_dev, I810_DMA_MASK)) {
+ if (pci_enable_device(pci_dev))
+ return -EIO;
+
+ if (pci_set_dma_mask(pci_dev, I810_DMA_MASK)) {
printk(KERN_ERR "intel810: architecture does not support"
" 32bit PCI busmaster DMA\n");
return -ENODEV;
}
- if (pci_enable_device(pci_dev))
- return -EIO;
if ((card = kmalloc(sizeof(struct i810_card), GFP_KERNEL)) == NULL) {
printk(KERN_ERR "i810_audio: out of memory\n");
return -ENOMEM;
@@ -1865,7 +1924,17 @@
card->alloc_pcm_channel = i810_alloc_pcm_channel;
card->alloc_rec_pcm_channel = i810_alloc_rec_pcm_channel;
+ card->alloc_rec_mic_channel = i810_alloc_rec_mic_channel;
card->free_pcm_channel = i810_free_pcm_channel;
+ card->channel[0].offset = 0;
+ card->channel[0].port = 0x00;
+ card->channel[0].num=0;
+ card->channel[1].offset = 0;
+ card->channel[1].port = 0x10;
+ card->channel[1].num=1;
+ card->channel[2].offset = 0;
+ card->channel[2].port = 0x20;
+ card->channel[2].num=2;
/* claim our iospace and irq */
request_region(card->iobase, 64, card_names[pci_id->driver_data]);
@@ -1900,7 +1969,6 @@
return -ENODEV;
}
pci_dev->driver_data = card;
- pci_dev->dma_mask = I810_DMA_MASK;
return 0;
}
@@ -1938,20 +2006,86 @@
remove: i810_remove,
};
+static void __init i810_configure_clocking (void)
+{
+ struct i810_card *card;
+ struct i810_state *state;
+ struct dmabuf *dmabuf;
+ unsigned int i, offset, new_offset;
+ unsigned long flags;
+
+ card = devs;
+ /* We could try to set the clocking for multiple cards, but can you even have
+ * more than one i810 in a machine? Besides, clocking is global, so unless
+ * someone actually thinks more than one i810 in a machine is possible and
+ * decides to rewrite that little bit, setting the rate for more than one card
+ * is a waste of time.
+ */
+ if(card != NULL) {
+ state = card->states[0] = (struct i810_state *)
+ kmalloc(sizeof(struct i810_state), GFP_KERNEL);
+ if (state == NULL)
+ return;
+ memset(state, 0, sizeof(struct i810_state));
+ dmabuf = &state->dmabuf;
+
+ dmabuf->write_channel = card->alloc_pcm_channel(card);
+ state->virt = 0;
+ state->card = card;
+ state->magic = I810_STATE_MAGIC;
+ init_waitqueue_head(&dmabuf->wait);
+ init_MUTEX(&state->open_sem);
+ dmabuf->fmt = I810_FMT_STEREO | I810_FMT_16BIT;
+ i810_set_dac_rate(state, 48000);
+ if(prog_dmabuf(state, 0) != 0) {
+ goto config_out_nodmabuf;
+ }
+ if(dmabuf->dmasize < 16384) {
+ goto config_out;
+ }
+ dmabuf->count = dmabuf->dmasize;
+ save_flags(flags);
+ cli();
+ start_dac(state);
+ offset = i810_get_dma_addr(state);
+ mdelay(50);
+ new_offset = i810_get_dma_addr(state);
+ stop_dac(state);
+ outb(2,card->iobase+dmabuf->write_channel->port+OFF_CR);
+ restore_flags(flags);
+ i = new_offset - offset;
+ printk("i810_audio: %d bytes in 50 milliseconds\n", i);
+ i = i / 4 * 20;
+ if (i > 48500 || i < 47500) {
+ clocking = clocking * clocking / i;
+ printk("i810_audio: setting clocking to %d to compensate\n", clocking);
+ }
+config_out_nodmabuf:
+ dealloc_dmabuf(state);
+config_out:
+ state->card->free_pcm_channel(state->card,state->dmabuf.write_channel->num);
+ kfree(state);
+ card->states[0] = NULL;
+ }
+}
+
static int __init i810_init_module (void)
{
if (!pci_present()) /* No PCI bus in this machine! */
return -ENODEV;
- if(ftsodell==1)
- clocking=41194;
-
printk(KERN_INFO "Intel 810 + AC97 Audio, version "
DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n");
if (!pci_register_driver(&i810_pci_driver)) {
pci_unregister_driver(&i810_pci_driver);
return -ENODEV;
+ }
+ if(ftsodell != 0) {
+ printk("i810_audio: ftsodell is now a deprecated option.\n");
+ }
+ if(clocking == 48000) {
+ i810_configure_clocking();
}
return 0;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)