162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for Digigram VXpocket soundcards 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * lowlevel routines for VXpocket soundcards 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/firmware.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <sound/core.h> 1562306a36Sopenharmony_ci#include "vxpocket.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic const int vxp_reg_offset[VX_REG_MAX] = { 1962306a36Sopenharmony_ci [VX_ICR] = 0x00, // ICR 2062306a36Sopenharmony_ci [VX_CVR] = 0x01, // CVR 2162306a36Sopenharmony_ci [VX_ISR] = 0x02, // ISR 2262306a36Sopenharmony_ci [VX_IVR] = 0x03, // IVR 2362306a36Sopenharmony_ci [VX_RXH] = 0x05, // RXH 2462306a36Sopenharmony_ci [VX_RXM] = 0x06, // RXM 2562306a36Sopenharmony_ci [VX_RXL] = 0x07, // RXL 2662306a36Sopenharmony_ci [VX_DMA] = 0x04, // DMA 2762306a36Sopenharmony_ci [VX_CDSP] = 0x08, // CDSP 2862306a36Sopenharmony_ci [VX_LOFREQ] = 0x09, // LFREQ 2962306a36Sopenharmony_ci [VX_HIFREQ] = 0x0a, // HFREQ 3062306a36Sopenharmony_ci [VX_DATA] = 0x0b, // DATA 3162306a36Sopenharmony_ci [VX_MICRO] = 0x0c, // MICRO 3262306a36Sopenharmony_ci [VX_DIALOG] = 0x0d, // DIALOG 3362306a36Sopenharmony_ci [VX_CSUER] = 0x0e, // CSUER 3462306a36Sopenharmony_ci [VX_RUER] = 0x0f, // RUER 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic inline unsigned long vxp_reg_addr(struct vx_core *_chip, int reg) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 4162306a36Sopenharmony_ci return chip->port + vxp_reg_offset[reg]; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * snd_vx_inb - read a byte from the register 4662306a36Sopenharmony_ci * @offset: register offset 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_cistatic unsigned char vxp_inb(struct vx_core *chip, int offset) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci return inb(vxp_reg_addr(chip, offset)); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* 5462306a36Sopenharmony_ci * snd_vx_outb - write a byte on the register 5562306a36Sopenharmony_ci * @offset: the register offset 5662306a36Sopenharmony_ci * @val: the value to write 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistatic void vxp_outb(struct vx_core *chip, int offset, unsigned char val) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci outb(val, vxp_reg_addr(chip, offset)); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * redefine macros to call directly 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ci#undef vx_inb 6762306a36Sopenharmony_ci#define vx_inb(chip,reg) vxp_inb((struct vx_core *)(chip), VX_##reg) 6862306a36Sopenharmony_ci#undef vx_outb 6962306a36Sopenharmony_ci#define vx_outb(chip,reg,val) vxp_outb((struct vx_core *)(chip), VX_##reg,val) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* 7362306a36Sopenharmony_ci * vx_check_magic - check the magic word on xilinx 7462306a36Sopenharmony_ci * 7562306a36Sopenharmony_ci * returns zero if a magic word is detected, or a negative error code. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_cistatic int vx_check_magic(struct vx_core *chip) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci unsigned long end_time = jiffies + HZ / 5; 8062306a36Sopenharmony_ci int c; 8162306a36Sopenharmony_ci do { 8262306a36Sopenharmony_ci c = vx_inb(chip, CDSP); 8362306a36Sopenharmony_ci if (c == CDSP_MAGIC) 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci msleep(10); 8662306a36Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 8762306a36Sopenharmony_ci snd_printk(KERN_ERR "cannot find xilinx magic word (%x)\n", c); 8862306a36Sopenharmony_ci return -EIO; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * vx_reset_dsp - reset the DSP 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define XX_DSP_RESET_WAIT_TIME 2 /* ms */ 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void vxp_reset_dsp(struct vx_core *_chip) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* set the reset dsp bit to 1 */ 10362306a36Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_DSP_RESET_MASK); 10462306a36Sopenharmony_ci vx_inb(chip, CDSP); 10562306a36Sopenharmony_ci mdelay(XX_DSP_RESET_WAIT_TIME); 10662306a36Sopenharmony_ci /* reset the bit */ 10762306a36Sopenharmony_ci chip->regCDSP &= ~VXP_CDSP_DSP_RESET_MASK; 10862306a36Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 10962306a36Sopenharmony_ci vx_inb(chip, CDSP); 11062306a36Sopenharmony_ci mdelay(XX_DSP_RESET_WAIT_TIME); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* 11462306a36Sopenharmony_ci * reset codec bit 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_cistatic void vxp_reset_codec(struct vx_core *_chip) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Set the reset CODEC bit to 1. */ 12162306a36Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_CODEC_RESET_MASK); 12262306a36Sopenharmony_ci vx_inb(chip, CDSP); 12362306a36Sopenharmony_ci msleep(10); 12462306a36Sopenharmony_ci /* Set the reset CODEC bit to 0. */ 12562306a36Sopenharmony_ci chip->regCDSP &= ~VXP_CDSP_CODEC_RESET_MASK; 12662306a36Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 12762306a36Sopenharmony_ci vx_inb(chip, CDSP); 12862306a36Sopenharmony_ci msleep(1); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* 13262306a36Sopenharmony_ci * vx_load_xilinx_binary - load the xilinx binary image 13362306a36Sopenharmony_ci * the binary image is the binary array converted from the bitstream file. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_cistatic int vxp_load_xilinx_binary(struct vx_core *_chip, const struct firmware *fw) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 13862306a36Sopenharmony_ci unsigned int i; 13962306a36Sopenharmony_ci int c; 14062306a36Sopenharmony_ci int regCSUER, regRUER; 14162306a36Sopenharmony_ci const unsigned char *image; 14262306a36Sopenharmony_ci unsigned char data; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* Switch to programmation mode */ 14562306a36Sopenharmony_ci chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK; 14662306a36Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* Save register CSUER and RUER */ 14962306a36Sopenharmony_ci regCSUER = vx_inb(chip, CSUER); 15062306a36Sopenharmony_ci regRUER = vx_inb(chip, RUER); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* reset HF0 and HF1 */ 15362306a36Sopenharmony_ci vx_outb(chip, ICR, 0); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Wait for answer HF2 equal to 1 */ 15662306a36Sopenharmony_ci snd_printdd(KERN_DEBUG "check ISR_HF2\n"); 15762306a36Sopenharmony_ci if (vx_check_isr(_chip, ISR_HF2, ISR_HF2, 20) < 0) 15862306a36Sopenharmony_ci goto _error; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* set HF1 for loading xilinx binary */ 16162306a36Sopenharmony_ci vx_outb(chip, ICR, ICR_HF1); 16262306a36Sopenharmony_ci image = fw->data; 16362306a36Sopenharmony_ci for (i = 0; i < fw->size; i++, image++) { 16462306a36Sopenharmony_ci data = *image; 16562306a36Sopenharmony_ci if (vx_wait_isr_bit(_chip, ISR_TX_EMPTY) < 0) 16662306a36Sopenharmony_ci goto _error; 16762306a36Sopenharmony_ci vx_outb(chip, TXL, data); 16862306a36Sopenharmony_ci /* wait for reading */ 16962306a36Sopenharmony_ci if (vx_wait_for_rx_full(_chip) < 0) 17062306a36Sopenharmony_ci goto _error; 17162306a36Sopenharmony_ci c = vx_inb(chip, RXL); 17262306a36Sopenharmony_ci if (c != (int)data) 17362306a36Sopenharmony_ci snd_printk(KERN_ERR "vxpocket: load xilinx mismatch at %d: 0x%x != 0x%x\n", i, c, (int)data); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* reset HF1 */ 17762306a36Sopenharmony_ci vx_outb(chip, ICR, 0); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* wait for HF3 */ 18062306a36Sopenharmony_ci if (vx_check_isr(_chip, ISR_HF3, ISR_HF3, 20) < 0) 18162306a36Sopenharmony_ci goto _error; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* read the number of bytes received */ 18462306a36Sopenharmony_ci if (vx_wait_for_rx_full(_chip) < 0) 18562306a36Sopenharmony_ci goto _error; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci c = (int)vx_inb(chip, RXH) << 16; 18862306a36Sopenharmony_ci c |= (int)vx_inb(chip, RXM) << 8; 18962306a36Sopenharmony_ci c |= vx_inb(chip, RXL); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%zx\n", c, fw->size); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci vx_outb(chip, ICR, ICR_HF0); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* TEMPO 250ms : wait until Xilinx is downloaded */ 19662306a36Sopenharmony_ci msleep(300); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* test magical word */ 19962306a36Sopenharmony_ci if (vx_check_magic(_chip) < 0) 20062306a36Sopenharmony_ci goto _error; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* Restore register 0x0E and 0x0F (thus replacing COR and FCSR) */ 20362306a36Sopenharmony_ci vx_outb(chip, CSUER, regCSUER); 20462306a36Sopenharmony_ci vx_outb(chip, RUER, regRUER); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* Reset the Xilinx's signal enabling IO access */ 20762306a36Sopenharmony_ci chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK; 20862306a36Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG); 20962306a36Sopenharmony_ci vx_inb(chip, DIALOG); 21062306a36Sopenharmony_ci msleep(10); 21162306a36Sopenharmony_ci chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK; 21262306a36Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG); 21362306a36Sopenharmony_ci vx_inb(chip, DIALOG); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Reset of the Codec */ 21662306a36Sopenharmony_ci vxp_reset_codec(_chip); 21762306a36Sopenharmony_ci vx_reset_dsp(_chip); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci _error: 22262306a36Sopenharmony_ci vx_outb(chip, CSUER, regCSUER); 22362306a36Sopenharmony_ci vx_outb(chip, RUER, regRUER); 22462306a36Sopenharmony_ci chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK; 22562306a36Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG); 22662306a36Sopenharmony_ci return -EIO; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* 23162306a36Sopenharmony_ci * vxp_load_dsp - load_dsp callback 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_cistatic int vxp_load_dsp(struct vx_core *vx, int index, const struct firmware *fw) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci int err; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci switch (index) { 23862306a36Sopenharmony_ci case 0: 23962306a36Sopenharmony_ci /* xilinx boot */ 24062306a36Sopenharmony_ci err = vx_check_magic(vx); 24162306a36Sopenharmony_ci if (err < 0) 24262306a36Sopenharmony_ci return err; 24362306a36Sopenharmony_ci err = snd_vx_load_boot_image(vx, fw); 24462306a36Sopenharmony_ci if (err < 0) 24562306a36Sopenharmony_ci return err; 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci case 1: 24862306a36Sopenharmony_ci /* xilinx image */ 24962306a36Sopenharmony_ci return vxp_load_xilinx_binary(vx, fw); 25062306a36Sopenharmony_ci case 2: 25162306a36Sopenharmony_ci /* DSP boot */ 25262306a36Sopenharmony_ci return snd_vx_dsp_boot(vx, fw); 25362306a36Sopenharmony_ci case 3: 25462306a36Sopenharmony_ci /* DSP image */ 25562306a36Sopenharmony_ci return snd_vx_dsp_load(vx, fw); 25662306a36Sopenharmony_ci default: 25762306a36Sopenharmony_ci snd_BUG(); 25862306a36Sopenharmony_ci return -EINVAL; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci/* 26462306a36Sopenharmony_ci * vx_test_and_ack - test and acknowledge interrupt 26562306a36Sopenharmony_ci * 26662306a36Sopenharmony_ci * called from irq hander, too 26762306a36Sopenharmony_ci * 26862306a36Sopenharmony_ci * spinlock held! 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_cistatic int vxp_test_and_ack(struct vx_core *_chip) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* not booted yet? */ 27562306a36Sopenharmony_ci if (! (_chip->chip_status & VX_STAT_XILINX_LOADED)) 27662306a36Sopenharmony_ci return -ENXIO; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (! (vx_inb(chip, DIALOG) & VXP_DLG_MEMIRQ_MASK)) 27962306a36Sopenharmony_ci return -EIO; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* ok, interrupts generated, now ack it */ 28262306a36Sopenharmony_ci /* set ACQUIT bit up and down */ 28362306a36Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG | VXP_DLG_ACK_MEMIRQ_MASK); 28462306a36Sopenharmony_ci /* useless read just to spend some time and maintain 28562306a36Sopenharmony_ci * the ACQUIT signal up for a while ( a bus cycle ) 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_ci vx_inb(chip, DIALOG); 28862306a36Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG & ~VXP_DLG_ACK_MEMIRQ_MASK); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/* 29562306a36Sopenharmony_ci * vx_validate_irq - enable/disable IRQ 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_cistatic void vxp_validate_irq(struct vx_core *_chip, int enable) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Set the interrupt enable bit to 1 in CDSP register */ 30262306a36Sopenharmony_ci if (enable) 30362306a36Sopenharmony_ci chip->regCDSP |= VXP_CDSP_VALID_IRQ_MASK; 30462306a36Sopenharmony_ci else 30562306a36Sopenharmony_ci chip->regCDSP &= ~VXP_CDSP_VALID_IRQ_MASK; 30662306a36Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci/* 31062306a36Sopenharmony_ci * vx_setup_pseudo_dma - set up the pseudo dma read/write mode. 31162306a36Sopenharmony_ci * @do_write: 0 = read, 1 = set up for DMA write 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_cistatic void vx_setup_pseudo_dma(struct vx_core *_chip, int do_write) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* Interrupt mode and HREQ pin enabled for host transmit / receive data transfers */ 31862306a36Sopenharmony_ci vx_outb(chip, ICR, do_write ? ICR_TREQ : ICR_RREQ); 31962306a36Sopenharmony_ci /* Reset the pseudo-dma register */ 32062306a36Sopenharmony_ci vx_inb(chip, ISR); 32162306a36Sopenharmony_ci vx_outb(chip, ISR, 0); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Select DMA in read/write transfer mode and in 16-bit accesses */ 32462306a36Sopenharmony_ci chip->regDIALOG |= VXP_DLG_DMA16_SEL_MASK; 32562306a36Sopenharmony_ci chip->regDIALOG |= do_write ? VXP_DLG_DMAWRITE_SEL_MASK : VXP_DLG_DMAREAD_SEL_MASK; 32662306a36Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/* 33162306a36Sopenharmony_ci * vx_release_pseudo_dma - disable the pseudo-DMA mode 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_cistatic void vx_release_pseudo_dma(struct vx_core *_chip) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Disable DMA and 16-bit accesses */ 33862306a36Sopenharmony_ci chip->regDIALOG &= ~(VXP_DLG_DMAWRITE_SEL_MASK| 33962306a36Sopenharmony_ci VXP_DLG_DMAREAD_SEL_MASK| 34062306a36Sopenharmony_ci VXP_DLG_DMA16_SEL_MASK); 34162306a36Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG); 34262306a36Sopenharmony_ci /* HREQ pin disabled. */ 34362306a36Sopenharmony_ci vx_outb(chip, ICR, 0); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/* 34762306a36Sopenharmony_ci * vx_pseudo_dma_write - write bulk data on pseudo-DMA mode 34862306a36Sopenharmony_ci * @count: data length to transfer in bytes 34962306a36Sopenharmony_ci * 35062306a36Sopenharmony_ci * data size must be aligned to 6 bytes to ensure the 24bit alignment on DSP. 35162306a36Sopenharmony_ci * NB: call with a certain lock! 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_cistatic void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, 35462306a36Sopenharmony_ci struct vx_pipe *pipe, int count) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci long port = vxp_reg_addr(chip, VX_DMA); 35762306a36Sopenharmony_ci int offset = pipe->hw_ptr; 35862306a36Sopenharmony_ci unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci vx_setup_pseudo_dma(chip, 1); 36162306a36Sopenharmony_ci if (offset + count >= pipe->buffer_bytes) { 36262306a36Sopenharmony_ci int length = pipe->buffer_bytes - offset; 36362306a36Sopenharmony_ci count -= length; 36462306a36Sopenharmony_ci length >>= 1; /* in 16bit words */ 36562306a36Sopenharmony_ci /* Transfer using pseudo-dma. */ 36662306a36Sopenharmony_ci for (; length > 0; length--) { 36762306a36Sopenharmony_ci outw(*addr, port); 36862306a36Sopenharmony_ci addr++; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci addr = (unsigned short *)runtime->dma_area; 37162306a36Sopenharmony_ci pipe->hw_ptr = 0; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci pipe->hw_ptr += count; 37462306a36Sopenharmony_ci count >>= 1; /* in 16bit words */ 37562306a36Sopenharmony_ci /* Transfer using pseudo-dma. */ 37662306a36Sopenharmony_ci for (; count > 0; count--) { 37762306a36Sopenharmony_ci outw(*addr, port); 37862306a36Sopenharmony_ci addr++; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci vx_release_pseudo_dma(chip); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/* 38562306a36Sopenharmony_ci * vx_pseudo_dma_read - read bulk data on pseudo DMA mode 38662306a36Sopenharmony_ci * @offset: buffer offset in bytes 38762306a36Sopenharmony_ci * @count: data length to transfer in bytes 38862306a36Sopenharmony_ci * 38962306a36Sopenharmony_ci * the read length must be aligned to 6 bytes, as well as write. 39062306a36Sopenharmony_ci * NB: call with a certain lock! 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_cistatic void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, 39362306a36Sopenharmony_ci struct vx_pipe *pipe, int count) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct snd_vxpocket *pchip = to_vxpocket(chip); 39662306a36Sopenharmony_ci long port = vxp_reg_addr(chip, VX_DMA); 39762306a36Sopenharmony_ci int offset = pipe->hw_ptr; 39862306a36Sopenharmony_ci unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (snd_BUG_ON(count % 2)) 40162306a36Sopenharmony_ci return; 40262306a36Sopenharmony_ci vx_setup_pseudo_dma(chip, 0); 40362306a36Sopenharmony_ci if (offset + count >= pipe->buffer_bytes) { 40462306a36Sopenharmony_ci int length = pipe->buffer_bytes - offset; 40562306a36Sopenharmony_ci count -= length; 40662306a36Sopenharmony_ci length >>= 1; /* in 16bit words */ 40762306a36Sopenharmony_ci /* Transfer using pseudo-dma. */ 40862306a36Sopenharmony_ci for (; length > 0; length--) 40962306a36Sopenharmony_ci *addr++ = inw(port); 41062306a36Sopenharmony_ci addr = (unsigned short *)runtime->dma_area; 41162306a36Sopenharmony_ci pipe->hw_ptr = 0; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci pipe->hw_ptr += count; 41462306a36Sopenharmony_ci count >>= 1; /* in 16bit words */ 41562306a36Sopenharmony_ci /* Transfer using pseudo-dma. */ 41662306a36Sopenharmony_ci for (; count > 1; count--) 41762306a36Sopenharmony_ci *addr++ = inw(port); 41862306a36Sopenharmony_ci /* Disable DMA */ 41962306a36Sopenharmony_ci pchip->regDIALOG &= ~VXP_DLG_DMAREAD_SEL_MASK; 42062306a36Sopenharmony_ci vx_outb(chip, DIALOG, pchip->regDIALOG); 42162306a36Sopenharmony_ci /* Read the last word (16 bits) */ 42262306a36Sopenharmony_ci *addr = inw(port); 42362306a36Sopenharmony_ci /* Disable 16-bit accesses */ 42462306a36Sopenharmony_ci pchip->regDIALOG &= ~VXP_DLG_DMA16_SEL_MASK; 42562306a36Sopenharmony_ci vx_outb(chip, DIALOG, pchip->regDIALOG); 42662306a36Sopenharmony_ci /* HREQ pin disabled. */ 42762306a36Sopenharmony_ci vx_outb(chip, ICR, 0); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci/* 43262306a36Sopenharmony_ci * write a codec data (24bit) 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_cistatic void vxp_write_codec_reg(struct vx_core *chip, int codec, unsigned int data) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci int i; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* Activate access to the corresponding codec register */ 43962306a36Sopenharmony_ci if (! codec) 44062306a36Sopenharmony_ci vx_inb(chip, LOFREQ); 44162306a36Sopenharmony_ci else 44262306a36Sopenharmony_ci vx_inb(chip, CODEC2); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* We have to send 24 bits (3 x 8 bits). Start with most signif. Bit */ 44562306a36Sopenharmony_ci for (i = 0; i < 24; i++, data <<= 1) 44662306a36Sopenharmony_ci vx_outb(chip, DATA, ((data & 0x800000) ? VX_DATA_CODEC_MASK : 0)); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* Terminate access to codec registers */ 44962306a36Sopenharmony_ci vx_inb(chip, HIFREQ); 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* 45462306a36Sopenharmony_ci * vx_set_mic_boost - set mic boost level (on vxp440 only) 45562306a36Sopenharmony_ci * @boost: 0 = 20dB, 1 = +38dB 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_civoid vx_set_mic_boost(struct vx_core *chip, int boost) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct snd_vxpocket *pchip = to_vxpocket(chip); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (chip->chip_status & VX_STAT_IS_STALE) 46262306a36Sopenharmony_ci return; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci mutex_lock(&chip->lock); 46562306a36Sopenharmony_ci if (pchip->regCDSP & P24_CDSP_MICS_SEL_MASK) { 46662306a36Sopenharmony_ci if (boost) { 46762306a36Sopenharmony_ci /* boost: 38 dB */ 46862306a36Sopenharmony_ci pchip->regCDSP &= ~P24_CDSP_MIC20_SEL_MASK; 46962306a36Sopenharmony_ci pchip->regCDSP |= P24_CDSP_MIC38_SEL_MASK; 47062306a36Sopenharmony_ci } else { 47162306a36Sopenharmony_ci /* minimum value: 20 dB */ 47262306a36Sopenharmony_ci pchip->regCDSP |= P24_CDSP_MIC20_SEL_MASK; 47362306a36Sopenharmony_ci pchip->regCDSP &= ~P24_CDSP_MIC38_SEL_MASK; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci vx_outb(chip, CDSP, pchip->regCDSP); 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci mutex_unlock(&chip->lock); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci/* 48162306a36Sopenharmony_ci * remap the linear value (0-8) to the actual value (0-15) 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_cistatic int vx_compute_mic_level(int level) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci switch (level) { 48662306a36Sopenharmony_ci case 5: level = 6 ; break; 48762306a36Sopenharmony_ci case 6: level = 8 ; break; 48862306a36Sopenharmony_ci case 7: level = 11; break; 48962306a36Sopenharmony_ci case 8: level = 15; break; 49062306a36Sopenharmony_ci default: break ; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci return level; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci/* 49662306a36Sopenharmony_ci * vx_set_mic_level - set mic level (on vxpocket only) 49762306a36Sopenharmony_ci * @level: the mic level = 0 - 8 (max) 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_civoid vx_set_mic_level(struct vx_core *chip, int level) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct snd_vxpocket *pchip = to_vxpocket(chip); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (chip->chip_status & VX_STAT_IS_STALE) 50462306a36Sopenharmony_ci return; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci mutex_lock(&chip->lock); 50762306a36Sopenharmony_ci if (pchip->regCDSP & VXP_CDSP_MIC_SEL_MASK) { 50862306a36Sopenharmony_ci level = vx_compute_mic_level(level); 50962306a36Sopenharmony_ci vx_outb(chip, MICRO, level); 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci mutex_unlock(&chip->lock); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci/* 51662306a36Sopenharmony_ci * change the input audio source 51762306a36Sopenharmony_ci */ 51862306a36Sopenharmony_cistatic void vxp_change_audio_source(struct vx_core *_chip, int src) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci switch (src) { 52362306a36Sopenharmony_ci case VX_AUDIO_SRC_DIGITAL: 52462306a36Sopenharmony_ci chip->regCDSP |= VXP_CDSP_DATAIN_SEL_MASK; 52562306a36Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci case VX_AUDIO_SRC_LINE: 52862306a36Sopenharmony_ci chip->regCDSP &= ~VXP_CDSP_DATAIN_SEL_MASK; 52962306a36Sopenharmony_ci if (_chip->type == VX_TYPE_VXP440) 53062306a36Sopenharmony_ci chip->regCDSP &= ~P24_CDSP_MICS_SEL_MASK; 53162306a36Sopenharmony_ci else 53262306a36Sopenharmony_ci chip->regCDSP &= ~VXP_CDSP_MIC_SEL_MASK; 53362306a36Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 53462306a36Sopenharmony_ci break; 53562306a36Sopenharmony_ci case VX_AUDIO_SRC_MIC: 53662306a36Sopenharmony_ci chip->regCDSP &= ~VXP_CDSP_DATAIN_SEL_MASK; 53762306a36Sopenharmony_ci /* reset mic levels */ 53862306a36Sopenharmony_ci if (_chip->type == VX_TYPE_VXP440) { 53962306a36Sopenharmony_ci chip->regCDSP &= ~P24_CDSP_MICS_SEL_MASK; 54062306a36Sopenharmony_ci if (chip->mic_level) 54162306a36Sopenharmony_ci chip->regCDSP |= P24_CDSP_MIC38_SEL_MASK; 54262306a36Sopenharmony_ci else 54362306a36Sopenharmony_ci chip->regCDSP |= P24_CDSP_MIC20_SEL_MASK; 54462306a36Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 54562306a36Sopenharmony_ci } else { 54662306a36Sopenharmony_ci chip->regCDSP |= VXP_CDSP_MIC_SEL_MASK; 54762306a36Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 54862306a36Sopenharmony_ci vx_outb(chip, MICRO, vx_compute_mic_level(chip->mic_level)); 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci break; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/* 55562306a36Sopenharmony_ci * change the clock source 55662306a36Sopenharmony_ci * source = INTERNAL_QUARTZ or UER_SYNC 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_cistatic void vxp_set_clock_source(struct vx_core *_chip, int source) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (source == INTERNAL_QUARTZ) 56362306a36Sopenharmony_ci chip->regCDSP &= ~VXP_CDSP_CLOCKIN_SEL_MASK; 56462306a36Sopenharmony_ci else 56562306a36Sopenharmony_ci chip->regCDSP |= VXP_CDSP_CLOCKIN_SEL_MASK; 56662306a36Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/* 57162306a36Sopenharmony_ci * reset the board 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_cistatic void vxp_reset_board(struct vx_core *_chip, int cold_reset) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci chip->regCDSP = 0; 57862306a36Sopenharmony_ci chip->regDIALOG = 0; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci/* 58362306a36Sopenharmony_ci * callbacks 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci/* exported */ 58662306a36Sopenharmony_ciconst struct snd_vx_ops snd_vxpocket_ops = { 58762306a36Sopenharmony_ci .in8 = vxp_inb, 58862306a36Sopenharmony_ci .out8 = vxp_outb, 58962306a36Sopenharmony_ci .test_and_ack = vxp_test_and_ack, 59062306a36Sopenharmony_ci .validate_irq = vxp_validate_irq, 59162306a36Sopenharmony_ci .write_codec = vxp_write_codec_reg, 59262306a36Sopenharmony_ci .reset_codec = vxp_reset_codec, 59362306a36Sopenharmony_ci .change_audio_source = vxp_change_audio_source, 59462306a36Sopenharmony_ci .set_clock_source = vxp_set_clock_source, 59562306a36Sopenharmony_ci .load_dsp = vxp_load_dsp, 59662306a36Sopenharmony_ci .add_controls = vxp_add_mic_controls, 59762306a36Sopenharmony_ci .reset_dsp = vxp_reset_dsp, 59862306a36Sopenharmony_ci .reset_board = vxp_reset_board, 59962306a36Sopenharmony_ci .dma_write = vxp_dma_write, 60062306a36Sopenharmony_ci .dma_read = vxp_dma_read, 60162306a36Sopenharmony_ci}; 602