lkml.org 
[lkml]   [2000]   [Dec]   [1]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
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 <zab@redhat.com>
* 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 <tjeerd.mulder@fujitsu-siemens.com>
+ *
* 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");
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
Please read the FAQ at http://www.tux.org/lkml/
\
 
 \ /
  Last update: 2005-03-22 12:51    [W:0.079 / U:0.344 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site