xref: /linux/sound/pcmcia/vx/vxp_ops.c (revision 2073fa44)
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