2000-12-01 11:51:08

by Mulder, Tjeerd

[permalink] [raw]
Subject: [PATCH] i810_audio 2.4.0-test11

This patch makes the same changes (dma_prog and poll) that are already made to
some other OSS drivers (test12-pre3). Further it finally includes all modifications
made in 2.2.18 (Alan: why do you do them line by line ?).

It implements mono output and fixes a bug in the dma logic (reset necessary
because some descriptors are already prefetched and are not updated
when the dma is only halted and not reset). There is still a bug in the
device close handling, it gives an occasional dma overrun error.

It has been tested on by 3 people on 4 different machines with 3 different
chip sets and at least 3 different codecs, so it really should work !
And it does the same things the alsa driver does.


diff -u --recursive linux-2.4.0-test11-org/drivers/sound/i810_audio.c linux-2.4.0-test11-clean/drivers/sound/i810_audio.c
--- linux-2.4.0-test11-org/drivers/sound/i810_audio.c Thu Nov 9 02:09:50 2000
+++ linux-2.4.0-test11-clean/drivers/sound/i810_audio.c Thu Nov 30 14:22:11 2000
@@ -10,6 +10,13 @@
* Extended by: Zach Brown <[email protected]>
* and others..
*
+ * Modified to support mono audio out, not supported in mapped mode.
+ * The chip can do stereo only, the output buffer always holds stereo data.
+ * I think it now conforms a little bit to the OSS Programmers Guide 1.11
+ * For me mono and stereo mp3 files and wav files now work with xmms and mpg123
+ * and other tools. And KDE sounds now work.
+ * Tjeerd Mulder <[email protected]>
+ *
* Hardware Provided By:
* Analog Devices (A major AC97 codec maker)
* Intel Corp (you've probably heard of them already)
@@ -107,10 +114,6 @@
#define ADC_RUNNING 1
#define DAC_RUNNING 2

-#define I810_FMT_16BIT 1
-#define I810_FMT_STEREO 2
-#define I810_FMT_MASK 3
-
/* the 810's array of pointers to data buffers */

struct sg_item {
@@ -131,15 +134,16 @@
struct sg_item sg[SG_LEN]; /* 32*8 */
u32 offset; /* 4 */
u32 port; /* 4 */
- u32 used;
- u32 num;
+ u32 used; /* 4 */
+ u32 num; /* 4 */
};

/*
* we have 3 seperate dma engines. pcm in, pcm out, and mic.
* each dma engine has controlling registers. These goofy
* names are from the datasheet, but make it easy to write
- * code while leafing through it.
+ * code while leafing through it. Right now we don't support
+ * the MIC input.
*/

#define ENUM_ENGINE(PRE,DIG) \
@@ -183,7 +187,8 @@
#define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI)


-#define DRIVER_VERSION "0.01"
+/* This version is the based on version 0.17 in the 2.2.18 kernel */
+#define DRIVER_VERSION "0.18 (for 2.4.0-test11)"

/* magic numbers to protect our data structures */
#define I810_CARD_MAGIC 0x5072696E /* "Prin" */
@@ -194,9 +199,6 @@
/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
#define NR_AC97 2

-static const unsigned sample_size[] = { 1, 2, 2, 4 };
-static const unsigned sample_shift[] = { 0, 1, 1, 2 };
-
enum {
ICH82801AA = 0,
ICH82901AB,
@@ -243,7 +245,9 @@
struct dmabuf {
/* wave sample stuff */
unsigned int rate;
- unsigned char fmt, enable;
+ unsigned int bps_buffer; /* bytes per sample in the dma buffer */
+ unsigned int bps_user; /* bytes per sample for the user */
+ unsigned char enable;

/* hardware channel */
struct i810_channel *channel;
@@ -259,13 +263,12 @@
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 */
- unsigned total_bytes; /* total bytes dmaed by hardware */

unsigned error; /* number of over/underruns */
wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */

/* redundant, but makes calculations easier */
- unsigned fragsize;
+ unsigned fragsize; /* size of a fragment in the dma buffer */
unsigned dmasize;
unsigned fragsamples;

@@ -277,6 +280,10 @@
unsigned ossfragshift;
int ossmaxfrags;
unsigned subdivision;
+
+ /* These fields are used by SNDCTL_DSP_GETIPTR and GETOPTR only */
+ unsigned total_bytes; /* total bytes dmaed by hardware */
+ unsigned blocks; /* fragments dmaed by hardware */
} dmabuf;
};

@@ -319,11 +326,12 @@
static struct i810_card *devs = NULL;

static int i810_open_mixdev(struct inode *inode, struct file *file);
+static int i810_release_mixdev(struct inode *inode, struct file *file);
static int i810_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg);
static loff_t i810_llseek(struct file *file, loff_t offset, int origin);

-extern __inline__ unsigned ld2(unsigned int x)
+static __inline__ unsigned ld2(unsigned int x)
{
unsigned r = 0;

@@ -369,7 +377,7 @@
card->channel[0].used=1;
card->channel[0].offset = 0;
card->channel[0].port = 0x00;
- card->channel[1].num=0;
+ card->channel[0].num=0;
return &card->channel[0];
}

@@ -475,53 +483,27 @@
printk("i810_audio: called i810_set_adc_rate : rate = %d\n", rate);
#endif
return rate;
-}

-/* prepare channel attributes for playback */
-static void i810_play_setup(struct i810_state *state)
-{
-// struct dmabuf *dmabuf = &state->dmabuf;
-// struct i810_channel *channel = dmabuf->channel;
- /* Fixed format. .. */
- //if (dmabuf->fmt & I810_FMT_16BIT)
- //if (dmabuf->fmt & I810_FMT_STEREO)
-}
-
-/* prepare channel attributes for recording */
-static void i810_rec_setup(struct i810_state *state)
-{
-// u16 w;
-// struct i810_card *card = state->card;
-// struct dmabuf *dmabuf = &state->dmabuf;
-// struct i810_channel *channel = dmabuf->channel;
-
- /* Enable AC-97 ADC (capture) */
-// if (dmabuf->fmt & I810_FMT_16BIT) {
-// if (dmabuf->fmt & I810_FMT_STEREO)
}

-
/* get current playback/recording dma buffer pointer (byte offset from LBA),
called with spinlock held! */

-extern __inline__ unsigned i810_get_dma_addr(struct i810_state *state)
+static __inline__ unsigned i810_get_dma_addr(struct i810_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
- u32 offset;
+ unsigned int civ, offset;
struct i810_channel *c = dmabuf->channel;

if (!dmabuf->enable)
return 0;
- offset = inb(state->card->iobase+c->port+OFF_CIV);
- offset++;
- offset&=31;
- /* Offset has to compensate for the fact we finished the segment
- on the IRQ so we are at next_segment,0 */
-// printk("BANK%d ", offset);
- offset *= (dmabuf->dmasize/SG_LEN);
-// printk("DMASZ=%d", dmabuf->dmasize);
-// offset += 1024-(4*inw(state->card->iobase+c->port+OFF_PICB));
-// printk("OFF%d ", offset);
+ do {
+ civ = inb(state->card->iobase+c->port+OFF_CIV);
+ offset = (civ + 1) * (dmabuf->dmasize/SG_LEN) -
+ 2 * inw(state->card->iobase+c->port+OFF_PICB);
+ /* CIV changed before we read PICB (very seldom) ?
+ * then PICB was rubbish, so try again */
+ } while (civ != inb(state->card->iobase+c->port+OFF_CIV));
return offset;
}

@@ -534,11 +516,11 @@
offset = inb(state->card->iobase+c->port+OFF_CIV);
offset *= (dmabuf->dmasize/SG_LEN);

- dmabuf->hwptr=dmabuf->swptr = offset;
+ dmabuf->hwptr = dmabuf->swptr = offset;
}

/* Stop recording (lock held) */
-extern __inline__ void __stop_adc(struct i810_state *state)
+static __inline__ void __stop_adc(struct i810_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
struct i810_card *card = state->card;
@@ -572,7 +554,7 @@
}

/* stop playback (lock held) */
-extern __inline__ void __stop_dac(struct i810_state *state)
+static __inline__ void __stop_dac(struct i810_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
struct i810_card *card = state->card;
@@ -666,6 +648,7 @@
static int prog_dmabuf(struct i810_state *state, unsigned rec)
{
struct dmabuf *dmabuf = &state->dmabuf;
+ struct i810_card *card = state->card;
struct sg_item *sg;
unsigned bytepersec;
unsigned bufsize;
@@ -674,11 +657,7 @@
unsigned fragsize;
int i;

- spin_lock_irqsave(&state->card->lock, flags);
- resync_dma_ptrs(state);
- dmabuf->total_bytes = 0;
- dmabuf->count = dmabuf->error = 0;
- spin_unlock_irqrestore(&state->card->lock, flags);
+ outb(0, card->iobase+dmabuf->channel->port + OFF_CR); /* halt DMA machine, should have been done already */

/* allocate DMA buffer if not allocated yet */
if (!dmabuf->rawbuf)
@@ -686,8 +665,9 @@
return ret;

/* FIXME: figure out all this OSS fragment stuff */
- bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt];
+ bytepersec = dmabuf->rate * dmabuf->bps_user;
bufsize = PAGE_SIZE << dmabuf->buforder;
+
if (dmabuf->ossfragshift) {
if ((1000 << dmabuf->ossfragshift) < bytepersec)
dmabuf->fragshift = ld2(bytepersec/1000);
@@ -697,19 +677,27 @@
/* lets hand out reasonable big ass buffers by default */
dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2);
}
+
+ dmabuf->ossfragshift = dmabuf->fragshift;
+ /* double or half the amount of data */
+ if (dmabuf->bps_user < dmabuf->bps_buffer) /* write: convert from 16 bit mono to 16 stereo */
+ dmabuf->fragshift++;
+ if (dmabuf->bps_user > dmabuf->bps_buffer) /* read: convert from 16 bit stereo to 16 mono (future) */
+ dmabuf->fragshift--;
+
dmabuf->numfrag = bufsize >> dmabuf->fragshift;
while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) {
dmabuf->fragshift--;
dmabuf->numfrag = bufsize >> dmabuf->fragshift;
}
- dmabuf->fragsize = 1 << dmabuf->fragshift;
+ dmabuf->fragsize = (1 << dmabuf->fragshift);
if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag)
dmabuf->numfrag = dmabuf->ossmaxfrags;
- dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt];
- dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;
+
+ dmabuf->fragsamples = dmabuf->fragsize / dmabuf->bps_buffer;
+ dmabuf->dmasize = dmabuf->numfrag * dmabuf->fragsize;

- memset(dmabuf->rawbuf, (dmabuf->fmt & I810_FMT_16BIT) ? 0 : 0x80,
- dmabuf->dmasize);
+ memset(dmabuf->rawbuf, 0 , dmabuf->dmasize);

/*
* Now set up the ring
@@ -719,8 +707,8 @@
fragsize = bufsize / SG_LEN;

/*
- * Load up 32 sg entries and take an interrupt at half
- * way (we might want more interrupts later..)
+ * Load up 32 sg entries and take an interrupt at each
+ * step (we might want less interrupts later..)
*/

for(i=0;i<32;i++)
@@ -730,68 +718,31 @@
sg->control|=CON_IOC;
sg++;
}
- spin_lock_irqsave(&state->card->lock, flags);
- 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 (rec) {
- i810_rec_setup(state);
- } else {
- i810_play_setup(state);
- }
- spin_unlock_irqrestore(&state->card->lock, flags);
+ spin_lock_irqsave(&card->lock, flags);
+ outb(2, card->iobase+dmabuf->channel->port + OFF_CR); /* reset DMA machine */
+ outl(virt_to_bus(&dmabuf->channel->sg[0]), card->iobase+dmabuf->channel->port+OFF_BDBAR);
+ outb(16, card->iobase+dmabuf->channel->port + OFF_LVI);
+ outb(0, card->iobase+dmabuf->channel->port + OFF_CIV);
+
+ dmabuf->total_bytes = 0;
+ dmabuf->count = dmabuf->error = 0;
+ resync_dma_ptrs(state);
+
+ spin_unlock_irqrestore(&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, "
- "fragsize = %d dmasize = %d\n",
- dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
- dmabuf->fragsize, dmabuf->dmasize);
+ printk("i810_audio: prog_dmabuf, sample rate = %d, bps_buffer = %d, bps_user = %d,\n\t"
+ "numfrag = %d, fragsize = %d dmasize = %d fragshift = %d, ossfragshift = %d\n",
+ dmabuf->rate, dmabuf->bps_buffer, dmabuf->bps_user, dmabuf->numfrag,
+ dmabuf->fragsize, dmabuf->dmasize, dmabuf->fragshift, dmabuf->ossfragshift);
#endif

return 0;
}

-/* we are doing quantum mechanics here, the buffer can only be empty, half or full filled i.e.
- |------------|------------| or |xxxxxxxxxxxx|------------| or |xxxxxxxxxxxx|xxxxxxxxxxxx|
- but we almost always get this
- |xxxxxx------|------------| or |xxxxxxxxxxxx|xxxxx-------|
- so we have to clear the tail space to "silence"
- |xxxxxx000000|------------| or |xxxxxxxxxxxx|xxxxxx000000|
-*/
-static void i810_clear_tail(struct i810_state *state)
-{
- struct dmabuf *dmabuf = &state->dmabuf;
- unsigned swptr;
- unsigned char silence = (dmabuf->fmt & I810_FMT_16BIT) ? 0 : 0x80;
- unsigned int len;
- unsigned long flags;
-
- spin_lock_irqsave(&state->card->lock, flags);
- swptr = dmabuf->swptr;
- spin_unlock_irqrestore(&state->card->lock, flags);
-
- if (swptr == 0 || swptr == dmabuf->dmasize / 2 || swptr == dmabuf->dmasize)
- return;
-
- if (swptr < dmabuf->dmasize/2)
- len = dmabuf->dmasize/2 - swptr;
- else
- len = dmabuf->dmasize - swptr;
-
- memset(dmabuf->rawbuf + swptr, silence, len);
-
- spin_lock_irqsave(&state->card->lock, flags);
- dmabuf->swptr += len;
- dmabuf->count += len;
- spin_unlock_irqrestore(&state->card->lock, flags);
-
- /* restart the dma machine in case it is halted */
- start_dac(state);
-}
-
static int drain_dac(struct i810_state *state, int nonblock)
{
DECLARE_WAITQUEUE(wait, current);
@@ -826,7 +777,7 @@
}

tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
- tmo >>= sample_shift[dmabuf->fmt];
+ tmo = tmo / dmabuf->bps_buffer;
if (!schedule_timeout(tmo ? tmo : 1) && tmo){
printk(KERN_ERR "i810_audio: drain_dac, dma timeout?\n");
break;
@@ -847,15 +798,14 @@
unsigned hwptr, swptr;
int clear_cnt = 0;
int diff;
- unsigned char silence;
-// unsigned half_dmasize;

/* update hardware pointer */
hwptr = i810_get_dma_addr(state);
diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
// printk("HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);
dmabuf->hwptr = hwptr;
- dmabuf->total_bytes += diff;
+ dmabuf->total_bytes += diff * dmabuf->bps_user/dmabuf->bps_buffer;
+ dmabuf->blocks += diff / dmabuf->fragsize;

/* error handling and process wake up for DAC */
if (dmabuf->enable == ADC_RUNNING) {
@@ -875,17 +825,17 @@
}
else if (!dmabuf->endcleared) {
swptr = dmabuf->swptr;
- silence = (dmabuf->fmt & I810_FMT_16BIT ? 0 : 0x80);
if (dmabuf->count < (signed) dmabuf->fragsize)
{
clear_cnt = dmabuf->fragsize;
if ((swptr + clear_cnt) > dmabuf->dmasize)
clear_cnt = dmabuf->dmasize - swptr;
- memset (dmabuf->rawbuf + swptr, silence, clear_cnt);
+ memset (dmabuf->rawbuf + swptr, 0, clear_cnt);
dmabuf->endcleared = 1;
}
- }
- wake_up(&dmabuf->wait);
+ }
+ if (dmabuf->count < (signed)dmabuf->dmasize/2)
+ wake_up(&dmabuf->wait);
}
}
/* error handling and process wake up for DAC */
@@ -902,10 +852,14 @@
it here, just stop the machine and let the process force hwptr
and swptr to sync */
__stop_dac(state);
- printk("DMA overrun on send\n");
+ if (dmabuf->count > dmabuf->dmasize)
+ printk(KERN_WARNING "i810_audio: DMA overrun on send.\n");
+ else
+ printk(KERN_WARNING "i810_audio: DMA underrun on send.\n");
dmabuf->error++;
}
- wake_up(&dmabuf->wait);
+ if (dmabuf->count < (signed)dmabuf->dmasize/2)
+ wake_up(&dmabuf->wait);
}
}
}
@@ -1038,7 +992,7 @@
}
/* This isnt strictly right for the 810 but it'll do */
tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
- tmo >>= sample_shift[dmabuf->fmt];
+ tmo = tmo / dmabuf->bps_buffer;
/* 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
@@ -1122,8 +1076,6 @@
cnt = dmabuf->dmasize - dmabuf->count;
spin_unlock_irqrestore(&state->card->lock, flags);

- if (cnt > count)
- cnt = count;
if (cnt <= 0) {
unsigned long tmo;
/* buffer is full, start the dma machine and wait for data to be
@@ -1135,7 +1087,7 @@
}
/* Not strictly correct but works */
tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
- tmo >>= sample_shift[dmabuf->fmt];
+ tmo = tmo / dmabuf->bps_buffer;
/* 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
@@ -1159,19 +1111,54 @@
}
continue;
}
- if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
- if (!ret) ret = -EFAULT;
- return ret;
- }
-
- swptr = (swptr + cnt) % dmabuf->dmasize;
-
- spin_lock_irqsave(&state->card->lock, flags);
- dmabuf->swptr = swptr;
- dmabuf->count += cnt;
- dmabuf->endcleared = 0;
- spin_unlock_irqrestore(&state->card->lock, flags);

+ if (dmabuf->bps_buffer > dmabuf->bps_user) {
+ /* this can only mean that we have to convert from mono to stereo */
+ int i;
+ unsigned short *p, *q;
+ /* We want to write count bytes and have room for cnt bytes,
+ * however because the i810 can do stereo only we have to duplicate
+ * all samples, so we really only have room for cnt/2 bytes. */
+ cnt = cnt/2;
+ if (cnt > count)
+ cnt = count;
+ /* we do 16 bits only */
+ if (cnt < 2)
+ break;
+ if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
+ if (!ret) ret = -EFAULT;
+ return ret;
+ }
+ /* duplicate the samples */
+ p = dmabuf->rawbuf + swptr;
+ q = p + cnt/2 - 1;
+ p = p + cnt - 1;
+ for (i=cnt/2; --i;) {
+ *p-- = *q;
+ *p-- = *q--;
+ }
+ swptr = (swptr + cnt*2) % dmabuf->dmasize;
+ spin_lock_irqsave(&state->card->lock, flags);
+ dmabuf->swptr = swptr;
+ dmabuf->count += cnt*2;
+ dmabuf->endcleared = 0;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ }
+ else
+ {
+ if (cnt > count)
+ cnt = count;
+ if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
+ if (!ret) ret = -EFAULT;
+ return ret;
+ }
+ swptr = (swptr + cnt) % dmabuf->dmasize;
+ spin_lock_irqsave(&state->card->lock, flags);
+ dmabuf->swptr = swptr;
+ dmabuf->count += cnt;
+ dmabuf->endcleared = 0;
+ spin_unlock_irqrestore(&state->card->lock, flags);
+ }
count -= cnt;
buffer += cnt;
ret += cnt;
@@ -1186,12 +1173,18 @@
struct i810_state *state = (struct i810_state *)file->private_data;
struct dmabuf *dmabuf = &state->dmabuf;
unsigned long flags;
- unsigned int mask = 0;
+ unsigned int mask = 0, ret;

- if (file->f_mode & FMODE_WRITE)
+ if (file->f_mode & FMODE_WRITE) {
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
+ return ret;
poll_wait(file, &dmabuf->wait, wait);
- if (file->f_mode & FMODE_READ)
+ }
+ if (file->f_mode & FMODE_READ) {
+ if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
+ return ret;
poll_wait(file, &dmabuf->wait, wait);
+ }

spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
@@ -1231,6 +1224,9 @@
goto out;

ret = -EINVAL;
+ if (dmabuf->bps_user != dmabuf->bps_buffer)
+ goto out;
+
if (vma->vm_pgoff != 0)
goto out;
size = vma->vm_end - vma->vm_start;
@@ -1274,17 +1270,15 @@
stop_dac(state);
synchronize_irq();
dmabuf->ready = 0;
- resync_dma_ptrs(state);
dmabuf->swptr = dmabuf->hwptr = 0;
- dmabuf->count = dmabuf->total_bytes = 0;
+ dmabuf->count = dmabuf->total_bytes = dmabuf->blocks = 0;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
synchronize_irq();
- resync_dma_ptrs(state);
dmabuf->ready = 0;
dmabuf->swptr = dmabuf->hwptr = 0;
- dmabuf->count = dmabuf->total_bytes = 0;
+ dmabuf->count = dmabuf->total_bytes = dmabuf->blocks = 0;
}
return 0;

@@ -1293,7 +1287,7 @@
return drain_dac(state, file->f_flags & O_NONBLOCK);
return 0;

- case SNDCTL_DSP_SPEED: /* set smaple rate */
+ case SNDCTL_DSP_SPEED: /* set sample rate */
if (get_user(val, (int *)arg))
return -EFAULT;
if (val >= 0) {
@@ -1317,30 +1311,39 @@
case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
if (get_user(val, (int *)arg))
return -EFAULT;
- if(val==0)
- return -EINVAL;
if (file->f_mode & FMODE_WRITE) {
+ if ((val==0) && !dmabuf->mapped) {
+ dmabuf->bps_user = 2;
+ dmabuf->bps_buffer = 4;
+ } else {
+ dmabuf->bps_user = 4;
+ dmabuf->bps_buffer = 4;
+ val = 1;
+ }
stop_dac(state);
dmabuf->ready = 0;
- dmabuf->fmt = I810_FMT_STEREO;
}
if (file->f_mode & FMODE_READ) {
+ /* READ is currently always stereo */
stop_adc(state);
dmabuf->ready = 0;
- dmabuf->fmt = I810_FMT_STEREO;
+ dmabuf->bps_user = 4;
+ dmabuf->bps_buffer = 4;
+ val = 1;
}
+ put_user(val, (int *)arg);
return 0;

case SNDCTL_DSP_GETBLKSIZE:
if (file->f_mode & FMODE_WRITE) {
if ((val = prog_dmabuf(state, 0)))
return val;
- return put_user(dmabuf->fragsize, (int *)arg);
+ return put_user(dmabuf->fragsamples * dmabuf->bps_user, (int *)arg);
}
if (file->f_mode & FMODE_READ) {
if ((val = prog_dmabuf(state, 1)))
return val;
- return put_user(dmabuf->fragsize, (int *)arg);
+ return put_user(dmabuf->fragsamples * dmabuf->bps_user, (int *)arg);
}

case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
@@ -1368,13 +1371,24 @@
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
dmabuf->ready = 0;
+ if ((val > 1) || dmabuf->mapped) {
+ dmabuf->bps_user = 4;
+ val = 2;
+ } else {
+ dmabuf->bps_user = 2;
+ }
+ dmabuf->bps_buffer = 4;
}
if (file->f_mode & FMODE_READ) {
+ /* READ is currently always stereo */
stop_adc(state);
dmabuf->ready = 0;
+ dmabuf->bps_user = 4;
+ dmabuf->bps_buffer = 4;
+ val = 2;
}
}
- return put_user(2, (int *)arg);
+ return put_user(val > 1 ? 2 : 1, (int *)arg);

case SNDCTL_DSP_POST:
/* FIXME: the same as RESET ?? */
@@ -1408,28 +1422,28 @@
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);
- abinfo.fragsize = dmabuf->fragsize;
- abinfo.bytes = dmabuf->dmasize - dmabuf->count;
+ abinfo.bytes = (dmabuf->dmasize - dmabuf->count) * dmabuf->bps_user / dmabuf->bps_buffer;
+ abinfo.fragsize = dmabuf->fragsamples * dmabuf->bps_user;
abinfo.fragstotal = dmabuf->numfrag;
- abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
+ abinfo.fragments = abinfo.bytes >> dmabuf->ossfragshift;
spin_unlock_irqrestore(&state->card->lock, flags);
return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;

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);
- abinfo.fragsize = dmabuf->fragsize;
- abinfo.bytes = dmabuf->count;
+ abinfo.bytes = dmabuf->count * dmabuf->bps_user / dmabuf->bps_buffer;
+ abinfo.fragsize = dmabuf->fragsamples * dmabuf->bps_user;
abinfo.fragstotal = dmabuf->numfrag;
- abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
+ abinfo.fragments = abinfo.bytes >> dmabuf->ossfragshift;
spin_unlock_irqrestore(&state->card->lock, flags);
return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;

@@ -1438,7 +1452,7 @@
return 0;

case SNDCTL_DSP_GETCAPS:
- return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND,
+ return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP,
(int *)arg);

case SNDCTL_DSP_GETTRIGGER:
@@ -1473,10 +1487,14 @@
case SNDCTL_DSP_GETIPTR:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
+ return val;
spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
cinfo.bytes = dmabuf->total_bytes;
- cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
+ cinfo.blocks = dmabuf->blocks;
+ dmabuf->blocks = 0;
+ /* this field is useless */
cinfo.ptr = dmabuf->hwptr;
if (dmabuf->mapped)
dmabuf->count &= dmabuf->fragsize-1;
@@ -1486,10 +1504,14 @@
case SNDCTL_DSP_GETOPTR:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+ return val;
spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
cinfo.bytes = dmabuf->total_bytes;
- cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
+ cinfo.blocks = dmabuf->blocks;
+ dmabuf->blocks = 0;
+ /* this field is useless */
cinfo.ptr = dmabuf->hwptr;
if (dmabuf->mapped)
dmabuf->count &= dmabuf->fragsize-1;
@@ -1502,6 +1524,8 @@
case SNDCTL_DSP_GETODELAY:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
+ if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+ return val;
spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
val = dmabuf->count;
@@ -1512,8 +1536,7 @@
return put_user(dmabuf->rate, (int *)arg);

case SOUND_PCM_READ_CHANNELS:
- return put_user((dmabuf->fmt & I810_FMT_STEREO) ? 2 : 1,
- (int *)arg);
+ return put_user(2, (int *)arg);

case SOUND_PCM_READ_BITS:
return put_user(AFMT_S16_LE, (int *)arg);
@@ -1531,7 +1554,6 @@
static int i810_open(struct inode *inode, struct file *file)
{
int i = 0;
- int minor = MINOR(inode->i_rdev);
struct i810_card *card = devs;
struct i810_state *state = NULL;
struct dmabuf *dmabuf = NULL;
@@ -1579,29 +1601,26 @@
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 */
+ should be default to unsigned 8-bits, mono, with sample rate 8kHz .
+ But not if the hardware does not support this format, so we default
+ to 48kHz, stereo 16 bit. All codecs support this. */
+ dmabuf->bps_user = 4;
+ dmabuf->bps_buffer = 4;
+ dmabuf->ossfragshift = 0;
+ dmabuf->ossmaxfrags = 0;
+ dmabuf->subdivision = 0;
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);
}

state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
up(&state->open_sem);

+ MOD_INC_USE_COUNT;
return 0;
}

@@ -1611,10 +1630,8 @@
struct dmabuf *dmabuf = &state->dmabuf;

lock_kernel();
- if (file->f_mode & FMODE_WRITE) {
- i810_clear_tail(state);
+ if (file->f_mode & FMODE_WRITE)
drain_dac(state, file->f_flags & O_NONBLOCK);
- }

/* stop DMA state machine and free DMA buffers/channels */
down(&state->open_sem);
@@ -1636,7 +1653,7 @@
kfree(state->card->states[state->virt]);
state->card->states[state->virt] = NULL;
unlock_kernel();
-
+ MOD_DEC_USE_COUNT;
return 0;
}

@@ -1661,6 +1678,8 @@

while(count-- && (inb(card->iobase + CAS) & 1))
udelay(1);
+ if(!count)
+ printk(KERN_ERR "i810_audio: AC97 access failed.\n");
return inw(card->ac97base + (reg&0x7f));
}

@@ -1671,6 +1690,8 @@

while(count-- && (inb(card->iobase + CAS) & 1))
udelay(1);
+ if(!count)
+ printk(KERN_ERR "i810_audio: AC97 write access failed.\n");
outw(data, card->ac97base + (reg&0x7f));
}

@@ -1695,6 +1716,13 @@
match:
file->private_data = card->ac97_codec[i];

+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int i810_release_mixdev(struct inode *inode, struct file *file)
+{
+ MOD_DEC_USE_COUNT;
return 0;
}

@@ -1711,6 +1739,7 @@
llseek: i810_llseek,
ioctl: i810_ioctl_mixdev,
open: i810_open_mixdev,
+ release: i810_release_mixdev,
};

/* AC97 codec initialisation. */
@@ -1723,6 +1752,7 @@
int i=0;
u32 reg;

+
reg = inl(card->iobase + GLOB_CNT);

if((reg&2)==0) /* Cold required */
@@ -1746,7 +1776,7 @@
printk(KERN_ERR "i810_audio: AC'97 reset failed.\n");
return 0;
}
-
+
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(HZ/5);

@@ -1768,6 +1798,9 @@
if (ac97_probe_codec(codec) == 0)
break;

+ /* Now check the codec for useful features to make up for
+ the dumbness of the 810 hardware engine */
+
eid = i810_ac97_get(codec, AC97_EXTENDED_ID);

if(eid==0xFFFFFF)
@@ -1777,10 +1810,36 @@
break;
}

+
card->ac97_features = eid;

- if(!(eid&0x0001))
+ if (!(eid&0x0001))
printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n");
+ else
+ {
+ /* In the AD1885 you cannot enable VRA when
+ * the analog sections are not yet ready */
+ for (i=10; i--;)
+ {
+ if ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) == 0xf)
+ break;
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(HZ/10);
+ }
+ if (i == 0)
+ printk(KERN_WARNING "i810_audio: Analog subsections not ready.\n");
+
+ /* 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 (!((i = i810_ac97_get(codec, AC97_EXTENDED_STATUS))&1))
+ {
+ printk(KERN_WARNING "i810_audio: Codec refused to allow VRA, using 48Khz only %04x.\n", i);
+ card->ac97_features&=~1;
+ }
+ }

if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) {
printk(KERN_ERR "i810_audio: couldn't register mixer!\n");
@@ -1788,9 +1847,6 @@
break;
}

- /* Now check the codec for useful features to make up for
- the dumbness of the 810 hardware engine */
-
card->ac97_codec[num_ac97] = codec;

/* if there is no secondary codec at all, don't probe any more */
@@ -1904,7 +1960,7 @@
}


-MODULE_AUTHOR("");
+MODULE_AUTHOR("Assorted");
MODULE_DESCRIPTION("Intel 810 audio support");
MODULE_PARM(ftsodell, "i");
MODULE_PARM(clocking, "i");


2000-12-01 13:34:34

by Alan

[permalink] [raw]
Subject: Re: [PATCH] i810_audio 2.4.0-test11

> It implements mono output and fixes a bug in the dma logic (reset necessary
> because some descriptors are already prefetched and are not updated

This is wrong. Linus please do not apply this patch, or if you have done back
it out. Not only does it do format conversions in kernel (which is a strict
not to be done in the sound driver policy) it also makes it impossible to make
mmap work correctly with the OSS API definitions.

Tjeerd. I deliberately applied only small bits of your patch before because
the mono mode stuff clutters the driver horribly and is not in the right place.
It belongs in the application/libraries

Alan


2000-12-04 10:07:35

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH] i810_audio 2.4.0-test11

Hi!

> > It implements mono output and fixes a bug in the dma logic (reset necessary
> > because some descriptors are already prefetched and are not updated
>
> This is wrong. Linus please do not apply this patch, or if you have done back
> it out. Not only does it do format conversions in kernel (which is a strict
> not to be done in the sound driver policy) it also makes it impossible to make
> mmap work correctly with the OSS API definitions.
>
> Tjeerd. I deliberately applied only small bits of your patch before because
> the mono mode stuff clutters the driver horribly and is not in the right place.
> It belongs in the application/libraries

Then you should kill parts of drivers/usb/audio - it contains format conversions.
--
I'm [email protected]. "In my country we have almost anarchy and I don't care."
Panos Katsaloulis describing me w.r.t. patents at [email protected]

2000-12-04 12:09:07

by Alan

[permalink] [raw]
Subject: Re: [PATCH] i810_audio 2.4.0-test11

> > Tjeerd. I deliberately applied only small bits of your patch before because
> > the mono mode stuff clutters the driver horribly and is not in the right place.
> > It belongs in the application/libraries
>
> Then you should kill parts of drivers/usb/audio - it contains format conversions.

Definitely we should

2000-12-04 14:16:39

by Thomas Sailer

[permalink] [raw]
Subject: Re: [PATCH] i810_audio 2.4.0-test11

Alan Cox wrote:

> Definitely we should

If you start killing format conversion then >99% of the
existing applications won't work anymore with usbaudio.
At that point you can dump the OSS interface just as well.

And before killing format conversion you should kill
the mmap stunt, because the format conversion complexity
(~25 LOC) is by far dwarfed by the mmap emulation stuff.

The underlying question is:

- do we want an usb audio driver that supports the OSS
interface and with which most existing applications work

- or do we want a simple driver with its own non-OSS
interface.

Anything in between is IMO silly. Killing the format
conversion drops the advantage of running many existing
applications but don't bring you much closer to the goal
of simplicity.

Tom

2000-12-04 17:09:27

by Jeff Garzik

[permalink] [raw]
Subject: Re: [PATCH] i810_audio 2.4.0-test11

On Mon, 4 Dec 2000, Thomas Sailer wrote:
> And before killing format conversion you should kill
> the mmap stunt, because the format conversion complexity
> (~25 LOC) is by far dwarfed by the mmap emulation stuff.

mmap -emulation- ?? Ug. Is Quake really worth that much? :)


> The underlying question is:
>
> - do we want an usb audio driver that supports the OSS
> interface and with which most existing applications work

of course

> Anything in between is IMO silly. Killing the format
> conversion drops the advantage of running many existing
> applications but don't bring you much closer to the goal
> of simplicity.

OSS has always implied that the software performs conversions when the
hardware cannot support certain formats. And the kernel direction has
always been to -remove- any software conversion code. We removed
SoftOSS, for example.

Jeff



2000-12-04 18:47:30

by Alan

[permalink] [raw]
Subject: Re: [PATCH] i810_audio 2.4.0-test11

> Anything in between is IMO silly. Killing the format
> conversion drops the advantage of running many existing
> applications but don't bring you much closer to the goal
> of simplicity.

Those applications already have to deal with the fact some devices only
support 48KHz 16bit stereo audio. I run a full desktop environment on such
hardware without problems.

What format is it that causes the problems, the only badly supported key format
right know I know of is 16bit bigendian. That needs some small esd patches.

Alan

2000-12-04 18:54:30

by Thomas Sailer

[permalink] [raw]
Subject: Re: [PATCH] i810_audio 2.4.0-test11

Alan Cox wrote:

> What format is it that causes the problems, the only badly supported key format
> right know I know of is 16bit bigendian. That needs some small esd patches.

S8 is a not very well supported format.

And btw there are many applications that cannot live with esd for
latency
reasons.

Tom