📄 ad1848.c
字号:
m, ad_read(d, 9) ) ; if (wr || ! FULL_DUPLEX(d) ) ad_write_cnt(d, 14, cnt); else ad_write_cnt(d, 30, cnt); break ; case SND_CB_STOP : case SND_CB_ABORT : /* XXX check this... */ m = ad_read(d,9) ; m &= wr ? ~I9_PEN : ~I9_CEN ; /* Stop DMA */ /* * on the OPTi931 the enable bit seems hard to set... */ for (retry = 10; retry ; retry-- ) { ad_write(d, 9, m ); if (ad_read(d,9) ==m) break; } if (retry == 0) printf("start dma, failed to clear bit 0x%02x 0x%02x\n", m, ad_read(d, 9) ) ;#if 1 /* * try to disable DMA by clearing count registers. Not sure it * is needed, and it might cause false interrupts when the * DMA is re-enabled later. */ if (wr || ! FULL_DUPLEX(d) ) ad_write_cnt(d, 14, 0); else ad_write_cnt(d, 30, 0); break;#endif } return 0 ;}/* * main irq handler for the CS423x. The OPTi931 code is * a separate one. * The correct way to operate for a device with multiple internal * interrupt sources is to loop on the status register and ack * interrupts until all interrupts are served and none are reported. At * this point the IRQ line to the ISA IRQ controller should go low * and be raised at the next interrupt. * * Since the ISA IRQ controller is sent EOI _before_ passing control * to the isr, it might happen that we serve an interrupt early, in * which case the status register at the next interrupt should just * say that there are no more interrupts... */static voidmss_intr(int unit){ snddev_info *d = &pcm_info[unit]; u_char c, served = 0; int i; DEB(printf("mss_intr\n")); ad_read(d, 11); /* fake read of status bits */ /* * loop until there are interrupts, but no more than 10 times. */ for (i=10 ; i && inb(io_Status(d)) & 1 ; i-- ) { /* get exact reason for full-duplex boards */ c = FULL_DUPLEX(d) ? ad_read(d, 24) : 0x30 ; c &= ~served ; if ( d->dbuf_out.dl && (c & 0x10) ) { served |= 0x10 ; dsp_wrintr(d); } if ( d->dbuf_in.dl && (c & 0x20) ) { served |= 0x20 ; dsp_rdintr(d); } /* * now ack the interrupt */ if ( FULL_DUPLEX(d) ) ad_write(d, 24, ~c); /* ack selectively */ else outb(io_Status(d), 0); /* Clear interrupt status */ } if (served == 0) { printf("How strange... mss_intr with no reason!\n"); /* * this should not happen... I have no idea what to do now. * maybe should do a sanity check and restart dmas ? */ outb(io_Status(d), 0); /* Clear interrupt status */ }}/* * the opti931 seems to miss interrupts when working in full * duplex, so we try some heuristics to catch them. */static voidopti931_intr(int unit){ snddev_info *d = &pcm_info[unit]; u_char masked=0, i11, mc11, c=0; u_char reason; /* b0 = playback, b1 = capture, b2 = timer */ int loops = 10;#if 0 reason = inb(io_Status(d)); if ( ! (reason & 1) ) {/* no int, maybe a shared line ? */ printf("opti931_intr: flag 0, mcir11 0x%02x\n", ad_read(d,11)); return; }#endif i11 = ad_read(d, 11); /* XXX what's for ? */again: c=mc11 = FULL_DUPLEX(d) ? opti_read(d->conf_base, 11) : 0xc ; mc11 &= 0x0c ; if (c & 0x10) { DEB(printf("Warning: CD interrupt\n");) mc11 |= 0x10 ; } if (c & 0x20) { DEB(printf("Warning: MPU interrupt\n");) mc11 |= 0x20 ; } if (mc11 & masked) printf("irq reset failed, mc11 0x%02x, masked 0x%02x\n", mc11, masked); masked |= mc11 ; /* * the nice OPTi931 sets the IRQ line before setting the bits in * mc11. So, on some occasions I have to retry (max 10 times). */ if ( mc11 == 0 ) { /* perhaps can return ... */ reason = inb(io_Status(d)); if (reason & 1) { DEB(printf("one more try...\n");) if (--loops) goto again; else DDB(printf("opti_intr: irq but mc11 not set!...\n");) } if (loops==10) printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11); return; } if ( d->dbuf_in.dl && (mc11 & 8) ) { dsp_rdintr(d); } if ( d->dbuf_out.dl && (mc11 & 4) ) { dsp_wrintr(d); } opti_write(d->conf_base, 11, ~mc11); /* ack */ if (--loops) goto again; DEB(printf("xxx too many loops\n");)}/* * Second part of the file: functions local to this module. * in this section a few routines to access MSS registers * */static voidopti_write(int io_base, u_char reg, u_char value){ outb(io_base, reg); outb(io_base+1, value);}static u_charopti_read(int io_base, u_char reg){ outb(io_base, reg); return inb(io_base+1);}static voidgus_write(int io_base, u_char reg, u_char value){ outb(io_base + 3, reg); outb(io_base + 5, value);}static voidgus_writew(int io_base, u_char reg, u_short value){ outb(io_base + 3, reg); outb(io_base + 4, value);}static u_chargus_read(int io_base, u_char reg){ outb(io_base+3, reg); return inb(io_base+5);}static u_shortgus_readw(int io_base, u_char reg){ outb(io_base+3, reg); return inw(io_base+4);}/* * AD_WAIT_INIT waits if we are initializing the board and * we cannot modify its settings */static intAD_WAIT_INIT(snddev_info *d, int x){ int arg=x, n = 0; /* to shut up the compiler... */ for (; x-- ; ) if ( (n=inb(io_Index_Addr(d))) & IA_BUSY) DELAY(10); else return n ; printf("AD_WAIT_INIT FAILED %d 0x%02x\n", arg, n); return n ;}static intad_read(snddev_info *d, int reg){ u_long flags; int x; flags = spltty(); AD_WAIT_INIT(d, 201); x = inb(io_Index_Addr(d)) & ~IA_AMASK ; outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ; x = inb(io_Indexed_Data(d)); splx(flags); return x;}static voidad_write(snddev_info *d, int reg, u_char data){ u_long flags; int x ; flags = spltty(); AD_WAIT_INIT(d, 1002); x = inb(io_Index_Addr(d)) & ~IA_AMASK ; outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ; outb(io_Indexed_Data(d), data); splx(flags);}static voidad_write_cnt(snddev_info *d, int reg, u_short cnt){ ad_write(d, reg+1, cnt & 0xff ); ad_write(d, reg, cnt >> 8 ); /* upper base must be last */}static voidwait_for_calibration(snddev_info *d){ int n, t; /* * Wait until the auto calibration process has finished. * * 1) Wait until the chip becomes ready (reads don't return 0x80). * 2) Wait until the ACI bit of I11 gets on * 3) Wait until the ACI bit of I11 gets off */ n = AD_WAIT_INIT(d, 1000); if (n & IA_BUSY) printf("mss: Auto calibration timed out(1).\n"); for (t = 100 ; t>0 && (ad_read(d, 11) & 0x20) == 0 ; t--) DELAY(100); for (t = 100 ; t>0 && ad_read(d, 11) & 0x20 ; t--) DELAY(100);}#if 0 /* unused right now... */static voidad_mute(snddev_info *d){ ad_write(d, 6, ad_read(d,6) | I6_MUTE); ad_write(d, 7, ad_read(d,7) | I6_MUTE);}static voidad_unmute(snddev_info *d){ ad_write(d, 6, ad_read(d,6) & ~I6_MUTE); ad_write(d, 7, ad_read(d,7) & ~I6_MUTE);}#endifstatic voidad_enter_MCE(snddev_info *d){ int prev; d->bd_flags |= BD_F_MCE_BIT; AD_WAIT_INIT(d, 203); prev = inb(io_Index_Addr(d)); prev &= ~IA_TRD ; outb(io_Index_Addr(d), prev | IA_MCE ) ;}static voidad_leave_MCE(snddev_info *d){ u_long flags; u_char prev; if ( (d->bd_flags & BD_F_MCE_BIT) == 0 ) { printf("--- hey, leave_MCE: MCE bit was not set!\n"); return; } AD_WAIT_INIT(d, 1000); flags = spltty(); d->bd_flags &= ~BD_F_MCE_BIT; prev = inb(io_Index_Addr(d)); prev &= ~IA_TRD ; outb(io_Index_Addr(d), prev & ~IA_MCE ); /* Clear the MCE bit */ wait_for_calibration(d); splx(flags);}/* * only one source can be set... */static intmss_set_recsrc(snddev_info *d, int mask){ u_char recdev; mask &= d->mix_rec_devs; switch (mask) { case SOUND_MASK_LINE: case SOUND_MASK_LINE3: recdev = 0; break; case SOUND_MASK_CD: case SOUND_MASK_LINE1: recdev = 0x40; break; case SOUND_MASK_IMIX: recdev = 0xc0; break; case SOUND_MASK_MIC: default: mask = SOUND_MASK_MIC; recdev = 0x80; } ad_write(d, 0, (ad_read(d, 0) & 0x3f) | recdev); ad_write(d, 1, (ad_read(d, 1) & 0x3f) | recdev); d->mix_recsrc = mask; return 0;}/* * mixer conversion table: from 0..100 scale to codec values * * I don't understand what's this for... maybe achieve a log-scale * volume control ? */static char mix_cvt[101] = { 0, 1, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42, 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65, 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79, 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90, 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99, 100}; /* * there are differences in the mixer depending on the actual sound * card. */static intmss_mixer_set(snddev_info *d, int dev, int value){ int left = value & 0x000000ff; int right = (value & 0x0000ff00) >> 8; int regoffs; mixer_tab *mix_d = &mix_devices; u_char old, val; if (dev > 31) return EINVAL; if (!(d->mix_devs & (1 << dev))) return EINVAL; if (d->bd_id == MD_OPTI931) mix_d = &(opti931_devices); if ((*mix_d)[dev][LEFT_CHN].nbits == 0) { DEB(printf("nbits = 0 for dev %d\n", dev) ); return EINVAL; } if (left > 100) left = 100; if (right > 100) right = 100; if ( (*mix_d)[dev][RIGHT_CHN].nbits == 0) /* Mono control */ right = left; d->mix_levels[dev] = left | (right << 8);#if 0 /* Scale volumes */ left = mix_cvt[left]; right = mix_cvt[right];#endif /* * Set the left channel */ regoffs = (*mix_d)[dev][LEFT_CHN].regno; old = val = ad_read(d, regoffs); /* * if volume is 0, mute chan. Otherwise, unmute. */ if (regoffs != 0) /* main input is different */ val = (left == 0 ) ? old | 0x80 : old & 0x7f ; change_bits(mix_d, &val, dev, LEFT_CHN, left); ad_write(d, regoffs, val); DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n", dev, regoffs, old, val)); if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */ /* * Set the right channel */ regoffs = (*mix_d)[dev][RIGHT_CHN].regno; old = val = ad_read(d, regoffs); if (regoffs != 1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -