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