📄 vwsnd.c
字号:
* dma_chan_desc is invariant information about a Lithium * DMA channel. There are two instances, li_comm1 and li_comm2. * * Note that the CCTL register fields are write ptr and read ptr, but what * we care about are which pointer is updated by software and which by * hardware. */typedef struct dma_chan_desc { int basereg; int cfgreg; int ctlreg; int hwptrreg; int swptrreg; int ustreg; int mscreg; unsigned long swptrmask; int ad1843_slot; int direction; /* LI_CCTL_DIR_IN/OUT */} dma_chan_desc_t;static const dma_chan_desc_t li_comm1 = { LI_COMM1_BASE, /* base register offset */ LI_COMM1_CFG, /* config register offset */ LI_COMM1_CTL, /* control register offset */ LI_COMM1_CTL + 0, /* hw ptr reg offset (write ptr) */ LI_COMM1_CTL + 1, /* sw ptr reg offset (read ptr) */ LI_AUDIO1_UST, /* ust reg offset */ LI_AUDIO1_MSC, /* msc reg offset */ LI_CCTL_RPTR, /* sw ptr bitmask in ctlval */ 2, /* ad1843 serial slot */ LI_CCFG_DIR_IN /* direction */};static const dma_chan_desc_t li_comm2 = { LI_COMM2_BASE, /* base register offset */ LI_COMM2_CFG, /* config register offset */ LI_COMM2_CTL, /* control register offset */ LI_COMM2_CTL + 1, /* hw ptr reg offset (read ptr) */ LI_COMM2_CTL + 0, /* sw ptr reg offset (writr ptr) */ LI_AUDIO2_UST, /* ust reg offset */ LI_AUDIO2_MSC, /* msc reg offset */ LI_CCTL_WPTR, /* sw ptr bitmask in ctlval */ 2, /* ad1843 serial slot */ LI_CCFG_DIR_OUT /* direction */};/* * dma_chan is variable information about a Lithium DMA channel. * * The desc field points to invariant information. * The lith field points to a lithium_t which is passed * to li_read* and li_write* to access the registers. * The *val fields shadow the lithium registers' contents. */typedef struct dma_chan { const dma_chan_desc_t *desc; lithium_t *lith; unsigned long baseval; unsigned long cfgval; unsigned long ctlval;} dma_chan_t;/* * ustmsc is a UST/MSC pair (Unadjusted System Time/Media Stream Counter). * UST is time in microseconds since the system booted, and MSC is a * counter that increments with every audio sample. */typedef struct ustmsc { unsigned long long ust; unsigned long msc;} ustmsc_t;/* * li_ad1843_wait waits until lithium says the AD1843 register * exchange is not busy. Returns 0 on success, -EBUSY on timeout. * * Locking: must be called with lithium_lock held. */static int li_ad1843_wait(lithium_t *lith){ unsigned long later = jiffies + 2; while (li_readl(lith, LI_CODEC_COMMAND) & LI_CC_BUSY) if (jiffies >= later) return -EBUSY; return 0;}/* * li_read_ad1843_reg returns the current contents of a 16 bit AD1843 register. * * Returns unsigned register value on success, -errno on failure. */static int li_read_ad1843_reg(lithium_t *lith, int reg){ int val; ASSERT(!in_interrupt()); spin_lock(&lith->lock); { val = li_ad1843_wait(lith); if (val == 0) { li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_RD | reg); val = li_ad1843_wait(lith); } if (val == 0) val = li_readl(lith, LI_CODEC_DATA); } spin_unlock(&lith->lock); DBGXV("li_read_ad1843_reg(lith=0x%p, reg=%d) returns 0x%04x\n", lith, reg, val); return val;}/* * li_write_ad1843_reg writes the specified value to a 16 bit AD1843 register. */static void li_write_ad1843_reg(lithium_t *lith, int reg, int newval){ spin_lock(&lith->lock); { if (li_ad1843_wait(lith) == 0) { li_writel(lith, LI_CODEC_DATA, newval); li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_WR | reg); } } spin_unlock(&lith->lock);}/* * li_setup_dma calculates all the register settings for DMA in a particular * mode. It takes too many arguments. */static void li_setup_dma(dma_chan_t *chan, const dma_chan_desc_t *desc, lithium_t *lith, unsigned long buffer_paddr, int bufshift, int fragshift, int channels, int sampsize){ unsigned long mode, format; unsigned long size, tmask; DBGEV("(chan=0x%p, desc=0x%p, lith=0x%p, buffer_paddr=0x%lx, " "bufshift=%d, fragshift=%d, channels=%d, sampsize=%d)\n", chan, desc, lith, buffer_paddr, bufshift, fragshift, channels, sampsize); /* Reset the channel first. */ li_writel(lith, desc->ctlreg, LI_CCTL_RESET); ASSERT(channels == 1 || channels == 2); if (channels == 2) mode = LI_CCFG_MODE_STEREO; else mode = LI_CCFG_MODE_MONO; ASSERT(sampsize == 1 || sampsize == 2); if (sampsize == 2) format = LI_CCFG_FMT_16BIT; else format = LI_CCFG_FMT_8BIT; chan->desc = desc; chan->lith = lith; /* * Lithium DMA address register takes a 40-bit physical * address, right-shifted by 8 so it fits in 32 bits. Bit 37 * must be set -- it enables cache coherence. */ ASSERT(!(buffer_paddr & 0xFF)); chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8); chan->cfgval = (!LI_CCFG_LOCK | SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) | desc->direction | mode | format); size = bufshift - 6; tmask = 13 - fragshift; /* See Lithium DMA Notes above. */ ASSERT(size >= 2 && size <= 7); ASSERT(tmask >= 1 && tmask <= 7); chan->ctlval = (!LI_CCTL_RESET | SHIFT_FIELD(size, LI_CCTL_SIZE) | !LI_CCTL_DMA_ENABLE | SHIFT_FIELD(tmask, LI_CCTL_TMASK) | SHIFT_FIELD(0, LI_CCTL_TPTR)); DBGPV("basereg 0x%x = 0x%lx\n", desc->basereg, chan->baseval); DBGPV("cfgreg 0x%x = 0x%lx\n", desc->cfgreg, chan->cfgval); DBGPV("ctlreg 0x%x = 0x%lx\n", desc->ctlreg, chan->ctlval); li_writel(lith, desc->basereg, chan->baseval); li_writel(lith, desc->cfgreg, chan->cfgval); li_writel(lith, desc->ctlreg, chan->ctlval); DBGRV();}static void li_shutdown_dma(dma_chan_t *chan){ lithium_t *lith = chan->lith; caddr_t lith1 = lith->page1; DBGEV("(chan=0x%p)\n", chan); chan->ctlval &= ~LI_CCTL_DMA_ENABLE; DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval); li_writel(lith, chan->desc->ctlreg, chan->ctlval); /* * Offset 0x500 on Lithium page 1 is an undocumented, * unsupported register that holds the zero sample value. * Lithium is supposed to output zero samples when DMA is * inactive, and repeat the last sample when DMA underflows. * But it has a bug, where, after underflow occurs, the zero * sample is not reset. * * I expect this to break in a future rev of Lithium. */ if (lith1 && chan->desc->direction == LI_CCFG_DIR_OUT) * (volatile unsigned long *) (lith1 + 0x500) = 0;}/* * li_activate_dma always starts dma at the beginning of the buffer. * * N.B., these may be called from interrupt. */static __inline__ void li_activate_dma(dma_chan_t *chan){ chan->ctlval |= LI_CCTL_DMA_ENABLE; DBGPV("ctlval = 0x%lx\n", chan->ctlval); li_writel(chan->lith, chan->desc->ctlreg, chan->ctlval);}static void li_deactivate_dma(dma_chan_t *chan){ lithium_t *lith = chan->lith; caddr_t lith2 = lith->page2; chan->ctlval &= ~(LI_CCTL_DMA_ENABLE | LI_CCTL_RPTR | LI_CCTL_WPTR); DBGPV("ctlval = 0x%lx\n", chan->ctlval); DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval); li_writel(lith, chan->desc->ctlreg, chan->ctlval); /* * Offsets 0x98 and 0x9C on Lithium page 2 are undocumented, * unsupported registers that are internal copies of the DMA * read and write pointers. Because of a Lithium bug, these * registers aren't zeroed correctly when DMA is shut off. So * we whack them directly. * * I expect this to break in a future rev of Lithium. */ if (lith2 && chan->desc->direction == LI_CCFG_DIR_OUT) { * (volatile unsigned long *) (lith2 + 0x98) = 0; * (volatile unsigned long *) (lith2 + 0x9C) = 0; }}/* * read/write the ring buffer pointers. These routines' arguments and results * are byte offsets from the beginning of the ring buffer. */static __inline__ int li_read_swptr(dma_chan_t *chan){ const unsigned long mask = chan->desc->swptrmask; return CHUNKS_TO_BYTES(UNSHIFT_FIELD(chan->ctlval, mask));}static __inline__ int li_read_hwptr(dma_chan_t *chan){ return CHUNKS_TO_BYTES(li_readb(chan->lith, chan->desc->hwptrreg));}static __inline__ void li_write_swptr(dma_chan_t *chan, int val){ const unsigned long mask = chan->desc->swptrmask; ASSERT(!(val & ~CHUNKS_TO_BYTES(0xFF))); val = BYTES_TO_CHUNKS(val); chan->ctlval = (chan->ctlval & ~mask) | SHIFT_FIELD(val, mask); li_writeb(chan->lith, chan->desc->swptrreg, val);}/* li_read_USTMSC() returns a UST/MSC pair for the given channel. */static void li_read_USTMSC(dma_chan_t *chan, ustmsc_t *ustmsc){ lithium_t *lith = chan->lith; const dma_chan_desc_t *desc = chan->desc; unsigned long now_low, now_high0, now_high1, chan_ust; spin_lock(&lith->lock); { /* * retry until we do all five reads without the * high word changing. (High word increments * every 2^32 microseconds, i.e., not often) */ do { now_high0 = li_readl(lith, LI_UST_HIGH); now_low = li_readl(lith, LI_UST_LOW); /* * Lithium guarantees these two reads will be * atomic -- ust will not increment after msc * is read. */ ustmsc->msc = li_readl(lith, desc->mscreg); chan_ust = li_readl(lith, desc->ustreg); now_high1 = li_readl(lith, LI_UST_HIGH); } while (now_high0 != now_high1); } spin_unlock(&lith->lock); ustmsc->ust = ((unsigned long long) now_high0 << 32 | chan_ust);}static void li_enable_interrupts(lithium_t *lith, unsigned int mask){ DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask); /* clear any already-pending interrupts. */ li_writel(lith, LI_INTR_STATUS, mask); /* enable the interrupts. */ mask |= li_readl(lith, LI_INTR_MASK); li_writel(lith, LI_INTR_MASK, mask);}static void li_disable_interrupts(lithium_t *lith, unsigned int mask){ unsigned int keepmask; DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask); /* disable the interrupts */ keepmask = li_readl(lith, LI_INTR_MASK) & ~mask; li_writel(lith, LI_INTR_MASK, keepmask); /* clear any pending interrupts. */ li_writel(lith, LI_INTR_STATUS, mask);}/* Get the interrupt status and clear all pending interrupts. */static unsigned int li_get_clear_intr_status(lithium_t *lith){ unsigned int status; status = li_readl(lith, LI_INTR_STATUS); li_writel(lith, LI_INTR_STATUS, ~0); return status & li_readl(lith, LI_INTR_MASK);}static int li_init(lithium_t *lith){ /* 1. System power supplies stabilize. */ /* 2. Assert the ~RESET signal. */ li_writel(lith, LI_HOST_CONTROLLER, LI_HC_RESET); udelay(1); /* 3. Deassert the ~RESET signal and enter a wait period to allow the AD1843 internal clocks and the external crystal oscillator to stabilize. */ li_writel(lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE); udelay(1); return 0;}/*****************************************************************************//* AD1843 access *//* * AD1843 bitfield definitions. All are named as in the AD1843 data * sheet, with ad1843_ prepended and individual bit numbers removed. * * E.g., bits LSS0 through LSS2 become ad1843_LSS. * * Only the bitfields we need are defined. */typedef struct ad1843_bitfield { char reg; char lo_bit; char nbits;} ad1843_bitfield_t;static const ad1843_bitfield_t ad1843_PDNO = { 0, 14, 1 }, /* Converter Power-Down Flag */ ad1843_INIT = { 0, 15, 1 }, /* Clock Initialization Flag */ ad1843_RIG = { 2, 0, 4 }, /* Right ADC Input Gain */ ad1843_RMGE = { 2, 4, 1 }, /* Right ADC Mic Gain Enable */ ad1843_RSS = { 2, 5, 3 }, /* Right ADC Source Select */ ad1843_LIG = { 2, 8, 4 }, /* Left ADC Input Gain */ ad1843_LMGE = { 2, 12, 1 }, /* Left ADC Mic Gain Enable */ ad1843_LSS = { 2, 13, 3 }, /* Left ADC Source Select */ ad1843_RX1M = { 4, 0, 5 }, /* Right Aux 1 Mix Gain/Atten */ ad1843_RX1MM = { 4, 7, 1 }, /* Right Aux 1 Mix Mute */ ad1843_LX1M = { 4, 8, 5 }, /* Left Aux 1 Mix Gain/Atten */ ad1843_LX1MM = { 4, 15, 1 }, /* Left Aux 1 Mix Mute */ ad1843_RX2M = { 5, 0, 5 }, /* Right Aux 2 Mix Gain/Atten */ ad1843_RX2MM = { 5, 7, 1 }, /* Right Aux 2 Mix Mute */ ad1843_LX2M = { 5, 8, 5 }, /* Left Aux 2 Mix Gain/Atten */ ad1843_LX2MM = { 5, 15, 1 }, /* Left Aux 2 Mix Mute */ ad1843_RMCM = { 7, 0, 5 }, /* Right Mic Mix Gain/Atten */ ad1843_RMCMM = { 7, 7, 1 }, /* Right Mic Mix Mute */ ad1843_LMCM = { 7, 8, 5 }, /* Left Mic Mix Gain/Atten */ ad1843_LMCMM = { 7, 15, 1 }, /* Left Mic Mix Mute */ ad1843_HPOS = { 8, 4, 1 }, /* Headphone Output Voltage Swing */ ad1843_HPOM = { 8, 5, 1 }, /* Headphone Output Mute */ ad1843_RDA1G = { 9, 0, 6 }, /* Right DAC1 Analog/Digital Gain */ ad1843_RDA1GM = { 9, 7, 1 }, /* Right DAC1 Analog Mute */ ad1843_LDA1G = { 9, 8, 6 }, /* Left DAC1 Analog/Digital Gain */ ad1843_LDA1GM = { 9, 15, 1 }, /* Left DAC1 Analog Mute */ ad1843_RDA1AM = { 11, 7, 1 }, /* Right DAC1 Digital Mute */ ad1843_LDA1AM = { 11, 15, 1 }, /* Left DAC1 Digital Mute */ ad1843_ADLC = { 15, 0, 2 }, /* ADC Left Sample Rate Source */ ad1843_ADRC = { 15, 2, 2 }, /* ADC Right Sample Rate Source */ ad1843_DA1C = { 15, 8, 2 }, /* DAC1 Sample Rate Source */ ad1843_C1C = { 17, 0, 16 }, /* Clock 1 Sample Rate Select */ ad1843_C2C = { 20, 0, 16 }, /* Clock 1 Sample Rate Select */ ad1843_DAADL = { 25, 4, 2 }, /* Digital ADC Left Source Select */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -