📄 vx_core.c
字号:
/* * Driver for Digigram VX soundcards * * Hardware core part * * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <sound/driver.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/device.h>#include <linux/firmware.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/asoundef.h>#include <sound/info.h>#include <asm/io.h>#include <sound/vx_core.h>#include "vx_cmd.h"MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");MODULE_DESCRIPTION("Common routines for Digigram VX drivers");MODULE_LICENSE("GPL");/* * snd_vx_delay - delay for the specified time * @xmsec: the time to delay in msec */void snd_vx_delay(vx_core_t *chip, int xmsec){ if (! in_interrupt() && xmsec >= 1000 / HZ) msleep(xmsec); else mdelay(xmsec);}/* * vx_check_reg_bit - wait for the specified bit is set/reset on a register * @reg: register to check * @mask: bit mask * @bit: resultant bit to be checked * @time: time-out of loop in msec * * returns zero if a bit matches, or a negative error code. */int snd_vx_check_reg_bit(vx_core_t *chip, int reg, int mask, int bit, int time){ unsigned long end_time = jiffies + (time * HZ + 999) / 1000;#ifdef CONFIG_SND_DEBUG static char *reg_names[VX_REG_MAX] = { "ICR", "CVR", "ISR", "IVR", "RXH", "RXM", "RXL", "DMA", "CDSP", "RFREQ", "RUER/V2", "DATA", "MEMIRQ", "ACQ", "BIT0", "BIT1", "MIC0", "MIC1", "MIC2", "MIC3", "INTCSR", "CNTRL", "GPIOC", "LOFREQ", "HIFREQ", "CSUER", "RUER" };#endif do { if ((snd_vx_inb(chip, reg) & mask) == bit) return 0; //snd_vx_delay(chip, 10); } while (time_after_eq(end_time, jiffies)); snd_printd(KERN_DEBUG "vx_check_reg_bit: timeout, reg=%s, mask=0x%x, val=0x%x\n", reg_names[reg], mask, snd_vx_inb(chip, reg)); return -EIO;}/* * vx_send_irq_dsp - set command irq bit * @num: the requested IRQ type, IRQ_XXX * * this triggers the specified IRQ request * returns 0 if successful, or a negative error code. * */static int vx_send_irq_dsp(vx_core_t *chip, int num){ int nirq; /* wait for Hc = 0 */ if (snd_vx_check_reg_bit(chip, VX_CVR, CVR_HC, 0, 200) < 0) return -EIO; nirq = num; if (vx_has_new_dsp(chip)) nirq += VXP_IRQ_OFFSET; vx_outb(chip, CVR, (nirq >> 1) | CVR_HC); return 0;}/* * vx_reset_chk - reset CHK bit on ISR * * returns 0 if successful, or a negative error code. */static int vx_reset_chk(vx_core_t *chip){ /* Reset irq CHK */ if (vx_send_irq_dsp(chip, IRQ_RESET_CHK) < 0) return -EIO; /* Wait until CHK = 0 */ if (vx_check_isr(chip, ISR_CHK, 0, 200) < 0) return -EIO; return 0;}/* * vx_transfer_end - terminate message transfer * @cmd: IRQ message to send (IRQ_MESS_XXX_END) * * returns 0 if successful, or a negative error code. * the error code can be VX-specific, retrieved via vx_get_error(). * NB: call with spinlock held! */static int vx_transfer_end(vx_core_t *chip, int cmd){ int err; if ((err = vx_reset_chk(chip)) < 0) return err; /* irq MESS_READ/WRITE_END */ if ((err = vx_send_irq_dsp(chip, cmd)) < 0) return err; /* Wait CHK = 1 */ if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0) return err; /* If error, Read RX */ if ((err = vx_inb(chip, ISR)) & ISR_ERR) { if ((err = vx_wait_for_rx_full(chip)) < 0) { snd_printd(KERN_DEBUG "transfer_end: error in rx_full\n"); return err; } err = vx_inb(chip, RXH) << 16; err |= vx_inb(chip, RXM) << 8; err |= vx_inb(chip, RXL); snd_printd(KERN_DEBUG "transfer_end: error = 0x%x\n", err); return -(VX_ERR_MASK | err); } return 0;}/* * vx_read_status - return the status rmh * @rmh: rmh record to store the status * * returns 0 if successful, or a negative error code. * the error code can be VX-specific, retrieved via vx_get_error(). * NB: call with spinlock held! */static int vx_read_status(vx_core_t *chip, struct vx_rmh *rmh){ int i, err, val, size; /* no read necessary? */ if (rmh->DspStat == RMH_SSIZE_FIXED && rmh->LgStat == 0) return 0; /* Wait for RX full (with timeout protection) * The first word of status is in RX */ err = vx_wait_for_rx_full(chip); if (err < 0) return err; /* Read RX */ val = vx_inb(chip, RXH) << 16; val |= vx_inb(chip, RXM) << 8; val |= vx_inb(chip, RXL); /* If status given by DSP, let's decode its size */ switch (rmh->DspStat) { case RMH_SSIZE_ARG: size = val & 0xff; rmh->Stat[0] = val & 0xffff00; rmh->LgStat = size + 1; break; case RMH_SSIZE_MASK: /* Let's count the arg numbers from a mask */ rmh->Stat[0] = val; size = 0; while (val) { if (val & 0x01) size++; val >>= 1; } rmh->LgStat = size + 1; break; default: /* else retrieve the status length given by the driver */ size = rmh->LgStat; rmh->Stat[0] = val; /* Val is the status 1st word */ size--; /* hence adjust remaining length */ break; } if (size < 1) return 0; snd_assert(size <= SIZE_MAX_STATUS, return -EINVAL); for (i = 1; i <= size; i++) { /* trigger an irq MESS_WRITE_NEXT */ err = vx_send_irq_dsp(chip, IRQ_MESS_WRITE_NEXT); if (err < 0) return err; /* Wait for RX full (with timeout protection) */ err = vx_wait_for_rx_full(chip); if (err < 0) return err; rmh->Stat[i] = vx_inb(chip, RXH) << 16; rmh->Stat[i] |= vx_inb(chip, RXM) << 8; rmh->Stat[i] |= vx_inb(chip, RXL); } return vx_transfer_end(chip, IRQ_MESS_WRITE_END);}#define MASK_MORE_THAN_1_WORD_COMMAND 0x00008000#define MASK_1_WORD_COMMAND 0x00ff7fff/* * vx_send_msg_nolock - send a DSP message and read back the status * @rmh: the rmh record to send and receive * * returns 0 if successful, or a negative error code. * the error code can be VX-specific, retrieved via vx_get_error(). * * this function doesn't call spinlock at all. */int vx_send_msg_nolock(vx_core_t *chip, struct vx_rmh *rmh){ int i, err; if (chip->chip_status & VX_STAT_IS_STALE) return -EBUSY; if ((err = vx_reset_chk(chip)) < 0) { snd_printd(KERN_DEBUG "vx_send_msg: vx_reset_chk error\n"); return err; }#if 0 printk(KERN_DEBUG "rmh: cmd = 0x%06x, length = %d, stype = %d\n", rmh->Cmd[0], rmh->LgCmd, rmh->DspStat); if (rmh->LgCmd > 1) { printk(KERN_DEBUG " "); for (i = 1; i < rmh->LgCmd; i++) printk("0x%06x ", rmh->Cmd[i]); printk("\n"); }#endif /* Check bit M is set according to length of the command */ if (rmh->LgCmd > 1) rmh->Cmd[0] |= MASK_MORE_THAN_1_WORD_COMMAND; else rmh->Cmd[0] &= MASK_1_WORD_COMMAND; /* Wait for TX empty */ if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) { snd_printd(KERN_DEBUG "vx_send_msg: wait tx empty error\n"); return err; } /* Write Cmd[0] */ vx_outb(chip, TXH, (rmh->Cmd[0] >> 16) & 0xff); vx_outb(chip, TXM, (rmh->Cmd[0] >> 8) & 0xff); vx_outb(chip, TXL, rmh->Cmd[0] & 0xff); /* Trigger irq MESSAGE */ if ((err = vx_send_irq_dsp(chip, IRQ_MESSAGE)) < 0) { snd_printd(KERN_DEBUG "vx_send_msg: send IRQ_MESSAGE error\n"); return err; } /* Wait for CHK = 1 */ if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0) return err; /* If error, get error value from RX */ if (vx_inb(chip, ISR) & ISR_ERR) { if ((err = vx_wait_for_rx_full(chip)) < 0) { snd_printd(KERN_DEBUG "vx_send_msg: rx_full read error\n"); return err; } err = vx_inb(chip, RXH) << 16; err |= vx_inb(chip, RXM) << 8; err |= vx_inb(chip, RXL); snd_printd(KERN_DEBUG "msg got error = 0x%x at cmd[0]\n", err); err = -(VX_ERR_MASK | err); return err; } /* Send the other words */ if (rmh->LgCmd > 1) { for (i = 1; i < rmh->LgCmd; i++) { /* Wait for TX ready */ if ((err = vx_wait_isr_bit(chip, ISR_TX_READY)) < 0) { snd_printd(KERN_DEBUG "vx_send_msg: tx_ready error\n"); return err; } /* Write Cmd[i] */ vx_outb(chip, TXH, (rmh->Cmd[i] >> 16) & 0xff); vx_outb(chip, TXM, (rmh->Cmd[i] >> 8) & 0xff); vx_outb(chip, TXL, rmh->Cmd[i] & 0xff); /* Trigger irq MESS_READ_NEXT */ if ((err = vx_send_irq_dsp(chip, IRQ_MESS_READ_NEXT)) < 0) { snd_printd(KERN_DEBUG "vx_send_msg: IRQ_READ_NEXT error\n"); return err; } } /* Wait for TX empty */ if ((err = vx_wait_isr_bit(chip, ISR_TX_READY)) < 0) { snd_printd(KERN_DEBUG "vx_send_msg: TX_READY error\n"); return err; } /* End of transfer */ err = vx_transfer_end(chip, IRQ_MESS_READ_END); if (err < 0) return err; } return vx_read_status(chip, rmh);}/* * vx_send_msg - send a DSP message with spinlock * @rmh: the rmh record to send and receive * * returns 0 if successful, or a negative error code. * see vx_send_msg_nolock(). */int vx_send_msg(vx_core_t *chip, struct vx_rmh *rmh){ unsigned long flags; int err; spin_lock_irqsave(&chip->lock, flags); err = vx_send_msg_nolock(chip, rmh); spin_unlock_irqrestore(&chip->lock, flags); return err;}/* * vx_send_rih_nolock - send an RIH to xilinx * @cmd: the command to send * * returns 0 if successful, or a negative error code. * the error code can be VX-specific, retrieved via vx_get_error(). * * this function doesn't call spinlock at all. * * unlike RMH, no command is sent to DSP. */int vx_send_rih_nolock(vx_core_t *chip, int cmd){ int err; if (chip->chip_status & VX_STAT_IS_STALE) return -EBUSY;#if 0 printk(KERN_DEBUG "send_rih: cmd = 0x%x\n", cmd);#endif if ((err = vx_reset_chk(chip)) < 0) return err; /* send the IRQ */ if ((err = vx_send_irq_dsp(chip, cmd)) < 0) return err; /* Wait CHK = 1 */ if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0) return err; /* If error, read RX */ if (vx_inb(chip, ISR) & ISR_ERR) { if ((err = vx_wait_for_rx_full(chip)) < 0) return err; err = vx_inb(chip, RXH) << 16; err |= vx_inb(chip, RXM) << 8; err |= vx_inb(chip, RXL); return -(VX_ERR_MASK | err); } return 0;}/* * vx_send_rih - send an RIH with spinlock * @cmd: the command to send * * see vx_send_rih_nolock(). */int vx_send_rih(vx_core_t *chip, int cmd){ unsigned long flags; int err;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -