#include <linux/slab.h>
#include <linux/soundcard.h>
#include <linux/init.h>
+#include <linux/page-flags.h>
#include <linux/poll.h>
#include <linux/pci.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/smp_lock.h>
#include <linux/ac97_codec.h>
-#include <linux/wrapper.h>
#include <linux/interrupt.h>
+#include <linux/mutex.h>
+
#include <asm/io.h>
#include <asm/uaccess.h>
-#include <asm/au1000.h>
-#include <asm/au1000_dma.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1000_dma.h>
/* --------------------------------------------------------------------- */
#undef OSS_DOCUMENTED_MIXER_SEMANTICS
-#define AU1000_DEBUG
+#undef AU1000_DEBUG
#undef AU1000_VERBOSE_DEBUG
-#define USE_COHERENT_DMA
-
#define AU1000_MODULE_NAME "Au1000 audio"
#define PFX AU1000_MODULE_NAME
/* Boot options */
static int vra = 0; // 0 = no VRA, 1 = use VRA if codec supports it
-MODULE_PARM(vra, "i");
+module_param(vra, bool, 0);
MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");
struct proc_dir_entry *ac97_ps;
#endif /* AU1000_DEBUG */
- struct ac97_codec *codec;
+ struct ac97_codec codec;
unsigned codec_base_caps;// AC'97 reg 00h, "Reset Register"
unsigned codec_ext_caps; // AC'97 reg 28h, "Extended Audio ID"
int no_vra; // do not use VRA
spinlock_t lock;
- struct semaphore open_sem;
- struct semaphore sem;
+ struct mutex open_mutex;
+ struct mutex sem;
mode_t open_mode;
wait_queue_head_t open_wait;
return r;
}
-
-#ifdef USE_COHERENT_DMA
-static inline void * dma_alloc(size_t size, dma_addr_t * dma_handle)
-{
- void* ret = (void *)__get_free_pages(GFP_ATOMIC | GFP_DMA,
- get_order(size));
- if (ret != NULL) {
- memset(ret, 0, size);
- *dma_handle = virt_to_phys(ret);
- }
- return ret;
-}
-
-static inline void dma_free(size_t size, void* va, dma_addr_t dma_handle)
-{
- free_pages((unsigned long)va, get_order(size));
-}
-#else
-static inline void * dma_alloc(size_t size, dma_addr_t * dma_handle)
-{
- return pci_alloc_consistent(NULL, size, dma_handle);
-}
-
-static inline void dma_free(size_t size, void* va, dma_addr_t dma_handle)
-{
- pci_free_consistent(NULL, size, va, dma_handle);
-}
-#endif
-
/* --------------------------------------------------------------------- */
static void au1000_delay(int msec)
adc->src_factor = 1;
- ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
+ ac97_extstat = rdcodec(&s->codec, AC97_EXTENDED_STATUS);
rate = rate > 48000 ? 48000 : rate;
// enable VRA
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
+ wrcodec(&s->codec, AC97_EXTENDED_STATUS,
ac97_extstat | AC97_EXTSTAT_VRA);
// now write the sample rate
- wrcodec(s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate);
+ wrcodec(&s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate);
// read it back for actual supported rate
- adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
+ adc_rate = rdcodec(&s->codec, AC97_PCM_LR_ADC_RATE);
#ifdef AU1000_VERBOSE_DEBUG
dbg("%s: set to %d Hz", __FUNCTION__, adc_rate);
// some codec's don't allow unequal DAC and ADC rates, in which case
// writing one rate reg actually changes both.
- dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
+ dac_rate = rdcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE);
if (dac->num_channels > 2)
- wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, dac_rate);
+ wrcodec(&s->codec, AC97_PCM_SURR_DAC_RATE, dac_rate);
if (dac->num_channels > 4)
- wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, dac_rate);
+ wrcodec(&s->codec, AC97_PCM_LFE_DAC_RATE, dac_rate);
adc->sample_rate = adc_rate;
dac->sample_rate = dac_rate;
dac->src_factor = 1;
- ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
+ ac97_extstat = rdcodec(&s->codec, AC97_EXTENDED_STATUS);
rate = rate > 48000 ? 48000 : rate;
// enable VRA
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
+ wrcodec(&s->codec, AC97_EXTENDED_STATUS,
ac97_extstat | AC97_EXTSTAT_VRA);
// now write the sample rate
- wrcodec(s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate);
+ wrcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate);
// I don't support different sample rates for multichannel,
// so make these channels the same.
if (dac->num_channels > 2)
- wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate);
+ wrcodec(&s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate);
if (dac->num_channels > 4)
- wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate);
+ wrcodec(&s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate);
// read it back for actual supported rate
- dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
+ dac_rate = rdcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE);
#ifdef AU1000_VERBOSE_DEBUG
dbg("%s: set to %d Hz", __FUNCTION__, dac_rate);
// some codec's don't allow unequal DAC and ADC rates, in which case
// writing one rate reg actually changes both.
- adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
+ adc_rate = rdcodec(&s->codec, AC97_PCM_LR_ADC_RATE);
dac->sample_rate = dac_rate;
adc->sample_rate = adc_rate;
#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT)
#define DMABUF_MINORDER 1
-extern inline void dealloc_dmabuf(struct au1000_state *s, struct dmabuf *db)
+static inline void dealloc_dmabuf(struct au1000_state *s, struct dmabuf *db)
{
struct page *page, *pend;
pend = virt_to_page(db->rawbuf +
(PAGE_SIZE << db->buforder) - 1);
for (page = virt_to_page(db->rawbuf); page <= pend; page++)
- mem_map_unreserve(page);
- dma_free(PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr);
+ ClearPageReserved(page);
+ dma_free_noncoherent(NULL,
+ PAGE_SIZE << db->buforder,
+ db->rawbuf,
+ db->dmaaddr);
}
db->rawbuf = db->nextIn = db->nextOut = NULL;
db->mapped = db->ready = 0;
db->ready = db->mapped = 0;
for (order = DMABUF_DEFAULTORDER;
order >= DMABUF_MINORDER; order--)
- if ((db->rawbuf = dma_alloc(PAGE_SIZE << order,
- &db->dmaaddr)))
+ if ((db->rawbuf = dma_alloc_noncoherent(NULL,
+ PAGE_SIZE << order,
+ &db->dmaaddr,
+ 0)))
break;
if (!db->rawbuf)
return -ENOMEM;
db->buforder = order;
/* now mark the pages as reserved;
- otherwise remap_page_range doesn't do what we want */
+ otherwise remap_pfn_range doesn't do what we want */
pend = virt_to_page(db->rawbuf +
(PAGE_SIZE << db->buforder) - 1);
for (page = virt_to_page(db->rawbuf); page <= pend; page++)
- mem_map_reserve(page);
+ SetPageReserved(page);
}
db->cnt_factor = 1;
return 0;
}
-extern inline int prog_dmabuf_adc(struct au1000_state *s)
+static inline int prog_dmabuf_adc(struct au1000_state *s)
{
stop_adc(s);
return prog_dmabuf(s, &s->dma_adc);
}
-extern inline int prog_dmabuf_dac(struct au1000_state *s)
+static inline int prog_dmabuf_dac(struct au1000_state *s)
{
stop_dac(s);
return prog_dmabuf(s, &s->dma_dac);
/* hold spinlock for the following */
-static void dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct au1000_state *s = (struct au1000_state *) dev_id;
struct dmabuf *dac = &s->dma_dac;
if ((buff_done = get_dma_buffer_done(dac->dmanr)) == 0) {
/* fastpath out, to ease interrupt sharing */
- return;
+ return IRQ_HANDLED;
}
spin_lock(&s->lock);
wake_up(&dac->wait);
spin_unlock(&s->lock);
+
+ return IRQ_HANDLED;
}
-static void adc_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t adc_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct au1000_state *s = (struct au1000_state *) dev_id;
struct dmabuf *adc = &s->dma_adc;
if ((buff_done = get_dma_buffer_done(adc->dmanr)) == 0) {
/* fastpath out, to ease interrupt sharing */
- return;
+ return IRQ_HANDLED;
}
spin_lock(&s->lock);
stop_adc(s);
adc->error++;
err("adc overrun");
- return;
+ return IRQ_NONE;
}
adc->nextIn += adc->dma_fragsize;
adc->error++;
err("adc overrun");
spin_unlock(&s->lock);
- return;
+ return IRQ_NONE;
}
adc->nextIn += 2*adc->dma_fragsize;
wake_up(&adc->wait);
spin_unlock(&s->lock);
+
+ return IRQ_HANDLED;
}
/* --------------------------------------------------------------------- */
unsigned int cmd, unsigned long arg)
{
struct au1000_state *s = (struct au1000_state *)file->private_data;
- struct ac97_codec *codec = s->codec;
+ struct ac97_codec *codec = &s->codec;
return mixdev_ioctl(codec, cmd, arg);
}
count *= db->cnt_factor;
- down(&s->sem);
+ mutex_lock(&s->sem);
add_wait_queue(&db->wait, &wait);
while (count > 0) {
ret = -EAGAIN;
goto out;
}
- up(&s->sem);
+ mutex_unlock(&s->sem);
schedule();
if (signal_pending(current)) {
if (!ret)
ret = -ERESTARTSYS;
goto out2;
}
- down(&s->sem);
+ mutex_lock(&s->sem);
}
} while (avail <= 0);
} // while (count > 0)
out:
- up(&s->sem);
+ mutex_unlock(&s->sem);
out2:
remove_wait_queue(&db->wait, &wait);
set_current_state(TASK_RUNNING);
count *= db->cnt_factor;
- down(&s->sem);
+ mutex_lock(&s->sem);
add_wait_queue(&db->wait, &wait);
while (count > 0) {
ret = -EAGAIN;
goto out;
}
- up(&s->sem);
+ mutex_unlock(&s->sem);
schedule();
if (signal_pending(current)) {
if (!ret)
ret = -ERESTARTSYS;
goto out2;
}
- down(&s->sem);
+ mutex_lock(&s->sem);
}
} while (avail <= 0);
} // while (count > 0)
out:
- up(&s->sem);
+ mutex_unlock(&s->sem);
out2:
remove_wait_queue(&db->wait, &wait);
set_current_state(TASK_RUNNING);
unsigned long size;
int ret = 0;
- dbg(__FUNCTION__);
+ dbg("%s", __FUNCTION__);
lock_kernel();
- down(&s->sem);
+ mutex_lock(&s->sem);
if (vma->vm_flags & VM_WRITE)
db = &s->dma_dac;
else if (vma->vm_flags & VM_READ)
ret = -EINVAL;
goto out;
}
- if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf),
+ if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(db->rawbuf),
size, vma->vm_page_prot)) {
ret = -EAGAIN;
goto out;
vma->vm_flags &= ~VM_IO;
db->mapped = 1;
out:
- up(&s->sem);
+ mutex_unlock(&s->sem);
unlock_kernel();
return ret;
}
s->dma_dac.num_channels = val ? 2 : 1;
if (s->codec_ext_caps & AC97_EXT_DACS) {
// disable surround and center/lfe in AC'97
- u16 ext_stat = rdcodec(s->codec,
+ u16 ext_stat = rdcodec(&s->codec,
AC97_EXTENDED_STATUS);
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
+ wrcodec(&s->codec, AC97_EXTENDED_STATUS,
ext_stat | (AC97_EXTSTAT_PRI |
AC97_EXTSTAT_PRJ |
AC97_EXTSTAT_PRK));
// disable surround and center/lfe
// channels in AC'97
u16 ext_stat =
- rdcodec(s->codec,
+ rdcodec(&s->codec,
AC97_EXTENDED_STATUS);
- wrcodec(s->codec,
+ wrcodec(&s->codec,
AC97_EXTENDED_STATUS,
ext_stat | (AC97_EXTSTAT_PRI |
AC97_EXTSTAT_PRJ |
// enable surround, center/lfe
// channels in AC'97
u16 ext_stat =
- rdcodec(s->codec,
+ rdcodec(&s->codec,
AC97_EXTENDED_STATUS);
ext_stat &= ~AC97_EXTSTAT_PRJ;
if (val == 6)
ext_stat &=
~(AC97_EXTSTAT_PRI |
AC97_EXTSTAT_PRK);
- wrcodec(s->codec,
+ wrcodec(&s->codec,
AC97_EXTENDED_STATUS,
ext_stat);
}
return -EINVAL;
}
- return mixdev_ioctl(s->codec, cmd, arg);
+ return mixdev_ioctl(&s->codec, cmd, arg);
}
file->private_data = s;
/* wait for device to become free */
- down(&s->open_sem);
+ mutex_lock(&s->open_mutex);
while (s->open_mode & file->f_mode) {
if (file->f_flags & O_NONBLOCK) {
- up(&s->open_sem);
+ mutex_unlock(&s->open_mutex);
return -EBUSY;
}
add_wait_queue(&s->open_wait, &wait);
__set_current_state(TASK_INTERRUPTIBLE);
- up(&s->open_sem);
+ mutex_unlock(&s->open_mutex);
schedule();
remove_wait_queue(&s->open_wait, &wait);
set_current_state(TASK_RUNNING);
if (signal_pending(current))
return -ERESTARTSYS;
- down(&s->open_sem);
+ mutex_lock(&s->open_mutex);
}
stop_dac(s);
}
s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- up(&s->open_sem);
- init_MUTEX(&s->sem);
+ mutex_unlock(&s->open_mutex);
+ mutex_init(&s->sem);
return nonseekable_open(inode, file);
}
lock_kernel();
}
- down(&s->open_sem);
+ mutex_lock(&s->open_mutex);
if (file->f_mode & FMODE_WRITE) {
stop_dac(s);
dealloc_dmabuf(s, &s->dma_dac);
dealloc_dmabuf(s, &s->dma_adc);
}
s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE));
- up(&s->open_sem);
+ mutex_unlock(&s->open_mutex);
wake_up(&s->open_wait);
unlock_kernel();
return 0;
len += sprintf(buf + len, "----------------------\n");
for (cnt = 0; cnt <= 0x7e; cnt += 2)
len += sprintf(buf + len, "reg %02x = %04x\n",
- cnt, rdcodec(s->codec, cnt));
+ cnt, rdcodec(&s->codec, cnt));
if (fpos >= len) {
*start = buf;
{
struct au1000_state *s = &au1000_state;
int val;
+#ifdef AU1000_DEBUG
char proc_str[80];
+#endif
memset(s, 0, sizeof(struct au1000_state));
init_waitqueue_head(&s->dma_adc.wait);
init_waitqueue_head(&s->dma_dac.wait);
init_waitqueue_head(&s->open_wait);
- init_MUTEX(&s->open_sem);
+ mutex_init(&s->open_mutex);
spin_lock_init(&s->lock);
-
- s->codec = ac97_alloc_codec();
- if(s->codec == NULL)
- {
- error("Out of memory");
- return -1;
- }
- s->codec->private_data = s;
- s->codec->id = 0;
- s->codec->codec_read = rdcodec;
- s->codec->codec_write = wrcodec;
- s->codec->codec_wait = waitcodec;
+ s->codec.private_data = s;
+ s->codec.id = 0;
+ s->codec.codec_read = rdcodec;
+ s->codec.codec_write = wrcodec;
+ s->codec.codec_wait = waitcodec;
- if (!request_region(virt_to_phys((void *) AC97C_CONFIG),
+ if (!request_mem_region(CPHYSADDR(AC97C_CONFIG),
0x14, AU1000_MODULE_NAME)) {
err("AC'97 ports in use");
- goto err_codec;
+ return -1;
}
// Allocate the DMA Channels
if ((s->dma_dac.dmanr = request_au1000_dma(DMA_ID_AC97C_TX,
s->dma_dac.dmanr, get_dma_done_irq(s->dma_dac.dmanr),
s->dma_adc.dmanr, get_dma_done_irq(s->dma_adc.dmanr));
-#ifdef USE_COHERENT_DMA
// enable DMA coherency in read/write DMA channels
set_dma_mode(s->dma_dac.dmanr,
get_dma_mode(s->dma_dac.dmanr) & ~DMA_NC);
set_dma_mode(s->dma_adc.dmanr,
get_dma_mode(s->dma_adc.dmanr) & ~DMA_NC);
-#else
- // disable DMA coherency in read/write DMA channels
- set_dma_mode(s->dma_dac.dmanr,
- get_dma_mode(s->dma_dac.dmanr) | DMA_NC);
- set_dma_mode(s->dma_adc.dmanr,
- get_dma_mode(s->dma_adc.dmanr) | DMA_NC);
-#endif
/* register devices */
if ((s->dev_audio = register_sound_dsp(&au1000_audio_fops, -1)) < 0)
goto err_dev1;
- if ((s->codec->dev_mixer =
+ if ((s->codec.dev_mixer =
register_sound_mixer(&au1000_mixer_fops, -1)) < 0)
goto err_dev2;
au_writel(0, AC97C_CONFIG);
/* codec init */
- if (!ac97_probe_codec(s->codec))
+ if (!ac97_probe_codec(&s->codec))
goto err_dev3;
- s->codec_base_caps = rdcodec(s->codec, AC97_RESET);
- s->codec_ext_caps = rdcodec(s->codec, AC97_EXTENDED_ID);
+ s->codec_base_caps = rdcodec(&s->codec, AC97_RESET);
+ s->codec_ext_caps = rdcodec(&s->codec, AC97_EXTENDED_ID);
info("AC'97 Base/Extended ID = %04x/%04x",
s->codec_base_caps, s->codec_ext_caps);
* ALTPCM). ac97_codec.c does not handle detection
* of this channel correctly.
*/
- s->codec->supported_mixers |= SOUND_MASK_ALTPCM;
+ s->codec.supported_mixers |= SOUND_MASK_ALTPCM;
/*
* Now set AUX_OUT's default volume.
*/
val = 0x4343;
- mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_ALTPCM,
+ mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_ALTPCM,
(unsigned long) &val);
if (!(s->codec_ext_caps & AC97_EXTID_VRA)) {
s->no_vra = 1;
} else if (!vra) {
// Boot option says disable VRA
- u16 ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
+ u16 ac97_extstat = rdcodec(&s->codec, AC97_EXTENDED_STATUS);
+ wrcodec(&s->codec, AC97_EXTENDED_STATUS,
ac97_extstat & ~AC97_EXTSTAT_VRA);
s->no_vra = 1;
}
/* set mic to be the recording source */
val = SOUND_MASK_MIC;
- mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC,
+ mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_RECSRC,
(unsigned long) &val);
#ifdef AU1000_DEBUG
sprintf(proc_str, "driver/%s/%d/ac97", AU1000_MODULE_NAME,
- s->codec->id);
+ s->codec.id);
s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL,
- ac97_read_proc, s->codec);
+ ac97_read_proc, &s->codec);
+#endif
+
+#ifdef CONFIG_MIPS_XXS1500
+ /* deassert eapd */
+ wrcodec(&s->codec, AC97_POWER_CONTROL,
+ rdcodec(&s->codec, AC97_POWER_CONTROL) & ~0x8000);
+ /* mute a number of signals which seem to be causing problems
+ * if not muted.
+ */
+ wrcodec(&s->codec, AC97_PCBEEP_VOL, 0x8000);
+ wrcodec(&s->codec, AC97_PHONE_VOL, 0x8008);
+ wrcodec(&s->codec, AC97_MIC_VOL, 0x8008);
+ wrcodec(&s->codec, AC97_LINEIN_VOL, 0x8808);
+ wrcodec(&s->codec, AC97_CD_VOL, 0x8808);
+ wrcodec(&s->codec, AC97_VIDEO_VOL, 0x8808);
+ wrcodec(&s->codec, AC97_AUX_VOL, 0x8808);
+ wrcodec(&s->codec, AC97_PCMOUT_VOL, 0x0808);
+ wrcodec(&s->codec, AC97_GENERAL_PURPOSE, 0x2000);
#endif
return 0;
err_dev3:
- unregister_sound_mixer(s->codec->dev_mixer);
+ unregister_sound_mixer(s->codec.dev_mixer);
err_dev2:
unregister_sound_dsp(s->dev_audio);
err_dev1:
err_dma2:
free_au1000_dma(s->dma_dac.dmanr);
err_dma1:
- release_region(virt_to_phys((void *) AC97C_CONFIG), 0x14);
- err_codec:
- ac97_release_codec(s->codec);
+ release_mem_region(CPHYSADDR(AC97C_CONFIG), 0x14);
return -1;
}
synchronize_irq();
free_au1000_dma(s->dma_adc.dmanr);
free_au1000_dma(s->dma_dac.dmanr);
- release_region(virt_to_phys((void *) AC97C_CONFIG), 0x14);
+ release_mem_region(CPHYSADDR(AC97C_CONFIG), 0x14);
unregister_sound_dsp(s->dev_audio);
- unregister_sound_mixer(s->codec->dev_mixer);
- ac97_release_codec(s->codec);
+ unregister_sound_mixer(s->codec.dev_mixer);
}
static int __init init_au1000(void)
if (!options || !*options)
return 0;
- while (this_opt = strsep(&options, ",")) {
+ while ((this_opt = strsep(&options, ","))) {
if (!*this_opt)
continue;
if (!strncmp(this_opt, "vra", 3)) {