📄 dmasound_awacs.c
字号:
if (!write_sq.active) WAKE_UP(write_sq.sync_queue);}static voidpmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs){ /* For some reason on my PowerBook G3, I get one interrupt * when the interrupt vector is installed (like something is * pending). This happens before the dbdma is initialize by * us, so I just check the command pointer and if it is zero, * just blow it off. */ if (in_le32(&awacs_rxdma->cmdptr) == 0) return; /* We also want to blow 'em off when shutting down. */ if (read_sq.active == 0) return; /* Check multiple buffers in case we were held off from * interrupt processing for a long time. Geeze, I really hope * this doesn't happen. */ while (awacs_rx_cmds[read_sq.rear].xfer_status) { /* Clear status and move on to next buffer. */ awacs_rx_cmds[read_sq.rear].xfer_status = 0; read_sq.rear++; /* Wrap the buffer ring. */ if (read_sq.rear >= read_sq.max_active) read_sq.rear = 0; /* If we have caught up to the front buffer, bump it. * This will cause weird (but not fatal) results if the * read loop is currently using this buffer. The user is * behind in this case anyway, so weird things are going * to happen. */ if (read_sq.rear == read_sq.front) { read_sq.front++; if (read_sq.front >= read_sq.max_active) read_sq.front = 0; } } WAKE_UP(read_sq.action_queue);}static voidpmac_awacs_intr(int irq, void *devid, struct pt_regs *regs){ int ctrl = in_le32(&awacs->control); if (ctrl & MASK_PORTCHG) { /* do something when headphone is plugged/unplugged? */ } if (ctrl & MASK_CNTLERR) { int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16; if (err != 0 && awacs_revision < AWACS_BURGUNDY) printk(KERN_ERR "AWACS: error %x\n", err); } /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ out_le32(&awacs->control, ctrl);}static voidawacs_write(int val){ if (awacs_revision >= AWACS_BURGUNDY) return; while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) ; /* XXX should have timeout */ out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22));}static void awacs_nosound(unsigned long xx){ unsigned long flags; save_flags(flags); cli(); if (beep_playing) { st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); out_le32(&awacs->control, (in_le32(&awacs->control) & ~0x1f00) | (awacs_rate_index << 8)); out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); beep_playing = 0; } restore_flags(flags);}static struct timer_list beep_timer = { function: awacs_nosound};static void awacs_mksound(unsigned int hz, unsigned int ticks){ unsigned long flags; int beep_speed = 0; int srate; int period, ncycles, nsamples; int i, j, f; short *p; static int beep_hz_cache; static int beep_nsamples_cache; static int beep_volume_cache; for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i) if (awacs_freqs_ok[i]) beep_speed = i; srate = awacs_freqs[beep_speed]; if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) {#if 1 /* this is a hack for broken X server code */ hz = 750; ticks = 12;#else /* cancel beep currently playing */ awacs_nosound(0); return;#endif } save_flags(flags); cli(); del_timer(&beep_timer); if (ticks) { beep_timer.expires = jiffies + ticks; add_timer(&beep_timer); } if (beep_playing || write_sq.active || beep_buf == NULL) { restore_flags(flags); return; /* too hard, sorry :-( */ } beep_playing = 1; st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS); restore_flags(flags); if (hz == beep_hz_cache && beep_volume == beep_volume_cache) { nsamples = beep_nsamples_cache; } else { period = srate * 256 / hz; /* fixed point */ ncycles = BEEP_BUFLEN * 256 / period; nsamples = (period * ncycles) >> 8; f = ncycles * 65536 / nsamples; j = 0; p = beep_buf; for (i = 0; i < nsamples; ++i, p += 2) { p[0] = p[1] = beep_wform[j >> 8] * beep_volume; j = (j + f) & 0xffff; } beep_hz_cache = hz; beep_volume_cache = beep_volume; beep_nsamples_cache = nsamples; } st_le16(&beep_dbdma_cmd->req_count, nsamples*4); st_le16(&beep_dbdma_cmd->xfer_status, 0); st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd)); st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf)); awacs_beep_state = 1; save_flags(flags); cli(); if (beep_playing) { /* i.e. haven't been terminated already */ out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); out_le32(&awacs->control, (in_le32(&awacs->control) & ~0x1f00) | (beep_speed << 8)); out_le32(&awacs->byteswap, 0); out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); out_le32(&awacs_txdma->control, RUN | (RUN << 16)); } restore_flags(flags);}#ifdef CONFIG_PMAC_PBOOK/* * Save state when going to sleep, restore it afterwards. */static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when){ switch (when) { case PBOOK_SLEEP_NOW: /* XXX we should stop any dma in progress when going to sleep and restart it when we wake. */ PMacSilence(); disable_irq(awacs_irq); disable_irq(awacs_tx_irq); if (is_pbook_G3) { feature_clear(awacs_node, FEATURE_Sound_CLK_enable); feature_clear(awacs_node, FEATURE_Sound_power); } break; case PBOOK_WAKE: /* There is still a problem on wake. Sound seems to work fine if I launch mpg123 and resumes fine if mpg123 was playing, but the console beep is dead until I do something with the mixer. Probably yet another timing issue */ if (!feature_test(awacs_node, FEATURE_Sound_CLK_enable) || !feature_test(awacs_node, FEATURE_Sound_power)) { /* these aren't present on the 3400 AFAIK -- paulus */ feature_set(awacs_node, FEATURE_Sound_CLK_enable); feature_set(awacs_node, FEATURE_Sound_power); mdelay(1000); } out_le32(&awacs->control, MASK_IEPC | (awacs_rate_index << 8) | 0x11 | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0)); awacs_write(awacs_reg[0] | MASK_ADDR0); awacs_write(awacs_reg[1] | MASK_ADDR1); awacs_write(awacs_reg[2] | MASK_ADDR2); awacs_write(awacs_reg[4] | MASK_ADDR4); if (awacs_is_screamer) { awacs_write(awacs_reg[5] + MASK_ADDR5); awacs_write(awacs_reg[6] + MASK_ADDR6); awacs_write(awacs_reg[7] + MASK_ADDR7); } out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); enable_irq(awacs_irq); enable_irq(awacs_tx_irq); if (awacs_revision == 3) { mdelay(100); awacs_write(0x6000); mdelay(2); awacs_write(awacs_reg[1] | MASK_ADDR1); } /* enable CD sound input */ if (macio_base && is_pbook_G3) { out_8(macio_base + 0x37, 3); } else if (is_pbook_3400) { feature_set(awacs_node, FEATURE_IOBUS_enable); udelay(10); in_8(latch_base + 0x190); } /* Resume pending sounds. */ PMacPlay(); } return PBOOK_SLEEP_OK;}#endif /* CONFIG_PMAC_PBOOK *//* All the burgundy functions: *//* Waits for busy flag to clear */inline static voidawacs_burgundy_busy_wait(void){ while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) ;}inline static voidawacs_burgundy_extend_wait(void){ while (!(in_le32(&awacs->codec_stat) & MASK_EXTEND)) ; while (in_le32(&awacs->codec_stat) & MASK_EXTEND) ;}static voidawacs_burgundy_wcw(unsigned addr, unsigned val){ out_le32(&awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff)); awacs_burgundy_busy_wait(); out_le32(&awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff)); awacs_burgundy_busy_wait(); out_le32(&awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff)); awacs_burgundy_busy_wait(); out_le32(&awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff)); awacs_burgundy_busy_wait();}static unsignedawacs_burgundy_rcw(unsigned addr){ unsigned val = 0; unsigned long flags; /* should have timeouts here */ save_flags(flags); cli(); out_le32(&awacs->codec_ctrl, addr + 0x100000); awacs_burgundy_busy_wait(); awacs_burgundy_extend_wait(); val += (in_le32(&awacs->codec_stat) >> 4) & 0xff; out_le32(&awacs->codec_ctrl, addr + 0x100100); awacs_burgundy_busy_wait(); awacs_burgundy_extend_wait(); val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<8; out_le32(&awacs->codec_ctrl, addr + 0x100200); awacs_burgundy_busy_wait(); awacs_burgundy_extend_wait(); val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<16; out_le32(&awacs->codec_ctrl, addr + 0x100300); awacs_burgundy_busy_wait(); awacs_burgundy_extend_wait(); val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<24; restore_flags(flags); return val;}static voidawacs_burgundy_wcb(unsigned addr, unsigned val){ out_le32(&awacs->codec_ctrl, addr + 0x300000 + (val & 0xff)); awacs_burgundy_busy_wait();}static unsignedawacs_burgundy_rcb(unsigned addr){ unsigned val = 0; unsigned long flags; /* should have timeouts here */ save_flags(flags); cli(); out_le32(&awacs->codec_ctrl, addr + 0x100000); awacs_burgundy_busy_wait(); awacs_burgundy_extend_wait(); val += (in_le32(&awacs->codec_stat) >> 4) & 0xff; restore_flags(flags); return val;}static intawacs_burgundy_check(void){ /* Checks to see the chip is alive and kicking */ int error = in_le32(&awacs->codec_ctrl) & MASK_ERRCODE; return error == 0xf0000;}static intawacs_burgundy_init(void){ if (awacs_burgundy_check()) { printk(KERN_WARNING "AWACS: disabled by MacOS :-(\n"); return 1; } awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_OUTPUTENABLES, DEF_BURGUNDY_OUTPUTENABLES); awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, DEF_BURGUNDY_MORE_OUTPUTENABLES); awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_OUTPUTSELECTS, DEF_BURGUNDY_OUTPUTSELECTS); awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL21, DEF_BURGUNDY_INPSEL21); awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL3, DEF_BURGUNDY_INPSEL3); awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINCD, DEF_BURGUNDY_GAINCD); awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINLINE, DEF_BURGUNDY_GAINLINE); awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMIC, DEF_BURGUNDY_GAINMIC); awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMODEM, DEF_BURGUNDY_GAINMODEM); awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, DEF_BURGUNDY_ATTENSPEAKER); awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENLINEOUT, DEF_BURGUNDY_ATTENLINEOUT); awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENHP, DEF_BURGUNDY_ATTENHP); awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_MASTER_VOLUME, DEF_BURGUNDY_MASTER_VOLUME); awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLCD, DEF_BURGUNDY_VOLCD); awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLLINE, DEF_BURGUNDY_VOLLINE); awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLMIC, DEF_BURGUNDY_VOLMIC); return 0;}static voidawacs_burgundy_write_volume(unsigned address, int volume){ int hardvolume,lvolume,rvolume; lvolume = (volume & 0xff) ? (volume & 0xff) + 155 : 0; rvolume = ((volume >>8)&0xff) ? ((volume >> 8)&0xff ) + 155 : 0; hardvolume = lvolume + (rvolume << 16); awacs_burgundy_wcw(address, hardvolume);}static intawacs_burgundy_read_volume(unsigned address){ int softvolume,wvolume; wvolume = awacs_burgundy_rcw(address); softvolume = (wvolume & 0xff) - 155; softvolume += (((wvolume >> 16) & 0xff) - 155)<<8; return softvolume > 0 ? softvolume : 0;}static intawacs_burgundy_read_mvolume(unsigned address){ int lvolume,rvolume,wvolume; wvolume = awacs_burgundy_rcw(address); wvolume &= 0xffff; rvolume = (wvolume & 0xff) - 155; lvolume = ((wvolume & 0xff00)>>8) - 155; return lvolume + (rvolume << 8);}static voidawacs_burgundy_write_mvolume(unsigned address, int volume){ int lvolume,rvolume,hardvolume; lvolume = (volume &0xff) ? (volume & 0xff) + 155 :0; rvolume = ((volume >>8) & 0xff) ? (volume >> 8) + 155 :0; hardvolume = lvolume + (rvolume << 8); hardvolume += (hardvolume << 16); awacs_burgundy_wcw(address, hardvolume);}/* End burgundy functions *//* Turn on sound output, needed on G3 desktop powermacs */static voidawacs_enable_amp(int spkr_vol){ struct adb_request req; awacs_spkr_vol = spkr_vol; if (sys_ctrler != SYS_CTRLER_CUDA) return;#ifdef CONFIG_ADB_CUDA /* turn on headphones */ cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, 4, 0); while (!req.complete) cuda_poll(); cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, 6, 0); while (!req.complete) cuda_poll(); /* turn on speaker */ cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, 3, (100 - (spkr_vol & 0xff)) * 32 / 100); while (!req.complete) cuda_poll(); cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, 5, (100 - ((spkr_vol >> 8) & 0xff)) * 32 / 100); while (!req.complete) cuda_poll(); cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, 1, 0x29); while (!req.complete) cuda_poll();#endif /* CONFIG_ADB_CUDA */}/*** Mid level stuff *********************************************************//* * /dev/mixer abstraction */static int awacs_mixer_ioctl(u_int cmd, u_long arg){ int data; switch (cmd) { case SOUND_MIXER_READ_DEVMASK: data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV | SOUND_MASK_ALTPCM | SOUND_MASK_MONITOR; return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_RECMASK: data = SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD; return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_RECSRC: data = 0; if (awacs_reg[0] & MASK_MUX_AUDIN) data |= SOUND_MASK_LINE; if (awacs_reg[0] & MASK_MUX_MIC) data |= SOUND_MASK_MIC; if (awacs_reg[0] & MASK_MUX_CD) data |= SOUND_MASK_CD; if (awacs_reg[1] & MASK_LOOPTHRU) data |= SOUND_MASK_MONITOR; return IOCTL_OUT(arg, data); case SOUND_MIXER_WRITE_RECSRC: IOCTL_IN(arg, data); data &= (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_MONITOR); awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC | MASK_MUX_AUDIN); awacs_reg[1] &= ~MASK_LOOPTHRU; if (data & SOUND_MASK_LINE) awacs_reg[0] |= MASK_MUX_AUDIN; if (data & SOUND_MASK_MIC) awacs_reg[0] |= MASK_MUX_MIC; if (data & SOUND_MASK_CD) awacs_reg[0] |= MASK_MUX_CD; if (data & SOUND_MASK_MONITOR)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -