/* * 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_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; }
/* * reset codec bit */ static void vxp_reset_codec(vx_core_t *_chip) { struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; /* Set the reset CODEC bit to 1. */ vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_CODEC_RESET_MASK); vx_inb(chip, CDSP); snd_vx_delay(_chip, 10); /* Set the reset CODEC bit to 0. */ chip->regCDSP &= ~VXP_CDSP_CODEC_RESET_MASK; vx_outb(chip, CDSP, chip->regCDSP); vx_inb(chip, CDSP); snd_vx_delay(_chip, 1); }
static void vxp_reset_dsp(vx_core_t *_chip) { struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; /* set the reset dsp bit to 1 */ vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_DSP_RESET_MASK); vx_inb(chip, CDSP); mdelay(XX_DSP_RESET_WAIT_TIME); /* reset the bit */ chip->regCDSP &= ~VXP_CDSP_DSP_RESET_MASK; vx_outb(chip, CDSP, chip->regCDSP); vx_inb(chip, CDSP); mdelay(XX_DSP_RESET_WAIT_TIME); }
/* * reset codec bit */ static void vxp_reset_codec(struct vx_core *_chip) { struct snd_vxpocket *chip = to_vxpocket(_chip); /* Set the reset CODEC bit to 1. */ vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_CODEC_RESET_MASK); vx_inb(chip, CDSP); msleep(10); /* Set the reset CODEC bit to 0. */ chip->regCDSP &= ~VXP_CDSP_CODEC_RESET_MASK; vx_outb(chip, CDSP, chip->regCDSP); vx_inb(chip, CDSP); msleep(1); }
/* * read three pending pcm bytes via inb() */ static void vx_pcm_read_per_bytes(vx_core_t *chip, snd_pcm_runtime_t *runtime, vx_pipe_t *pipe) { int offset = pipe->hw_ptr; unsigned char *buf = (unsigned char *)(runtime->dma_area + offset); *buf++ = vx_inb(chip, RXH); if (++offset >= pipe->buffer_bytes) { offset = 0; buf = (unsigned char *)runtime->dma_area; } *buf++ = vx_inb(chip, RXM); if (++offset >= pipe->buffer_bytes) { offset = 0; buf = (unsigned char *)runtime->dma_area; } *buf++ = vx_inb(chip, RXL); if (++offset >= pipe->buffer_bytes) { offset = 0; buf = (unsigned char *)runtime->dma_area; } pipe->hw_ptr = offset; }
/* * vx_check_magic - check the magic word on xilinx * * returns zero if a magic word is detected, or a negative error code. */ static int vx_check_magic(vx_core_t *chip) { unsigned long end_time = jiffies + HZ / 5; int c; do { c = vx_inb(chip, CDSP); if (c == CDSP_MAGIC) return 0; snd_vx_delay(chip, 10); } while (time_after_eq(end_time, jiffies)); snd_printk(KERN_ERR "cannot find xilinx magic word (%x)\n", c); return -EIO; }
/* * vx_test_and_ack - test and acknowledge interrupt * * called from irq hander, too * * spinlock held! */ static int vxp_test_and_ack(vx_core_t *_chip) { struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; /* not booted yet? */ if (! (_chip->chip_status & VX_STAT_XILINX_LOADED)) return -ENXIO; if (! (vx_inb(chip, DIALOG) & VXP_DLG_MEMIRQ_MASK)) return -EIO; /* ok, interrupts generated, now ack it */ /* set ACQUIT bit up and down */ vx_outb(chip, DIALOG, chip->regDIALOG | VXP_DLG_ACK_MEMIRQ_MASK); /* useless read just to spend some time and maintain * the ACQUIT signal up for a while ( a bus cycle ) */ vx_inb(chip, DIALOG); vx_outb(chip, DIALOG, chip->regDIALOG & ~VXP_DLG_ACK_MEMIRQ_MASK); return 0; }
/* * vx_setup_pseudo_dma - set up the pseudo dma read/write mode. * @do_write: 0 = read, 1 = set up for DMA write */ static void vx_setup_pseudo_dma(vx_core_t *_chip, int do_write) { struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; /* Interrupt mode and HREQ pin enabled for host transmit / receive data transfers */ vx_outb(chip, ICR, do_write ? ICR_TREQ : ICR_RREQ); /* Reset the pseudo-dma register */ vx_inb(chip, ISR); vx_outb(chip, ISR, 0); /* Select DMA in read/write transfer mode and in 16-bit accesses */ chip->regDIALOG |= VXP_DLG_DMA16_SEL_MASK; chip->regDIALOG |= do_write ? VXP_DLG_DMAWRITE_SEL_MASK : VXP_DLG_DMAREAD_SEL_MASK; vx_outb(chip, DIALOG, chip->regDIALOG); }
/* * vx_load_xilinx_binary - load the xilinx binary image * the binary image is the binary array converted from the bitstream file. */ static int vxp_load_xilinx_binary(vx_core_t *_chip, const struct firmware *fw) { struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; unsigned int i; int c; int regCSUER, regRUER; unsigned char *image; unsigned char data; /* Switch to programmation mode */ chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK; vx_outb(chip, DIALOG, chip->regDIALOG); /* Save register CSUER and RUER */ regCSUER = vx_inb(chip, CSUER); regRUER = vx_inb(chip, RUER); /* reset HF0 and HF1 */ vx_outb(chip, ICR, 0); /* Wait for answer HF2 equal to 1 */ snd_printdd(KERN_DEBUG "check ISR_HF2\n"); if (vx_check_isr(_chip, ISR_HF2, ISR_HF2, 20) < 0) goto _error; /* set HF1 for loading xilinx binary */ vx_outb(chip, ICR, ICR_HF1); image = fw->data; for (i = 0; i < fw->size; i++, image++) { data = *image; if (vx_wait_isr_bit(_chip, ISR_TX_EMPTY) < 0) goto _error; vx_outb(chip, TXL, data); /* wait for reading */ if (vx_wait_for_rx_full(_chip) < 0) goto _error; c = vx_inb(chip, RXL); if (c != (int)data) snd_printk(KERN_ERR "vxpocket: load xilinx mismatch at %d: 0x%x != 0x%x\n", i, c, (int)data); } /* reset HF1 */ vx_outb(chip, ICR, 0); /* wait for HF3 */ if (vx_check_isr(_chip, ISR_HF3, ISR_HF3, 20) < 0) goto _error; /* read the number of bytes received */ if (vx_wait_for_rx_full(_chip) < 0) goto _error; c = (int)vx_inb(chip, RXH) << 16; c |= (int)vx_inb(chip, RXM) << 8; c |= vx_inb(chip, RXL); snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%x\n", c, fw->size); vx_outb(chip, ICR, ICR_HF0); /* TEMPO 250ms : wait until Xilinx is downloaded */ snd_vx_delay(_chip, 300); /* test magical word */ if (vx_check_magic(_chip) < 0) goto _error; /* Restore register 0x0E and 0x0F (thus replacing COR and FCSR) */ vx_outb(chip, CSUER, regCSUER); vx_outb(chip, RUER, regRUER); /* Reset the Xilinx's signal enabling IO access */ chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK; vx_outb(chip, DIALOG, chip->regDIALOG); vx_inb(chip, DIALOG); snd_vx_delay(_chip, 10); chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK; vx_outb(chip, DIALOG, chip->regDIALOG); vx_inb(chip, DIALOG); /* Reset of the Codec */ vxp_reset_codec(_chip); vx_reset_dsp(_chip); return 0; _error: vx_outb(chip, CSUER, regCSUER); vx_outb(chip, RUER, regRUER); chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK; vx_outb(chip, DIALOG, chip->regDIALOG); return -EIO; }
/* * 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_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); }