11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Driver for Digigram VXpocket soundcards
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * lowlevel routines for VXpocket soundcards
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
81da177e4SLinus Torvalds */
91da177e4SLinus Torvalds
101da177e4SLinus Torvalds #include <linux/delay.h>
111da177e4SLinus Torvalds #include <linux/device.h>
121da177e4SLinus Torvalds #include <linux/firmware.h>
13ddfb3199STakashi Iwai #include <linux/io.h>
141da177e4SLinus Torvalds #include <sound/core.h>
151da177e4SLinus Torvalds #include "vxpocket.h"
161da177e4SLinus Torvalds
171da177e4SLinus Torvalds
18c0948245STakashi Iwai static const int vxp_reg_offset[VX_REG_MAX] = {
191da177e4SLinus Torvalds [VX_ICR] = 0x00, // ICR
201da177e4SLinus Torvalds [VX_CVR] = 0x01, // CVR
211da177e4SLinus Torvalds [VX_ISR] = 0x02, // ISR
221da177e4SLinus Torvalds [VX_IVR] = 0x03, // IVR
231da177e4SLinus Torvalds [VX_RXH] = 0x05, // RXH
241da177e4SLinus Torvalds [VX_RXM] = 0x06, // RXM
251da177e4SLinus Torvalds [VX_RXL] = 0x07, // RXL
261da177e4SLinus Torvalds [VX_DMA] = 0x04, // DMA
271da177e4SLinus Torvalds [VX_CDSP] = 0x08, // CDSP
281da177e4SLinus Torvalds [VX_LOFREQ] = 0x09, // LFREQ
291da177e4SLinus Torvalds [VX_HIFREQ] = 0x0a, // HFREQ
301da177e4SLinus Torvalds [VX_DATA] = 0x0b, // DATA
311da177e4SLinus Torvalds [VX_MICRO] = 0x0c, // MICRO
321da177e4SLinus Torvalds [VX_DIALOG] = 0x0d, // DIALOG
331da177e4SLinus Torvalds [VX_CSUER] = 0x0e, // CSUER
341da177e4SLinus Torvalds [VX_RUER] = 0x0f, // RUER
351da177e4SLinus Torvalds };
361da177e4SLinus Torvalds
371da177e4SLinus Torvalds
vxp_reg_addr(struct vx_core * _chip,int reg)38af26367fSTakashi Iwai static inline unsigned long vxp_reg_addr(struct vx_core *_chip, int reg)
391da177e4SLinus Torvalds {
402e0de6eaSTakashi Iwai struct snd_vxpocket *chip = to_vxpocket(_chip);
411da177e4SLinus Torvalds return chip->port + vxp_reg_offset[reg];
421da177e4SLinus Torvalds }
431da177e4SLinus Torvalds
441da177e4SLinus Torvalds /*
451da177e4SLinus Torvalds * snd_vx_inb - read a byte from the register
461da177e4SLinus Torvalds * @offset: register offset
471da177e4SLinus Torvalds */
vxp_inb(struct vx_core * chip,int offset)48af26367fSTakashi Iwai static unsigned char vxp_inb(struct vx_core *chip, int offset)
491da177e4SLinus Torvalds {
501da177e4SLinus Torvalds return inb(vxp_reg_addr(chip, offset));
511da177e4SLinus Torvalds }
521da177e4SLinus Torvalds
531da177e4SLinus Torvalds /*
541da177e4SLinus Torvalds * snd_vx_outb - write a byte on the register
551da177e4SLinus Torvalds * @offset: the register offset
561da177e4SLinus Torvalds * @val: the value to write
571da177e4SLinus Torvalds */
vxp_outb(struct vx_core * chip,int offset,unsigned char val)58af26367fSTakashi Iwai static void vxp_outb(struct vx_core *chip, int offset, unsigned char val)
591da177e4SLinus Torvalds {
601da177e4SLinus Torvalds outb(val, vxp_reg_addr(chip, offset));
611da177e4SLinus Torvalds }
621da177e4SLinus Torvalds
631da177e4SLinus Torvalds /*
641da177e4SLinus Torvalds * redefine macros to call directly
651da177e4SLinus Torvalds */
661da177e4SLinus Torvalds #undef vx_inb
67af26367fSTakashi Iwai #define vx_inb(chip,reg) vxp_inb((struct vx_core *)(chip), VX_##reg)
681da177e4SLinus Torvalds #undef vx_outb
69af26367fSTakashi Iwai #define vx_outb(chip,reg,val) vxp_outb((struct vx_core *)(chip), VX_##reg,val)
701da177e4SLinus Torvalds
711da177e4SLinus Torvalds
721da177e4SLinus Torvalds /*
731da177e4SLinus Torvalds * vx_check_magic - check the magic word on xilinx
741da177e4SLinus Torvalds *
751da177e4SLinus Torvalds * returns zero if a magic word is detected, or a negative error code.
761da177e4SLinus Torvalds */
vx_check_magic(struct vx_core * chip)77af26367fSTakashi Iwai static int vx_check_magic(struct vx_core *chip)
781da177e4SLinus Torvalds {
791da177e4SLinus Torvalds unsigned long end_time = jiffies + HZ / 5;
801da177e4SLinus Torvalds int c;
811da177e4SLinus Torvalds do {
821da177e4SLinus Torvalds c = vx_inb(chip, CDSP);
831da177e4SLinus Torvalds if (c == CDSP_MAGIC)
841da177e4SLinus Torvalds return 0;
85bdbae7e6STakashi Iwai msleep(10);
861da177e4SLinus Torvalds } while (time_after_eq(end_time, jiffies));
871da177e4SLinus Torvalds snd_printk(KERN_ERR "cannot find xilinx magic word (%x)\n", c);
881da177e4SLinus Torvalds return -EIO;
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds
911da177e4SLinus Torvalds
921da177e4SLinus Torvalds /*
931da177e4SLinus Torvalds * vx_reset_dsp - reset the DSP
941da177e4SLinus Torvalds */
951da177e4SLinus Torvalds
961da177e4SLinus Torvalds #define XX_DSP_RESET_WAIT_TIME 2 /* ms */
971da177e4SLinus Torvalds
vxp_reset_dsp(struct vx_core * _chip)98af26367fSTakashi Iwai static void vxp_reset_dsp(struct vx_core *_chip)
991da177e4SLinus Torvalds {
1002e0de6eaSTakashi Iwai struct snd_vxpocket *chip = to_vxpocket(_chip);
1011da177e4SLinus Torvalds
1021da177e4SLinus Torvalds /* set the reset dsp bit to 1 */
1031da177e4SLinus Torvalds vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_DSP_RESET_MASK);
1041da177e4SLinus Torvalds vx_inb(chip, CDSP);
1051da177e4SLinus Torvalds mdelay(XX_DSP_RESET_WAIT_TIME);
1061da177e4SLinus Torvalds /* reset the bit */
1071da177e4SLinus Torvalds chip->regCDSP &= ~VXP_CDSP_DSP_RESET_MASK;
1081da177e4SLinus Torvalds vx_outb(chip, CDSP, chip->regCDSP);
1091da177e4SLinus Torvalds vx_inb(chip, CDSP);
1101da177e4SLinus Torvalds mdelay(XX_DSP_RESET_WAIT_TIME);
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvalds /*
1141da177e4SLinus Torvalds * reset codec bit
1151da177e4SLinus Torvalds */
vxp_reset_codec(struct vx_core * _chip)116af26367fSTakashi Iwai static void vxp_reset_codec(struct vx_core *_chip)
1171da177e4SLinus Torvalds {
1182e0de6eaSTakashi Iwai struct snd_vxpocket *chip = to_vxpocket(_chip);
1191da177e4SLinus Torvalds
1201da177e4SLinus Torvalds /* Set the reset CODEC bit to 1. */
1211da177e4SLinus Torvalds vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_CODEC_RESET_MASK);
1221da177e4SLinus Torvalds vx_inb(chip, CDSP);
123bdbae7e6STakashi Iwai msleep(10);
1241da177e4SLinus Torvalds /* Set the reset CODEC bit to 0. */
1251da177e4SLinus Torvalds chip->regCDSP &= ~VXP_CDSP_CODEC_RESET_MASK;
1261da177e4SLinus Torvalds vx_outb(chip, CDSP, chip->regCDSP);
1271da177e4SLinus Torvalds vx_inb(chip, CDSP);
128bdbae7e6STakashi Iwai msleep(1);
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds
1311da177e4SLinus Torvalds /*
1321da177e4SLinus Torvalds * vx_load_xilinx_binary - load the xilinx binary image
1331da177e4SLinus Torvalds * the binary image is the binary array converted from the bitstream file.
1341da177e4SLinus Torvalds */
vxp_load_xilinx_binary(struct vx_core * _chip,const struct firmware * fw)135af26367fSTakashi Iwai static int vxp_load_xilinx_binary(struct vx_core *_chip, const struct firmware *fw)
1361da177e4SLinus Torvalds {
1372e0de6eaSTakashi Iwai struct snd_vxpocket *chip = to_vxpocket(_chip);
1381da177e4SLinus Torvalds unsigned int i;
1391da177e4SLinus Torvalds int c;
1401da177e4SLinus Torvalds int regCSUER, regRUER;
1417fc077fbSDavid Howells const unsigned char *image;
1421da177e4SLinus Torvalds unsigned char data;
1431da177e4SLinus Torvalds
1441da177e4SLinus Torvalds /* Switch to programmation mode */
1451da177e4SLinus Torvalds chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK;
1461da177e4SLinus Torvalds vx_outb(chip, DIALOG, chip->regDIALOG);
1471da177e4SLinus Torvalds
1481da177e4SLinus Torvalds /* Save register CSUER and RUER */
1491da177e4SLinus Torvalds regCSUER = vx_inb(chip, CSUER);
1501da177e4SLinus Torvalds regRUER = vx_inb(chip, RUER);
1511da177e4SLinus Torvalds
1521da177e4SLinus Torvalds /* reset HF0 and HF1 */
1531da177e4SLinus Torvalds vx_outb(chip, ICR, 0);
1541da177e4SLinus Torvalds
1551da177e4SLinus Torvalds /* Wait for answer HF2 equal to 1 */
1561da177e4SLinus Torvalds snd_printdd(KERN_DEBUG "check ISR_HF2\n");
1571da177e4SLinus Torvalds if (vx_check_isr(_chip, ISR_HF2, ISR_HF2, 20) < 0)
1581da177e4SLinus Torvalds goto _error;
1591da177e4SLinus Torvalds
1601da177e4SLinus Torvalds /* set HF1 for loading xilinx binary */
1611da177e4SLinus Torvalds vx_outb(chip, ICR, ICR_HF1);
1621da177e4SLinus Torvalds image = fw->data;
1631da177e4SLinus Torvalds for (i = 0; i < fw->size; i++, image++) {
1641da177e4SLinus Torvalds data = *image;
1651da177e4SLinus Torvalds if (vx_wait_isr_bit(_chip, ISR_TX_EMPTY) < 0)
1661da177e4SLinus Torvalds goto _error;
1671da177e4SLinus Torvalds vx_outb(chip, TXL, data);
1681da177e4SLinus Torvalds /* wait for reading */
1691da177e4SLinus Torvalds if (vx_wait_for_rx_full(_chip) < 0)
1701da177e4SLinus Torvalds goto _error;
1711da177e4SLinus Torvalds c = vx_inb(chip, RXL);
1721da177e4SLinus Torvalds if (c != (int)data)
1731da177e4SLinus Torvalds snd_printk(KERN_ERR "vxpocket: load xilinx mismatch at %d: 0x%x != 0x%x\n", i, c, (int)data);
1741da177e4SLinus Torvalds }
1751da177e4SLinus Torvalds
1761da177e4SLinus Torvalds /* reset HF1 */
1771da177e4SLinus Torvalds vx_outb(chip, ICR, 0);
1781da177e4SLinus Torvalds
1791da177e4SLinus Torvalds /* wait for HF3 */
1801da177e4SLinus Torvalds if (vx_check_isr(_chip, ISR_HF3, ISR_HF3, 20) < 0)
1811da177e4SLinus Torvalds goto _error;
1821da177e4SLinus Torvalds
1831da177e4SLinus Torvalds /* read the number of bytes received */
1841da177e4SLinus Torvalds if (vx_wait_for_rx_full(_chip) < 0)
1851da177e4SLinus Torvalds goto _error;
1861da177e4SLinus Torvalds
1871da177e4SLinus Torvalds c = (int)vx_inb(chip, RXH) << 16;
1881da177e4SLinus Torvalds c |= (int)vx_inb(chip, RXM) << 8;
1891da177e4SLinus Torvalds c |= vx_inb(chip, RXL);
1901da177e4SLinus Torvalds
1915b5e0928SAlexey Dobriyan snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%zx\n", c, fw->size);
1921da177e4SLinus Torvalds
1931da177e4SLinus Torvalds vx_outb(chip, ICR, ICR_HF0);
1941da177e4SLinus Torvalds
1951da177e4SLinus Torvalds /* TEMPO 250ms : wait until Xilinx is downloaded */
196bdbae7e6STakashi Iwai msleep(300);
1971da177e4SLinus Torvalds
1981da177e4SLinus Torvalds /* test magical word */
1991da177e4SLinus Torvalds if (vx_check_magic(_chip) < 0)
2001da177e4SLinus Torvalds goto _error;
2011da177e4SLinus Torvalds
2021da177e4SLinus Torvalds /* Restore register 0x0E and 0x0F (thus replacing COR and FCSR) */
2031da177e4SLinus Torvalds vx_outb(chip, CSUER, regCSUER);
2041da177e4SLinus Torvalds vx_outb(chip, RUER, regRUER);
2051da177e4SLinus Torvalds
2061da177e4SLinus Torvalds /* Reset the Xilinx's signal enabling IO access */
2071da177e4SLinus Torvalds chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK;
2081da177e4SLinus Torvalds vx_outb(chip, DIALOG, chip->regDIALOG);
2091da177e4SLinus Torvalds vx_inb(chip, DIALOG);
210bdbae7e6STakashi Iwai msleep(10);
2111da177e4SLinus Torvalds chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK;
2121da177e4SLinus Torvalds vx_outb(chip, DIALOG, chip->regDIALOG);
2131da177e4SLinus Torvalds vx_inb(chip, DIALOG);
2141da177e4SLinus Torvalds
2151da177e4SLinus Torvalds /* Reset of the Codec */
2161da177e4SLinus Torvalds vxp_reset_codec(_chip);
2171da177e4SLinus Torvalds vx_reset_dsp(_chip);
2181da177e4SLinus Torvalds
2191da177e4SLinus Torvalds return 0;
2201da177e4SLinus Torvalds
2211da177e4SLinus Torvalds _error:
2221da177e4SLinus Torvalds vx_outb(chip, CSUER, regCSUER);
2231da177e4SLinus Torvalds vx_outb(chip, RUER, regRUER);
2241da177e4SLinus Torvalds chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK;
2251da177e4SLinus Torvalds vx_outb(chip, DIALOG, chip->regDIALOG);
2261da177e4SLinus Torvalds return -EIO;
2271da177e4SLinus Torvalds }
2281da177e4SLinus Torvalds
2291da177e4SLinus Torvalds
2301da177e4SLinus Torvalds /*
2311da177e4SLinus Torvalds * vxp_load_dsp - load_dsp callback
2321da177e4SLinus Torvalds */
vxp_load_dsp(struct vx_core * vx,int index,const struct firmware * fw)233af26367fSTakashi Iwai static int vxp_load_dsp(struct vx_core *vx, int index, const struct firmware *fw)
2341da177e4SLinus Torvalds {
2351da177e4SLinus Torvalds int err;
2361da177e4SLinus Torvalds
2371da177e4SLinus Torvalds switch (index) {
2381da177e4SLinus Torvalds case 0:
2391da177e4SLinus Torvalds /* xilinx boot */
240*2073fa44STakashi Iwai err = vx_check_magic(vx);
241*2073fa44STakashi Iwai if (err < 0)
2421da177e4SLinus Torvalds return err;
243*2073fa44STakashi Iwai err = snd_vx_load_boot_image(vx, fw);
244*2073fa44STakashi Iwai if (err < 0)
2451da177e4SLinus Torvalds return err;
2461da177e4SLinus Torvalds return 0;
2471da177e4SLinus Torvalds case 1:
2481da177e4SLinus Torvalds /* xilinx image */
2491da177e4SLinus Torvalds return vxp_load_xilinx_binary(vx, fw);
2501da177e4SLinus Torvalds case 2:
2511da177e4SLinus Torvalds /* DSP boot */
2521da177e4SLinus Torvalds return snd_vx_dsp_boot(vx, fw);
2531da177e4SLinus Torvalds case 3:
2541da177e4SLinus Torvalds /* DSP image */
2551da177e4SLinus Torvalds return snd_vx_dsp_load(vx, fw);
2561da177e4SLinus Torvalds default:
2571da177e4SLinus Torvalds snd_BUG();
2581da177e4SLinus Torvalds return -EINVAL;
2591da177e4SLinus Torvalds }
2601da177e4SLinus Torvalds }
2611da177e4SLinus Torvalds
2621da177e4SLinus Torvalds
2631da177e4SLinus Torvalds /*
2641da177e4SLinus Torvalds * vx_test_and_ack - test and acknowledge interrupt
2651da177e4SLinus Torvalds *
2661da177e4SLinus Torvalds * called from irq hander, too
2671da177e4SLinus Torvalds *
2681da177e4SLinus Torvalds * spinlock held!
2691da177e4SLinus Torvalds */
vxp_test_and_ack(struct vx_core * _chip)270af26367fSTakashi Iwai static int vxp_test_and_ack(struct vx_core *_chip)
2711da177e4SLinus Torvalds {
2722e0de6eaSTakashi Iwai struct snd_vxpocket *chip = to_vxpocket(_chip);
2731da177e4SLinus Torvalds
2741da177e4SLinus Torvalds /* not booted yet? */
2751da177e4SLinus Torvalds if (! (_chip->chip_status & VX_STAT_XILINX_LOADED))
2761da177e4SLinus Torvalds return -ENXIO;
2771da177e4SLinus Torvalds
2781da177e4SLinus Torvalds if (! (vx_inb(chip, DIALOG) & VXP_DLG_MEMIRQ_MASK))
2791da177e4SLinus Torvalds return -EIO;
2801da177e4SLinus Torvalds
2811da177e4SLinus Torvalds /* ok, interrupts generated, now ack it */
2821da177e4SLinus Torvalds /* set ACQUIT bit up and down */
2831da177e4SLinus Torvalds vx_outb(chip, DIALOG, chip->regDIALOG | VXP_DLG_ACK_MEMIRQ_MASK);
2841da177e4SLinus Torvalds /* useless read just to spend some time and maintain
2851da177e4SLinus Torvalds * the ACQUIT signal up for a while ( a bus cycle )
2861da177e4SLinus Torvalds */
2871da177e4SLinus Torvalds vx_inb(chip, DIALOG);
2881da177e4SLinus Torvalds vx_outb(chip, DIALOG, chip->regDIALOG & ~VXP_DLG_ACK_MEMIRQ_MASK);
2891da177e4SLinus Torvalds
2901da177e4SLinus Torvalds return 0;
2911da177e4SLinus Torvalds }
2921da177e4SLinus Torvalds
2931da177e4SLinus Torvalds
2941da177e4SLinus Torvalds /*
2951da177e4SLinus Torvalds * vx_validate_irq - enable/disable IRQ
2961da177e4SLinus Torvalds */
vxp_validate_irq(struct vx_core * _chip,int enable)297af26367fSTakashi Iwai static void vxp_validate_irq(struct vx_core *_chip, int enable)
2981da177e4SLinus Torvalds {
2992e0de6eaSTakashi Iwai struct snd_vxpocket *chip = to_vxpocket(_chip);
3001da177e4SLinus Torvalds
3011da177e4SLinus Torvalds /* Set the interrupt enable bit to 1 in CDSP register */
3021da177e4SLinus Torvalds if (enable)
3031da177e4SLinus Torvalds chip->regCDSP |= VXP_CDSP_VALID_IRQ_MASK;
3041da177e4SLinus Torvalds else
3051da177e4SLinus Torvalds chip->regCDSP &= ~VXP_CDSP_VALID_IRQ_MASK;
3061da177e4SLinus Torvalds vx_outb(chip, CDSP, chip->regCDSP);
3071da177e4SLinus Torvalds }
3081da177e4SLinus Torvalds
3091da177e4SLinus Torvalds /*
3101da177e4SLinus Torvalds * vx_setup_pseudo_dma - set up the pseudo dma read/write mode.
3111da177e4SLinus Torvalds * @do_write: 0 = read, 1 = set up for DMA write
3121da177e4SLinus Torvalds */
vx_setup_pseudo_dma(struct vx_core * _chip,int do_write)313af26367fSTakashi Iwai static void vx_setup_pseudo_dma(struct vx_core *_chip, int do_write)
3141da177e4SLinus Torvalds {
3152e0de6eaSTakashi Iwai struct snd_vxpocket *chip = to_vxpocket(_chip);
3161da177e4SLinus Torvalds
3171da177e4SLinus Torvalds /* Interrupt mode and HREQ pin enabled for host transmit / receive data transfers */
3181da177e4SLinus Torvalds vx_outb(chip, ICR, do_write ? ICR_TREQ : ICR_RREQ);
3191da177e4SLinus Torvalds /* Reset the pseudo-dma register */
3201da177e4SLinus Torvalds vx_inb(chip, ISR);
3211da177e4SLinus Torvalds vx_outb(chip, ISR, 0);
3221da177e4SLinus Torvalds
3231da177e4SLinus Torvalds /* Select DMA in read/write transfer mode and in 16-bit accesses */
3241da177e4SLinus Torvalds chip->regDIALOG |= VXP_DLG_DMA16_SEL_MASK;
3251da177e4SLinus Torvalds chip->regDIALOG |= do_write ? VXP_DLG_DMAWRITE_SEL_MASK : VXP_DLG_DMAREAD_SEL_MASK;
3261da177e4SLinus Torvalds vx_outb(chip, DIALOG, chip->regDIALOG);
3271da177e4SLinus Torvalds
3281da177e4SLinus Torvalds }
3291da177e4SLinus Torvalds
3301da177e4SLinus Torvalds /*
3311da177e4SLinus Torvalds * vx_release_pseudo_dma - disable the pseudo-DMA mode
3321da177e4SLinus Torvalds */
vx_release_pseudo_dma(struct vx_core * _chip)333af26367fSTakashi Iwai static void vx_release_pseudo_dma(struct vx_core *_chip)
3341da177e4SLinus Torvalds {
3352e0de6eaSTakashi Iwai struct snd_vxpocket *chip = to_vxpocket(_chip);
3361da177e4SLinus Torvalds
3371da177e4SLinus Torvalds /* Disable DMA and 16-bit accesses */
3381da177e4SLinus Torvalds chip->regDIALOG &= ~(VXP_DLG_DMAWRITE_SEL_MASK|
3391da177e4SLinus Torvalds VXP_DLG_DMAREAD_SEL_MASK|
3401da177e4SLinus Torvalds VXP_DLG_DMA16_SEL_MASK);
3411da177e4SLinus Torvalds vx_outb(chip, DIALOG, chip->regDIALOG);
3421da177e4SLinus Torvalds /* HREQ pin disabled. */
3431da177e4SLinus Torvalds vx_outb(chip, ICR, 0);
3441da177e4SLinus Torvalds }
3451da177e4SLinus Torvalds
3461da177e4SLinus Torvalds /*
3471da177e4SLinus Torvalds * vx_pseudo_dma_write - write bulk data on pseudo-DMA mode
3481da177e4SLinus Torvalds * @count: data length to transfer in bytes
3491da177e4SLinus Torvalds *
3501da177e4SLinus Torvalds * data size must be aligned to 6 bytes to ensure the 24bit alignment on DSP.
3511da177e4SLinus Torvalds * NB: call with a certain lock!
3521da177e4SLinus Torvalds */
vxp_dma_write(struct vx_core * chip,struct snd_pcm_runtime * runtime,struct vx_pipe * pipe,int count)353af26367fSTakashi Iwai static void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime,
354af26367fSTakashi Iwai struct vx_pipe *pipe, int count)
3551da177e4SLinus Torvalds {
3561da177e4SLinus Torvalds long port = vxp_reg_addr(chip, VX_DMA);
3571da177e4SLinus Torvalds int offset = pipe->hw_ptr;
3581da177e4SLinus Torvalds unsigned short *addr = (unsigned short *)(runtime->dma_area + offset);
3591da177e4SLinus Torvalds
3601da177e4SLinus Torvalds vx_setup_pseudo_dma(chip, 1);
361874e1f6fSTakashi Iwai if (offset + count >= pipe->buffer_bytes) {
3621da177e4SLinus Torvalds int length = pipe->buffer_bytes - offset;
3631da177e4SLinus Torvalds count -= length;
3641da177e4SLinus Torvalds length >>= 1; /* in 16bit words */
3651da177e4SLinus Torvalds /* Transfer using pseudo-dma. */
366874e1f6fSTakashi Iwai for (; length > 0; length--) {
3673acd3e3bSTakashi Iwai outw(*addr, port);
3681da177e4SLinus Torvalds addr++;
3691da177e4SLinus Torvalds }
3701da177e4SLinus Torvalds addr = (unsigned short *)runtime->dma_area;
3711da177e4SLinus Torvalds pipe->hw_ptr = 0;
3721da177e4SLinus Torvalds }
3731da177e4SLinus Torvalds pipe->hw_ptr += count;
3741da177e4SLinus Torvalds count >>= 1; /* in 16bit words */
3751da177e4SLinus Torvalds /* Transfer using pseudo-dma. */
376874e1f6fSTakashi Iwai for (; count > 0; count--) {
3773acd3e3bSTakashi Iwai outw(*addr, port);
3781da177e4SLinus Torvalds addr++;
3791da177e4SLinus Torvalds }
3801da177e4SLinus Torvalds vx_release_pseudo_dma(chip);
3811da177e4SLinus Torvalds }
3821da177e4SLinus Torvalds
3831da177e4SLinus Torvalds
3841da177e4SLinus Torvalds /*
3851da177e4SLinus Torvalds * vx_pseudo_dma_read - read bulk data on pseudo DMA mode
3861da177e4SLinus Torvalds * @offset: buffer offset in bytes
3871da177e4SLinus Torvalds * @count: data length to transfer in bytes
3881da177e4SLinus Torvalds *
3891da177e4SLinus Torvalds * the read length must be aligned to 6 bytes, as well as write.
3901da177e4SLinus Torvalds * NB: call with a certain lock!
3911da177e4SLinus Torvalds */
vxp_dma_read(struct vx_core * chip,struct snd_pcm_runtime * runtime,struct vx_pipe * pipe,int count)392af26367fSTakashi Iwai static void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime,
393af26367fSTakashi Iwai struct vx_pipe *pipe, int count)
3941da177e4SLinus Torvalds {
3952e0de6eaSTakashi Iwai struct snd_vxpocket *pchip = to_vxpocket(chip);
3961da177e4SLinus Torvalds long port = vxp_reg_addr(chip, VX_DMA);
3971da177e4SLinus Torvalds int offset = pipe->hw_ptr;
3981da177e4SLinus Torvalds unsigned short *addr = (unsigned short *)(runtime->dma_area + offset);
3991da177e4SLinus Torvalds
4005e246b85STakashi Iwai if (snd_BUG_ON(count % 2))
4015e246b85STakashi Iwai return;
4021da177e4SLinus Torvalds vx_setup_pseudo_dma(chip, 0);
403874e1f6fSTakashi Iwai if (offset + count >= pipe->buffer_bytes) {
4041da177e4SLinus Torvalds int length = pipe->buffer_bytes - offset;
4051da177e4SLinus Torvalds count -= length;
4061da177e4SLinus Torvalds length >>= 1; /* in 16bit words */
4071da177e4SLinus Torvalds /* Transfer using pseudo-dma. */
408874e1f6fSTakashi Iwai for (; length > 0; length--)
4093acd3e3bSTakashi Iwai *addr++ = inw(port);
4101da177e4SLinus Torvalds addr = (unsigned short *)runtime->dma_area;
4111da177e4SLinus Torvalds pipe->hw_ptr = 0;
4121da177e4SLinus Torvalds }
4131da177e4SLinus Torvalds pipe->hw_ptr += count;
4141da177e4SLinus Torvalds count >>= 1; /* in 16bit words */
4151da177e4SLinus Torvalds /* Transfer using pseudo-dma. */
416874e1f6fSTakashi Iwai for (; count > 1; count--)
4173acd3e3bSTakashi Iwai *addr++ = inw(port);
4181da177e4SLinus Torvalds /* Disable DMA */
4191da177e4SLinus Torvalds pchip->regDIALOG &= ~VXP_DLG_DMAREAD_SEL_MASK;
4201da177e4SLinus Torvalds vx_outb(chip, DIALOG, pchip->regDIALOG);
4211da177e4SLinus Torvalds /* Read the last word (16 bits) */
4223acd3e3bSTakashi Iwai *addr = inw(port);
4231da177e4SLinus Torvalds /* Disable 16-bit accesses */
4241da177e4SLinus Torvalds pchip->regDIALOG &= ~VXP_DLG_DMA16_SEL_MASK;
4251da177e4SLinus Torvalds vx_outb(chip, DIALOG, pchip->regDIALOG);
4261da177e4SLinus Torvalds /* HREQ pin disabled. */
4271da177e4SLinus Torvalds vx_outb(chip, ICR, 0);
4281da177e4SLinus Torvalds }
4291da177e4SLinus Torvalds
4301da177e4SLinus Torvalds
4311da177e4SLinus Torvalds /*
4321da177e4SLinus Torvalds * write a codec data (24bit)
4331da177e4SLinus Torvalds */
vxp_write_codec_reg(struct vx_core * chip,int codec,unsigned int data)434af26367fSTakashi Iwai static void vxp_write_codec_reg(struct vx_core *chip, int codec, unsigned int data)
4351da177e4SLinus Torvalds {
4361da177e4SLinus Torvalds int i;
4371da177e4SLinus Torvalds
4381da177e4SLinus Torvalds /* Activate access to the corresponding codec register */
4391da177e4SLinus Torvalds if (! codec)
4401da177e4SLinus Torvalds vx_inb(chip, LOFREQ);
4411da177e4SLinus Torvalds else
4421da177e4SLinus Torvalds vx_inb(chip, CODEC2);
4431da177e4SLinus Torvalds
4441da177e4SLinus Torvalds /* We have to send 24 bits (3 x 8 bits). Start with most signif. Bit */
4451da177e4SLinus Torvalds for (i = 0; i < 24; i++, data <<= 1)
4461da177e4SLinus Torvalds vx_outb(chip, DATA, ((data & 0x800000) ? VX_DATA_CODEC_MASK : 0));
4471da177e4SLinus Torvalds
4481da177e4SLinus Torvalds /* Terminate access to codec registers */
4491da177e4SLinus Torvalds vx_inb(chip, HIFREQ);
4501da177e4SLinus Torvalds }
4511da177e4SLinus Torvalds
4521da177e4SLinus Torvalds
4531da177e4SLinus Torvalds /*
4541da177e4SLinus Torvalds * vx_set_mic_boost - set mic boost level (on vxp440 only)
4551da177e4SLinus Torvalds * @boost: 0 = 20dB, 1 = +38dB
4561da177e4SLinus Torvalds */
vx_set_mic_boost(struct vx_core * chip,int boost)457af26367fSTakashi Iwai void vx_set_mic_boost(struct vx_core *chip, int boost)
4581da177e4SLinus Torvalds {
4592e0de6eaSTakashi Iwai struct snd_vxpocket *pchip = to_vxpocket(chip);
4601da177e4SLinus Torvalds
4611da177e4SLinus Torvalds if (chip->chip_status & VX_STAT_IS_STALE)
4621da177e4SLinus Torvalds return;
4631da177e4SLinus Torvalds
464db0a5214STakashi Iwai mutex_lock(&chip->lock);
4651da177e4SLinus Torvalds if (pchip->regCDSP & P24_CDSP_MICS_SEL_MASK) {
4661da177e4SLinus Torvalds if (boost) {
4671da177e4SLinus Torvalds /* boost: 38 dB */
4681da177e4SLinus Torvalds pchip->regCDSP &= ~P24_CDSP_MIC20_SEL_MASK;
4691da177e4SLinus Torvalds pchip->regCDSP |= P24_CDSP_MIC38_SEL_MASK;
4701da177e4SLinus Torvalds } else {
4711da177e4SLinus Torvalds /* minimum value: 20 dB */
4721da177e4SLinus Torvalds pchip->regCDSP |= P24_CDSP_MIC20_SEL_MASK;
4731da177e4SLinus Torvalds pchip->regCDSP &= ~P24_CDSP_MIC38_SEL_MASK;
4741da177e4SLinus Torvalds }
4751da177e4SLinus Torvalds vx_outb(chip, CDSP, pchip->regCDSP);
4761da177e4SLinus Torvalds }
477db0a5214STakashi Iwai mutex_unlock(&chip->lock);
4781da177e4SLinus Torvalds }
4791da177e4SLinus Torvalds
4801da177e4SLinus Torvalds /*
4811da177e4SLinus Torvalds * remap the linear value (0-8) to the actual value (0-15)
4821da177e4SLinus Torvalds */
vx_compute_mic_level(int level)4831da177e4SLinus Torvalds static int vx_compute_mic_level(int level)
4841da177e4SLinus Torvalds {
4851da177e4SLinus Torvalds switch (level) {
4861da177e4SLinus Torvalds case 5: level = 6 ; break;
4871da177e4SLinus Torvalds case 6: level = 8 ; break;
4881da177e4SLinus Torvalds case 7: level = 11; break;
4891da177e4SLinus Torvalds case 8: level = 15; break;
4901da177e4SLinus Torvalds default: break ;
4911da177e4SLinus Torvalds }
4921da177e4SLinus Torvalds return level;
4931da177e4SLinus Torvalds }
4941da177e4SLinus Torvalds
4951da177e4SLinus Torvalds /*
4961da177e4SLinus Torvalds * vx_set_mic_level - set mic level (on vxpocket only)
4971da177e4SLinus Torvalds * @level: the mic level = 0 - 8 (max)
4981da177e4SLinus Torvalds */
vx_set_mic_level(struct vx_core * chip,int level)499af26367fSTakashi Iwai void vx_set_mic_level(struct vx_core *chip, int level)
5001da177e4SLinus Torvalds {
5012e0de6eaSTakashi Iwai struct snd_vxpocket *pchip = to_vxpocket(chip);
5021da177e4SLinus Torvalds
5031da177e4SLinus Torvalds if (chip->chip_status & VX_STAT_IS_STALE)
5041da177e4SLinus Torvalds return;
5051da177e4SLinus Torvalds
506db0a5214STakashi Iwai mutex_lock(&chip->lock);
5071da177e4SLinus Torvalds if (pchip->regCDSP & VXP_CDSP_MIC_SEL_MASK) {
5081da177e4SLinus Torvalds level = vx_compute_mic_level(level);
5091da177e4SLinus Torvalds vx_outb(chip, MICRO, level);
5101da177e4SLinus Torvalds }
511db0a5214STakashi Iwai mutex_unlock(&chip->lock);
5121da177e4SLinus Torvalds }
5131da177e4SLinus Torvalds
5141da177e4SLinus Torvalds
5151da177e4SLinus Torvalds /*
5161da177e4SLinus Torvalds * change the input audio source
5171da177e4SLinus Torvalds */
vxp_change_audio_source(struct vx_core * _chip,int src)518af26367fSTakashi Iwai static void vxp_change_audio_source(struct vx_core *_chip, int src)
5191da177e4SLinus Torvalds {
5202e0de6eaSTakashi Iwai struct snd_vxpocket *chip = to_vxpocket(_chip);
5211da177e4SLinus Torvalds
5221da177e4SLinus Torvalds switch (src) {
5231da177e4SLinus Torvalds case VX_AUDIO_SRC_DIGITAL:
5241da177e4SLinus Torvalds chip->regCDSP |= VXP_CDSP_DATAIN_SEL_MASK;
5251da177e4SLinus Torvalds vx_outb(chip, CDSP, chip->regCDSP);
5261da177e4SLinus Torvalds break;
5271da177e4SLinus Torvalds case VX_AUDIO_SRC_LINE:
5281da177e4SLinus Torvalds chip->regCDSP &= ~VXP_CDSP_DATAIN_SEL_MASK;
5291da177e4SLinus Torvalds if (_chip->type == VX_TYPE_VXP440)
5301da177e4SLinus Torvalds chip->regCDSP &= ~P24_CDSP_MICS_SEL_MASK;
5311da177e4SLinus Torvalds else
5321da177e4SLinus Torvalds chip->regCDSP &= ~VXP_CDSP_MIC_SEL_MASK;
5331da177e4SLinus Torvalds vx_outb(chip, CDSP, chip->regCDSP);
5341da177e4SLinus Torvalds break;
5351da177e4SLinus Torvalds case VX_AUDIO_SRC_MIC:
5361da177e4SLinus Torvalds chip->regCDSP &= ~VXP_CDSP_DATAIN_SEL_MASK;
5371da177e4SLinus Torvalds /* reset mic levels */
5381da177e4SLinus Torvalds if (_chip->type == VX_TYPE_VXP440) {
5391da177e4SLinus Torvalds chip->regCDSP &= ~P24_CDSP_MICS_SEL_MASK;
5401da177e4SLinus Torvalds if (chip->mic_level)
5411da177e4SLinus Torvalds chip->regCDSP |= P24_CDSP_MIC38_SEL_MASK;
5421da177e4SLinus Torvalds else
5431da177e4SLinus Torvalds chip->regCDSP |= P24_CDSP_MIC20_SEL_MASK;
5441da177e4SLinus Torvalds vx_outb(chip, CDSP, chip->regCDSP);
5451da177e4SLinus Torvalds } else {
5461da177e4SLinus Torvalds chip->regCDSP |= VXP_CDSP_MIC_SEL_MASK;
5471da177e4SLinus Torvalds vx_outb(chip, CDSP, chip->regCDSP);
5481da177e4SLinus Torvalds vx_outb(chip, MICRO, vx_compute_mic_level(chip->mic_level));
5491da177e4SLinus Torvalds }
5501da177e4SLinus Torvalds break;
5511da177e4SLinus Torvalds }
5521da177e4SLinus Torvalds }
5531da177e4SLinus Torvalds
5541da177e4SLinus Torvalds /*
5551da177e4SLinus Torvalds * change the clock source
5561da177e4SLinus Torvalds * source = INTERNAL_QUARTZ or UER_SYNC
5571da177e4SLinus Torvalds */
vxp_set_clock_source(struct vx_core * _chip,int source)558af26367fSTakashi Iwai static void vxp_set_clock_source(struct vx_core *_chip, int source)
5591da177e4SLinus Torvalds {
5602e0de6eaSTakashi Iwai struct snd_vxpocket *chip = to_vxpocket(_chip);
5611da177e4SLinus Torvalds
5621da177e4SLinus Torvalds if (source == INTERNAL_QUARTZ)
5631da177e4SLinus Torvalds chip->regCDSP &= ~VXP_CDSP_CLOCKIN_SEL_MASK;
5641da177e4SLinus Torvalds else
5651da177e4SLinus Torvalds chip->regCDSP |= VXP_CDSP_CLOCKIN_SEL_MASK;
5661da177e4SLinus Torvalds vx_outb(chip, CDSP, chip->regCDSP);
5671da177e4SLinus Torvalds }
5681da177e4SLinus Torvalds
5691da177e4SLinus Torvalds
5701da177e4SLinus Torvalds /*
5711da177e4SLinus Torvalds * reset the board
5721da177e4SLinus Torvalds */
vxp_reset_board(struct vx_core * _chip,int cold_reset)573af26367fSTakashi Iwai static void vxp_reset_board(struct vx_core *_chip, int cold_reset)
5741da177e4SLinus Torvalds {
5752e0de6eaSTakashi Iwai struct snd_vxpocket *chip = to_vxpocket(_chip);
5761da177e4SLinus Torvalds
5771da177e4SLinus Torvalds chip->regCDSP = 0;
5781da177e4SLinus Torvalds chip->regDIALOG = 0;
5791da177e4SLinus Torvalds }
5801da177e4SLinus Torvalds
5811da177e4SLinus Torvalds
5821da177e4SLinus Torvalds /*
5831da177e4SLinus Torvalds * callbacks
5841da177e4SLinus Torvalds */
5851da177e4SLinus Torvalds /* exported */
586f8ae2d29STakashi Iwai const struct snd_vx_ops snd_vxpocket_ops = {
5871da177e4SLinus Torvalds .in8 = vxp_inb,
5881da177e4SLinus Torvalds .out8 = vxp_outb,
5891da177e4SLinus Torvalds .test_and_ack = vxp_test_and_ack,
5901da177e4SLinus Torvalds .validate_irq = vxp_validate_irq,
5911da177e4SLinus Torvalds .write_codec = vxp_write_codec_reg,
5921da177e4SLinus Torvalds .reset_codec = vxp_reset_codec,
5931da177e4SLinus Torvalds .change_audio_source = vxp_change_audio_source,
5941da177e4SLinus Torvalds .set_clock_source = vxp_set_clock_source,
5951da177e4SLinus Torvalds .load_dsp = vxp_load_dsp,
5961da177e4SLinus Torvalds .add_controls = vxp_add_mic_controls,
5971da177e4SLinus Torvalds .reset_dsp = vxp_reset_dsp,
5981da177e4SLinus Torvalds .reset_board = vxp_reset_board,
5991da177e4SLinus Torvalds .dma_write = vxp_dma_write,
6001da177e4SLinus Torvalds .dma_read = vxp_dma_read,
6011da177e4SLinus Torvalds };
602