/*
- * Copyright 2001 Randolph Chung <tausq@debian.org>
+ * Copyright 2001-2004 Randolph Chung <tausq@debian.org>
*
* Analog Devices 1889 PCI audio driver (AD1819 AC97-compatible codec)
*
#define AD1889_WRITEL(dev,reg,val) writel((val), dev->regbase + reg)
//now 100ms
+/* #define WAIT_10MS() schedule_timeout(HZ/10) */
#define WAIT_10MS() do { int __i; for (__i = 0; __i < 100; __i++) udelay(1000); } while(0)
/* currently only support a single device */
/************************* helper routines ***************************** */
static inline void ad1889_set_wav_rate(ad1889_dev_t *dev, int rate)
{
+ struct ac97_codec *ac97_codec = dev->ac97_codec;
+
+ DBG("Setting WAV rate to %d\n", rate);
dev->state[AD_WAV_STATE].dmabuf.rate = rate;
AD1889_WRITEW(dev, AD_DSWAS, rate);
-}
-static inline void ad1889_set_adc_rate(ad1889_dev_t *dev, int rate)
-{
- dev->state[AD_ADC_STATE].dmabuf.rate = rate;
- AD1889_WRITEW(dev, AD_DSRES, rate);
+ /* Cycle the DAC to enable the new rate */
+ ac97_codec->codec_write(dev->ac97_codec, AC97_POWER_CONTROL, 0x0200);
+ WAIT_10MS();
+ ac97_codec->codec_write(dev->ac97_codec, AC97_POWER_CONTROL, 0);
}
static inline void ad1889_set_wav_fmt(ad1889_dev_t *dev, int fmt)
{
u16 tmp;
+ DBG("Setting WAV format to 0x%x\n", fmt);
+
tmp = AD1889_READW(ad1889_dev, AD_DSWSMC);
- if (fmt == AFMT_S16_LE) {
+ if (fmt & AFMT_S16_LE) {
//tmp |= 0x0100; /* set WA16 */
tmp |= 0x0300; /* set WA16 stereo */
- } else if (fmt == AFMT_U8) {
+ } else if (fmt & AFMT_U8) {
tmp &= ~0x0100; /* clear WA16 */
}
AD1889_WRITEW(ad1889_dev, AD_DSWSMC, tmp);
{
u16 tmp;
+ DBG("Setting ADC format to 0x%x\n", fmt);
+
tmp = AD1889_READW(ad1889_dev, AD_DSRAMC);
- if (fmt == AFMT_S16_LE) {
+ if (fmt & AFMT_S16_LE) {
tmp |= 0x0100; /* set WA16 */
- } else if (fmt == AFMT_U8) {
+ } else if (fmt & AFMT_U8) {
tmp &= ~0x0100; /* clear WA16 */
}
AD1889_WRITEW(ad1889_dev, AD_DSRAMC, tmp);
dmabuf->dma_len = cnt;
dmabuf->ready = 1;
+ DBG("Starting playback at 0x%p for %ld bytes\n", dmabuf->rawbuf +
+ dmabuf->rd_ptr, dmabuf->dma_len);
+
/* load up the current register set */
AD1889_WRITEL(ad1889_dev, AD_DMAWAVCC, cnt);
AD1889_WRITEL(ad1889_dev, AD_DMAWAVICC, cnt);
dmabuf->dma_handle = 0;
dmabuf->rd_ptr = dmabuf->wr_ptr = dmabuf->dma_len = 0UL;
dmabuf->ready = 0;
- dmabuf->rate = 44100;
+ dmabuf->rate = 48000;
}
return dev;
ad1889_start_wav(&dev->state[AD_WAV_STATE]);
}
-int ad1889_read_proc (char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int ad1889_read_proc (char *page, char **start, off_t off,
+ int count, int *eof, void *data)
{
char *out = page;
int len, i;
{ "AC97_3D_CONTROL", 0x100 + AC97_3D_CONTROL, 16 },
{ "AC97_MODEM_RATE", 0x100 + AC97_MODEM_RATE, 16 },
{ "AC97_POWER_CONTROL", 0x100 + AC97_POWER_CONTROL, 16 },
- { 0 }
+ { NULL }
};
if (dev == NULL)
/************************* /dev/dsp interfaces ************************* */
-static ssize_t ad1889_read(struct file *file, char *buffer, size_t count,
+static ssize_t ad1889_read(struct file *file, char __user *buffer, size_t count,
loff_t *ppos)
{
return 0;
}
-static ssize_t ad1889_write(struct file *file, const char *buffer, size_t count,
+static ssize_t ad1889_write(struct file *file, const char __user *buffer, size_t count,
loff_t *ppos)
{
ad1889_dev_t *dev = (ad1889_dev_t *)file->private_data;
ssize_t ret = 0;
DECLARE_WAITQUEUE(wait, current);
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
down(&state->sem);
#if 0
if (dmabuf->mapped) {
long cnt = count;
unsigned long flags;
-
for (;;) {
long used_bytes;
long timeout; /* max time for DMA in jiffies */
}
set_current_state(TASK_INTERRUPTIBLE);
- if (!schedule_timeout(timeout + 1))
- printk(KERN_WARNING "AD1889 timeout(%ld) r/w %lx/%lx len %lx\n",
- timeout+1,
- dmabuf->rd_ptr, dmabuf->wr_ptr,
- dmabuf->dma_len);
-
+ schedule_timeout(timeout + 1);
if (signal_pending(current)) {
ret = -ERESTARTSYS;
goto err2;
}
-
}
/* watch out for wrapping around static buffer */
ad1889_dev_t *dev = (ad1889_dev_t *)file->private_data;
struct dmabuf *dmabuf;
audio_buf_info abinfo;
+ int __user *p = (int __user *)arg;
+
+ DBG("ad1889_ioctl cmd 0x%x arg %lu\n", cmd, arg);
switch (cmd)
{
case OSS_GETVERSION:
- return put_user(SOUND_VERSION, (int *)arg);
+ return put_user(SOUND_VERSION, p);
case SNDCTL_DSP_RESET:
break;
case SNDCTL_DSP_SPEED:
/* set sampling rate */
- if (get_user(val, (int *)arg))
+ if (get_user(val, p))
return -EFAULT;
if (val > 5400 && val < 48000)
{
return 0;
case SNDCTL_DSP_STEREO: /* undocumented? */
- if (get_user(val, (int *)arg))
+ if (get_user(val, p))
return -EFAULT;
if (file->f_mode & FMODE_READ) {
val = AD1889_READW(ad1889_dev, AD_DSWSMC);
return 0;
case SNDCTL_DSP_GETBLKSIZE:
- return put_user(DMA_SIZE, (int *)arg);
+ return put_user(DMA_SIZE, p);
case SNDCTL_DSP_GETFMTS:
- return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+ return put_user(AFMT_S16_LE|AFMT_U8, p);
case SNDCTL_DSP_SETFMT:
- if (get_user(val, (int *)arg))
+ if (get_user(val, p))
return -EFAULT;
- if (file->f_mode & FMODE_READ)
- ad1889_set_adc_fmt(dev, val);
+ if (val == 0) {
+ if (file->f_mode & FMODE_READ)
+ ad1889_set_adc_fmt(dev, val);
- if (file->f_mode & FMODE_WRITE)
- ad1889_set_wav_fmt(dev, val);
+ if (file->f_mode & FMODE_WRITE)
+ ad1889_set_wav_fmt(dev, val);
+ } else {
+ val = AFMT_S16_LE | AFMT_U8;
+ }
- return put_user(val, (int *)arg);
+ return put_user(val, p);
case SNDCTL_DSP_CHANNELS:
break;
case SNDCTL_DSP_SETFRAGMENT:
/* not supported; uses fixed fragment sizes */
- return put_user(DMA_SIZE, (int *)arg);
+ return put_user(DMA_SIZE, p);
case SNDCTL_DSP_GETOSPACE:
case SNDCTL_DSP_GETISPACE:
abinfo.fragstotal = 1;
abinfo.fragsize = DMA_SIZE;
abinfo.bytes = DMA_SIZE;
- return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+ return copy_to_user(p, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_NONBLOCK:
file->f_flags |= O_NONBLOCK;
return 0;
case SNDCTL_DSP_GETCAPS:
- return put_user(0, (int *)arg);
+ return put_user(0, p);
case SNDCTL_DSP_GETTRIGGER:
case SNDCTL_DSP_SETTRIGGER:
break;
case SOUND_PCM_READ_RATE:
- return put_user(AD1889_READW(ad1889_dev, AD_DSWAS), (int *)arg);
+ return put_user(AD1889_READW(ad1889_dev, AD_DSWAS), p);
case SOUND_PCM_READ_CHANNELS:
case SOUND_PCM_READ_BITS:
file->private_data = ad1889_dev;
- ad1889_set_wav_rate(ad1889_dev, 44100);
+ ad1889_set_wav_rate(ad1889_dev, 48000);
ad1889_set_wav_fmt(ad1889_dev, AFMT_S16_LE);
AD1889_WRITEW(ad1889_dev, AD_DSWADA, 0x0404); /* attenuation */
- return 0;
+ return nonseekable_open(inode, file);
}
static int ad1889_release(struct inode *inode, struct file *file)
}
eid = ad1889_codec_read(ac97, AC97_EXTENDED_ID);
- if (eid == 0xffffff) {
+ if (eid == 0xffff) {
printk(KERN_WARNING DEVNAME ": no codec attached?\n");
goto out_free;
}
ad1889_stop_wav(&dev->state[AD_WAV_STATE]); /* clean up */
ad1889_start_wav(&dev->state[AD_WAV_STATE]); /* start new */
}
-
}
if ((stat & 0x2) && dev->state[AD_ADC_STATE].dmabuf.ready) { /* ADCI */
static void ad1889_initcfg(ad1889_dev_t *dev)
{
- u16 tmp;
+ u16 tmp16;
+ u32 tmp32;
/* make sure the interrupt bits are setup the way we want */
- tmp = AD1889_READW(dev, AD_DMAWAVCTRL);
- tmp &= ~0x00ff; /* flat dma, no sg, mask out the intr bits */
- tmp |= 0x0004; /* intr on count, loop */
- AD1889_WRITEW(dev, AD_DMAWAVCTRL, tmp);
+ tmp32 = AD1889_READL(dev, AD_DMAWAVCTRL);
+ tmp32 &= ~0xff; /* flat dma, no sg, mask out the intr bits */
+ tmp32 |= 0x6; /* intr on count, loop */
+ AD1889_WRITEL(dev, AD_DMAWAVCTRL, tmp32);
/* unmute... */
- tmp = AD1889_READW(dev, AD_DSWADA);
- tmp &= ~0x8080;
- AD1889_WRITEW(dev, AD_DSWADA, tmp);
+ tmp16 = AD1889_READW(dev, AD_DSWADA);
+ tmp16 &= ~0x8080;
+ AD1889_WRITEW(dev, AD_DSWADA, tmp16);
}
static int __devinit ad1889_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_MEM)) {
printk(KERN_ERR DEVNAME ": memory region not assigned\n");
- goto err_free_mem;
+ goto out1;
+ }
+
+ if (pci_request_region(pcidev, 0, DEVNAME)) {
+ printk(KERN_ERR DEVNAME ": unable to request memory region\n");
+ goto out1;
+ }
+
+ dev->regbase = ioremap_nocache(bar, AD_DSIOMEMSIZE);
+ if (!dev->regbase) {
+ printk(KERN_ERR DEVNAME ": unable to remap iomem\n");
+ goto out2;
}
if (request_irq(pcidev->irq, ad1889_interrupt, SA_SHIRQ, DEVNAME, dev) != 0) {
printk(KERN_ERR DEVNAME ": unable to request interrupt\n");
- goto err_free_mem;
+ goto out3;
}
- request_mem_region(bar, AD_DSIOMEMSIZE, DEVNAME);
- dev->regbase = (unsigned long)ioremap_nocache(bar, AD_DSIOMEMSIZE);
-
- printk(KERN_INFO DEVNAME ": %s at 0x%lx IRQ %d\n",
+ printk(KERN_INFO DEVNAME ": %s at %p IRQ %d\n",
(char *)ent->driver_data, dev->regbase, pcidev->irq);
if (ad1889_aclink_reset(pcidev) != 0)
- goto err_free_mem;
+ goto out4;
/* register /dev/dsp */
if ((dev->dev_audio = register_sound_dsp(&ad1889_fops, -1)) < 0) {
printk(KERN_ERR DEVNAME ": cannot register /dev/dsp\n");
- goto err_free_irq;
+ goto out4;
}
if ((err = ad1889_ac97_init(dev, 0)) != 0)
- goto err_free_dsp;
+ goto out5;
- if (((proc_root = proc_mkdir("driver/ad1889", 0)) == NULL) ||
+ /* XXX: cleanups */
+ if (((proc_root = proc_mkdir("driver/ad1889", NULL)) == NULL) ||
create_proc_read_entry("ac97", S_IFREG|S_IRUGO, proc_root, ac97_read_proc, dev->ac97_codec) == NULL ||
create_proc_read_entry("info", S_IFREG|S_IRUGO, proc_root, ad1889_read_proc, dev) == NULL)
- goto err_free_dsp;
+ goto out5;
ad1889_initcfg(dev);
return 0;
-err_free_dsp:
+out5:
unregister_sound_dsp(dev->dev_audio);
-
-err_free_irq:
+out4:
free_irq(pcidev->irq, dev);
-
-err_free_mem:
+out3:
+ iounmap(dev->regbase);
+out2:
+ pci_release_region(pcidev, 0);
+out1:
ad1889_free_dev(dev);
- pci_set_drvdata(pcidev, 0);
+ pci_set_drvdata(pcidev, NULL);
return -ENODEV;
}
unregister_sound_mixer(dev->ac97_codec->dev_mixer);
unregister_sound_dsp(dev->dev_audio);
free_irq(pcidev->irq, dev);
- release_mem_region(dev->regbase, AD_DSIOMEMSIZE);
+ iounmap(dev->regbase);
+ pci_release_region(pcidev, 0);
/* any hw programming needed? */
ad1889_free_dev(dev);
+ pci_set_drvdata(pcidev, NULL);
}
MODULE_AUTHOR("Randolph Chung");