/*
* Copyright 2001-2004 Randolph Chung <tausq@debian.org>
*
* Analog Devices 1889 PCI audio driver (AD1819 AC97-compatible codec)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Notes:
* 1. Only flat DMA is supported; s-g is not supported right now
*
*
<jsm> tausq: Anyway, to set up sample rates for D to A, you just use the sample rate on the codec. For A to D, you need to set the codec always to 48K (using the split sample rate feature on the codec) and then set the resampler on the AD1889 to the sample rate you want.
<jsm> Also, when changing the sample rate on the codec you need to power it down and re power it up for the change to take effect!
*
* $Id: ad1889.c,v 1.3 2002/10/19 21:31:44 grundler Exp $
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/soundcard.h>
#include <linux/ac97_codec.h>
#include <linux/sound.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <asm/delay.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/uaccess.h>
#include "ad1889.h"
#define DBG(fmt, arg...) printk(fmt, ##arg)
#define DEVNAME "ad1889"
#define NR_HW_CH 4
#define DAC_RUNNING 1
#define ADC_RUNNING 2
#define UNDERRUN(dev) (0)
#define AD1889_READW(dev,reg) readw(dev->regbase + reg)
#define AD1889_WRITEW(dev,reg,val) writew((val), dev->regbase + reg)
#define AD1889_READL(dev,reg) readl(dev->regbase + reg)
#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 */
static ad1889_dev_t *ad1889_dev = NULL;
/************************* 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_DS_WAS, 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_DS_WSMC);
if (fmt & AFMT_S16_LE) {
//tmp |= 0x0100; /* set WA16 */
tmp |= 0x0300; /* set WA16 stereo */
} else if (fmt & AFMT_U8) {
tmp &= ~0x0100; /* clear WA16 */
}
AD1889_WRITEW(ad1889_dev, AD_DS_WSMC, 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_DS_RAMC);
if (fmt & AFMT_S16_LE) {
tmp |= 0x0100; /* set WA16 */
} else if (fmt & AFMT_U8) {
tmp &= ~0x0100; /* clear WA16 */
}
AD1889_WRITEW(ad1889_dev, AD_DS_RAMC, tmp);
}
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_WRIT