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