📄 dmasound_awacs.c
字号:
/* stop the dma channel */ out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); while ( (in_le32(&awacs_txdma->status) & RUN) && count--) udelay(1); if (awacs) awacs_setup_for_beep(-1); out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[next_frg]))); beep_playing = 0; awacs_beep_state = 0; } /* this won't allow more than two frags to be in the output queue at once. (or one, if the max frags is 2 - because count can't exceed 2 in that case) */ while (write_sq.active < 2 && write_sq.active < write_sq.count) { count = (write_sq.count == write_sq.active + 1) ? write_sq.rear_size:write_sq.block_size ; if (count < write_sq.block_size) { if (!write_sq.syncing) /* last block not yet filled,*/ break; /* and we're not syncing or POST-ed */ else { /* pretend the block is full to force a new block to be started on the next write */ write_sq.rear_size = write_sq.block_size ; write_sq.syncing &= ~2 ; /* clear POST */ } } cp = &awacs_tx_cmds[next_frg]; st_le16(&cp->req_count, count); st_le16(&cp->xfer_status, 0); st_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS); /* put a STOP at the end of the queue - but only if we have space for it. This means that, if we under-run and we only have two fragments, we might re-play sound from an existing queued frag. I guess the solution to that is not to set two frags if you are likely to under-run... */ if (write_sq.count < write_sq.max_count) { if (++next_frg >= write_sq.max_count) next_frg = 0 ; /* wrap */ /* if we get here then we've underrun so we will stop*/ st_le16(&awacs_tx_cmds[next_frg].command, DBDMA_STOP); } /* set the dbdma controller going, if it is not already */ if (write_sq.active == 0) out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp)); (void)in_le32(&awacs_txdma->status); out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); ++write_sq.active; }}static void PMacPlay(void){ LOCK(); if (!awacs_sleeping) { unsigned long flags; spin_lock_irqsave(&dmasound.lock, flags); __PMacPlay(); spin_unlock_irqrestore(&dmasound.lock, flags); } UNLOCK();}static void PMacRecord(void){ unsigned long flags; if (read_sq.active) return; spin_lock_irqsave(&dmasound.lock, flags); /* This is all we have to do......Just start it up. */ out_le32(&awacs_rxdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); read_sq.active = 1; spin_unlock_irqrestore(&dmasound.lock, flags);}/* if the TX status comes up "DEAD" - reported on some Power Computing machines we need to re-start the dbdma - but from a different physical start address and with a different transfer length. It would get very messy to do this with the normal dbdma_cmd blocks - we would have to re-write the buffer start addresses each time. So, we will keep a single dbdma_cmd block which can be fiddled with. When DEAD status is first reported the content of the faulted dbdma block is copied into the emergency buffer and we note that the buffer is in use. we then bump the start physical address by the amount that was successfully output before it died. On any subsequent DEAD result we just do the bump-ups (we know that we are already using the emergency dbdma_cmd). CHECK: this just tries to "do it". It is possible that we should abandon xfers when the number of residual bytes gets below a certain value - I can see that this might cause a loop-forever if too small a transfer causes DEAD status. However this is a TODO for now - we'll see what gets reported. When we get a successful transfer result with the emergency buffer we just pretend that it completed using the original dmdma_cmd and carry on. The 'next_cmd' field will already point back to the original loop of blocks.*/static irqreturn_tpmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs){ int i = write_sq.front; int stat; int i_nowrap = write_sq.front; volatile struct dbdma_cmd *cp; /* != 0 when we are dealing with a DEAD xfer */ static int emergency_in_use; spin_lock(&dmasound.lock); while (write_sq.active > 0) { /* we expect to have done something*/ if (emergency_in_use) /* we are dealing with DEAD xfer */ cp = emergency_dbdma_cmd ; else cp = &awacs_tx_cmds[i]; stat = ld_le16(&cp->xfer_status); if (stat & DEAD) { unsigned short req, res ; unsigned int phy ;#ifdef DEBUG_DMASOUNDprintk("dmasound_pmac: tx-irq: xfer died - patching it up...\n") ;#endif /* to clear DEAD status we must first clear RUN set it to quiescent to be on the safe side */ (void)in_le32(&awacs_txdma->status); out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); write_sq.died++ ; if (!emergency_in_use) { /* new problem */ memcpy((void *)emergency_dbdma_cmd, (void *)cp, sizeof(struct dbdma_cmd)); emergency_in_use = 1; cp = emergency_dbdma_cmd; } /* now bump the values to reflect the amount we haven't yet shifted */ req = ld_le16(&cp->req_count); res = ld_le16(&cp->res_count); phy = ld_le32(&cp->phy_addr); phy += (req - res); st_le16(&cp->req_count, res); st_le16(&cp->res_count, 0); st_le16(&cp->xfer_status, 0); st_le32(&cp->phy_addr, phy); st_le32(&cp->cmd_dep, virt_to_bus(&awacs_tx_cmds[(i+1)%write_sq.max_count])); st_le16(&cp->command, OUTPUT_MORE | BR_ALWAYS | INTR_ALWAYS); /* point at our patched up command block */ out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp)); /* we must re-start the controller */ (void)in_le32(&awacs_txdma->status); /* should complete clearing the DEAD status */ out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); break; /* this block is still going */ } if ((stat & ACTIVE) == 0) break; /* this frame is still going */ if (emergency_in_use) emergency_in_use = 0 ; /* done that */ --write_sq.count; --write_sq.active; i_nowrap++; if (++i >= write_sq.max_count) i = 0; } /* if we stopped and we were not sync-ing - then we under-ran */ if( write_sq.syncing == 0 ){ stat = in_le32(&awacs_txdma->status) ; /* we hit the dbdma_stop */ if( (stat & ACTIVE) == 0 ) write_sq.xruns++ ; } /* if we used some data up then wake the writer to supply some more*/ if (i_nowrap != write_sq.front) WAKE_UP(write_sq.action_queue); write_sq.front = i; /* but make sure we funnel what we've already got */\ if (!awacs_sleeping) __PMacPlay(); /* make the wake-on-empty conditional on syncing */ if (!write_sq.active && (write_sq.syncing & 1)) WAKE_UP(write_sq.sync_queue); /* any time we're empty */ spin_unlock(&dmasound.lock); return IRQ_HANDLED;}static irqreturn_tpmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs){ int stat ; /* 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 initialized 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 IRQ_HANDLED; /* We also want to blow 'em off when shutting down. */ if (read_sq.active == 0) return IRQ_HANDLED; spin_lock(&dmasound.lock); /* 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 ((stat=awacs_rx_cmds[read_sq.rear].xfer_status)) { /* if we got a "DEAD" status then just log it for now. and try to restart dma. TODO: figure out how best to fix it up */ if (stat & DEAD){#ifdef DEBUG_DMASOUNDprintk("dmasound_pmac: rx-irq: DIED - attempting resurection\n");#endif /* to clear DEAD status we must first clear RUN set it to quiescent to be on the safe side */ (void)in_le32(&awacs_txdma->status); out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); awacs_rx_cmds[read_sq.rear].xfer_status = 0; awacs_rx_cmds[read_sq.rear].res_count = 0; read_sq.died++ ; (void)in_le32(&awacs_txdma->status); /* re-start the same block */ out_le32(&awacs_rxdma->cmdptr, virt_to_bus(&awacs_rx_cmds[read_sq.rear])); /* we must re-start the controller */ (void)in_le32(&awacs_rxdma->status); /* should complete clearing the DEAD status */ out_le32(&awacs_rxdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); spin_unlock(&dmasound.lock); return IRQ_HANDLED; /* try this block again */ } /* 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++; read_sq.xruns++ ; /* we overan */ if (read_sq.front >= read_sq.max_active) read_sq.front = 0; } } WAKE_UP(read_sq.action_queue); spin_unlock(&dmasound.lock); return IRQ_HANDLED;}static irqreturn_tpmac_awacs_intr(int irq, void *devid, struct pt_regs *regs){ int ctrl; int status; int r1; spin_lock(&dmasound.lock); ctrl = in_le32(&awacs->control); status = in_le32(&awacs->codec_stat); if (ctrl & MASK_PORTCHG) { /* tested on Screamer, should work on others too */ if (awacs_revision == AWACS_SCREAMER) { if (((status & MASK_HDPCONN) >> 3) && (hdp_connected == 0)) { hdp_connected = 1; r1 = awacs_reg[1] | MASK_SPKMUTE; awacs_reg[1] = r1; awacs_write(r1 | MASK_ADDR_MUTE); } else if (((status & MASK_HDPCONN) >> 3 == 0) && (hdp_connected == 1)) { hdp_connected = 0; r1 = awacs_reg[1] & ~MASK_SPKMUTE; awacs_reg[1] = r1; awacs_write(r1 | MASK_ADDR_MUTE); } } } if (ctrl & MASK_CNTLERR) { int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16; /* CHECK: we just swallow burgundy errors at the moment..*/ if (err != 0 && awacs_revision != AWACS_BURGUNDY) printk(KERN_ERR "dmasound_pmac: error %x\n", err); } /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ out_le32(&awacs->control, ctrl); spin_unlock(&dmasound.lock); return IRQ_HANDLED;}static voidawacs_write(int val){ int count = 300 ; if (awacs_revision >= AWACS_DACA || !awacs) return ; while ((in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) && count--) udelay(1) ; /* timeout is > 2 samples at lowest rate */ out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22)); (void)in_le32(&awacs->byteswap);}/* this is called when the beep timer expires... it will be called even if the beep has been overidden by other sound output.*/static void awacs_nosound(unsigned long xx){ unsigned long flags; int count = 600 ; /* > four samples at lowest rate */ spin_lock_irqsave(&dmasound.lock, flags); if (beep_playing) { st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); while ((in_le32(&awacs_txdma->status) & RUN) && count--) udelay(1); if (awacs) awacs_setup_for_beep(-1); beep_playing = 0; } spin_unlock_irqrestore(&dmasound.lock, flags);}/* * We generate the beep with a single dbdma command that loops a buffer * forever - without generating interrupts. * * So, to stop it you have to stop dma output as per awacs_nosound. */static int awacs_beep_event(struct input_dev *dev, unsigned int type, unsigned int code, int hz){ 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; if (type != EV_SND) return -1; switch (code) { case SND_BELL: if (hz) hz = 1000; break; case SND_TONE: break; default: return -1; } if (beep_buf == NULL) return -1; /* quick-hack fix for DACA, Burgundy & Tumbler */ if (awacs_revision >= AWACS_DACA){ srate = 44100 ; } else { 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) { /* cancel beep currently playing */ awacs_nosound(0); return 0; } spin_lock_irqsave(&dmasound.lock, flags); if (beep_playing || write_sq.active || beep_buf == NULL) { spin_unlock_irqrestore(&dmasound.lock, flags); return -1; /* too hard, sorry :-( */ } beep_playing = 1; st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS); spin_unlock_irqrestore(&dmasound.lock, flags); if (hz == beep_hz_cache && beep_vol == 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_vol; j = (j + f) & 0xffff; } beep_hz_cache = hz; beep_volume_cache = beep_vol; 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; spin_lock_irqsave(&dmasound.lock, flags); if (beep_playing) { /* i.e. haven't been terminated already */ int count = 300 ; out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); while ((in_le32(&awacs_txdma->status) & RUN) && count--) udelay(1); /* timeout > 2 samples at lowest rate*/ if (awacs) awacs_setup_for_beep(beep_speed); out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); (void)in_le32(&awacs_txdma->status); out_le32(&awacs_txdma->control, RUN | (RUN << 16)); } spin_unlock_irqrestore(&dmasound.lock, flags); return 0;}/* used in init and for wake-up */static voidload_awacs(void){ 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_revision == AWACS_SCREAMER) { awacs_write(awacs_reg[5] + MASK_ADDR5);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -