📄 nec_vrc5477.c
字号:
/*********************************************************************** * Copyright 2001 MontaVista Software Inc. * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net * * drivers/sound/nec_vrc5477.c * AC97 sound dirver for NEC Vrc5477 chip (an integrated, * multi-function controller chip for MIPS CPUs) * * 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 code is derived from ite8172.c, which is written by Steve Longerbeam. * * Features: * Currently we only support the following capabilities: * . mono output to PCM L/R (line out). * . stereo output to PCM L/R (line out). * . mono input from PCM L (line in). * . stereo output from PCM (line in). * . sampling rate at 48k or variable sampling rate * . support /dev/dsp, /dev/mixer devices, standard OSS devices. * . only support 16-bit PCM format (hardware limit, no software * translation) * . support duplex, but no trigger or realtime. * * Specifically the following are not supported: * . app-set frag size. * . mmap'ed buffer access *//* * Original comments from ite8172.c file. *//* * * Notes: * * 1. Much of the OSS buffer allocation, ioctl's, and mmap'ing are * taken, slightly modified or not at all, from the ES1371 driver, * so refer to the credits in es1371.c for those. The rest of the * code (probe, open, read, write, the ISR, etc.) is new. * 2. The following support is untested: * * Memory mapping the audio buffers, and the ioctl controls that go * with it. * * S/PDIF output. * 3. The following is not supported: * * I2S input. * * legacy audio mode. * 4. Support for volume button interrupts is implemented but doesn't * work yet. * * Revision history * 02.08.2001 0.1 Initial release */#include <linux/version.h>#include <linux/module.h>#include <linux/string.h>#include <linux/kernel.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/sound.h>#include <linux/slab.h>#include <linux/soundcard.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/poll.h>#include <linux/bitops.h>#include <linux/proc_fs.h>#include <linux/spinlock.h>#include <linux/smp_lock.h>#include <linux/ac97_codec.h>#include <linux/wrapper.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/uaccess.h>#include <asm/hardirq.h>#include <asm/ddb5xxx/debug.h>#undef VRC5477_AC97_VERBOSE_DEBUG/* one must turn on CONFIG_LL_DEBUG before VERBOSE_DEBUG is turned */#if defined(VRC5477_AC97_VERBOSE_DEBUG)#if !defined(CONFIG_LL_DEBUG)#error "You must turn CONFIG_LL_DEBUG"#endif#endif#if defined(VRC5477_AC97_VERBOSE_DEBUG)static u16 inTicket=0; /* check sync between intr & write */static u16 outTicket=0;#endif/* --------------------------------------------------------------------- */#undef OSS_DOCUMENTED_MIXER_SEMANTICSstatic const unsigned sample_shift[] = { 0, 1, 1, 2 };#define VRC5477_INT_CLR 0x0#define VRC5477_INT_STATUS 0x0#define VRC5477_CODEC_WR 0x4#define VRC5477_CODEC_RD 0x8#define VRC5477_CTRL 0x18#define VRC5477_ACLINK_CTRL 0x1c#define VRC5477_INT_MASK 0x24#define VRC5477_DAC1_CTRL 0x30#define VRC5477_DAC1L 0x34#define VRC5477_DAC1_BADDR 0x38#define VRC5477_DAC2_CTRL 0x3c#define VRC5477_DAC2L 0x40#define VRC5477_DAC2_BADDR 0x44#define VRC5477_DAC3_CTRL 0x48#define VRC5477_DAC3L 0x4c#define VRC5477_DAC3_BADDR 0x50#define VRC5477_ADC1_CTRL 0x54#define VRC5477_ADC1L 0x58#define VRC5477_ADC1_BADDR 0x5c#define VRC5477_ADC2_CTRL 0x60#define VRC5477_ADC2L 0x64#define VRC5477_ADC2_BADDR 0x68#define VRC5477_ADC3_CTRL 0x6c#define VRC5477_ADC3L 0x70#define VRC5477_ADC3_BADDR 0x74#define VRC5477_CODEC_WR_RWC (1 << 23)#define VRC5477_CODEC_RD_RRDYA (1 << 31)#define VRC5477_CODEC_RD_RRDYD (1 << 30)#define VRC5477_ACLINK_CTRL_RST_ON (1 << 15)#define VRC5477_ACLINK_CTRL_RST_TIME 0x7f#define VRC5477_ACLINK_CTRL_SYNC_ON (1 << 30)#define VRC5477_ACLINK_CTRL_CK_STOP_ON (1 << 31)#define VRC5477_CTRL_DAC2ENB (1 << 15) #define VRC5477_CTRL_ADC2ENB (1 << 14) #define VRC5477_CTRL_DAC1ENB (1 << 13) #define VRC5477_CTRL_ADC1ENB (1 << 12) #define VRC5477_INT_MASK_NMASK (1 << 31) #define VRC5477_INT_MASK_DAC1END (1 << 5) #define VRC5477_INT_MASK_DAC2END (1 << 4) #define VRC5477_INT_MASK_DAC3END (1 << 3) #define VRC5477_INT_MASK_ADC1END (1 << 2) #define VRC5477_INT_MASK_ADC2END (1 << 1) #define VRC5477_INT_MASK_ADC3END (1 << 0) #define VRC5477_DMA_ACTIVATION (1 << 31)#define VRC5477_DMA_WIP (1 << 30)#define VRC5477_AC97_MODULE_NAME "NEC_Vrc5477_audio"#define PFX VRC5477_AC97_MODULE_NAME ": "/* --------------------------------------------------------------------- */struct vrc5477_ac97_state { /* list of vrc5477_ac97 devices */ struct list_head devs; /* the corresponding pci_dev structure */ struct pci_dev *dev; /* soundcore stuff */ int dev_audio; /* hardware resources */ unsigned long io; unsigned int irq;#ifdef CONFIG_LL_DEBUG /* debug /proc entry */ struct proc_dir_entry *ps; struct proc_dir_entry *ac97_ps;#endif /* CONFIG_LL_DEBUG */ struct ac97_codec codec; unsigned dacChannels, adcChannels; unsigned short dacRate, adcRate; spinlock_t lock; struct semaphore open_sem; mode_t open_mode; wait_queue_head_t open_wait; struct dmabuf { void *lbuf, *rbuf; dma_addr_t lbufDma, rbufDma; unsigned bufOrder; unsigned numFrag; unsigned fragShift; unsigned fragSize; /* redundant */ unsigned fragTotalSize; /* = numFrag * fragSize(real) */ unsigned nextIn; unsigned nextOut; int count; unsigned error; /* over/underrun */ wait_queue_head_t wait; /* OSS stuff */ unsigned stopped:1; unsigned ready:1; } dma_dac, dma_adc; #define WORK_BUF_SIZE 2048 struct { u16 lchannel; u16 rchannel; } workBuf[WORK_BUF_SIZE/4];};/* --------------------------------------------------------------------- */static LIST_HEAD(devs);/* --------------------------------------------------------------------- */extern inline unsigned ld2(unsigned int x){ unsigned r = 0; if (x >= 0x10000) { x >>= 16; r += 16; } if (x >= 0x100) { x >>= 8; r += 8; } if (x >= 0x10) { x >>= 4; r += 4; } if (x >= 4) { x >>= 2; r += 2; } if (x >= 2) r++; return r;}/* --------------------------------------------------------------------- */static u16 rdcodec(struct ac97_codec *codec, u8 addr){ struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)codec->private_data; unsigned long flags; u32 result; spin_lock_irqsave(&s->lock, flags); /* wait until we can access codec registers */ while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); /* write the address and "read" command to codec */ addr = addr & 0x7f; outl((addr << 16) | VRC5477_CODEC_WR_RWC, s->io + VRC5477_CODEC_WR); /* get the return result */ udelay(100); /* workaround hardware bug */ while ( (result = inl(s->io + VRC5477_CODEC_RD)) & (VRC5477_CODEC_RD_RRDYA | VRC5477_CODEC_RD_RRDYD) ) { /* we get either addr or data, or both */ if (result & VRC5477_CODEC_RD_RRDYA) { MIPS_ASSERT(addr == ((result >> 16) & 0x7f) ); } if (result & VRC5477_CODEC_RD_RRDYD) { break; } } spin_unlock_irqrestore(&s->lock, flags); return result & 0xffff;;}static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data){ struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)codec->private_data; unsigned long flags; spin_lock_irqsave(&s->lock, flags); /* wait until we can access codec registers */ while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); /* write the address and value to codec */ outl((addr << 16) | data, s->io + VRC5477_CODEC_WR); spin_unlock_irqrestore(&s->lock, flags);}static void waitcodec(struct ac97_codec *codec){ struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)codec->private_data; /* wait until we can access codec registers */ while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000);}/* --------------------------------------------------------------------- */static void vrc5477_ac97_delay(int msec){ unsigned long tmo; signed long tmo2; if (in_interrupt()) return; tmo = jiffies + (msec*HZ)/1000; for (;;) { tmo2 = tmo - jiffies; if (tmo2 <= 0) break; schedule_timeout(tmo2); }}static void set_adc_rate(struct vrc5477_ac97_state *s, unsigned rate){ wrcodec(&s->codec, AC97_PCM_LR_ADC_RATE, rate); s->adcRate = rate;}static void set_dac_rate(struct vrc5477_ac97_state *s, unsigned rate){ wrcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE, rate); s->dacRate = rate;}/* --------------------------------------------------------------------- */extern inline void stop_dac(struct vrc5477_ac97_state *s){ struct dmabuf* db = &s->dma_dac; unsigned long flags; u32 temp; spin_lock_irqsave(&s->lock, flags); if (db->stopped) { spin_unlock_irqrestore(&s->lock, flags); return; } /* deactivate the dma */ outl(0, s->io + VRC5477_DAC1_CTRL); outl(0, s->io + VRC5477_DAC2_CTRL); /* wait for DAM completely stop */ while (inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP); while (inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP); /* disable dac slots in aclink */ temp = inl(s->io + VRC5477_CTRL); temp &= ~ (VRC5477_CTRL_DAC1ENB | VRC5477_CTRL_DAC2ENB); outl (temp, s->io + VRC5477_CTRL); /* disable interrupts */ temp = inl(s->io + VRC5477_INT_MASK); temp &= ~ (VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END); outl (temp, s->io + VRC5477_INT_MASK); /* clear pending ones */ outl(VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END, s->io + VRC5477_INT_CLR); db->stopped = 1; spin_unlock_irqrestore(&s->lock, flags);} static void start_dac(struct vrc5477_ac97_state *s){ struct dmabuf* db = &s->dma_dac; unsigned long flags; u32 dmaLength; u32 temp; spin_lock_irqsave(&s->lock, flags); if (!db->stopped) { spin_unlock_irqrestore(&s->lock, flags); return; } /* we should have some data to do the DMA trasnfer */ MIPS_ASSERT(db->count >= db->fragSize); /* clear pending fales interrupts */ outl(VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END, s->io + VRC5477_INT_CLR); /* enable interrupts */ temp = inl(s->io + VRC5477_INT_MASK); temp |= VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END; outl(temp, s->io + VRC5477_INT_MASK); /* setup dma base addr */ outl(db->lbufDma + db->nextOut, s->io + VRC5477_DAC1_BADDR); if (s->dacChannels == 1) { outl(db->lbufDma + db->nextOut, s->io + VRC5477_DAC2_BADDR); } else { outl(db->rbufDma + db->nextOut, s->io + VRC5477_DAC2_BADDR); } /* set dma length, in the unit of 0x10 bytes */ dmaLength = db->fragSize >> 4; outl(dmaLength, s->io + VRC5477_DAC1L); outl(dmaLength, s->io + VRC5477_DAC2L); /* activate dma */ outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_DAC1_CTRL); outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_DAC2_CTRL); /* enable dac slots - we should hear the music now! */ temp = inl(s->io + VRC5477_CTRL); temp |= (VRC5477_CTRL_DAC1ENB | VRC5477_CTRL_DAC2ENB); outl (temp, s->io + VRC5477_CTRL); /* it is time to setup next dma transfer */ MIPS_ASSERT(inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP); MIPS_ASSERT(inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP); temp = db->nextOut + db->fragSize; if (temp >= db->fragTotalSize) { MIPS_ASSERT(temp == db->fragTotalSize); temp = 0; } outl(db->lbufDma + temp, s->io + VRC5477_DAC1_BADDR); if (s->dacChannels == 1) { outl(db->lbufDma + temp, s->io + VRC5477_DAC2_BADDR); } else { outl(db->rbufDma + temp, s->io + VRC5477_DAC2_BADDR); } db->stopped = 0;#if defined(VRC5477_AC97_VERBOSE_DEBUG) outTicket = *(u16*)(db->lbuf+db->nextOut); if (db->count > db->fragSize) { MIPS_ASSERT((u16)(outTicket+1) == *(u16*)(db->lbuf+temp)); }#endif spin_unlock_irqrestore(&s->lock, flags);} extern inline void stop_adc(struct vrc5477_ac97_state *s){ struct dmabuf* db = &s->dma_adc; unsigned long flags; u32 temp; spin_lock_irqsave(&s->lock, flags); if (db->stopped) { spin_unlock_irqrestore(&s->lock, flags); return; } /* deactivate the dma */ outl(0, s->io + VRC5477_ADC1_CTRL); outl(0, s->io + VRC5477_ADC2_CTRL); /* disable adc slots in aclink */ temp = inl(s->io + VRC5477_CTRL); temp &= ~ (VRC5477_CTRL_ADC1ENB | VRC5477_CTRL_ADC2ENB); outl (temp, s->io + VRC5477_CTRL); /* disable interrupts */ temp = inl(s->io + VRC5477_INT_MASK); temp &= ~ (VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END); outl (temp, s->io + VRC5477_INT_MASK); /* clear pending ones */ outl(VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END, s->io + VRC5477_INT_CLR); db->stopped = 1; spin_unlock_irqrestore(&s->lock, flags);} static void start_adc(struct vrc5477_ac97_state *s){ struct dmabuf* db = &s->dma_adc; unsigned long flags;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -