static void ad1889_stop_wav(ad1889_state_t *state) { unsigned long flags; struct dmabuf *dmabuf = &state->dmabuf; spin_lock_irqsave(&state->card->lock, flags); if (dmabuf->enable & DAC_RUNNING) { u16 tmp; unsigned long cnt = dmabuf->dma_len; tmp = AD1889_READW(ad1889_dev, AD_DSWSMC); tmp &= ~0x0400; /* clear WAEN */ AD1889_WRITEW(ad1889_dev, AD_DSWSMC, tmp); (void) AD1889_READW(ad1889_dev, AD_DSWSMC); /* flush posted PCI write */ pci_unmap_single(ad1889_dev->pci, dmabuf->dma_handle, cnt, PCI_DMA_TODEVICE); dmabuf->enable &= ~DAC_RUNNING; /* update dma pointers */ dmabuf->rd_ptr += cnt; dmabuf->rd_ptr &= (DMA_SIZE - 1); dmabuf->dma_handle = 0; dmabuf->dma_len = 0; dmabuf->ready = 0; wake_up(&dmabuf->wait); } spin_unlock_irqrestore(&state->card->lock, flags); }
static void ad1889_start_wav(ad1889_state_t *state) { unsigned long flags; struct dmabuf *dmabuf = &state->dmabuf; int cnt; u16 tmp; spin_lock_irqsave(&state->card->lock, flags); if (dmabuf->dma_len) /* DMA already in flight */ goto skip_dma; /* setup dma */ cnt = dmabuf->wr_ptr - dmabuf->rd_ptr; if (cnt == 0) /* done - don't need to do anything */ goto skip_dma; /* If the wr_ptr has wrapped, only map to the end */ if (cnt < 0) cnt = DMA_SIZE - dmabuf->rd_ptr; dmabuf->dma_handle = pci_map_single(ad1889_dev->pci, dmabuf->rawbuf + dmabuf->rd_ptr, cnt, PCI_DMA_TODEVICE); 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); AD1889_WRITEL(ad1889_dev, AD_DMAWAVCA, dmabuf->dma_handle); /* TODO: for now we load the base registers with the same thing */ AD1889_WRITEL(ad1889_dev, AD_DMAWAVBC, cnt); AD1889_WRITEL(ad1889_dev, AD_DMAWAVIBC, cnt); AD1889_WRITEL(ad1889_dev, AD_DMAWAVBA, dmabuf->dma_handle); /* and we're off to the races... */ AD1889_WRITEL(ad1889_dev, AD_DMACHSS, 0x8); tmp = AD1889_READW(ad1889_dev, AD_DSWSMC); tmp |= 0x0400; /* set WAEN */ AD1889_WRITEW(ad1889_dev, AD_DSWSMC, tmp); (void) AD1889_READW(ad1889_dev, AD_DSWSMC); /* flush posted PCI write */ dmabuf->enable |= DAC_RUNNING; skip_dma: spin_unlock_irqrestore(&state->card->lock, flags); }
static void ad1889_initcfg(ad1889_dev_t *dev) { u16 tmp; /* 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); /* unmute... */ tmp = AD1889_READW(dev, AD_DSWADA); tmp &= ~0x8080; AD1889_WRITEW(dev, AD_DSWADA, tmp); }
static inline void ad1889_set_adc_fmt(ad1889_dev_t *dev, int fmt) { u16 tmp; tmp = AD1889_READW(ad1889_dev, AD_DSRAMC); if (fmt == AFMT_S16_LE) { tmp |= 0x0100; /* set WA16 */ } else if (fmt == AFMT_U8) { tmp &= ~0x0100; /* clear WA16 */ } AD1889_WRITEW(ad1889_dev, AD_DSRAMC, tmp); }
static inline void ad1889_set_adc_fmt(ad1889_dev_t *dev, int fmt) { u16 tmp; DBG("Setting ADC format to 0x%x\n", fmt); tmp = AD1889_READW(ad1889_dev, AD_DSRAMC); if (fmt & AFMT_S16_LE) { tmp |= 0x0100; /* set WA16 */ } else if (fmt & AFMT_U8) { tmp &= ~0x0100; /* clear WA16 */ } AD1889_WRITEW(ad1889_dev, AD_DSRAMC, tmp); }
static void ad1889_initcfg(ad1889_dev_t *dev) { u16 tmp16; u32 tmp32; /* make sure the interrupt bits are setup the way we want */ 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... */ tmp16 = AD1889_READW(dev, AD_DSWADA); tmp16 &= ~0x8080; AD1889_WRITEW(dev, AD_DSWADA, tmp16); }
static void ad1889_startstop_adc(ad1889_state_t *state, int start) { u16 tmp; unsigned long flags; spin_lock_irqsave(&state->card->lock, flags); tmp = AD1889_READW(ad1889_dev, AD_DSRAMC); if (start) { state->dmabuf.enable |= ADC_RUNNING; tmp |= 0x0004; /* set ADEN */ } else { state->dmabuf.enable &= ~ADC_RUNNING; tmp &= ~0x0004; /* clear ADEN */ } AD1889_WRITEW(ad1889_dev, AD_DSRAMC, tmp); spin_unlock_irqrestore(&state->card->lock, flags); }
static int ad1889_aclink_reset(struct pci_dev * pcidev) { u16 stat; int retry = 200; ad1889_dev_t *dev = pci_get_drvdata(pcidev); AD1889_WRITEW(dev, AD_DSCCS, 0x8000); /* turn on clock */ AD1889_READW(dev, AD_DSCCS); WAIT_10MS(); stat = AD1889_READW(dev, AD_ACIC); stat |= 0x0002; /* Reset Disable */ AD1889_WRITEW(dev, AD_ACIC, stat); (void) AD1889_READW(dev, AD_ACIC); /* flush posted write */ udelay(10); stat = AD1889_READW(dev, AD_ACIC); stat |= 0x0001; /* Interface Enable */ AD1889_WRITEW(dev, AD_ACIC, stat); do { if (AD1889_READW(dev, AD_ACIC) & 0x8000) /* Ready */ break; WAIT_10MS(); retry--; } while (retry > 0); if (!retry) { printk(KERN_ERR "ad1889_aclink_reset: codec is not ready [0x%x]\n", AD1889_READW(dev, AD_ACIC)); return -EBUSY; } /* TODO reset AC97 codec */ /* TODO set wave/adc pci ctrl status */ stat = AD1889_READW(dev, AD_ACIC); stat |= 0x0004; /* Audio Stream Output Enable */ AD1889_WRITEW(dev, AD_ACIC, stat); return 0; }
static u16 ad1889_codec_read(struct ac97_codec *ac97, u8 reg) { ad1889_dev_t *dev = ac97->private_data; //DBG("Reading from 0x%lx\n", dev->regbase + 0x100 + reg); return AD1889_READW(dev, 0x100 + reg); }
static int ad1889_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int val = 0; ad1889_dev_t *dev = (ad1889_dev_t *)file->private_data; struct dmabuf *dmabuf; audio_buf_info abinfo; switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, (int *)arg); case SNDCTL_DSP_RESET: break; case SNDCTL_DSP_SYNC: break; case SNDCTL_DSP_SPEED: /* set sampling rate */ if (get_user(val, (int *)arg)) return -EFAULT; if (val > 5400 && val < 48000) { if (file->f_mode & FMODE_WRITE) AD1889_WRITEW(ad1889_dev, AD_DSWAS, val); if (file->f_mode & FMODE_READ) AD1889_WRITEW(ad1889_dev, AD_DSRES, val); } return 0; case SNDCTL_DSP_STEREO: /* undocumented? */ if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { val = AD1889_READW(ad1889_dev, AD_DSWSMC); if (val) { val |= 0x0200; /* set WAST */ } else { val &= ~0x0200; /* clear WAST */ } AD1889_WRITEW(ad1889_dev, AD_DSWSMC, val); } if (file->f_mode & FMODE_WRITE) { val = AD1889_READW(ad1889_dev, AD_DSRAMC); if (val) { val |= 0x0002; /* set ADST */ } else { val &= ~0x0002; /* clear ADST */ } AD1889_WRITEW(ad1889_dev, AD_DSRAMC, val); } return 0; case SNDCTL_DSP_GETBLKSIZE: return put_user(DMA_SIZE, (int *)arg); case SNDCTL_DSP_GETFMTS: return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); case SNDCTL_DSP_SETFMT: if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_READ) ad1889_set_adc_fmt(dev, val); if (file->f_mode & FMODE_WRITE) ad1889_set_wav_fmt(dev, val); return put_user(val, (int *)arg); case SNDCTL_DSP_CHANNELS: break; case SNDCTL_DSP_POST: /* send all data to device */ break; case SNDCTL_DSP_SUBDIVIDE: break; case SNDCTL_DSP_SETFRAGMENT: /* not supported; uses fixed fragment sizes */ return put_user(DMA_SIZE, (int *)arg); case SNDCTL_DSP_GETOSPACE: case SNDCTL_DSP_GETISPACE: /* space left in dma buffers */ if (cmd == SNDCTL_DSP_GETOSPACE) dmabuf = &dev->state[AD_WAV_STATE].dmabuf; else dmabuf = &dev->state[AD_ADC_STATE].dmabuf; abinfo.fragments = 1; abinfo.fragstotal = 1; abinfo.fragsize = DMA_SIZE; abinfo.bytes = DMA_SIZE; return copy_to_user((void *)arg, &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); case SNDCTL_DSP_GETTRIGGER: case SNDCTL_DSP_SETTRIGGER: break; case SNDCTL_DSP_GETIPTR: case SNDCTL_DSP_GETOPTR: break; case SNDCTL_DSP_SETDUPLEX: break; case SNDCTL_DSP_GETODELAY: break; case SOUND_PCM_READ_RATE: return put_user(AD1889_READW(ad1889_dev, AD_DSWAS), (int *)arg); case SOUND_PCM_READ_CHANNELS: case SOUND_PCM_READ_BITS: break; case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: case SOUND_PCM_WRITE_FILTER: case SOUND_PCM_READ_FILTER: break; default: break; } return -ENOTTY; }
int ad1889_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) { char *out = page; int len, i; ad1889_dev_t *dev = data; ad1889_reg_t regs[] = { { "WSMC", AD_DSWSMC, 16 }, { "RAMC", AD_DSRAMC, 16 }, { "WADA", AD_DSWADA, 16 }, { "SYDA", AD_DSSYDA, 16 }, { "WAS", AD_DSWAS, 16 }, { "RES", AD_DSRES, 16 }, { "CCS", AD_DSCCS, 16 }, { "ADCBA", AD_DMAADCBA, 32 }, { "ADCCA", AD_DMAADCCA, 32 }, { "ADCBC", AD_DMAADCBC, 32 }, { "ADCCC", AD_DMAADCCC, 32 }, { "ADCIBC", AD_DMAADCIBC, 32 }, { "ADCICC", AD_DMAADCICC, 32 }, { "ADCCTRL", AD_DMAADCCTRL, 16 }, { "WAVBA", AD_DMAWAVBA, 32 }, { "WAVCA", AD_DMAWAVCA, 32 }, { "WAVBC", AD_DMAWAVBC, 32 }, { "WAVCC", AD_DMAWAVCC, 32 }, { "WAVIBC", AD_DMAWAVIBC, 32 }, { "WAVICC", AD_DMAWAVICC, 32 }, { "WAVCTRL", AD_DMAWAVCTRL, 16 }, { "DISR", AD_DMADISR, 32 }, { "CHSS", AD_DMACHSS, 32 }, { "IPC", AD_GPIOIPC, 16 }, { "OP", AD_GPIOOP, 16 }, { "IP", AD_GPIOIP, 16 }, { "ACIC", AD_ACIC, 16 }, { "AC97_RESET", 0x100 + AC97_RESET, 16 }, { "AC97_MASTER_VOL_STEREO", 0x100 + AC97_MASTER_VOL_STEREO, 16 }, { "AC97_HEADPHONE_VOL", 0x100 + AC97_HEADPHONE_VOL, 16 }, { "AC97_MASTER_VOL_MONO", 0x100 + AC97_MASTER_VOL_MONO, 16 }, { "AC97_MASTER_TONE", 0x100 + AC97_MASTER_TONE, 16 }, { "AC97_PCBEEP_VOL", 0x100 + AC97_PCBEEP_VOL, 16 }, { "AC97_PHONE_VOL", 0x100 + AC97_PHONE_VOL, 16 }, { "AC97_MIC_VOL", 0x100 + AC97_MIC_VOL, 16 }, { "AC97_LINEIN_VOL", 0x100 + AC97_LINEIN_VOL, 16 }, { "AC97_CD_VOL", 0x100 + AC97_CD_VOL, 16 }, { "AC97_VIDEO_VOL", 0x100 + AC97_VIDEO_VOL, 16 }, { "AC97_AUX_VOL", 0x100 + AC97_AUX_VOL, 16 }, { "AC97_PCMOUT_VOL", 0x100 + AC97_PCMOUT_VOL, 16 }, { "AC97_RECORD_SELECT", 0x100 + AC97_RECORD_SELECT, 16 }, { "AC97_RECORD_GAIN", 0x100 + AC97_RECORD_GAIN, 16 }, { "AC97_RECORD_GAIN_MIC", 0x100 + AC97_RECORD_GAIN_MIC, 16 }, { "AC97_GENERAL_PURPOSE", 0x100 + AC97_GENERAL_PURPOSE, 16 }, { "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 } }; if (dev == NULL) return -ENODEV; for (i = 0; regs[i].name != 0; i++) out += sprintf(out, "%s: 0x%0*x\n", regs[i].name, regs[i].width >> 2, (regs[i].width == 16 ? AD1889_READW(dev, regs[i].offset) : AD1889_READL(dev, regs[i].offset))); for (i = 0; i < AD_MAX_STATES; i++) { out += sprintf(out, "DMA status for %s:\n", (i == AD_WAV_STATE ? "WAV" : "ADC")); out += sprintf(out, "\t\t0x%p (IOVA: 0x%u)\n", dev->state[i].dmabuf.rawbuf, dev->state[i].dmabuf.dma_handle); out += sprintf(out, "\tread ptr: offset %u\n", (unsigned int)dev->state[i].dmabuf.rd_ptr); out += sprintf(out, "\twrite ptr: offset %u\n", (unsigned int)dev->state[i].dmabuf.wr_ptr); out += sprintf(out, "\tdma len: offset %u\n", (unsigned int)dev->state[i].dmabuf.dma_len); } len = out - page - off; if (len < count) { *eof = 1; if (len <= 0) return 0; } else { len = count; } *start = page + off; return len; }