📄 vwsnd.c
字号:
ad1843_DAADR = { 25, 6, 2 }, /* Digital ADC Right Source Select */ ad1843_DRSFLT = { 25, 15, 1 }, /* Digital Reampler Filter Mode */ ad1843_ADLF = { 26, 0, 2 }, /* ADC Left Channel Data Format */ ad1843_ADRF = { 26, 2, 2 }, /* ADC Right Channel Data Format */ ad1843_ADTLK = { 26, 4, 1 }, /* ADC Transmit Lock Mode Select */ ad1843_SCF = { 26, 7, 1 }, /* SCLK Frequency Select */ ad1843_DA1F = { 26, 8, 2 }, /* DAC1 Data Format Select */ ad1843_DA1SM = { 26, 14, 1 }, /* DAC1 Stereo/Mono Mode Select */ ad1843_ADLEN = { 27, 0, 1 }, /* ADC Left Channel Enable */ ad1843_ADREN = { 27, 1, 1 }, /* ADC Right Channel Enable */ ad1843_AAMEN = { 27, 4, 1 }, /* Analog to Analog Mix Enable */ ad1843_ANAEN = { 27, 7, 1 }, /* Analog Channel Enable */ ad1843_DA1EN = { 27, 8, 1 }, /* DAC1 Enable */ ad1843_DA2EN = { 27, 9, 1 }, /* DAC2 Enable */ ad1843_C1EN = { 28, 11, 1 }, /* Clock Generator 1 Enable */ ad1843_C2EN = { 28, 12, 1 }, /* Clock Generator 2 Enable */ ad1843_PDNI = { 28, 15, 1 }; /* Converter Power Down *//* * The various registers of the AD1843 use three different formats for * specifying gain. The ad1843_gain structure parameterizes the * formats. */typedef struct ad1843_gain { int negative; /* nonzero if gain is negative. */ const ad1843_bitfield_t *lfield; const ad1843_bitfield_t *rfield;} ad1843_gain_t;static const ad1843_gain_t ad1843_gain_RECLEV = { 0, &ad1843_LIG, &ad1843_RIG };static const ad1843_gain_t ad1843_gain_LINE = { 1, &ad1843_LX1M, &ad1843_RX1M };static const ad1843_gain_t ad1843_gain_CD = { 1, &ad1843_LX2M, &ad1843_RX2M };static const ad1843_gain_t ad1843_gain_MIC = { 1, &ad1843_LMCM, &ad1843_RMCM };static const ad1843_gain_t ad1843_gain_PCM = { 1, &ad1843_LDA1G, &ad1843_RDA1G };/* read the current value of an AD1843 bitfield. */static int ad1843_read_bits(lithium_t *lith, const ad1843_bitfield_t *field){ int w = li_read_ad1843_reg(lith, field->reg); int val = w >> field->lo_bit & ((1 << field->nbits) - 1); DBGXV("ad1843_read_bits(lith=0x%p, field->{%d %d %d}) returns 0x%x\n", lith, field->reg, field->lo_bit, field->nbits, val); return val;}/* * write a new value to an AD1843 bitfield and return the old value. */static int ad1843_write_bits(lithium_t *lith, const ad1843_bitfield_t *field, int newval){ int w = li_read_ad1843_reg(lith, field->reg); int mask = ((1 << field->nbits) - 1) << field->lo_bit; int oldval = (w & mask) >> field->lo_bit; int newbits = (newval << field->lo_bit) & mask; w = (w & ~mask) | newbits; (void) li_write_ad1843_reg(lith, field->reg, w); DBGXV("ad1843_write_bits(lith=0x%p, field->{%d %d %d}, val=0x%x) " "returns 0x%x\n", lith, field->reg, field->lo_bit, field->nbits, newval, oldval); return oldval;}/* * ad1843_read_multi reads multiple bitfields from the same AD1843 * register. It uses a single read cycle to do it. (Reading the * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20 * microseconds.) * * Called ike this. * * ad1843_read_multi(lith, nfields, * &ad1843_FIELD1, &val1, * &ad1843_FIELD2, &val2, ...); */static void ad1843_read_multi(lithium_t *lith, int argcount, ...){ va_list ap; const ad1843_bitfield_t *fp; int w = 0, mask, *value, reg = -1; va_start(ap, argcount); while (--argcount >= 0) { fp = va_arg(ap, const ad1843_bitfield_t *); value = va_arg(ap, int *); if (reg == -1) { reg = fp->reg; w = li_read_ad1843_reg(lith, reg); } ASSERT(reg == fp->reg); mask = (1 << fp->nbits) - 1; *value = w >> fp->lo_bit & mask; } va_end(ap);}/* * ad1843_write_multi stores multiple bitfields into the same AD1843 * register. It uses one read and one write cycle to do it. * * Called like this. * * ad1843_write_multi(lith, nfields, * &ad1843_FIELD1, val1, * &ad1843_FIELF2, val2, ...); */static void ad1843_write_multi(lithium_t *lith, int argcount, ...){ va_list ap; int reg; const ad1843_bitfield_t *fp; int value; int w, m, mask, bits; mask = 0; bits = 0; reg = -1; va_start(ap, argcount); while (--argcount >= 0) { fp = va_arg(ap, const ad1843_bitfield_t *); value = va_arg(ap, int); if (reg == -1) reg = fp->reg; ASSERT(fp->reg == reg); m = ((1 << fp->nbits) - 1) << fp->lo_bit; mask |= m; bits |= (value << fp->lo_bit) & m; } va_end(ap); ASSERT(!(bits & ~mask)); if (~mask & 0xFFFF) w = li_read_ad1843_reg(lith, reg); else w = 0; w = (w & ~mask) | bits; (void) li_write_ad1843_reg(lith, reg, w);}/* * ad1843_get_gain reads the specified register and extracts the gain value * using the supplied gain type. It returns the gain in OSS format. */static int ad1843_get_gain(lithium_t *lith, const ad1843_gain_t *gp){ int lg, rg; unsigned short mask = (1 << gp->lfield->nbits) - 1; ad1843_read_multi(lith, 2, gp->lfield, &lg, gp->rfield, &rg); if (gp->negative) { lg = mask - lg; rg = mask - rg; } lg = (lg * 100 + (mask >> 1)) / mask; rg = (rg * 100 + (mask >> 1)) / mask; return lg << 0 | rg << 8;}/* * Set an audio channel's gain. Converts from OSS format to AD1843's * format. * * Returns the new gain, which may be lower than the old gain. */static int ad1843_set_gain(lithium_t *lith, const ad1843_gain_t *gp, int newval){ unsigned short mask = (1 << gp->lfield->nbits) - 1; int lg = newval >> 0 & 0xFF; int rg = newval >> 8; if (lg < 0 || lg > 100 || rg < 0 || rg > 100) return -EINVAL; lg = (lg * mask + (mask >> 1)) / 100; rg = (rg * mask + (mask >> 1)) / 100; if (gp->negative) { lg = mask - lg; rg = mask - rg; } ad1843_write_multi(lith, 2, gp->lfield, lg, gp->rfield, rg); return ad1843_get_gain(lith, gp);}/* Returns the current recording source, in OSS format. */static int ad1843_get_recsrc(lithium_t *lith){ int ls = ad1843_read_bits(lith, &ad1843_LSS); switch (ls) { case 1: return SOUND_MASK_MIC; case 2: return SOUND_MASK_LINE; case 3: return SOUND_MASK_CD; case 6: return SOUND_MASK_PCM; default: ASSERT(0); return -1; }}/* * Enable/disable digital resample mode in the AD1843. * * The AD1843 requires that ADL, ADR, DA1 and DA2 be powered down * while switching modes. So we save DA1's state (DA2's state is not * interesting), power them down, switch into/out of resample mode, * power them up, and restore state. * * This will cause audible glitches if D/A or A/D is going on, so the * driver disallows that (in mixer_write_ioctl()). * * The open question is, is this worth doing? I'm leaving it in, * because it's written, but... */static void ad1843_set_resample_mode(lithium_t *lith, int onoff){ /* Save DA1 mute and gain (addr 9 is DA1 analog gain/attenuation) */ int save_da1 = li_read_ad1843_reg(lith, 9); /* Power down A/D and D/A. */ ad1843_write_multi(lith, 4, &ad1843_DA1EN, 0, &ad1843_DA2EN, 0, &ad1843_ADLEN, 0, &ad1843_ADREN, 0); /* Switch mode */ ASSERT(onoff == 0 || onoff == 1); ad1843_write_bits(lith, &ad1843_DRSFLT, onoff); /* Power up A/D and D/A. */ ad1843_write_multi(lith, 3, &ad1843_DA1EN, 1, &ad1843_ADLEN, 1, &ad1843_ADREN, 1); /* Restore DA1 mute and gain. */ li_write_ad1843_reg(lith, 9, save_da1);}/* * Set recording source. Arg newsrc specifies an OSS channel mask. * * The complication is that when we switch into/out of loopback mode * (i.e., src = SOUND_MASK_PCM), we change the AD1843 into/out of * digital resampling mode. * * Returns newsrc on success, -errno on failure. */static int ad1843_set_recsrc(lithium_t *lith, int newsrc){ int bits; int oldbits; switch (newsrc) { case SOUND_MASK_PCM: bits = 6; break; case SOUND_MASK_MIC: bits = 1; break; case SOUND_MASK_LINE: bits = 2; break; case SOUND_MASK_CD: bits = 3; break; default: return -EINVAL; } oldbits = ad1843_read_bits(lith, &ad1843_LSS); if (newsrc == SOUND_MASK_PCM && oldbits != 6) { DBGP("enabling digital resample mode\n"); ad1843_set_resample_mode(lith, 1); ad1843_write_multi(lith, 2, &ad1843_DAADL, 2, &ad1843_DAADR, 2); } else if (newsrc != SOUND_MASK_PCM && oldbits == 6) { DBGP("disabling digital resample mode\n"); ad1843_set_resample_mode(lith, 0); ad1843_write_multi(lith, 2, &ad1843_DAADL, 0, &ad1843_DAADR, 0); } ad1843_write_multi(lith, 2, &ad1843_LSS, bits, &ad1843_RSS, bits); return newsrc;}/* * Return current output sources, in OSS format. */static int ad1843_get_outsrc(lithium_t *lith){ int pcm, line, mic, cd; pcm = ad1843_read_bits(lith, &ad1843_LDA1GM) ? 0 : SOUND_MASK_PCM; line = ad1843_read_bits(lith, &ad1843_LX1MM) ? 0 : SOUND_MASK_LINE; cd = ad1843_read_bits(lith, &ad1843_LX2MM) ? 0 : SOUND_MASK_CD; mic = ad1843_read_bits(lith, &ad1843_LMCMM) ? 0 : SOUND_MASK_MIC; return pcm | line | cd | mic;}/* * Set output sources. Arg is a mask of active sources in OSS format. * * Returns source mask on success, -errno on failure. */static int ad1843_set_outsrc(lithium_t *lith, int mask){ int pcm, line, mic, cd; if (mask & ~(SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_MIC)) return -EINVAL; pcm = (mask & SOUND_MASK_PCM) ? 0 : 1; line = (mask & SOUND_MASK_LINE) ? 0 : 1; mic = (mask & SOUND_MASK_MIC) ? 0 : 1; cd = (mask & SOUND_MASK_CD) ? 0 : 1; ad1843_write_multi(lith, 2, &ad1843_LDA1GM, pcm, &ad1843_RDA1GM, pcm); ad1843_write_multi(lith, 2, &ad1843_LX1MM, line, &ad1843_RX1MM, line); ad1843_write_multi(lith, 2, &ad1843_LX2MM, cd, &ad1843_RX2MM, cd); ad1843_write_multi(lith, 2, &ad1843_LMCMM, mic, &ad1843_RMCMM, mic); return mask;}/* Setup ad1843 for D/A conversion. */static void ad1843_setup_dac(lithium_t *lith, int framerate, int fmt, int channels){ int ad_fmt = 0, ad_mode = 0; DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n", lith, framerate, fmt, channels); switch (fmt) { case AFMT_S8: ad_fmt = 1; break; case AFMT_U8: ad_fmt = 1; break; case AFMT_S16_LE: ad_fmt = 1; break; case AFMT_MU_LAW: ad_fmt = 2; break; case AFMT_A_LAW: ad_fmt = 3; break; default: ASSERT(0); } switch (channels) { case 2: ad_mode = 0; break; case 1: ad_mode = 1; break; default: ASSERT(0); } DBGPV("ad_mode = %d, ad_fmt = %d\n", ad_mode, ad_fmt); ASSERT(framerate >= 4000 && framerate <= 49000); ad1843_write_bits(lith, &ad1843_C1C, framerate); ad1843_write_multi(lith, 2, &ad1843_DA1SM, ad_mode, &ad1843_DA1F, ad_fmt);}static void ad1843_shutdown_dac(lithium_t *lith){ ad1843_write_bits(lith, &ad1843_DA1F, 1);}static void ad1843_setup_adc(lithium_t *lith, int framerate, int fmt, int channels){ int da_fmt = 0; DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n", lith, framerate, fmt, channels); switch (fmt) { case AFMT_S8: da_fmt = 1; break; case AFMT_U8: da_fmt = 1; break; case AFMT_S16_LE: da_fmt = 1; break; case AFMT_MU_LAW: da_fmt = 2; break; case AFMT_A_LAW: da_fmt = 3; break; default: ASSERT(0); } DBGPV("da_fmt = %d\n", da_fmt); ASSERT(framerate >= 4000 && framerate <= 49000); ad1843_write_bits(lith, &ad1843_C2C, framerate); ad1843_write_multi(lith, 2, &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt);}static void ad1843_shutdown_adc(lithium_t *lith){ /* nothing to do */}/* * Fully initialize the ad1843. As described in the AD1843 data * sheet, section "START-UP SEQUENCE". The numbered comments are * subsection headings from the data sheet. See the data sheet, pages * 52-54, for more info. * * return 0 on success, -errno on failure. */static int __init ad1843_init(lithium_t *lith){ unsigned long later; int err; err = li_init(lith); if (err) return err; if (ad1843_read_bits(lith, &ad1843_INIT) != 0) { printk(KERN_ERR "vwsnd sound: AD1843 won't initialize\n"); return -EIO; } ad1843_write_bits(lith, &ad1843_SCF, 1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -