18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Digigram VXpocket soundcards 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * lowlevel routines for VXpocket soundcards 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/firmware.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <sound/core.h> 158c2ecf20Sopenharmony_ci#include "vxpocket.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic const int vxp_reg_offset[VX_REG_MAX] = { 198c2ecf20Sopenharmony_ci [VX_ICR] = 0x00, // ICR 208c2ecf20Sopenharmony_ci [VX_CVR] = 0x01, // CVR 218c2ecf20Sopenharmony_ci [VX_ISR] = 0x02, // ISR 228c2ecf20Sopenharmony_ci [VX_IVR] = 0x03, // IVR 238c2ecf20Sopenharmony_ci [VX_RXH] = 0x05, // RXH 248c2ecf20Sopenharmony_ci [VX_RXM] = 0x06, // RXM 258c2ecf20Sopenharmony_ci [VX_RXL] = 0x07, // RXL 268c2ecf20Sopenharmony_ci [VX_DMA] = 0x04, // DMA 278c2ecf20Sopenharmony_ci [VX_CDSP] = 0x08, // CDSP 288c2ecf20Sopenharmony_ci [VX_LOFREQ] = 0x09, // LFREQ 298c2ecf20Sopenharmony_ci [VX_HIFREQ] = 0x0a, // HFREQ 308c2ecf20Sopenharmony_ci [VX_DATA] = 0x0b, // DATA 318c2ecf20Sopenharmony_ci [VX_MICRO] = 0x0c, // MICRO 328c2ecf20Sopenharmony_ci [VX_DIALOG] = 0x0d, // DIALOG 338c2ecf20Sopenharmony_ci [VX_CSUER] = 0x0e, // CSUER 348c2ecf20Sopenharmony_ci [VX_RUER] = 0x0f, // RUER 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic inline unsigned long vxp_reg_addr(struct vx_core *_chip, int reg) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 418c2ecf20Sopenharmony_ci return chip->port + vxp_reg_offset[reg]; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * snd_vx_inb - read a byte from the register 468c2ecf20Sopenharmony_ci * @offset: register offset 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_cistatic unsigned char vxp_inb(struct vx_core *chip, int offset) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci return inb(vxp_reg_addr(chip, offset)); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * snd_vx_outb - write a byte on the register 558c2ecf20Sopenharmony_ci * @offset: the register offset 568c2ecf20Sopenharmony_ci * @val: the value to write 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_cistatic void vxp_outb(struct vx_core *chip, int offset, unsigned char val) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci outb(val, vxp_reg_addr(chip, offset)); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * redefine macros to call directly 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci#undef vx_inb 678c2ecf20Sopenharmony_ci#define vx_inb(chip,reg) vxp_inb((struct vx_core *)(chip), VX_##reg) 688c2ecf20Sopenharmony_ci#undef vx_outb 698c2ecf20Sopenharmony_ci#define vx_outb(chip,reg,val) vxp_outb((struct vx_core *)(chip), VX_##reg,val) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * vx_check_magic - check the magic word on xilinx 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * returns zero if a magic word is detected, or a negative error code. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_cistatic int vx_check_magic(struct vx_core *chip) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci unsigned long end_time = jiffies + HZ / 5; 808c2ecf20Sopenharmony_ci int c; 818c2ecf20Sopenharmony_ci do { 828c2ecf20Sopenharmony_ci c = vx_inb(chip, CDSP); 838c2ecf20Sopenharmony_ci if (c == CDSP_MAGIC) 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci msleep(10); 868c2ecf20Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 878c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "cannot find xilinx magic word (%x)\n", c); 888c2ecf20Sopenharmony_ci return -EIO; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* 938c2ecf20Sopenharmony_ci * vx_reset_dsp - reset the DSP 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define XX_DSP_RESET_WAIT_TIME 2 /* ms */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void vxp_reset_dsp(struct vx_core *_chip) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* set the reset dsp bit to 1 */ 1038c2ecf20Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_DSP_RESET_MASK); 1048c2ecf20Sopenharmony_ci vx_inb(chip, CDSP); 1058c2ecf20Sopenharmony_ci mdelay(XX_DSP_RESET_WAIT_TIME); 1068c2ecf20Sopenharmony_ci /* reset the bit */ 1078c2ecf20Sopenharmony_ci chip->regCDSP &= ~VXP_CDSP_DSP_RESET_MASK; 1088c2ecf20Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 1098c2ecf20Sopenharmony_ci vx_inb(chip, CDSP); 1108c2ecf20Sopenharmony_ci mdelay(XX_DSP_RESET_WAIT_TIME); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* 1148c2ecf20Sopenharmony_ci * reset codec bit 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_cistatic void vxp_reset_codec(struct vx_core *_chip) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Set the reset CODEC bit to 1. */ 1218c2ecf20Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_CODEC_RESET_MASK); 1228c2ecf20Sopenharmony_ci vx_inb(chip, CDSP); 1238c2ecf20Sopenharmony_ci msleep(10); 1248c2ecf20Sopenharmony_ci /* Set the reset CODEC bit to 0. */ 1258c2ecf20Sopenharmony_ci chip->regCDSP &= ~VXP_CDSP_CODEC_RESET_MASK; 1268c2ecf20Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 1278c2ecf20Sopenharmony_ci vx_inb(chip, CDSP); 1288c2ecf20Sopenharmony_ci msleep(1); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* 1328c2ecf20Sopenharmony_ci * vx_load_xilinx_binary - load the xilinx binary image 1338c2ecf20Sopenharmony_ci * the binary image is the binary array converted from the bitstream file. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_cistatic int vxp_load_xilinx_binary(struct vx_core *_chip, const struct firmware *fw) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 1388c2ecf20Sopenharmony_ci unsigned int i; 1398c2ecf20Sopenharmony_ci int c; 1408c2ecf20Sopenharmony_ci int regCSUER, regRUER; 1418c2ecf20Sopenharmony_ci const unsigned char *image; 1428c2ecf20Sopenharmony_ci unsigned char data; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* Switch to programmation mode */ 1458c2ecf20Sopenharmony_ci chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK; 1468c2ecf20Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* Save register CSUER and RUER */ 1498c2ecf20Sopenharmony_ci regCSUER = vx_inb(chip, CSUER); 1508c2ecf20Sopenharmony_ci regRUER = vx_inb(chip, RUER); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* reset HF0 and HF1 */ 1538c2ecf20Sopenharmony_ci vx_outb(chip, ICR, 0); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* Wait for answer HF2 equal to 1 */ 1568c2ecf20Sopenharmony_ci snd_printdd(KERN_DEBUG "check ISR_HF2\n"); 1578c2ecf20Sopenharmony_ci if (vx_check_isr(_chip, ISR_HF2, ISR_HF2, 20) < 0) 1588c2ecf20Sopenharmony_ci goto _error; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* set HF1 for loading xilinx binary */ 1618c2ecf20Sopenharmony_ci vx_outb(chip, ICR, ICR_HF1); 1628c2ecf20Sopenharmony_ci image = fw->data; 1638c2ecf20Sopenharmony_ci for (i = 0; i < fw->size; i++, image++) { 1648c2ecf20Sopenharmony_ci data = *image; 1658c2ecf20Sopenharmony_ci if (vx_wait_isr_bit(_chip, ISR_TX_EMPTY) < 0) 1668c2ecf20Sopenharmony_ci goto _error; 1678c2ecf20Sopenharmony_ci vx_outb(chip, TXL, data); 1688c2ecf20Sopenharmony_ci /* wait for reading */ 1698c2ecf20Sopenharmony_ci if (vx_wait_for_rx_full(_chip) < 0) 1708c2ecf20Sopenharmony_ci goto _error; 1718c2ecf20Sopenharmony_ci c = vx_inb(chip, RXL); 1728c2ecf20Sopenharmony_ci if (c != (int)data) 1738c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "vxpocket: load xilinx mismatch at %d: 0x%x != 0x%x\n", i, c, (int)data); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* reset HF1 */ 1778c2ecf20Sopenharmony_ci vx_outb(chip, ICR, 0); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* wait for HF3 */ 1808c2ecf20Sopenharmony_ci if (vx_check_isr(_chip, ISR_HF3, ISR_HF3, 20) < 0) 1818c2ecf20Sopenharmony_ci goto _error; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* read the number of bytes received */ 1848c2ecf20Sopenharmony_ci if (vx_wait_for_rx_full(_chip) < 0) 1858c2ecf20Sopenharmony_ci goto _error; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci c = (int)vx_inb(chip, RXH) << 16; 1888c2ecf20Sopenharmony_ci c |= (int)vx_inb(chip, RXM) << 8; 1898c2ecf20Sopenharmony_ci c |= vx_inb(chip, RXL); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%zx\n", c, fw->size); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci vx_outb(chip, ICR, ICR_HF0); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* TEMPO 250ms : wait until Xilinx is downloaded */ 1968c2ecf20Sopenharmony_ci msleep(300); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* test magical word */ 1998c2ecf20Sopenharmony_ci if (vx_check_magic(_chip) < 0) 2008c2ecf20Sopenharmony_ci goto _error; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Restore register 0x0E and 0x0F (thus replacing COR and FCSR) */ 2038c2ecf20Sopenharmony_ci vx_outb(chip, CSUER, regCSUER); 2048c2ecf20Sopenharmony_ci vx_outb(chip, RUER, regRUER); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* Reset the Xilinx's signal enabling IO access */ 2078c2ecf20Sopenharmony_ci chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK; 2088c2ecf20Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG); 2098c2ecf20Sopenharmony_ci vx_inb(chip, DIALOG); 2108c2ecf20Sopenharmony_ci msleep(10); 2118c2ecf20Sopenharmony_ci chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK; 2128c2ecf20Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG); 2138c2ecf20Sopenharmony_ci vx_inb(chip, DIALOG); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* Reset of the Codec */ 2168c2ecf20Sopenharmony_ci vxp_reset_codec(_chip); 2178c2ecf20Sopenharmony_ci vx_reset_dsp(_chip); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci _error: 2228c2ecf20Sopenharmony_ci vx_outb(chip, CSUER, regCSUER); 2238c2ecf20Sopenharmony_ci vx_outb(chip, RUER, regRUER); 2248c2ecf20Sopenharmony_ci chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK; 2258c2ecf20Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG); 2268c2ecf20Sopenharmony_ci return -EIO; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* 2318c2ecf20Sopenharmony_ci * vxp_load_dsp - load_dsp callback 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_cistatic int vxp_load_dsp(struct vx_core *vx, int index, const struct firmware *fw) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci int err; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci switch (index) { 2388c2ecf20Sopenharmony_ci case 0: 2398c2ecf20Sopenharmony_ci /* xilinx boot */ 2408c2ecf20Sopenharmony_ci if ((err = vx_check_magic(vx)) < 0) 2418c2ecf20Sopenharmony_ci return err; 2428c2ecf20Sopenharmony_ci if ((err = snd_vx_load_boot_image(vx, fw)) < 0) 2438c2ecf20Sopenharmony_ci return err; 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci case 1: 2468c2ecf20Sopenharmony_ci /* xilinx image */ 2478c2ecf20Sopenharmony_ci return vxp_load_xilinx_binary(vx, fw); 2488c2ecf20Sopenharmony_ci case 2: 2498c2ecf20Sopenharmony_ci /* DSP boot */ 2508c2ecf20Sopenharmony_ci return snd_vx_dsp_boot(vx, fw); 2518c2ecf20Sopenharmony_ci case 3: 2528c2ecf20Sopenharmony_ci /* DSP image */ 2538c2ecf20Sopenharmony_ci return snd_vx_dsp_load(vx, fw); 2548c2ecf20Sopenharmony_ci default: 2558c2ecf20Sopenharmony_ci snd_BUG(); 2568c2ecf20Sopenharmony_ci return -EINVAL; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci/* 2628c2ecf20Sopenharmony_ci * vx_test_and_ack - test and acknowledge interrupt 2638c2ecf20Sopenharmony_ci * 2648c2ecf20Sopenharmony_ci * called from irq hander, too 2658c2ecf20Sopenharmony_ci * 2668c2ecf20Sopenharmony_ci * spinlock held! 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_cistatic int vxp_test_and_ack(struct vx_core *_chip) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* not booted yet? */ 2738c2ecf20Sopenharmony_ci if (! (_chip->chip_status & VX_STAT_XILINX_LOADED)) 2748c2ecf20Sopenharmony_ci return -ENXIO; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (! (vx_inb(chip, DIALOG) & VXP_DLG_MEMIRQ_MASK)) 2778c2ecf20Sopenharmony_ci return -EIO; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* ok, interrupts generated, now ack it */ 2808c2ecf20Sopenharmony_ci /* set ACQUIT bit up and down */ 2818c2ecf20Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG | VXP_DLG_ACK_MEMIRQ_MASK); 2828c2ecf20Sopenharmony_ci /* useless read just to spend some time and maintain 2838c2ecf20Sopenharmony_ci * the ACQUIT signal up for a while ( a bus cycle ) 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_ci vx_inb(chip, DIALOG); 2868c2ecf20Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG & ~VXP_DLG_ACK_MEMIRQ_MASK); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* 2938c2ecf20Sopenharmony_ci * vx_validate_irq - enable/disable IRQ 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_cistatic void vxp_validate_irq(struct vx_core *_chip, int enable) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* Set the interrupt enable bit to 1 in CDSP register */ 3008c2ecf20Sopenharmony_ci if (enable) 3018c2ecf20Sopenharmony_ci chip->regCDSP |= VXP_CDSP_VALID_IRQ_MASK; 3028c2ecf20Sopenharmony_ci else 3038c2ecf20Sopenharmony_ci chip->regCDSP &= ~VXP_CDSP_VALID_IRQ_MASK; 3048c2ecf20Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci/* 3088c2ecf20Sopenharmony_ci * vx_setup_pseudo_dma - set up the pseudo dma read/write mode. 3098c2ecf20Sopenharmony_ci * @do_write: 0 = read, 1 = set up for DMA write 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_cistatic void vx_setup_pseudo_dma(struct vx_core *_chip, int do_write) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* Interrupt mode and HREQ pin enabled for host transmit / receive data transfers */ 3168c2ecf20Sopenharmony_ci vx_outb(chip, ICR, do_write ? ICR_TREQ : ICR_RREQ); 3178c2ecf20Sopenharmony_ci /* Reset the pseudo-dma register */ 3188c2ecf20Sopenharmony_ci vx_inb(chip, ISR); 3198c2ecf20Sopenharmony_ci vx_outb(chip, ISR, 0); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* Select DMA in read/write transfer mode and in 16-bit accesses */ 3228c2ecf20Sopenharmony_ci chip->regDIALOG |= VXP_DLG_DMA16_SEL_MASK; 3238c2ecf20Sopenharmony_ci chip->regDIALOG |= do_write ? VXP_DLG_DMAWRITE_SEL_MASK : VXP_DLG_DMAREAD_SEL_MASK; 3248c2ecf20Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci/* 3298c2ecf20Sopenharmony_ci * vx_release_pseudo_dma - disable the pseudo-DMA mode 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_cistatic void vx_release_pseudo_dma(struct vx_core *_chip) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* Disable DMA and 16-bit accesses */ 3368c2ecf20Sopenharmony_ci chip->regDIALOG &= ~(VXP_DLG_DMAWRITE_SEL_MASK| 3378c2ecf20Sopenharmony_ci VXP_DLG_DMAREAD_SEL_MASK| 3388c2ecf20Sopenharmony_ci VXP_DLG_DMA16_SEL_MASK); 3398c2ecf20Sopenharmony_ci vx_outb(chip, DIALOG, chip->regDIALOG); 3408c2ecf20Sopenharmony_ci /* HREQ pin disabled. */ 3418c2ecf20Sopenharmony_ci vx_outb(chip, ICR, 0); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci/* 3458c2ecf20Sopenharmony_ci * vx_pseudo_dma_write - write bulk data on pseudo-DMA mode 3468c2ecf20Sopenharmony_ci * @count: data length to transfer in bytes 3478c2ecf20Sopenharmony_ci * 3488c2ecf20Sopenharmony_ci * data size must be aligned to 6 bytes to ensure the 24bit alignment on DSP. 3498c2ecf20Sopenharmony_ci * NB: call with a certain lock! 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_cistatic void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, 3528c2ecf20Sopenharmony_ci struct vx_pipe *pipe, int count) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci long port = vxp_reg_addr(chip, VX_DMA); 3558c2ecf20Sopenharmony_ci int offset = pipe->hw_ptr; 3568c2ecf20Sopenharmony_ci unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci vx_setup_pseudo_dma(chip, 1); 3598c2ecf20Sopenharmony_ci if (offset + count >= pipe->buffer_bytes) { 3608c2ecf20Sopenharmony_ci int length = pipe->buffer_bytes - offset; 3618c2ecf20Sopenharmony_ci count -= length; 3628c2ecf20Sopenharmony_ci length >>= 1; /* in 16bit words */ 3638c2ecf20Sopenharmony_ci /* Transfer using pseudo-dma. */ 3648c2ecf20Sopenharmony_ci for (; length > 0; length--) { 3658c2ecf20Sopenharmony_ci outw(*addr, port); 3668c2ecf20Sopenharmony_ci addr++; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci addr = (unsigned short *)runtime->dma_area; 3698c2ecf20Sopenharmony_ci pipe->hw_ptr = 0; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci pipe->hw_ptr += count; 3728c2ecf20Sopenharmony_ci count >>= 1; /* in 16bit words */ 3738c2ecf20Sopenharmony_ci /* Transfer using pseudo-dma. */ 3748c2ecf20Sopenharmony_ci for (; count > 0; count--) { 3758c2ecf20Sopenharmony_ci outw(*addr, port); 3768c2ecf20Sopenharmony_ci addr++; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci vx_release_pseudo_dma(chip); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci/* 3838c2ecf20Sopenharmony_ci * vx_pseudo_dma_read - read bulk data on pseudo DMA mode 3848c2ecf20Sopenharmony_ci * @offset: buffer offset in bytes 3858c2ecf20Sopenharmony_ci * @count: data length to transfer in bytes 3868c2ecf20Sopenharmony_ci * 3878c2ecf20Sopenharmony_ci * the read length must be aligned to 6 bytes, as well as write. 3888c2ecf20Sopenharmony_ci * NB: call with a certain lock! 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_cistatic void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, 3918c2ecf20Sopenharmony_ci struct vx_pipe *pipe, int count) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct snd_vxpocket *pchip = to_vxpocket(chip); 3948c2ecf20Sopenharmony_ci long port = vxp_reg_addr(chip, VX_DMA); 3958c2ecf20Sopenharmony_ci int offset = pipe->hw_ptr; 3968c2ecf20Sopenharmony_ci unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (snd_BUG_ON(count % 2)) 3998c2ecf20Sopenharmony_ci return; 4008c2ecf20Sopenharmony_ci vx_setup_pseudo_dma(chip, 0); 4018c2ecf20Sopenharmony_ci if (offset + count >= pipe->buffer_bytes) { 4028c2ecf20Sopenharmony_ci int length = pipe->buffer_bytes - offset; 4038c2ecf20Sopenharmony_ci count -= length; 4048c2ecf20Sopenharmony_ci length >>= 1; /* in 16bit words */ 4058c2ecf20Sopenharmony_ci /* Transfer using pseudo-dma. */ 4068c2ecf20Sopenharmony_ci for (; length > 0; length--) 4078c2ecf20Sopenharmony_ci *addr++ = inw(port); 4088c2ecf20Sopenharmony_ci addr = (unsigned short *)runtime->dma_area; 4098c2ecf20Sopenharmony_ci pipe->hw_ptr = 0; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci pipe->hw_ptr += count; 4128c2ecf20Sopenharmony_ci count >>= 1; /* in 16bit words */ 4138c2ecf20Sopenharmony_ci /* Transfer using pseudo-dma. */ 4148c2ecf20Sopenharmony_ci for (; count > 1; count--) 4158c2ecf20Sopenharmony_ci *addr++ = inw(port); 4168c2ecf20Sopenharmony_ci /* Disable DMA */ 4178c2ecf20Sopenharmony_ci pchip->regDIALOG &= ~VXP_DLG_DMAREAD_SEL_MASK; 4188c2ecf20Sopenharmony_ci vx_outb(chip, DIALOG, pchip->regDIALOG); 4198c2ecf20Sopenharmony_ci /* Read the last word (16 bits) */ 4208c2ecf20Sopenharmony_ci *addr = inw(port); 4218c2ecf20Sopenharmony_ci /* Disable 16-bit accesses */ 4228c2ecf20Sopenharmony_ci pchip->regDIALOG &= ~VXP_DLG_DMA16_SEL_MASK; 4238c2ecf20Sopenharmony_ci vx_outb(chip, DIALOG, pchip->regDIALOG); 4248c2ecf20Sopenharmony_ci /* HREQ pin disabled. */ 4258c2ecf20Sopenharmony_ci vx_outb(chip, ICR, 0); 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci/* 4308c2ecf20Sopenharmony_ci * write a codec data (24bit) 4318c2ecf20Sopenharmony_ci */ 4328c2ecf20Sopenharmony_cistatic void vxp_write_codec_reg(struct vx_core *chip, int codec, unsigned int data) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci int i; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* Activate access to the corresponding codec register */ 4378c2ecf20Sopenharmony_ci if (! codec) 4388c2ecf20Sopenharmony_ci vx_inb(chip, LOFREQ); 4398c2ecf20Sopenharmony_ci else 4408c2ecf20Sopenharmony_ci vx_inb(chip, CODEC2); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* We have to send 24 bits (3 x 8 bits). Start with most signif. Bit */ 4438c2ecf20Sopenharmony_ci for (i = 0; i < 24; i++, data <<= 1) 4448c2ecf20Sopenharmony_ci vx_outb(chip, DATA, ((data & 0x800000) ? VX_DATA_CODEC_MASK : 0)); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* Terminate access to codec registers */ 4478c2ecf20Sopenharmony_ci vx_inb(chip, HIFREQ); 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci/* 4528c2ecf20Sopenharmony_ci * vx_set_mic_boost - set mic boost level (on vxp440 only) 4538c2ecf20Sopenharmony_ci * @boost: 0 = 20dB, 1 = +38dB 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_civoid vx_set_mic_boost(struct vx_core *chip, int boost) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct snd_vxpocket *pchip = to_vxpocket(chip); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (chip->chip_status & VX_STAT_IS_STALE) 4608c2ecf20Sopenharmony_ci return; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 4638c2ecf20Sopenharmony_ci if (pchip->regCDSP & P24_CDSP_MICS_SEL_MASK) { 4648c2ecf20Sopenharmony_ci if (boost) { 4658c2ecf20Sopenharmony_ci /* boost: 38 dB */ 4668c2ecf20Sopenharmony_ci pchip->regCDSP &= ~P24_CDSP_MIC20_SEL_MASK; 4678c2ecf20Sopenharmony_ci pchip->regCDSP |= P24_CDSP_MIC38_SEL_MASK; 4688c2ecf20Sopenharmony_ci } else { 4698c2ecf20Sopenharmony_ci /* minimum value: 20 dB */ 4708c2ecf20Sopenharmony_ci pchip->regCDSP |= P24_CDSP_MIC20_SEL_MASK; 4718c2ecf20Sopenharmony_ci pchip->regCDSP &= ~P24_CDSP_MIC38_SEL_MASK; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci vx_outb(chip, CDSP, pchip->regCDSP); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/* 4798c2ecf20Sopenharmony_ci * remap the linear value (0-8) to the actual value (0-15) 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_cistatic int vx_compute_mic_level(int level) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci switch (level) { 4848c2ecf20Sopenharmony_ci case 5: level = 6 ; break; 4858c2ecf20Sopenharmony_ci case 6: level = 8 ; break; 4868c2ecf20Sopenharmony_ci case 7: level = 11; break; 4878c2ecf20Sopenharmony_ci case 8: level = 15; break; 4888c2ecf20Sopenharmony_ci default: break ; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci return level; 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci/* 4948c2ecf20Sopenharmony_ci * vx_set_mic_level - set mic level (on vxpocket only) 4958c2ecf20Sopenharmony_ci * @level: the mic level = 0 - 8 (max) 4968c2ecf20Sopenharmony_ci */ 4978c2ecf20Sopenharmony_civoid vx_set_mic_level(struct vx_core *chip, int level) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct snd_vxpocket *pchip = to_vxpocket(chip); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (chip->chip_status & VX_STAT_IS_STALE) 5028c2ecf20Sopenharmony_ci return; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 5058c2ecf20Sopenharmony_ci if (pchip->regCDSP & VXP_CDSP_MIC_SEL_MASK) { 5068c2ecf20Sopenharmony_ci level = vx_compute_mic_level(level); 5078c2ecf20Sopenharmony_ci vx_outb(chip, MICRO, level); 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci/* 5148c2ecf20Sopenharmony_ci * change the input audio source 5158c2ecf20Sopenharmony_ci */ 5168c2ecf20Sopenharmony_cistatic void vxp_change_audio_source(struct vx_core *_chip, int src) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci switch (src) { 5218c2ecf20Sopenharmony_ci case VX_AUDIO_SRC_DIGITAL: 5228c2ecf20Sopenharmony_ci chip->regCDSP |= VXP_CDSP_DATAIN_SEL_MASK; 5238c2ecf20Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 5248c2ecf20Sopenharmony_ci break; 5258c2ecf20Sopenharmony_ci case VX_AUDIO_SRC_LINE: 5268c2ecf20Sopenharmony_ci chip->regCDSP &= ~VXP_CDSP_DATAIN_SEL_MASK; 5278c2ecf20Sopenharmony_ci if (_chip->type == VX_TYPE_VXP440) 5288c2ecf20Sopenharmony_ci chip->regCDSP &= ~P24_CDSP_MICS_SEL_MASK; 5298c2ecf20Sopenharmony_ci else 5308c2ecf20Sopenharmony_ci chip->regCDSP &= ~VXP_CDSP_MIC_SEL_MASK; 5318c2ecf20Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 5328c2ecf20Sopenharmony_ci break; 5338c2ecf20Sopenharmony_ci case VX_AUDIO_SRC_MIC: 5348c2ecf20Sopenharmony_ci chip->regCDSP &= ~VXP_CDSP_DATAIN_SEL_MASK; 5358c2ecf20Sopenharmony_ci /* reset mic levels */ 5368c2ecf20Sopenharmony_ci if (_chip->type == VX_TYPE_VXP440) { 5378c2ecf20Sopenharmony_ci chip->regCDSP &= ~P24_CDSP_MICS_SEL_MASK; 5388c2ecf20Sopenharmony_ci if (chip->mic_level) 5398c2ecf20Sopenharmony_ci chip->regCDSP |= P24_CDSP_MIC38_SEL_MASK; 5408c2ecf20Sopenharmony_ci else 5418c2ecf20Sopenharmony_ci chip->regCDSP |= P24_CDSP_MIC20_SEL_MASK; 5428c2ecf20Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 5438c2ecf20Sopenharmony_ci } else { 5448c2ecf20Sopenharmony_ci chip->regCDSP |= VXP_CDSP_MIC_SEL_MASK; 5458c2ecf20Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 5468c2ecf20Sopenharmony_ci vx_outb(chip, MICRO, vx_compute_mic_level(chip->mic_level)); 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci break; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci/* 5538c2ecf20Sopenharmony_ci * change the clock source 5548c2ecf20Sopenharmony_ci * source = INTERNAL_QUARTZ or UER_SYNC 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_cistatic void vxp_set_clock_source(struct vx_core *_chip, int source) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci if (source == INTERNAL_QUARTZ) 5618c2ecf20Sopenharmony_ci chip->regCDSP &= ~VXP_CDSP_CLOCKIN_SEL_MASK; 5628c2ecf20Sopenharmony_ci else 5638c2ecf20Sopenharmony_ci chip->regCDSP |= VXP_CDSP_CLOCKIN_SEL_MASK; 5648c2ecf20Sopenharmony_ci vx_outb(chip, CDSP, chip->regCDSP); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci/* 5698c2ecf20Sopenharmony_ci * reset the board 5708c2ecf20Sopenharmony_ci */ 5718c2ecf20Sopenharmony_cistatic void vxp_reset_board(struct vx_core *_chip, int cold_reset) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct snd_vxpocket *chip = to_vxpocket(_chip); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci chip->regCDSP = 0; 5768c2ecf20Sopenharmony_ci chip->regDIALOG = 0; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci/* 5818c2ecf20Sopenharmony_ci * callbacks 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_ci/* exported */ 5848c2ecf20Sopenharmony_ciconst struct snd_vx_ops snd_vxpocket_ops = { 5858c2ecf20Sopenharmony_ci .in8 = vxp_inb, 5868c2ecf20Sopenharmony_ci .out8 = vxp_outb, 5878c2ecf20Sopenharmony_ci .test_and_ack = vxp_test_and_ack, 5888c2ecf20Sopenharmony_ci .validate_irq = vxp_validate_irq, 5898c2ecf20Sopenharmony_ci .write_codec = vxp_write_codec_reg, 5908c2ecf20Sopenharmony_ci .reset_codec = vxp_reset_codec, 5918c2ecf20Sopenharmony_ci .change_audio_source = vxp_change_audio_source, 5928c2ecf20Sopenharmony_ci .set_clock_source = vxp_set_clock_source, 5938c2ecf20Sopenharmony_ci .load_dsp = vxp_load_dsp, 5948c2ecf20Sopenharmony_ci .add_controls = vxp_add_mic_controls, 5958c2ecf20Sopenharmony_ci .reset_dsp = vxp_reset_dsp, 5968c2ecf20Sopenharmony_ci .reset_board = vxp_reset_board, 5978c2ecf20Sopenharmony_ci .dma_write = vxp_dma_write, 5988c2ecf20Sopenharmony_ci .dma_read = vxp_dma_read, 5998c2ecf20Sopenharmony_ci}; 600