18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Maintained by Jaroslav Kysela <perex@perex.cz>
48c2ecf20Sopenharmony_ci *  Originated by audio@tridentmicro.com
58c2ecf20Sopenharmony_ci *  Fri Feb 19 15:55:28 MST 1999
68c2ecf20Sopenharmony_ci *  Routines for control of Trident 4DWave (DX and NX) chip
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *  BUGS:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *  TODO:
118c2ecf20Sopenharmony_ci *    ---
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci *  SiS7018 S/PDIF support by Thomas Winischhofer <thomas@winischhofer.net>
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/delay.h>
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
198c2ecf20Sopenharmony_ci#include <linux/pci.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
228c2ecf20Sopenharmony_ci#include <linux/gameport.h>
238c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
248c2ecf20Sopenharmony_ci#include <linux/export.h>
258c2ecf20Sopenharmony_ci#include <linux/io.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <sound/core.h>
288c2ecf20Sopenharmony_ci#include <sound/info.h>
298c2ecf20Sopenharmony_ci#include <sound/control.h>
308c2ecf20Sopenharmony_ci#include <sound/tlv.h>
318c2ecf20Sopenharmony_ci#include "trident.h"
328c2ecf20Sopenharmony_ci#include <sound/asoundef.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic int snd_trident_pcm_mixer_build(struct snd_trident *trident,
358c2ecf20Sopenharmony_ci				       struct snd_trident_voice * voice,
368c2ecf20Sopenharmony_ci				       struct snd_pcm_substream *substream);
378c2ecf20Sopenharmony_cistatic int snd_trident_pcm_mixer_free(struct snd_trident *trident,
388c2ecf20Sopenharmony_ci				      struct snd_trident_voice * voice,
398c2ecf20Sopenharmony_ci				      struct snd_pcm_substream *substream);
408c2ecf20Sopenharmony_cistatic irqreturn_t snd_trident_interrupt(int irq, void *dev_id);
418c2ecf20Sopenharmony_cistatic int snd_trident_sis_reset(struct snd_trident *trident);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic void snd_trident_clear_voices(struct snd_trident * trident,
448c2ecf20Sopenharmony_ci				     unsigned short v_min, unsigned short v_max);
458c2ecf20Sopenharmony_cistatic int snd_trident_free(struct snd_trident *trident);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/*
488c2ecf20Sopenharmony_ci *  common I/O routines
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#if 0
538c2ecf20Sopenharmony_cistatic void snd_trident_print_voice_regs(struct snd_trident *trident, int voice)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	unsigned int val, tmp;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "Trident voice %i:\n", voice);
588c2ecf20Sopenharmony_ci	outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR));
598c2ecf20Sopenharmony_ci	val = inl(TRID_REG(trident, CH_LBA));
608c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "LBA: 0x%x\n", val);
618c2ecf20Sopenharmony_ci	val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
628c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "GVSel: %i\n", val >> 31);
638c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "Pan: 0x%x\n", (val >> 24) & 0x7f);
648c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "Vol: 0x%x\n", (val >> 16) & 0xff);
658c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "CTRL: 0x%x\n", (val >> 12) & 0x0f);
668c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "EC: 0x%x\n", val & 0x0fff);
678c2ecf20Sopenharmony_ci	if (trident->device != TRIDENT_DEVICE_ID_NX) {
688c2ecf20Sopenharmony_ci		val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS));
698c2ecf20Sopenharmony_ci		dev_dbg(trident->card->dev, "CSO: 0x%x\n", val >> 16);
708c2ecf20Sopenharmony_ci		dev_dbg(trident->card->dev, "Alpha: 0x%x\n", (val >> 4) & 0x0fff);
718c2ecf20Sopenharmony_ci		dev_dbg(trident->card->dev, "FMS: 0x%x\n", val & 0x0f);
728c2ecf20Sopenharmony_ci		val = inl(TRID_REG(trident, CH_DX_ESO_DELTA));
738c2ecf20Sopenharmony_ci		dev_dbg(trident->card->dev, "ESO: 0x%x\n", val >> 16);
748c2ecf20Sopenharmony_ci		dev_dbg(trident->card->dev, "Delta: 0x%x\n", val & 0xffff);
758c2ecf20Sopenharmony_ci		val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
768c2ecf20Sopenharmony_ci	} else {		// TRIDENT_DEVICE_ID_NX
778c2ecf20Sopenharmony_ci		val = inl(TRID_REG(trident, CH_NX_DELTA_CSO));
788c2ecf20Sopenharmony_ci		tmp = (val >> 24) & 0xff;
798c2ecf20Sopenharmony_ci		dev_dbg(trident->card->dev, "CSO: 0x%x\n", val & 0x00ffffff);
808c2ecf20Sopenharmony_ci		val = inl(TRID_REG(trident, CH_NX_DELTA_ESO));
818c2ecf20Sopenharmony_ci		tmp |= (val >> 16) & 0xff00;
828c2ecf20Sopenharmony_ci		dev_dbg(trident->card->dev, "Delta: 0x%x\n", tmp);
838c2ecf20Sopenharmony_ci		dev_dbg(trident->card->dev, "ESO: 0x%x\n", val & 0x00ffffff);
848c2ecf20Sopenharmony_ci		val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL));
858c2ecf20Sopenharmony_ci		dev_dbg(trident->card->dev, "Alpha: 0x%x\n", val >> 20);
868c2ecf20Sopenharmony_ci		dev_dbg(trident->card->dev, "FMS: 0x%x\n", (val >> 16) & 0x0f);
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "FMC: 0x%x\n", (val >> 14) & 3);
898c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "RVol: 0x%x\n", (val >> 7) & 0x7f);
908c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "CVol: 0x%x\n", val & 0x7f);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci#endif
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
958c2ecf20Sopenharmony_ci   unsigned short snd_trident_codec_read(struct snd_ac97 *ac97, unsigned short reg)
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci   Description: This routine will do all of the reading from the external
988c2ecf20Sopenharmony_ci                CODEC (AC97).
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci   Parameters:  ac97 - ac97 codec structure
1018c2ecf20Sopenharmony_ci                reg - CODEC register index, from AC97 Hal.
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci   returns:     16 bit value read from the AC97.
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
1068c2ecf20Sopenharmony_cistatic unsigned short snd_trident_codec_read(struct snd_ac97 *ac97, unsigned short reg)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	unsigned int data = 0, treg;
1098c2ecf20Sopenharmony_ci	unsigned short count = 0xffff;
1108c2ecf20Sopenharmony_ci	unsigned long flags;
1118c2ecf20Sopenharmony_ci	struct snd_trident *trident = ac97->private_data;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&trident->reg_lock, flags);
1148c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_DX) {
1158c2ecf20Sopenharmony_ci		data = (DX_AC97_BUSY_READ | (reg & 0x000000ff));
1168c2ecf20Sopenharmony_ci		outl(data, TRID_REG(trident, DX_ACR1_AC97_R));
1178c2ecf20Sopenharmony_ci		do {
1188c2ecf20Sopenharmony_ci			data = inl(TRID_REG(trident, DX_ACR1_AC97_R));
1198c2ecf20Sopenharmony_ci			if ((data & DX_AC97_BUSY_READ) == 0)
1208c2ecf20Sopenharmony_ci				break;
1218c2ecf20Sopenharmony_ci		} while (--count);
1228c2ecf20Sopenharmony_ci	} else if (trident->device == TRIDENT_DEVICE_ID_NX) {
1238c2ecf20Sopenharmony_ci		data = (NX_AC97_BUSY_READ | (reg & 0x000000ff));
1248c2ecf20Sopenharmony_ci		treg = ac97->num == 0 ? NX_ACR2_AC97_R_PRIMARY : NX_ACR3_AC97_R_SECONDARY;
1258c2ecf20Sopenharmony_ci		outl(data, TRID_REG(trident, treg));
1268c2ecf20Sopenharmony_ci		do {
1278c2ecf20Sopenharmony_ci			data = inl(TRID_REG(trident, treg));
1288c2ecf20Sopenharmony_ci			if ((data & 0x00000C00) == 0)
1298c2ecf20Sopenharmony_ci				break;
1308c2ecf20Sopenharmony_ci		} while (--count);
1318c2ecf20Sopenharmony_ci	} else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
1328c2ecf20Sopenharmony_ci		data = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
1338c2ecf20Sopenharmony_ci		if (ac97->num == 1)
1348c2ecf20Sopenharmony_ci			data |= SI_AC97_SECONDARY;
1358c2ecf20Sopenharmony_ci		outl(data, TRID_REG(trident, SI_AC97_READ));
1368c2ecf20Sopenharmony_ci		do {
1378c2ecf20Sopenharmony_ci			data = inl(TRID_REG(trident, SI_AC97_READ));
1388c2ecf20Sopenharmony_ci			if ((data & (SI_AC97_BUSY_READ)) == 0)
1398c2ecf20Sopenharmony_ci				break;
1408c2ecf20Sopenharmony_ci		} while (--count);
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (count == 0 && !trident->ac97_detect) {
1448c2ecf20Sopenharmony_ci		dev_err(trident->card->dev,
1458c2ecf20Sopenharmony_ci			"ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n",
1468c2ecf20Sopenharmony_ci			   reg, data);
1478c2ecf20Sopenharmony_ci		data = 0;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&trident->reg_lock, flags);
1518c2ecf20Sopenharmony_ci	return ((unsigned short) (data >> 16));
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
1558c2ecf20Sopenharmony_ci   void snd_trident_codec_write(struct snd_ac97 *ac97, unsigned short reg,
1568c2ecf20Sopenharmony_ci   unsigned short wdata)
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci   Description: This routine will do all of the writing to the external
1598c2ecf20Sopenharmony_ci                CODEC (AC97).
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci   Parameters:	ac97 - ac97 codec structure
1628c2ecf20Sopenharmony_ci   	        reg - CODEC register index, from AC97 Hal.
1638c2ecf20Sopenharmony_ci                data  - Lower 16 bits are the data to write to CODEC.
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci   returns:     TRUE if everything went ok, else FALSE.
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
1688c2ecf20Sopenharmony_cistatic void snd_trident_codec_write(struct snd_ac97 *ac97, unsigned short reg,
1698c2ecf20Sopenharmony_ci				    unsigned short wdata)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	unsigned int address, data;
1728c2ecf20Sopenharmony_ci	unsigned short count = 0xffff;
1738c2ecf20Sopenharmony_ci	unsigned long flags;
1748c2ecf20Sopenharmony_ci	struct snd_trident *trident = ac97->private_data;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	data = ((unsigned long) wdata) << 16;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	spin_lock_irqsave(&trident->reg_lock, flags);
1798c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_DX) {
1808c2ecf20Sopenharmony_ci		address = DX_ACR0_AC97_W;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		/* read AC-97 write register status */
1838c2ecf20Sopenharmony_ci		do {
1848c2ecf20Sopenharmony_ci			if ((inw(TRID_REG(trident, address)) & DX_AC97_BUSY_WRITE) == 0)
1858c2ecf20Sopenharmony_ci				break;
1868c2ecf20Sopenharmony_ci		} while (--count);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci		data |= (DX_AC97_BUSY_WRITE | (reg & 0x000000ff));
1898c2ecf20Sopenharmony_ci	} else if (trident->device == TRIDENT_DEVICE_ID_NX) {
1908c2ecf20Sopenharmony_ci		address = NX_ACR1_AC97_W;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		/* read AC-97 write register status */
1938c2ecf20Sopenharmony_ci		do {
1948c2ecf20Sopenharmony_ci			if ((inw(TRID_REG(trident, address)) & NX_AC97_BUSY_WRITE) == 0)
1958c2ecf20Sopenharmony_ci				break;
1968c2ecf20Sopenharmony_ci		} while (--count);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		data |= (NX_AC97_BUSY_WRITE | (ac97->num << 8) | (reg & 0x000000ff));
1998c2ecf20Sopenharmony_ci	} else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
2008c2ecf20Sopenharmony_ci		address = SI_AC97_WRITE;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		/* read AC-97 write register status */
2038c2ecf20Sopenharmony_ci		do {
2048c2ecf20Sopenharmony_ci			if ((inw(TRID_REG(trident, address)) & (SI_AC97_BUSY_WRITE)) == 0)
2058c2ecf20Sopenharmony_ci				break;
2068c2ecf20Sopenharmony_ci		} while (--count);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci		data |= SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
2098c2ecf20Sopenharmony_ci		if (ac97->num == 1)
2108c2ecf20Sopenharmony_ci			data |= SI_AC97_SECONDARY;
2118c2ecf20Sopenharmony_ci	} else {
2128c2ecf20Sopenharmony_ci		address = 0;	/* keep GCC happy */
2138c2ecf20Sopenharmony_ci		count = 0;	/* return */
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (count == 0) {
2178c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&trident->reg_lock, flags);
2188c2ecf20Sopenharmony_ci		return;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci	outl(data, TRID_REG(trident, address));
2218c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&trident->reg_lock, flags);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
2258c2ecf20Sopenharmony_ci   void snd_trident_enable_eso(struct snd_trident *trident)
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci   Description: This routine will enable end of loop interrupts.
2288c2ecf20Sopenharmony_ci                End of loop interrupts will occur when a running
2298c2ecf20Sopenharmony_ci                channel reaches ESO.
2308c2ecf20Sopenharmony_ci                Also enables middle of loop interrupts.
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic void snd_trident_enable_eso(struct snd_trident * trident)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	unsigned int val;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	val = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
2418c2ecf20Sopenharmony_ci	val |= ENDLP_IE;
2428c2ecf20Sopenharmony_ci	val |= MIDLP_IE;
2438c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_SI7018)
2448c2ecf20Sopenharmony_ci		val |= BANK_B_EN;
2458c2ecf20Sopenharmony_ci	outl(val, TRID_REG(trident, T4D_LFO_GC_CIR));
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
2498c2ecf20Sopenharmony_ci   void snd_trident_disable_eso(struct snd_trident *trident)
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci   Description: This routine will disable end of loop interrupts.
2528c2ecf20Sopenharmony_ci                End of loop interrupts will occur when a running
2538c2ecf20Sopenharmony_ci                channel reaches ESO.
2548c2ecf20Sopenharmony_ci                Also disables middle of loop interrupts.
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci   Parameters:
2578c2ecf20Sopenharmony_ci                trident - pointer to target device class for 4DWave.
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci   returns:     TRUE if everything went ok, else FALSE.
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic void snd_trident_disable_eso(struct snd_trident * trident)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	unsigned int tmp;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	tmp = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
2688c2ecf20Sopenharmony_ci	tmp &= ~ENDLP_IE;
2698c2ecf20Sopenharmony_ci	tmp &= ~MIDLP_IE;
2708c2ecf20Sopenharmony_ci	outl(tmp, TRID_REG(trident, T4D_LFO_GC_CIR));
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
2748c2ecf20Sopenharmony_ci   void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice)
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci    Description: Start a voice, any channel 0 thru 63.
2778c2ecf20Sopenharmony_ci                 This routine automatically handles the fact that there are
2788c2ecf20Sopenharmony_ci                 more than 32 channels available.
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci    Parameters : voice - Voice number 0 thru n.
2818c2ecf20Sopenharmony_ci                 trident - pointer to target device class for 4DWave.
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci    Return Value: None.
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_civoid snd_trident_start_voice(struct snd_trident * trident, unsigned int voice)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	unsigned int mask = 1 << (voice & 0x1f);
2908c2ecf20Sopenharmony_ci	unsigned int reg = (voice & 0x20) ? T4D_START_B : T4D_START_A;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	outl(mask, TRID_REG(trident, reg));
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_trident_start_voice);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
2988c2ecf20Sopenharmony_ci   void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice)
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci    Description: Stop a voice, any channel 0 thru 63.
3018c2ecf20Sopenharmony_ci                 This routine automatically handles the fact that there are
3028c2ecf20Sopenharmony_ci                 more than 32 channels available.
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci    Parameters : voice - Voice number 0 thru n.
3058c2ecf20Sopenharmony_ci                 trident - pointer to target device class for 4DWave.
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci    Return Value: None.
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_civoid snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	unsigned int mask = 1 << (voice & 0x1f);
3148c2ecf20Sopenharmony_ci	unsigned int reg = (voice & 0x20) ? T4D_STOP_B : T4D_STOP_A;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	outl(mask, TRID_REG(trident, reg));
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_trident_stop_voice);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
3228c2ecf20Sopenharmony_ci    int snd_trident_allocate_pcm_channel(struct snd_trident *trident)
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci    Description: Allocate hardware channel in Bank B (32-63).
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci    Parameters :  trident - pointer to target device class for 4DWave.
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci    Return Value: hardware channel - 32-63 or -1 when no channel is available
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic int snd_trident_allocate_pcm_channel(struct snd_trident * trident)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	int idx;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (trident->ChanPCMcnt >= trident->ChanPCM)
3378c2ecf20Sopenharmony_ci		return -1;
3388c2ecf20Sopenharmony_ci	for (idx = 31; idx >= 0; idx--) {
3398c2ecf20Sopenharmony_ci		if (!(trident->ChanMap[T4D_BANK_B] & (1 << idx))) {
3408c2ecf20Sopenharmony_ci			trident->ChanMap[T4D_BANK_B] |= 1 << idx;
3418c2ecf20Sopenharmony_ci			trident->ChanPCMcnt++;
3428c2ecf20Sopenharmony_ci			return idx + 32;
3438c2ecf20Sopenharmony_ci		}
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci	return -1;
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
3498c2ecf20Sopenharmony_ci    void snd_trident_free_pcm_channel(int channel)
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci    Description: Free hardware channel in Bank B (32-63)
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci    Parameters :  trident - pointer to target device class for 4DWave.
3548c2ecf20Sopenharmony_ci	          channel - hardware channel number 0-63
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci    Return Value: none
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic void snd_trident_free_pcm_channel(struct snd_trident *trident, int channel)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	if (channel < 32 || channel > 63)
3638c2ecf20Sopenharmony_ci		return;
3648c2ecf20Sopenharmony_ci	channel &= 0x1f;
3658c2ecf20Sopenharmony_ci	if (trident->ChanMap[T4D_BANK_B] & (1 << channel)) {
3668c2ecf20Sopenharmony_ci		trident->ChanMap[T4D_BANK_B] &= ~(1 << channel);
3678c2ecf20Sopenharmony_ci		trident->ChanPCMcnt--;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
3728c2ecf20Sopenharmony_ci    unsigned int snd_trident_allocate_synth_channel(void)
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci    Description: Allocate hardware channel in Bank A (0-31).
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci    Parameters :  trident - pointer to target device class for 4DWave.
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci    Return Value: hardware channel - 0-31 or -1 when no channel is available
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic int snd_trident_allocate_synth_channel(struct snd_trident * trident)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	int idx;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	for (idx = 31; idx >= 0; idx--) {
3878c2ecf20Sopenharmony_ci		if (!(trident->ChanMap[T4D_BANK_A] & (1 << idx))) {
3888c2ecf20Sopenharmony_ci			trident->ChanMap[T4D_BANK_A] |= 1 << idx;
3898c2ecf20Sopenharmony_ci			trident->synth.ChanSynthCount++;
3908c2ecf20Sopenharmony_ci			return idx;
3918c2ecf20Sopenharmony_ci		}
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci	return -1;
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
3978c2ecf20Sopenharmony_ci    void snd_trident_free_synth_channel( int channel )
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci    Description: Free hardware channel in Bank B (0-31).
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci    Parameters :  trident - pointer to target device class for 4DWave.
4028c2ecf20Sopenharmony_ci	          channel - hardware channel number 0-63
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci    Return Value: none
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic void snd_trident_free_synth_channel(struct snd_trident *trident, int channel)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	if (channel < 0 || channel > 31)
4118c2ecf20Sopenharmony_ci		return;
4128c2ecf20Sopenharmony_ci	channel &= 0x1f;
4138c2ecf20Sopenharmony_ci	if (trident->ChanMap[T4D_BANK_A] & (1 << channel)) {
4148c2ecf20Sopenharmony_ci		trident->ChanMap[T4D_BANK_A] &= ~(1 << channel);
4158c2ecf20Sopenharmony_ci		trident->synth.ChanSynthCount--;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
4208c2ecf20Sopenharmony_ci   snd_trident_write_voice_regs
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci   Description: This routine will complete and write the 5 hardware channel
4238c2ecf20Sopenharmony_ci                registers to hardware.
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
4268c2ecf20Sopenharmony_ci                voice - synthesizer voice structure
4278c2ecf20Sopenharmony_ci                Each register field.
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_civoid snd_trident_write_voice_regs(struct snd_trident * trident,
4328c2ecf20Sopenharmony_ci				  struct snd_trident_voice * voice)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	unsigned int FmcRvolCvol;
4358c2ecf20Sopenharmony_ci	unsigned int regs[5];
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	regs[1] = voice->LBA;
4388c2ecf20Sopenharmony_ci	regs[4] = (voice->GVSel << 31) |
4398c2ecf20Sopenharmony_ci		  ((voice->Pan & 0x0000007f) << 24) |
4408c2ecf20Sopenharmony_ci		  ((voice->CTRL & 0x0000000f) << 12);
4418c2ecf20Sopenharmony_ci	FmcRvolCvol = ((voice->FMC & 3) << 14) |
4428c2ecf20Sopenharmony_ci	              ((voice->RVol & 0x7f) << 7) |
4438c2ecf20Sopenharmony_ci	              (voice->CVol & 0x7f);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	switch (trident->device) {
4468c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_SI7018:
4478c2ecf20Sopenharmony_ci		regs[4] |= voice->number > 31 ?
4488c2ecf20Sopenharmony_ci				(voice->Vol & 0x000003ff) :
4498c2ecf20Sopenharmony_ci				((voice->Vol & 0x00003fc) << (16-2)) |
4508c2ecf20Sopenharmony_ci				(voice->EC & 0x00000fff);
4518c2ecf20Sopenharmony_ci		regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) |
4528c2ecf20Sopenharmony_ci			(voice->FMS & 0x0000000f);
4538c2ecf20Sopenharmony_ci		regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff);
4548c2ecf20Sopenharmony_ci		regs[3] = (voice->Attribute << 16) | FmcRvolCvol;
4558c2ecf20Sopenharmony_ci		break;
4568c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_DX:
4578c2ecf20Sopenharmony_ci		regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) |
4588c2ecf20Sopenharmony_ci			   (voice->EC & 0x00000fff);
4598c2ecf20Sopenharmony_ci		regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) |
4608c2ecf20Sopenharmony_ci			(voice->FMS & 0x0000000f);
4618c2ecf20Sopenharmony_ci		regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff);
4628c2ecf20Sopenharmony_ci		regs[3] = FmcRvolCvol;
4638c2ecf20Sopenharmony_ci		break;
4648c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_NX:
4658c2ecf20Sopenharmony_ci		regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) |
4668c2ecf20Sopenharmony_ci			   (voice->EC & 0x00000fff);
4678c2ecf20Sopenharmony_ci		regs[0] = (voice->Delta << 24) | (voice->CSO & 0x00ffffff);
4688c2ecf20Sopenharmony_ci		regs[2] = ((voice->Delta << 16) & 0xff000000) |
4698c2ecf20Sopenharmony_ci			(voice->ESO & 0x00ffffff);
4708c2ecf20Sopenharmony_ci		regs[3] = (voice->Alpha << 20) |
4718c2ecf20Sopenharmony_ci			((voice->FMS & 0x0000000f) << 16) | FmcRvolCvol;
4728c2ecf20Sopenharmony_ci		break;
4738c2ecf20Sopenharmony_ci	default:
4748c2ecf20Sopenharmony_ci		snd_BUG();
4758c2ecf20Sopenharmony_ci		return;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
4798c2ecf20Sopenharmony_ci	outl(regs[0], TRID_REG(trident, CH_START + 0));
4808c2ecf20Sopenharmony_ci	outl(regs[1], TRID_REG(trident, CH_START + 4));
4818c2ecf20Sopenharmony_ci	outl(regs[2], TRID_REG(trident, CH_START + 8));
4828c2ecf20Sopenharmony_ci	outl(regs[3], TRID_REG(trident, CH_START + 12));
4838c2ecf20Sopenharmony_ci	outl(regs[4], TRID_REG(trident, CH_START + 16));
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci#if 0
4868c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "written %i channel:\n", voice->number);
4878c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "  regs[0] = 0x%x/0x%x\n",
4888c2ecf20Sopenharmony_ci	       regs[0], inl(TRID_REG(trident, CH_START + 0)));
4898c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "  regs[1] = 0x%x/0x%x\n",
4908c2ecf20Sopenharmony_ci	       regs[1], inl(TRID_REG(trident, CH_START + 4)));
4918c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "  regs[2] = 0x%x/0x%x\n",
4928c2ecf20Sopenharmony_ci	       regs[2], inl(TRID_REG(trident, CH_START + 8)));
4938c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "  regs[3] = 0x%x/0x%x\n",
4948c2ecf20Sopenharmony_ci	       regs[3], inl(TRID_REG(trident, CH_START + 12)));
4958c2ecf20Sopenharmony_ci	dev_dbg(trident->card->dev, "  regs[4] = 0x%x/0x%x\n",
4968c2ecf20Sopenharmony_ci	       regs[4], inl(TRID_REG(trident, CH_START + 16)));
4978c2ecf20Sopenharmony_ci#endif
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_trident_write_voice_regs);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
5038c2ecf20Sopenharmony_ci   snd_trident_write_cso_reg
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci   Description: This routine will write the new CSO offset
5068c2ecf20Sopenharmony_ci                register to hardware.
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
5098c2ecf20Sopenharmony_ci                voice - synthesizer voice structure
5108c2ecf20Sopenharmony_ci                CSO - new CSO value
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic void snd_trident_write_cso_reg(struct snd_trident * trident,
5158c2ecf20Sopenharmony_ci				      struct snd_trident_voice * voice,
5168c2ecf20Sopenharmony_ci				      unsigned int CSO)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	voice->CSO = CSO;
5198c2ecf20Sopenharmony_ci	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
5208c2ecf20Sopenharmony_ci	if (trident->device != TRIDENT_DEVICE_ID_NX) {
5218c2ecf20Sopenharmony_ci		outw(voice->CSO, TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
5228c2ecf20Sopenharmony_ci	} else {
5238c2ecf20Sopenharmony_ci		outl((voice->Delta << 24) |
5248c2ecf20Sopenharmony_ci		     (voice->CSO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_CSO));
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
5298c2ecf20Sopenharmony_ci   snd_trident_write_eso_reg
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci   Description: This routine will write the new ESO offset
5328c2ecf20Sopenharmony_ci                register to hardware.
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
5358c2ecf20Sopenharmony_ci                voice - synthesizer voice structure
5368c2ecf20Sopenharmony_ci                ESO - new ESO value
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cistatic void snd_trident_write_eso_reg(struct snd_trident * trident,
5418c2ecf20Sopenharmony_ci				      struct snd_trident_voice * voice,
5428c2ecf20Sopenharmony_ci				      unsigned int ESO)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	voice->ESO = ESO;
5458c2ecf20Sopenharmony_ci	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
5468c2ecf20Sopenharmony_ci	if (trident->device != TRIDENT_DEVICE_ID_NX) {
5478c2ecf20Sopenharmony_ci		outw(voice->ESO, TRID_REG(trident, CH_DX_ESO_DELTA) + 2);
5488c2ecf20Sopenharmony_ci	} else {
5498c2ecf20Sopenharmony_ci		outl(((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff),
5508c2ecf20Sopenharmony_ci		     TRID_REG(trident, CH_NX_DELTA_ESO));
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
5558c2ecf20Sopenharmony_ci   snd_trident_write_vol_reg
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci   Description: This routine will write the new voice volume
5588c2ecf20Sopenharmony_ci                register to hardware.
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
5618c2ecf20Sopenharmony_ci                voice - synthesizer voice structure
5628c2ecf20Sopenharmony_ci                Vol - new voice volume
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic void snd_trident_write_vol_reg(struct snd_trident * trident,
5678c2ecf20Sopenharmony_ci				      struct snd_trident_voice * voice,
5688c2ecf20Sopenharmony_ci				      unsigned int Vol)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	voice->Vol = Vol;
5718c2ecf20Sopenharmony_ci	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
5728c2ecf20Sopenharmony_ci	switch (trident->device) {
5738c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_DX:
5748c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_NX:
5758c2ecf20Sopenharmony_ci		outb(voice->Vol >> 2, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 2));
5768c2ecf20Sopenharmony_ci		break;
5778c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_SI7018:
5788c2ecf20Sopenharmony_ci		/* dev_dbg(trident->card->dev, "voice->Vol = 0x%x\n", voice->Vol); */
5798c2ecf20Sopenharmony_ci		outw((voice->CTRL << 12) | voice->Vol,
5808c2ecf20Sopenharmony_ci		     TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
5818c2ecf20Sopenharmony_ci		break;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
5868c2ecf20Sopenharmony_ci   snd_trident_write_pan_reg
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci   Description: This routine will write the new voice pan
5898c2ecf20Sopenharmony_ci                register to hardware.
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
5928c2ecf20Sopenharmony_ci                voice - synthesizer voice structure
5938c2ecf20Sopenharmony_ci                Pan - new pan value
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cistatic void snd_trident_write_pan_reg(struct snd_trident * trident,
5988c2ecf20Sopenharmony_ci				      struct snd_trident_voice * voice,
5998c2ecf20Sopenharmony_ci				      unsigned int Pan)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	voice->Pan = Pan;
6028c2ecf20Sopenharmony_ci	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
6038c2ecf20Sopenharmony_ci	outb(((voice->GVSel & 0x01) << 7) | (voice->Pan & 0x7f),
6048c2ecf20Sopenharmony_ci	     TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 3));
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
6088c2ecf20Sopenharmony_ci   snd_trident_write_rvol_reg
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci   Description: This routine will write the new reverb volume
6118c2ecf20Sopenharmony_ci                register to hardware.
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
6148c2ecf20Sopenharmony_ci                voice - synthesizer voice structure
6158c2ecf20Sopenharmony_ci                RVol - new reverb volume
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic void snd_trident_write_rvol_reg(struct snd_trident * trident,
6208c2ecf20Sopenharmony_ci				       struct snd_trident_voice * voice,
6218c2ecf20Sopenharmony_ci				       unsigned int RVol)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	voice->RVol = RVol;
6248c2ecf20Sopenharmony_ci	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
6258c2ecf20Sopenharmony_ci	outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) |
6268c2ecf20Sopenharmony_ci	     (voice->CVol & 0x007f),
6278c2ecf20Sopenharmony_ci	     TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ?
6288c2ecf20Sopenharmony_ci		      CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL));
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
6328c2ecf20Sopenharmony_ci   snd_trident_write_cvol_reg
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci   Description: This routine will write the new chorus volume
6358c2ecf20Sopenharmony_ci                register to hardware.
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
6388c2ecf20Sopenharmony_ci                voice - synthesizer voice structure
6398c2ecf20Sopenharmony_ci                CVol - new chorus volume
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic void snd_trident_write_cvol_reg(struct snd_trident * trident,
6448c2ecf20Sopenharmony_ci				       struct snd_trident_voice * voice,
6458c2ecf20Sopenharmony_ci				       unsigned int CVol)
6468c2ecf20Sopenharmony_ci{
6478c2ecf20Sopenharmony_ci	voice->CVol = CVol;
6488c2ecf20Sopenharmony_ci	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
6498c2ecf20Sopenharmony_ci	outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) |
6508c2ecf20Sopenharmony_ci	     (voice->CVol & 0x007f),
6518c2ecf20Sopenharmony_ci	     TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ?
6528c2ecf20Sopenharmony_ci		      CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL));
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
6568c2ecf20Sopenharmony_ci   snd_trident_convert_rate
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci   Description: This routine converts rate in HZ to hardware delta value.
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
6618c2ecf20Sopenharmony_ci                rate - Real or Virtual channel number.
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci   Returns:     Delta value.
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
6668c2ecf20Sopenharmony_cistatic unsigned int snd_trident_convert_rate(unsigned int rate)
6678c2ecf20Sopenharmony_ci{
6688c2ecf20Sopenharmony_ci	unsigned int delta;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	// We special case 44100 and 8000 since rounding with the equation
6718c2ecf20Sopenharmony_ci	// does not give us an accurate enough value. For 11025 and 22050
6728c2ecf20Sopenharmony_ci	// the equation gives us the best answer. All other frequencies will
6738c2ecf20Sopenharmony_ci	// also use the equation. JDW
6748c2ecf20Sopenharmony_ci	if (rate == 44100)
6758c2ecf20Sopenharmony_ci		delta = 0xeb3;
6768c2ecf20Sopenharmony_ci	else if (rate == 8000)
6778c2ecf20Sopenharmony_ci		delta = 0x2ab;
6788c2ecf20Sopenharmony_ci	else if (rate == 48000)
6798c2ecf20Sopenharmony_ci		delta = 0x1000;
6808c2ecf20Sopenharmony_ci	else
6818c2ecf20Sopenharmony_ci		delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff;
6828c2ecf20Sopenharmony_ci	return delta;
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
6868c2ecf20Sopenharmony_ci   snd_trident_convert_adc_rate
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci   Description: This routine converts rate in HZ to hardware delta value.
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
6918c2ecf20Sopenharmony_ci                rate - Real or Virtual channel number.
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci   Returns:     Delta value.
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
6968c2ecf20Sopenharmony_cistatic unsigned int snd_trident_convert_adc_rate(unsigned int rate)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	unsigned int delta;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	// We special case 44100 and 8000 since rounding with the equation
7018c2ecf20Sopenharmony_ci	// does not give us an accurate enough value. For 11025 and 22050
7028c2ecf20Sopenharmony_ci	// the equation gives us the best answer. All other frequencies will
7038c2ecf20Sopenharmony_ci	// also use the equation. JDW
7048c2ecf20Sopenharmony_ci	if (rate == 44100)
7058c2ecf20Sopenharmony_ci		delta = 0x116a;
7068c2ecf20Sopenharmony_ci	else if (rate == 8000)
7078c2ecf20Sopenharmony_ci		delta = 0x6000;
7088c2ecf20Sopenharmony_ci	else if (rate == 48000)
7098c2ecf20Sopenharmony_ci		delta = 0x1000;
7108c2ecf20Sopenharmony_ci	else
7118c2ecf20Sopenharmony_ci		delta = ((48000 << 12) / rate) & 0x0000ffff;
7128c2ecf20Sopenharmony_ci	return delta;
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
7168c2ecf20Sopenharmony_ci   snd_trident_spurious_threshold
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci   Description: This routine converts rate in HZ to spurious threshold.
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
7218c2ecf20Sopenharmony_ci                rate - Real or Virtual channel number.
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci   Returns:     Delta value.
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
7268c2ecf20Sopenharmony_cistatic unsigned int snd_trident_spurious_threshold(unsigned int rate,
7278c2ecf20Sopenharmony_ci						   unsigned int period_size)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	unsigned int res = (rate * period_size) / 48000;
7308c2ecf20Sopenharmony_ci	if (res < 64)
7318c2ecf20Sopenharmony_ci		res = res / 2;
7328c2ecf20Sopenharmony_ci	else
7338c2ecf20Sopenharmony_ci		res -= 32;
7348c2ecf20Sopenharmony_ci	return res;
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
7388c2ecf20Sopenharmony_ci   snd_trident_control_mode
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci   Description: This routine returns a control mode for a PCM channel.
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
7438c2ecf20Sopenharmony_ci                substream  - PCM substream
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci   Returns:     Control value.
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
7488c2ecf20Sopenharmony_cistatic unsigned int snd_trident_control_mode(struct snd_pcm_substream *substream)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	unsigned int CTRL;
7518c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	/* set ctrl mode
7548c2ecf20Sopenharmony_ci	   CTRL default: 8-bit (unsigned) mono, loop mode enabled
7558c2ecf20Sopenharmony_ci	 */
7568c2ecf20Sopenharmony_ci	CTRL = 0x00000001;
7578c2ecf20Sopenharmony_ci	if (snd_pcm_format_width(runtime->format) == 16)
7588c2ecf20Sopenharmony_ci		CTRL |= 0x00000008;	// 16-bit data
7598c2ecf20Sopenharmony_ci	if (snd_pcm_format_signed(runtime->format))
7608c2ecf20Sopenharmony_ci		CTRL |= 0x00000002;	// signed data
7618c2ecf20Sopenharmony_ci	if (runtime->channels > 1)
7628c2ecf20Sopenharmony_ci		CTRL |= 0x00000004;	// stereo data
7638c2ecf20Sopenharmony_ci	return CTRL;
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci/*
7678c2ecf20Sopenharmony_ci *  PCM part
7688c2ecf20Sopenharmony_ci */
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
7718c2ecf20Sopenharmony_ci   snd_trident_allocate_pcm_mem
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci   Description: Allocate PCM ring buffer for given substream
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci   Parameters:  substream  - PCM substream class
7768c2ecf20Sopenharmony_ci		hw_params  - hardware parameters
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci   Returns:     Error status
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_cistatic int snd_trident_allocate_pcm_mem(struct snd_pcm_substream *substream,
7838c2ecf20Sopenharmony_ci					struct snd_pcm_hw_params *hw_params)
7848c2ecf20Sopenharmony_ci{
7858c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
7868c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
7878c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice = runtime->private_data;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	if (trident->tlb.entries) {
7908c2ecf20Sopenharmony_ci		if (runtime->buffer_changed) {
7918c2ecf20Sopenharmony_ci			if (voice->memblk)
7928c2ecf20Sopenharmony_ci				snd_trident_free_pages(trident, voice->memblk);
7938c2ecf20Sopenharmony_ci			voice->memblk = snd_trident_alloc_pages(trident, substream);
7948c2ecf20Sopenharmony_ci			if (voice->memblk == NULL)
7958c2ecf20Sopenharmony_ci				return -ENOMEM;
7968c2ecf20Sopenharmony_ci		}
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci	return 0;
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
8028c2ecf20Sopenharmony_ci   snd_trident_allocate_evoice
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci   Description: Allocate extra voice as interrupt generator
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci   Parameters:  substream  - PCM substream class
8078c2ecf20Sopenharmony_ci		hw_params  - hardware parameters
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci   Returns:     Error status
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_cistatic int snd_trident_allocate_evoice(struct snd_pcm_substream *substream,
8148c2ecf20Sopenharmony_ci				       struct snd_pcm_hw_params *hw_params)
8158c2ecf20Sopenharmony_ci{
8168c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
8178c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
8188c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice = runtime->private_data;
8198c2ecf20Sopenharmony_ci	struct snd_trident_voice *evoice = voice->extra;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	/* voice management */
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) {
8248c2ecf20Sopenharmony_ci		if (evoice == NULL) {
8258c2ecf20Sopenharmony_ci			evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
8268c2ecf20Sopenharmony_ci			if (evoice == NULL)
8278c2ecf20Sopenharmony_ci				return -ENOMEM;
8288c2ecf20Sopenharmony_ci			voice->extra = evoice;
8298c2ecf20Sopenharmony_ci			evoice->substream = substream;
8308c2ecf20Sopenharmony_ci		}
8318c2ecf20Sopenharmony_ci	} else {
8328c2ecf20Sopenharmony_ci		if (evoice != NULL) {
8338c2ecf20Sopenharmony_ci			snd_trident_free_voice(trident, evoice);
8348c2ecf20Sopenharmony_ci			voice->extra = evoice = NULL;
8358c2ecf20Sopenharmony_ci		}
8368c2ecf20Sopenharmony_ci	}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	return 0;
8398c2ecf20Sopenharmony_ci}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
8428c2ecf20Sopenharmony_ci   snd_trident_hw_params
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci   Description: Set the hardware parameters for the playback device.
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci   Parameters:  substream  - PCM substream class
8478c2ecf20Sopenharmony_ci		hw_params  - hardware parameters
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci   Returns:     Error status
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_cistatic int snd_trident_hw_params(struct snd_pcm_substream *substream,
8548c2ecf20Sopenharmony_ci				 struct snd_pcm_hw_params *hw_params)
8558c2ecf20Sopenharmony_ci{
8568c2ecf20Sopenharmony_ci	int err;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	err = snd_trident_allocate_pcm_mem(substream, hw_params);
8598c2ecf20Sopenharmony_ci	if (err >= 0)
8608c2ecf20Sopenharmony_ci		err = snd_trident_allocate_evoice(substream, hw_params);
8618c2ecf20Sopenharmony_ci	return err;
8628c2ecf20Sopenharmony_ci}
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
8658c2ecf20Sopenharmony_ci   snd_trident_playback_hw_free
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci   Description: Release the hardware resources for the playback device.
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci   Parameters:  substream  - PCM substream class
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci   Returns:     Error status
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_cistatic int snd_trident_hw_free(struct snd_pcm_substream *substream)
8768c2ecf20Sopenharmony_ci{
8778c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
8788c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
8798c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice = runtime->private_data;
8808c2ecf20Sopenharmony_ci	struct snd_trident_voice *evoice = voice ? voice->extra : NULL;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	if (trident->tlb.entries) {
8838c2ecf20Sopenharmony_ci		if (voice && voice->memblk) {
8848c2ecf20Sopenharmony_ci			snd_trident_free_pages(trident, voice->memblk);
8858c2ecf20Sopenharmony_ci			voice->memblk = NULL;
8868c2ecf20Sopenharmony_ci		}
8878c2ecf20Sopenharmony_ci	}
8888c2ecf20Sopenharmony_ci	if (evoice != NULL) {
8898c2ecf20Sopenharmony_ci		snd_trident_free_voice(trident, evoice);
8908c2ecf20Sopenharmony_ci		voice->extra = NULL;
8918c2ecf20Sopenharmony_ci	}
8928c2ecf20Sopenharmony_ci	return 0;
8938c2ecf20Sopenharmony_ci}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
8968c2ecf20Sopenharmony_ci   snd_trident_playback_prepare
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci   Description: Prepare playback device for playback.
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci   Parameters:  substream  - PCM substream class
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci   Returns:     Error status
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_cistatic int snd_trident_playback_prepare(struct snd_pcm_substream *substream)
9078c2ecf20Sopenharmony_ci{
9088c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
9098c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
9108c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice = runtime->private_data;
9118c2ecf20Sopenharmony_ci	struct snd_trident_voice *evoice = voice->extra;
9128c2ecf20Sopenharmony_ci	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[substream->number];
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	/* set delta (rate) value */
9178c2ecf20Sopenharmony_ci	voice->Delta = snd_trident_convert_rate(runtime->rate);
9188c2ecf20Sopenharmony_ci	voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	/* set Loop Begin Address */
9218c2ecf20Sopenharmony_ci	if (voice->memblk)
9228c2ecf20Sopenharmony_ci		voice->LBA = voice->memblk->offset;
9238c2ecf20Sopenharmony_ci	else
9248c2ecf20Sopenharmony_ci		voice->LBA = runtime->dma_addr;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	voice->CSO = 0;
9278c2ecf20Sopenharmony_ci	voice->ESO = runtime->buffer_size - 1;	/* in samples */
9288c2ecf20Sopenharmony_ci	voice->CTRL = snd_trident_control_mode(substream);
9298c2ecf20Sopenharmony_ci	voice->FMC = 3;
9308c2ecf20Sopenharmony_ci	voice->GVSel = 1;
9318c2ecf20Sopenharmony_ci	voice->EC = 0;
9328c2ecf20Sopenharmony_ci	voice->Alpha = 0;
9338c2ecf20Sopenharmony_ci	voice->FMS = 0;
9348c2ecf20Sopenharmony_ci	voice->Vol = mix->vol;
9358c2ecf20Sopenharmony_ci	voice->RVol = mix->rvol;
9368c2ecf20Sopenharmony_ci	voice->CVol = mix->cvol;
9378c2ecf20Sopenharmony_ci	voice->Pan = mix->pan;
9388c2ecf20Sopenharmony_ci	voice->Attribute = 0;
9398c2ecf20Sopenharmony_ci#if 0
9408c2ecf20Sopenharmony_ci	voice->Attribute = (1<<(30-16))|(2<<(26-16))|
9418c2ecf20Sopenharmony_ci			   (0<<(24-16))|(0x1f<<(19-16));
9428c2ecf20Sopenharmony_ci#else
9438c2ecf20Sopenharmony_ci	voice->Attribute = 0;
9448c2ecf20Sopenharmony_ci#endif
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	snd_trident_write_voice_regs(trident, voice);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	if (evoice != NULL) {
9498c2ecf20Sopenharmony_ci		evoice->Delta = voice->Delta;
9508c2ecf20Sopenharmony_ci		evoice->spurious_threshold = voice->spurious_threshold;
9518c2ecf20Sopenharmony_ci		evoice->LBA = voice->LBA;
9528c2ecf20Sopenharmony_ci		evoice->CSO = 0;
9538c2ecf20Sopenharmony_ci		evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
9548c2ecf20Sopenharmony_ci		evoice->CTRL = voice->CTRL;
9558c2ecf20Sopenharmony_ci		evoice->FMC = 3;
9568c2ecf20Sopenharmony_ci		evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
9578c2ecf20Sopenharmony_ci		evoice->EC = 0;
9588c2ecf20Sopenharmony_ci		evoice->Alpha = 0;
9598c2ecf20Sopenharmony_ci		evoice->FMS = 0;
9608c2ecf20Sopenharmony_ci		evoice->Vol = 0x3ff;			/* mute */
9618c2ecf20Sopenharmony_ci		evoice->RVol = evoice->CVol = 0x7f;	/* mute */
9628c2ecf20Sopenharmony_ci		evoice->Pan = 0x7f;			/* mute */
9638c2ecf20Sopenharmony_ci#if 0
9648c2ecf20Sopenharmony_ci		evoice->Attribute = (1<<(30-16))|(2<<(26-16))|
9658c2ecf20Sopenharmony_ci				    (0<<(24-16))|(0x1f<<(19-16));
9668c2ecf20Sopenharmony_ci#else
9678c2ecf20Sopenharmony_ci		evoice->Attribute = 0;
9688c2ecf20Sopenharmony_ci#endif
9698c2ecf20Sopenharmony_ci		snd_trident_write_voice_regs(trident, evoice);
9708c2ecf20Sopenharmony_ci		evoice->isync2 = 1;
9718c2ecf20Sopenharmony_ci		evoice->isync_mark = runtime->period_size;
9728c2ecf20Sopenharmony_ci		evoice->ESO = (runtime->period_size * 2) - 1;
9738c2ecf20Sopenharmony_ci	}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	return 0;
9788c2ecf20Sopenharmony_ci}
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
9818c2ecf20Sopenharmony_ci   snd_trident_capture_hw_params
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci   Description: Set the hardware parameters for the capture device.
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci   Parameters:  substream  - PCM substream class
9868c2ecf20Sopenharmony_ci		hw_params  - hardware parameters
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci   Returns:     Error status
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_cistatic int snd_trident_capture_hw_params(struct snd_pcm_substream *substream,
9938c2ecf20Sopenharmony_ci					 struct snd_pcm_hw_params *hw_params)
9948c2ecf20Sopenharmony_ci{
9958c2ecf20Sopenharmony_ci	return snd_trident_allocate_pcm_mem(substream, hw_params);
9968c2ecf20Sopenharmony_ci}
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
9998c2ecf20Sopenharmony_ci   snd_trident_capture_prepare
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci   Description: Prepare capture device for playback.
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci   Parameters:  substream  - PCM substream class
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci   Returns:     Error status
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_cistatic int snd_trident_capture_prepare(struct snd_pcm_substream *substream)
10108c2ecf20Sopenharmony_ci{
10118c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
10128c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
10138c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice = runtime->private_data;
10148c2ecf20Sopenharmony_ci	unsigned int val, ESO_bytes;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	// Initialize the channel and set channel Mode
10198c2ecf20Sopenharmony_ci	outb(0, TRID_REG(trident, LEGACY_DMAR15));
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	// Set DMA channel operation mode register
10228c2ecf20Sopenharmony_ci	outb(0x54, TRID_REG(trident, LEGACY_DMAR11));
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	// Set channel buffer Address, DMAR0 expects contiguous PCI memory area
10258c2ecf20Sopenharmony_ci	voice->LBA = runtime->dma_addr;
10268c2ecf20Sopenharmony_ci	outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0));
10278c2ecf20Sopenharmony_ci	if (voice->memblk)
10288c2ecf20Sopenharmony_ci		voice->LBA = voice->memblk->offset;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	// set ESO
10318c2ecf20Sopenharmony_ci	ESO_bytes = snd_pcm_lib_buffer_bytes(substream) - 1;
10328c2ecf20Sopenharmony_ci	outb((ESO_bytes & 0x00ff0000) >> 16, TRID_REG(trident, LEGACY_DMAR6));
10338c2ecf20Sopenharmony_ci	outw((ESO_bytes & 0x0000ffff), TRID_REG(trident, LEGACY_DMAR4));
10348c2ecf20Sopenharmony_ci	ESO_bytes++;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	// Set channel sample rate, 4.12 format
10378c2ecf20Sopenharmony_ci	val = (((unsigned int) 48000L << 12) + (runtime->rate/2)) / runtime->rate;
10388c2ecf20Sopenharmony_ci	outw(val, TRID_REG(trident, T4D_SBDELTA_DELTA_R));
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	// Set channel interrupt blk length
10418c2ecf20Sopenharmony_ci	if (snd_pcm_format_width(runtime->format) == 16) {
10428c2ecf20Sopenharmony_ci		val = (unsigned short) ((ESO_bytes >> 1) - 1);
10438c2ecf20Sopenharmony_ci	} else {
10448c2ecf20Sopenharmony_ci		val = (unsigned short) (ESO_bytes - 1);
10458c2ecf20Sopenharmony_ci	}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	outl((val << 16) | val, TRID_REG(trident, T4D_SBBL_SBCL));
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	// Right now, set format and start to run captureing,
10508c2ecf20Sopenharmony_ci	// continuous run loop enable.
10518c2ecf20Sopenharmony_ci	trident->bDMAStart = 0x19;	// 0001 1001b
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	if (snd_pcm_format_width(runtime->format) == 16)
10548c2ecf20Sopenharmony_ci		trident->bDMAStart |= 0x80;
10558c2ecf20Sopenharmony_ci	if (snd_pcm_format_signed(runtime->format))
10568c2ecf20Sopenharmony_ci		trident->bDMAStart |= 0x20;
10578c2ecf20Sopenharmony_ci	if (runtime->channels > 1)
10588c2ecf20Sopenharmony_ci		trident->bDMAStart |= 0x40;
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	// Prepare capture intr channel
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	voice->Delta = snd_trident_convert_rate(runtime->rate);
10638c2ecf20Sopenharmony_ci	voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
10648c2ecf20Sopenharmony_ci	voice->isync = 1;
10658c2ecf20Sopenharmony_ci	voice->isync_mark = runtime->period_size;
10668c2ecf20Sopenharmony_ci	voice->isync_max = runtime->buffer_size;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	// Set voice parameters
10698c2ecf20Sopenharmony_ci	voice->CSO = 0;
10708c2ecf20Sopenharmony_ci	voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1;
10718c2ecf20Sopenharmony_ci	voice->CTRL = snd_trident_control_mode(substream);
10728c2ecf20Sopenharmony_ci	voice->FMC = 3;
10738c2ecf20Sopenharmony_ci	voice->RVol = 0x7f;
10748c2ecf20Sopenharmony_ci	voice->CVol = 0x7f;
10758c2ecf20Sopenharmony_ci	voice->GVSel = 1;
10768c2ecf20Sopenharmony_ci	voice->Pan = 0x7f;		/* mute */
10778c2ecf20Sopenharmony_ci	voice->Vol = 0x3ff;		/* mute */
10788c2ecf20Sopenharmony_ci	voice->EC = 0;
10798c2ecf20Sopenharmony_ci	voice->Alpha = 0;
10808c2ecf20Sopenharmony_ci	voice->FMS = 0;
10818c2ecf20Sopenharmony_ci	voice->Attribute = 0;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	snd_trident_write_voice_regs(trident, voice);
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
10868c2ecf20Sopenharmony_ci	return 0;
10878c2ecf20Sopenharmony_ci}
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
10908c2ecf20Sopenharmony_ci   snd_trident_si7018_capture_hw_params
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci   Description: Set the hardware parameters for the capture device.
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci   Parameters:  substream  - PCM substream class
10958c2ecf20Sopenharmony_ci		hw_params  - hardware parameters
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci   Returns:     Error status
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_cistatic int snd_trident_si7018_capture_hw_params(struct snd_pcm_substream *substream,
11028c2ecf20Sopenharmony_ci						struct snd_pcm_hw_params *hw_params)
11038c2ecf20Sopenharmony_ci{
11048c2ecf20Sopenharmony_ci	return snd_trident_allocate_evoice(substream, hw_params);
11058c2ecf20Sopenharmony_ci}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
11088c2ecf20Sopenharmony_ci   snd_trident_si7018_capture_hw_free
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci   Description: Release the hardware resources for the capture device.
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci   Parameters:  substream  - PCM substream class
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci   Returns:     Error status
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_cistatic int snd_trident_si7018_capture_hw_free(struct snd_pcm_substream *substream)
11198c2ecf20Sopenharmony_ci{
11208c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
11218c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
11228c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice = runtime->private_data;
11238c2ecf20Sopenharmony_ci	struct snd_trident_voice *evoice = voice ? voice->extra : NULL;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	if (evoice != NULL) {
11268c2ecf20Sopenharmony_ci		snd_trident_free_voice(trident, evoice);
11278c2ecf20Sopenharmony_ci		voice->extra = NULL;
11288c2ecf20Sopenharmony_ci	}
11298c2ecf20Sopenharmony_ci	return 0;
11308c2ecf20Sopenharmony_ci}
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
11338c2ecf20Sopenharmony_ci   snd_trident_si7018_capture_prepare
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci   Description: Prepare capture device for playback.
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci   Parameters:  substream  - PCM substream class
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci   Returns:     Error status
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_cistatic int snd_trident_si7018_capture_prepare(struct snd_pcm_substream *substream)
11448c2ecf20Sopenharmony_ci{
11458c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
11468c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
11478c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice = runtime->private_data;
11488c2ecf20Sopenharmony_ci	struct snd_trident_voice *evoice = voice->extra;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	voice->LBA = runtime->dma_addr;
11538c2ecf20Sopenharmony_ci	voice->Delta = snd_trident_convert_adc_rate(runtime->rate);
11548c2ecf20Sopenharmony_ci	voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	// Set voice parameters
11578c2ecf20Sopenharmony_ci	voice->CSO = 0;
11588c2ecf20Sopenharmony_ci	voice->ESO = runtime->buffer_size - 1;		/* in samples */
11598c2ecf20Sopenharmony_ci	voice->CTRL = snd_trident_control_mode(substream);
11608c2ecf20Sopenharmony_ci	voice->FMC = 0;
11618c2ecf20Sopenharmony_ci	voice->RVol = 0;
11628c2ecf20Sopenharmony_ci	voice->CVol = 0;
11638c2ecf20Sopenharmony_ci	voice->GVSel = 1;
11648c2ecf20Sopenharmony_ci	voice->Pan = T4D_DEFAULT_PCM_PAN;
11658c2ecf20Sopenharmony_ci	voice->Vol = 0;
11668c2ecf20Sopenharmony_ci	voice->EC = 0;
11678c2ecf20Sopenharmony_ci	voice->Alpha = 0;
11688c2ecf20Sopenharmony_ci	voice->FMS = 0;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	voice->Attribute = (2 << (30-16)) |
11718c2ecf20Sopenharmony_ci			   (2 << (26-16)) |
11728c2ecf20Sopenharmony_ci			   (2 << (24-16)) |
11738c2ecf20Sopenharmony_ci			   (1 << (23-16));
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	snd_trident_write_voice_regs(trident, voice);
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	if (evoice != NULL) {
11788c2ecf20Sopenharmony_ci		evoice->Delta = snd_trident_convert_rate(runtime->rate);
11798c2ecf20Sopenharmony_ci		evoice->spurious_threshold = voice->spurious_threshold;
11808c2ecf20Sopenharmony_ci		evoice->LBA = voice->LBA;
11818c2ecf20Sopenharmony_ci		evoice->CSO = 0;
11828c2ecf20Sopenharmony_ci		evoice->ESO = (runtime->period_size * 2) + 20 - 1; /* in samples, 20 means correction */
11838c2ecf20Sopenharmony_ci		evoice->CTRL = voice->CTRL;
11848c2ecf20Sopenharmony_ci		evoice->FMC = 3;
11858c2ecf20Sopenharmony_ci		evoice->GVSel = 0;
11868c2ecf20Sopenharmony_ci		evoice->EC = 0;
11878c2ecf20Sopenharmony_ci		evoice->Alpha = 0;
11888c2ecf20Sopenharmony_ci		evoice->FMS = 0;
11898c2ecf20Sopenharmony_ci		evoice->Vol = 0x3ff;			/* mute */
11908c2ecf20Sopenharmony_ci		evoice->RVol = evoice->CVol = 0x7f;	/* mute */
11918c2ecf20Sopenharmony_ci		evoice->Pan = 0x7f;			/* mute */
11928c2ecf20Sopenharmony_ci		evoice->Attribute = 0;
11938c2ecf20Sopenharmony_ci		snd_trident_write_voice_regs(trident, evoice);
11948c2ecf20Sopenharmony_ci		evoice->isync2 = 1;
11958c2ecf20Sopenharmony_ci		evoice->isync_mark = runtime->period_size;
11968c2ecf20Sopenharmony_ci		evoice->ESO = (runtime->period_size * 2) - 1;
11978c2ecf20Sopenharmony_ci	}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
12008c2ecf20Sopenharmony_ci	return 0;
12018c2ecf20Sopenharmony_ci}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
12048c2ecf20Sopenharmony_ci   snd_trident_foldback_prepare
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci   Description: Prepare foldback capture device for playback.
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci   Parameters:  substream  - PCM substream class
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci   Returns:     Error status
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_cistatic int snd_trident_foldback_prepare(struct snd_pcm_substream *substream)
12158c2ecf20Sopenharmony_ci{
12168c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
12178c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
12188c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice = runtime->private_data;
12198c2ecf20Sopenharmony_ci	struct snd_trident_voice *evoice = voice->extra;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	/* Set channel buffer Address */
12248c2ecf20Sopenharmony_ci	if (voice->memblk)
12258c2ecf20Sopenharmony_ci		voice->LBA = voice->memblk->offset;
12268c2ecf20Sopenharmony_ci	else
12278c2ecf20Sopenharmony_ci		voice->LBA = runtime->dma_addr;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	/* set target ESO for channel */
12308c2ecf20Sopenharmony_ci	voice->ESO = runtime->buffer_size - 1;	/* in samples */
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	/* set sample rate */
12338c2ecf20Sopenharmony_ci	voice->Delta = 0x1000;
12348c2ecf20Sopenharmony_ci	voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size);
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	voice->CSO = 0;
12378c2ecf20Sopenharmony_ci	voice->CTRL = snd_trident_control_mode(substream);
12388c2ecf20Sopenharmony_ci	voice->FMC = 3;
12398c2ecf20Sopenharmony_ci	voice->RVol = 0x7f;
12408c2ecf20Sopenharmony_ci	voice->CVol = 0x7f;
12418c2ecf20Sopenharmony_ci	voice->GVSel = 1;
12428c2ecf20Sopenharmony_ci	voice->Pan = 0x7f;	/* mute */
12438c2ecf20Sopenharmony_ci	voice->Vol = 0x3ff;	/* mute */
12448c2ecf20Sopenharmony_ci	voice->EC = 0;
12458c2ecf20Sopenharmony_ci	voice->Alpha = 0;
12468c2ecf20Sopenharmony_ci	voice->FMS = 0;
12478c2ecf20Sopenharmony_ci	voice->Attribute = 0;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	/* set up capture channel */
12508c2ecf20Sopenharmony_ci	outb(((voice->number & 0x3f) | 0x80), TRID_REG(trident, T4D_RCI + voice->foldback_chan));
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	snd_trident_write_voice_regs(trident, voice);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	if (evoice != NULL) {
12558c2ecf20Sopenharmony_ci		evoice->Delta = voice->Delta;
12568c2ecf20Sopenharmony_ci		evoice->spurious_threshold = voice->spurious_threshold;
12578c2ecf20Sopenharmony_ci		evoice->LBA = voice->LBA;
12588c2ecf20Sopenharmony_ci		evoice->CSO = 0;
12598c2ecf20Sopenharmony_ci		evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
12608c2ecf20Sopenharmony_ci		evoice->CTRL = voice->CTRL;
12618c2ecf20Sopenharmony_ci		evoice->FMC = 3;
12628c2ecf20Sopenharmony_ci		evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
12638c2ecf20Sopenharmony_ci		evoice->EC = 0;
12648c2ecf20Sopenharmony_ci		evoice->Alpha = 0;
12658c2ecf20Sopenharmony_ci		evoice->FMS = 0;
12668c2ecf20Sopenharmony_ci		evoice->Vol = 0x3ff;			/* mute */
12678c2ecf20Sopenharmony_ci		evoice->RVol = evoice->CVol = 0x7f;	/* mute */
12688c2ecf20Sopenharmony_ci		evoice->Pan = 0x7f;			/* mute */
12698c2ecf20Sopenharmony_ci		evoice->Attribute = 0;
12708c2ecf20Sopenharmony_ci		snd_trident_write_voice_regs(trident, evoice);
12718c2ecf20Sopenharmony_ci		evoice->isync2 = 1;
12728c2ecf20Sopenharmony_ci		evoice->isync_mark = runtime->period_size;
12738c2ecf20Sopenharmony_ci		evoice->ESO = (runtime->period_size * 2) - 1;
12748c2ecf20Sopenharmony_ci	}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
12778c2ecf20Sopenharmony_ci	return 0;
12788c2ecf20Sopenharmony_ci}
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
12818c2ecf20Sopenharmony_ci   snd_trident_spdif_hw_params
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci   Description: Set the hardware parameters for the spdif device.
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci   Parameters:  substream  - PCM substream class
12868c2ecf20Sopenharmony_ci		hw_params  - hardware parameters
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci   Returns:     Error status
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_cistatic int snd_trident_spdif_hw_params(struct snd_pcm_substream *substream,
12938c2ecf20Sopenharmony_ci				       struct snd_pcm_hw_params *hw_params)
12948c2ecf20Sopenharmony_ci{
12958c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
12968c2ecf20Sopenharmony_ci	unsigned int old_bits = 0, change = 0;
12978c2ecf20Sopenharmony_ci	int err;
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	err = snd_trident_allocate_pcm_mem(substream, hw_params);
13008c2ecf20Sopenharmony_ci	if (err < 0)
13018c2ecf20Sopenharmony_ci		return err;
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
13048c2ecf20Sopenharmony_ci		err = snd_trident_allocate_evoice(substream, hw_params);
13058c2ecf20Sopenharmony_ci		if (err < 0)
13068c2ecf20Sopenharmony_ci			return err;
13078c2ecf20Sopenharmony_ci	}
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	/* prepare SPDIF channel */
13108c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
13118c2ecf20Sopenharmony_ci	old_bits = trident->spdif_pcm_bits;
13128c2ecf20Sopenharmony_ci	if (old_bits & IEC958_AES0_PROFESSIONAL)
13138c2ecf20Sopenharmony_ci		trident->spdif_pcm_bits &= ~IEC958_AES0_PRO_FS;
13148c2ecf20Sopenharmony_ci	else
13158c2ecf20Sopenharmony_ci		trident->spdif_pcm_bits &= ~(IEC958_AES3_CON_FS << 24);
13168c2ecf20Sopenharmony_ci	if (params_rate(hw_params) >= 48000) {
13178c2ecf20Sopenharmony_ci		trident->spdif_pcm_ctrl = 0x3c;	// 48000 Hz
13188c2ecf20Sopenharmony_ci		trident->spdif_pcm_bits |=
13198c2ecf20Sopenharmony_ci			trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
13208c2ecf20Sopenharmony_ci				IEC958_AES0_PRO_FS_48000 :
13218c2ecf20Sopenharmony_ci				(IEC958_AES3_CON_FS_48000 << 24);
13228c2ecf20Sopenharmony_ci	}
13238c2ecf20Sopenharmony_ci	else if (params_rate(hw_params) >= 44100) {
13248c2ecf20Sopenharmony_ci		trident->spdif_pcm_ctrl = 0x3e;	// 44100 Hz
13258c2ecf20Sopenharmony_ci		trident->spdif_pcm_bits |=
13268c2ecf20Sopenharmony_ci			trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
13278c2ecf20Sopenharmony_ci				IEC958_AES0_PRO_FS_44100 :
13288c2ecf20Sopenharmony_ci				(IEC958_AES3_CON_FS_44100 << 24);
13298c2ecf20Sopenharmony_ci	}
13308c2ecf20Sopenharmony_ci	else {
13318c2ecf20Sopenharmony_ci		trident->spdif_pcm_ctrl = 0x3d;	// 32000 Hz
13328c2ecf20Sopenharmony_ci		trident->spdif_pcm_bits |=
13338c2ecf20Sopenharmony_ci			trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
13348c2ecf20Sopenharmony_ci				IEC958_AES0_PRO_FS_32000 :
13358c2ecf20Sopenharmony_ci				(IEC958_AES3_CON_FS_32000 << 24);
13368c2ecf20Sopenharmony_ci	}
13378c2ecf20Sopenharmony_ci	change = old_bits != trident->spdif_pcm_bits;
13388c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	if (change)
13418c2ecf20Sopenharmony_ci		snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE, &trident->spdif_pcm_ctl->id);
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	return 0;
13448c2ecf20Sopenharmony_ci}
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
13478c2ecf20Sopenharmony_ci   snd_trident_spdif_prepare
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci   Description: Prepare SPDIF device for playback.
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci   Parameters:  substream  - PCM substream class
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci   Returns:     Error status
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_cistatic int snd_trident_spdif_prepare(struct snd_pcm_substream *substream)
13588c2ecf20Sopenharmony_ci{
13598c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
13608c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
13618c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice = runtime->private_data;
13628c2ecf20Sopenharmony_ci	struct snd_trident_voice *evoice = voice->extra;
13638c2ecf20Sopenharmony_ci	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[substream->number];
13648c2ecf20Sopenharmony_ci	unsigned int RESO, LBAO;
13658c2ecf20Sopenharmony_ci	unsigned int temp;
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci		/* set delta (rate) value */
13728c2ecf20Sopenharmony_ci		voice->Delta = snd_trident_convert_rate(runtime->rate);
13738c2ecf20Sopenharmony_ci		voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci		/* set Loop Back Address */
13768c2ecf20Sopenharmony_ci		LBAO = runtime->dma_addr;
13778c2ecf20Sopenharmony_ci		if (voice->memblk)
13788c2ecf20Sopenharmony_ci			voice->LBA = voice->memblk->offset;
13798c2ecf20Sopenharmony_ci		else
13808c2ecf20Sopenharmony_ci			voice->LBA = LBAO;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci		voice->isync = 1;
13838c2ecf20Sopenharmony_ci		voice->isync3 = 1;
13848c2ecf20Sopenharmony_ci		voice->isync_mark = runtime->period_size;
13858c2ecf20Sopenharmony_ci		voice->isync_max = runtime->buffer_size;
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci		/* set target ESO for channel */
13888c2ecf20Sopenharmony_ci		RESO = runtime->buffer_size - 1;
13898c2ecf20Sopenharmony_ci		voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1;
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci		/* set ctrl mode */
13928c2ecf20Sopenharmony_ci		voice->CTRL = snd_trident_control_mode(substream);
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci		voice->FMC = 3;
13958c2ecf20Sopenharmony_ci		voice->RVol = 0x7f;
13968c2ecf20Sopenharmony_ci		voice->CVol = 0x7f;
13978c2ecf20Sopenharmony_ci		voice->GVSel = 1;
13988c2ecf20Sopenharmony_ci		voice->Pan = 0x7f;
13998c2ecf20Sopenharmony_ci		voice->Vol = 0x3ff;
14008c2ecf20Sopenharmony_ci		voice->EC = 0;
14018c2ecf20Sopenharmony_ci		voice->CSO = 0;
14028c2ecf20Sopenharmony_ci		voice->Alpha = 0;
14038c2ecf20Sopenharmony_ci		voice->FMS = 0;
14048c2ecf20Sopenharmony_ci		voice->Attribute = 0;
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci		/* prepare surrogate IRQ channel */
14078c2ecf20Sopenharmony_ci		snd_trident_write_voice_regs(trident, voice);
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci		outw((RESO & 0xffff), TRID_REG(trident, NX_SPESO));
14108c2ecf20Sopenharmony_ci		outb((RESO >> 16), TRID_REG(trident, NX_SPESO + 2));
14118c2ecf20Sopenharmony_ci		outl((LBAO & 0xfffffffc), TRID_REG(trident, NX_SPLBA));
14128c2ecf20Sopenharmony_ci		outw((voice->CSO & 0xffff), TRID_REG(trident, NX_SPCTRL_SPCSO));
14138c2ecf20Sopenharmony_ci		outb((voice->CSO >> 16), TRID_REG(trident, NX_SPCTRL_SPCSO + 2));
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci		/* set SPDIF setting */
14168c2ecf20Sopenharmony_ci		outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
14178c2ecf20Sopenharmony_ci		outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	} else {	/* SiS */
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci		/* set delta (rate) value */
14228c2ecf20Sopenharmony_ci		voice->Delta = 0x800;
14238c2ecf20Sopenharmony_ci		voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size);
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci		/* set Loop Begin Address */
14268c2ecf20Sopenharmony_ci		if (voice->memblk)
14278c2ecf20Sopenharmony_ci			voice->LBA = voice->memblk->offset;
14288c2ecf20Sopenharmony_ci		else
14298c2ecf20Sopenharmony_ci			voice->LBA = runtime->dma_addr;
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci		voice->CSO = 0;
14328c2ecf20Sopenharmony_ci		voice->ESO = runtime->buffer_size - 1;	/* in samples */
14338c2ecf20Sopenharmony_ci		voice->CTRL = snd_trident_control_mode(substream);
14348c2ecf20Sopenharmony_ci		voice->FMC = 3;
14358c2ecf20Sopenharmony_ci		voice->GVSel = 1;
14368c2ecf20Sopenharmony_ci		voice->EC = 0;
14378c2ecf20Sopenharmony_ci		voice->Alpha = 0;
14388c2ecf20Sopenharmony_ci		voice->FMS = 0;
14398c2ecf20Sopenharmony_ci		voice->Vol = mix->vol;
14408c2ecf20Sopenharmony_ci		voice->RVol = mix->rvol;
14418c2ecf20Sopenharmony_ci		voice->CVol = mix->cvol;
14428c2ecf20Sopenharmony_ci		voice->Pan = mix->pan;
14438c2ecf20Sopenharmony_ci		voice->Attribute = (1<<(30-16))|(7<<(26-16))|
14448c2ecf20Sopenharmony_ci				   (0<<(24-16))|(0<<(19-16));
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci		snd_trident_write_voice_regs(trident, voice);
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci		if (evoice != NULL) {
14498c2ecf20Sopenharmony_ci			evoice->Delta = voice->Delta;
14508c2ecf20Sopenharmony_ci			evoice->spurious_threshold = voice->spurious_threshold;
14518c2ecf20Sopenharmony_ci			evoice->LBA = voice->LBA;
14528c2ecf20Sopenharmony_ci			evoice->CSO = 0;
14538c2ecf20Sopenharmony_ci			evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
14548c2ecf20Sopenharmony_ci			evoice->CTRL = voice->CTRL;
14558c2ecf20Sopenharmony_ci			evoice->FMC = 3;
14568c2ecf20Sopenharmony_ci			evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
14578c2ecf20Sopenharmony_ci			evoice->EC = 0;
14588c2ecf20Sopenharmony_ci			evoice->Alpha = 0;
14598c2ecf20Sopenharmony_ci			evoice->FMS = 0;
14608c2ecf20Sopenharmony_ci			evoice->Vol = 0x3ff;			/* mute */
14618c2ecf20Sopenharmony_ci			evoice->RVol = evoice->CVol = 0x7f;	/* mute */
14628c2ecf20Sopenharmony_ci			evoice->Pan = 0x7f;			/* mute */
14638c2ecf20Sopenharmony_ci			evoice->Attribute = 0;
14648c2ecf20Sopenharmony_ci			snd_trident_write_voice_regs(trident, evoice);
14658c2ecf20Sopenharmony_ci			evoice->isync2 = 1;
14668c2ecf20Sopenharmony_ci			evoice->isync_mark = runtime->period_size;
14678c2ecf20Sopenharmony_ci			evoice->ESO = (runtime->period_size * 2) - 1;
14688c2ecf20Sopenharmony_ci		}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci		outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
14718c2ecf20Sopenharmony_ci		temp = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
14728c2ecf20Sopenharmony_ci		temp &= ~(1<<19);
14738c2ecf20Sopenharmony_ci		outl(temp, TRID_REG(trident, T4D_LFO_GC_CIR));
14748c2ecf20Sopenharmony_ci		temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL));
14758c2ecf20Sopenharmony_ci		temp |= SPDIF_EN;
14768c2ecf20Sopenharmony_ci		outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
14778c2ecf20Sopenharmony_ci	}
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	return 0;
14828c2ecf20Sopenharmony_ci}
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
14858c2ecf20Sopenharmony_ci   snd_trident_trigger
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci   Description: Start/stop devices
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci   Parameters:  substream  - PCM substream class
14908c2ecf20Sopenharmony_ci   		cmd	- trigger command (STOP, GO)
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci   Returns:     Error status
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_cistatic int snd_trident_trigger(struct snd_pcm_substream *substream,
14978c2ecf20Sopenharmony_ci			       int cmd)
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci{
15008c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
15018c2ecf20Sopenharmony_ci	struct snd_pcm_substream *s;
15028c2ecf20Sopenharmony_ci	unsigned int what, whati, capture_flag, spdif_flag;
15038c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice, *evoice;
15048c2ecf20Sopenharmony_ci	unsigned int val, go;
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	switch (cmd) {
15078c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
15088c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
15098c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
15108c2ecf20Sopenharmony_ci		go = 1;
15118c2ecf20Sopenharmony_ci		break;
15128c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
15138c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
15148c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
15158c2ecf20Sopenharmony_ci		go = 0;
15168c2ecf20Sopenharmony_ci		break;
15178c2ecf20Sopenharmony_ci	default:
15188c2ecf20Sopenharmony_ci		return -EINVAL;
15198c2ecf20Sopenharmony_ci	}
15208c2ecf20Sopenharmony_ci	what = whati = capture_flag = spdif_flag = 0;
15218c2ecf20Sopenharmony_ci	spin_lock(&trident->reg_lock);
15228c2ecf20Sopenharmony_ci	val = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
15238c2ecf20Sopenharmony_ci	snd_pcm_group_for_each_entry(s, substream) {
15248c2ecf20Sopenharmony_ci		if ((struct snd_trident *) snd_pcm_substream_chip(s) == trident) {
15258c2ecf20Sopenharmony_ci			voice = s->runtime->private_data;
15268c2ecf20Sopenharmony_ci			evoice = voice->extra;
15278c2ecf20Sopenharmony_ci			what |= 1 << (voice->number & 0x1f);
15288c2ecf20Sopenharmony_ci			if (evoice == NULL) {
15298c2ecf20Sopenharmony_ci				whati |= 1 << (voice->number & 0x1f);
15308c2ecf20Sopenharmony_ci			} else {
15318c2ecf20Sopenharmony_ci				what |= 1 << (evoice->number & 0x1f);
15328c2ecf20Sopenharmony_ci				whati |= 1 << (evoice->number & 0x1f);
15338c2ecf20Sopenharmony_ci				if (go)
15348c2ecf20Sopenharmony_ci					evoice->stimer = val;
15358c2ecf20Sopenharmony_ci			}
15368c2ecf20Sopenharmony_ci			if (go) {
15378c2ecf20Sopenharmony_ci				voice->running = 1;
15388c2ecf20Sopenharmony_ci				voice->stimer = val;
15398c2ecf20Sopenharmony_ci			} else {
15408c2ecf20Sopenharmony_ci				voice->running = 0;
15418c2ecf20Sopenharmony_ci			}
15428c2ecf20Sopenharmony_ci			snd_pcm_trigger_done(s, substream);
15438c2ecf20Sopenharmony_ci			if (voice->capture)
15448c2ecf20Sopenharmony_ci				capture_flag = 1;
15458c2ecf20Sopenharmony_ci			if (voice->spdif)
15468c2ecf20Sopenharmony_ci				spdif_flag = 1;
15478c2ecf20Sopenharmony_ci		}
15488c2ecf20Sopenharmony_ci	}
15498c2ecf20Sopenharmony_ci	if (spdif_flag) {
15508c2ecf20Sopenharmony_ci		if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
15518c2ecf20Sopenharmony_ci			outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
15528c2ecf20Sopenharmony_ci			val = trident->spdif_pcm_ctrl;
15538c2ecf20Sopenharmony_ci			if (!go)
15548c2ecf20Sopenharmony_ci				val &= ~(0x28);
15558c2ecf20Sopenharmony_ci			outb(val, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
15568c2ecf20Sopenharmony_ci		} else {
15578c2ecf20Sopenharmony_ci			outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
15588c2ecf20Sopenharmony_ci			val = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) | SPDIF_EN;
15598c2ecf20Sopenharmony_ci			outl(val, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
15608c2ecf20Sopenharmony_ci		}
15618c2ecf20Sopenharmony_ci	}
15628c2ecf20Sopenharmony_ci	if (!go)
15638c2ecf20Sopenharmony_ci		outl(what, TRID_REG(trident, T4D_STOP_B));
15648c2ecf20Sopenharmony_ci	val = inl(TRID_REG(trident, T4D_AINTEN_B));
15658c2ecf20Sopenharmony_ci	if (go) {
15668c2ecf20Sopenharmony_ci		val |= whati;
15678c2ecf20Sopenharmony_ci	} else {
15688c2ecf20Sopenharmony_ci		val &= ~whati;
15698c2ecf20Sopenharmony_ci	}
15708c2ecf20Sopenharmony_ci	outl(val, TRID_REG(trident, T4D_AINTEN_B));
15718c2ecf20Sopenharmony_ci	if (go) {
15728c2ecf20Sopenharmony_ci		outl(what, TRID_REG(trident, T4D_START_B));
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci		if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
15758c2ecf20Sopenharmony_ci			outb(trident->bDMAStart, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
15768c2ecf20Sopenharmony_ci	} else {
15778c2ecf20Sopenharmony_ci		if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
15788c2ecf20Sopenharmony_ci			outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
15798c2ecf20Sopenharmony_ci	}
15808c2ecf20Sopenharmony_ci	spin_unlock(&trident->reg_lock);
15818c2ecf20Sopenharmony_ci	return 0;
15828c2ecf20Sopenharmony_ci}
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
15858c2ecf20Sopenharmony_ci   snd_trident_playback_pointer
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci   Description: This routine return the playback position
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci   Parameters:	substream  - PCM substream class
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci   Returns:     position of buffer
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_trident_playback_pointer(struct snd_pcm_substream *substream)
15968c2ecf20Sopenharmony_ci{
15978c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
15988c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
15998c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice = runtime->private_data;
16008c2ecf20Sopenharmony_ci	unsigned int cso;
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci	if (!voice->running)
16038c2ecf20Sopenharmony_ci		return 0;
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci	spin_lock(&trident->reg_lock);
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci	if (trident->device != TRIDENT_DEVICE_ID_NX) {
16108c2ecf20Sopenharmony_ci		cso = inw(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
16118c2ecf20Sopenharmony_ci	} else {		// ID_4DWAVE_NX
16128c2ecf20Sopenharmony_ci		cso = (unsigned int) inl(TRID_REG(trident, CH_NX_DELTA_CSO)) & 0x00ffffff;
16138c2ecf20Sopenharmony_ci	}
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	spin_unlock(&trident->reg_lock);
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	if (cso >= runtime->buffer_size)
16188c2ecf20Sopenharmony_ci		cso = 0;
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	return cso;
16218c2ecf20Sopenharmony_ci}
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
16248c2ecf20Sopenharmony_ci   snd_trident_capture_pointer
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci   Description: This routine return the capture position
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci   Parameters:   pcm1    - PCM device class
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci   Returns:     position of buffer
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_trident_capture_pointer(struct snd_pcm_substream *substream)
16358c2ecf20Sopenharmony_ci{
16368c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
16378c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
16388c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice = runtime->private_data;
16398c2ecf20Sopenharmony_ci	unsigned int result;
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	if (!voice->running)
16428c2ecf20Sopenharmony_ci		return 0;
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	result = inw(TRID_REG(trident, T4D_SBBL_SBCL));
16458c2ecf20Sopenharmony_ci	if (runtime->channels > 1)
16468c2ecf20Sopenharmony_ci		result >>= 1;
16478c2ecf20Sopenharmony_ci	if (result > 0)
16488c2ecf20Sopenharmony_ci		result = runtime->buffer_size - result;
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	return result;
16518c2ecf20Sopenharmony_ci}
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
16548c2ecf20Sopenharmony_ci   snd_trident_spdif_pointer
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ci   Description: This routine return the SPDIF playback position
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci   Parameters:	substream  - PCM substream class
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci   Returns:     position of buffer
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_trident_spdif_pointer(struct snd_pcm_substream *substream)
16658c2ecf20Sopenharmony_ci{
16668c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
16678c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
16688c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice = runtime->private_data;
16698c2ecf20Sopenharmony_ci	unsigned int result;
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci	if (!voice->running)
16728c2ecf20Sopenharmony_ci		return 0;
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	result = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff;
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci	return result;
16778c2ecf20Sopenharmony_ci}
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci/*
16808c2ecf20Sopenharmony_ci *  Playback support device description
16818c2ecf20Sopenharmony_ci */
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_trident_playback =
16848c2ecf20Sopenharmony_ci{
16858c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
16868c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
16878c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
16888c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
16898c2ecf20Sopenharmony_ci	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
16908c2ecf20Sopenharmony_ci				 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
16918c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
16928c2ecf20Sopenharmony_ci	.rate_min =		4000,
16938c2ecf20Sopenharmony_ci	.rate_max =		48000,
16948c2ecf20Sopenharmony_ci	.channels_min =		1,
16958c2ecf20Sopenharmony_ci	.channels_max =		2,
16968c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(256*1024),
16978c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
16988c2ecf20Sopenharmony_ci	.period_bytes_max =	(256*1024),
16998c2ecf20Sopenharmony_ci	.periods_min =		1,
17008c2ecf20Sopenharmony_ci	.periods_max =		1024,
17018c2ecf20Sopenharmony_ci	.fifo_size =		0,
17028c2ecf20Sopenharmony_ci};
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci/*
17058c2ecf20Sopenharmony_ci *  Capture support device description
17068c2ecf20Sopenharmony_ci */
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_trident_capture =
17098c2ecf20Sopenharmony_ci{
17108c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
17118c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
17128c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
17138c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
17148c2ecf20Sopenharmony_ci	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
17158c2ecf20Sopenharmony_ci				 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
17168c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
17178c2ecf20Sopenharmony_ci	.rate_min =		4000,
17188c2ecf20Sopenharmony_ci	.rate_max =		48000,
17198c2ecf20Sopenharmony_ci	.channels_min =		1,
17208c2ecf20Sopenharmony_ci	.channels_max =		2,
17218c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(128*1024),
17228c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
17238c2ecf20Sopenharmony_ci	.period_bytes_max =	(128*1024),
17248c2ecf20Sopenharmony_ci	.periods_min =		1,
17258c2ecf20Sopenharmony_ci	.periods_max =		1024,
17268c2ecf20Sopenharmony_ci	.fifo_size =		0,
17278c2ecf20Sopenharmony_ci};
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci/*
17308c2ecf20Sopenharmony_ci *  Foldback capture support device description
17318c2ecf20Sopenharmony_ci */
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_trident_foldback =
17348c2ecf20Sopenharmony_ci{
17358c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
17368c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
17378c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
17388c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
17398c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
17408c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_48000,
17418c2ecf20Sopenharmony_ci	.rate_min =		48000,
17428c2ecf20Sopenharmony_ci	.rate_max =		48000,
17438c2ecf20Sopenharmony_ci	.channels_min =		2,
17448c2ecf20Sopenharmony_ci	.channels_max =		2,
17458c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(128*1024),
17468c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
17478c2ecf20Sopenharmony_ci	.period_bytes_max =	(128*1024),
17488c2ecf20Sopenharmony_ci	.periods_min =		1,
17498c2ecf20Sopenharmony_ci	.periods_max =		1024,
17508c2ecf20Sopenharmony_ci	.fifo_size =		0,
17518c2ecf20Sopenharmony_ci};
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci/*
17548c2ecf20Sopenharmony_ci *  SPDIF playback support device description
17558c2ecf20Sopenharmony_ci */
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_trident_spdif =
17588c2ecf20Sopenharmony_ci{
17598c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
17608c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
17618c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
17628c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
17638c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
17648c2ecf20Sopenharmony_ci	.rates =		(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
17658c2ecf20Sopenharmony_ci				 SNDRV_PCM_RATE_48000),
17668c2ecf20Sopenharmony_ci	.rate_min =		32000,
17678c2ecf20Sopenharmony_ci	.rate_max =		48000,
17688c2ecf20Sopenharmony_ci	.channels_min =		2,
17698c2ecf20Sopenharmony_ci	.channels_max =		2,
17708c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(128*1024),
17718c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
17728c2ecf20Sopenharmony_ci	.period_bytes_max =	(128*1024),
17738c2ecf20Sopenharmony_ci	.periods_min =		1,
17748c2ecf20Sopenharmony_ci	.periods_max =		1024,
17758c2ecf20Sopenharmony_ci	.fifo_size =		0,
17768c2ecf20Sopenharmony_ci};
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_trident_spdif_7018 =
17798c2ecf20Sopenharmony_ci{
17808c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
17818c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
17828c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
17838c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
17848c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
17858c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_48000,
17868c2ecf20Sopenharmony_ci	.rate_min =		48000,
17878c2ecf20Sopenharmony_ci	.rate_max =		48000,
17888c2ecf20Sopenharmony_ci	.channels_min =		2,
17898c2ecf20Sopenharmony_ci	.channels_max =		2,
17908c2ecf20Sopenharmony_ci	.buffer_bytes_max =	(128*1024),
17918c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
17928c2ecf20Sopenharmony_ci	.period_bytes_max =	(128*1024),
17938c2ecf20Sopenharmony_ci	.periods_min =		1,
17948c2ecf20Sopenharmony_ci	.periods_max =		1024,
17958c2ecf20Sopenharmony_ci	.fifo_size =		0,
17968c2ecf20Sopenharmony_ci};
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_cistatic void snd_trident_pcm_free_substream(struct snd_pcm_runtime *runtime)
17998c2ecf20Sopenharmony_ci{
18008c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice = runtime->private_data;
18018c2ecf20Sopenharmony_ci	struct snd_trident *trident;
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_ci	if (voice) {
18048c2ecf20Sopenharmony_ci		trident = voice->trident;
18058c2ecf20Sopenharmony_ci		snd_trident_free_voice(trident, voice);
18068c2ecf20Sopenharmony_ci	}
18078c2ecf20Sopenharmony_ci}
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_cistatic int snd_trident_playback_open(struct snd_pcm_substream *substream)
18108c2ecf20Sopenharmony_ci{
18118c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
18128c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
18138c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice;
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci	voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
18168c2ecf20Sopenharmony_ci	if (voice == NULL)
18178c2ecf20Sopenharmony_ci		return -EAGAIN;
18188c2ecf20Sopenharmony_ci	snd_trident_pcm_mixer_build(trident, voice, substream);
18198c2ecf20Sopenharmony_ci	voice->substream = substream;
18208c2ecf20Sopenharmony_ci	runtime->private_data = voice;
18218c2ecf20Sopenharmony_ci	runtime->private_free = snd_trident_pcm_free_substream;
18228c2ecf20Sopenharmony_ci	runtime->hw = snd_trident_playback;
18238c2ecf20Sopenharmony_ci	snd_pcm_set_sync(substream);
18248c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
18258c2ecf20Sopenharmony_ci	return 0;
18268c2ecf20Sopenharmony_ci}
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
18298c2ecf20Sopenharmony_ci   snd_trident_playback_close
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci   Description: This routine will close the 4DWave playback device. For now
18328c2ecf20Sopenharmony_ci                we will simply free the dma transfer buffer.
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_ci   Parameters:	substream  - PCM substream class
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
18378c2ecf20Sopenharmony_cistatic int snd_trident_playback_close(struct snd_pcm_substream *substream)
18388c2ecf20Sopenharmony_ci{
18398c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
18408c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
18418c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice = runtime->private_data;
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci	snd_trident_pcm_mixer_free(trident, voice, substream);
18448c2ecf20Sopenharmony_ci	return 0;
18458c2ecf20Sopenharmony_ci}
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
18488c2ecf20Sopenharmony_ci   snd_trident_spdif_open
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci   Description: This routine will open the 4DWave SPDIF device.
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci   Parameters:	substream  - PCM substream class
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci   Returns:     status  - success or failure flag
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_cistatic int snd_trident_spdif_open(struct snd_pcm_substream *substream)
18598c2ecf20Sopenharmony_ci{
18608c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
18618c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice;
18628c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
18658c2ecf20Sopenharmony_ci	if (voice == NULL)
18668c2ecf20Sopenharmony_ci		return -EAGAIN;
18678c2ecf20Sopenharmony_ci	voice->spdif = 1;
18688c2ecf20Sopenharmony_ci	voice->substream = substream;
18698c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
18708c2ecf20Sopenharmony_ci	trident->spdif_pcm_bits = trident->spdif_bits;
18718c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci	runtime->private_data = voice;
18748c2ecf20Sopenharmony_ci	runtime->private_free = snd_trident_pcm_free_substream;
18758c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
18768c2ecf20Sopenharmony_ci		runtime->hw = snd_trident_spdif;
18778c2ecf20Sopenharmony_ci	} else {
18788c2ecf20Sopenharmony_ci		runtime->hw = snd_trident_spdif_7018;
18798c2ecf20Sopenharmony_ci	}
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	trident->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
18828c2ecf20Sopenharmony_ci	snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
18838c2ecf20Sopenharmony_ci		       SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
18868c2ecf20Sopenharmony_ci	return 0;
18878c2ecf20Sopenharmony_ci}
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
18918c2ecf20Sopenharmony_ci   snd_trident_spdif_close
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci   Description: This routine will close the 4DWave SPDIF device.
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci   Parameters:	substream  - PCM substream class
18968c2ecf20Sopenharmony_ci
18978c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_cistatic int snd_trident_spdif_close(struct snd_pcm_substream *substream)
19008c2ecf20Sopenharmony_ci{
19018c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
19028c2ecf20Sopenharmony_ci	unsigned int temp;
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
19058c2ecf20Sopenharmony_ci	// restore default SPDIF setting
19068c2ecf20Sopenharmony_ci	if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
19078c2ecf20Sopenharmony_ci		outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
19088c2ecf20Sopenharmony_ci		outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
19098c2ecf20Sopenharmony_ci	} else {
19108c2ecf20Sopenharmony_ci		outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
19118c2ecf20Sopenharmony_ci		temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL));
19128c2ecf20Sopenharmony_ci		if (trident->spdif_ctrl) {
19138c2ecf20Sopenharmony_ci			temp |= SPDIF_EN;
19148c2ecf20Sopenharmony_ci		} else {
19158c2ecf20Sopenharmony_ci			temp &= ~SPDIF_EN;
19168c2ecf20Sopenharmony_ci		}
19178c2ecf20Sopenharmony_ci		outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
19188c2ecf20Sopenharmony_ci	}
19198c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
19208c2ecf20Sopenharmony_ci	trident->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
19218c2ecf20Sopenharmony_ci	snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
19228c2ecf20Sopenharmony_ci		       SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);
19238c2ecf20Sopenharmony_ci	return 0;
19248c2ecf20Sopenharmony_ci}
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
19278c2ecf20Sopenharmony_ci   snd_trident_capture_open
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ci   Description: This routine will open the 4DWave capture device.
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_ci   Parameters:	substream  - PCM substream class
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci   Returns:     status  - success or failure flag
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_cistatic int snd_trident_capture_open(struct snd_pcm_substream *substream)
19388c2ecf20Sopenharmony_ci{
19398c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
19408c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice;
19418c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci	voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
19448c2ecf20Sopenharmony_ci	if (voice == NULL)
19458c2ecf20Sopenharmony_ci		return -EAGAIN;
19468c2ecf20Sopenharmony_ci	voice->capture = 1;
19478c2ecf20Sopenharmony_ci	voice->substream = substream;
19488c2ecf20Sopenharmony_ci	runtime->private_data = voice;
19498c2ecf20Sopenharmony_ci	runtime->private_free = snd_trident_pcm_free_substream;
19508c2ecf20Sopenharmony_ci	runtime->hw = snd_trident_capture;
19518c2ecf20Sopenharmony_ci	snd_pcm_set_sync(substream);
19528c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
19538c2ecf20Sopenharmony_ci	return 0;
19548c2ecf20Sopenharmony_ci}
19558c2ecf20Sopenharmony_ci
19568c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
19578c2ecf20Sopenharmony_ci   snd_trident_capture_close
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci   Description: This routine will close the 4DWave capture device. For now
19608c2ecf20Sopenharmony_ci                we will simply free the dma transfer buffer.
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_ci   Parameters:	substream  - PCM substream class
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
19658c2ecf20Sopenharmony_cistatic int snd_trident_capture_close(struct snd_pcm_substream *substream)
19668c2ecf20Sopenharmony_ci{
19678c2ecf20Sopenharmony_ci	return 0;
19688c2ecf20Sopenharmony_ci}
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
19718c2ecf20Sopenharmony_ci   snd_trident_foldback_open
19728c2ecf20Sopenharmony_ci
19738c2ecf20Sopenharmony_ci   Description: This routine will open the 4DWave foldback capture device.
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci   Parameters:	substream  - PCM substream class
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci   Returns:     status  - success or failure flag
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_cistatic int snd_trident_foldback_open(struct snd_pcm_substream *substream)
19828c2ecf20Sopenharmony_ci{
19838c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
19848c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice;
19858c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
19888c2ecf20Sopenharmony_ci	if (voice == NULL)
19898c2ecf20Sopenharmony_ci		return -EAGAIN;
19908c2ecf20Sopenharmony_ci	voice->foldback_chan = substream->number;
19918c2ecf20Sopenharmony_ci	voice->substream = substream;
19928c2ecf20Sopenharmony_ci	runtime->private_data = voice;
19938c2ecf20Sopenharmony_ci	runtime->private_free = snd_trident_pcm_free_substream;
19948c2ecf20Sopenharmony_ci	runtime->hw = snd_trident_foldback;
19958c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
19968c2ecf20Sopenharmony_ci	return 0;
19978c2ecf20Sopenharmony_ci}
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
20008c2ecf20Sopenharmony_ci   snd_trident_foldback_close
20018c2ecf20Sopenharmony_ci
20028c2ecf20Sopenharmony_ci   Description: This routine will close the 4DWave foldback capture device.
20038c2ecf20Sopenharmony_ci		For now we will simply free the dma transfer buffer.
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_ci   Parameters:	substream  - PCM substream class
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
20088c2ecf20Sopenharmony_cistatic int snd_trident_foldback_close(struct snd_pcm_substream *substream)
20098c2ecf20Sopenharmony_ci{
20108c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_pcm_substream_chip(substream);
20118c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice;
20128c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
20138c2ecf20Sopenharmony_ci	voice = runtime->private_data;
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci	/* stop capture channel */
20168c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
20178c2ecf20Sopenharmony_ci	outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan));
20188c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
20198c2ecf20Sopenharmony_ci	return 0;
20208c2ecf20Sopenharmony_ci}
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
20238c2ecf20Sopenharmony_ci   PCM operations
20248c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_playback_ops = {
20278c2ecf20Sopenharmony_ci	.open =		snd_trident_playback_open,
20288c2ecf20Sopenharmony_ci	.close =	snd_trident_playback_close,
20298c2ecf20Sopenharmony_ci	.hw_params =	snd_trident_hw_params,
20308c2ecf20Sopenharmony_ci	.hw_free =	snd_trident_hw_free,
20318c2ecf20Sopenharmony_ci	.prepare =	snd_trident_playback_prepare,
20328c2ecf20Sopenharmony_ci	.trigger =	snd_trident_trigger,
20338c2ecf20Sopenharmony_ci	.pointer =	snd_trident_playback_pointer,
20348c2ecf20Sopenharmony_ci};
20358c2ecf20Sopenharmony_ci
20368c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_nx_playback_ops = {
20378c2ecf20Sopenharmony_ci	.open =		snd_trident_playback_open,
20388c2ecf20Sopenharmony_ci	.close =	snd_trident_playback_close,
20398c2ecf20Sopenharmony_ci	.hw_params =	snd_trident_hw_params,
20408c2ecf20Sopenharmony_ci	.hw_free =	snd_trident_hw_free,
20418c2ecf20Sopenharmony_ci	.prepare =	snd_trident_playback_prepare,
20428c2ecf20Sopenharmony_ci	.trigger =	snd_trident_trigger,
20438c2ecf20Sopenharmony_ci	.pointer =	snd_trident_playback_pointer,
20448c2ecf20Sopenharmony_ci};
20458c2ecf20Sopenharmony_ci
20468c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_capture_ops = {
20478c2ecf20Sopenharmony_ci	.open =		snd_trident_capture_open,
20488c2ecf20Sopenharmony_ci	.close =	snd_trident_capture_close,
20498c2ecf20Sopenharmony_ci	.hw_params =	snd_trident_capture_hw_params,
20508c2ecf20Sopenharmony_ci	.hw_free =	snd_trident_hw_free,
20518c2ecf20Sopenharmony_ci	.prepare =	snd_trident_capture_prepare,
20528c2ecf20Sopenharmony_ci	.trigger =	snd_trident_trigger,
20538c2ecf20Sopenharmony_ci	.pointer =	snd_trident_capture_pointer,
20548c2ecf20Sopenharmony_ci};
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_si7018_capture_ops = {
20578c2ecf20Sopenharmony_ci	.open =		snd_trident_capture_open,
20588c2ecf20Sopenharmony_ci	.close =	snd_trident_capture_close,
20598c2ecf20Sopenharmony_ci	.hw_params =	snd_trident_si7018_capture_hw_params,
20608c2ecf20Sopenharmony_ci	.hw_free =	snd_trident_si7018_capture_hw_free,
20618c2ecf20Sopenharmony_ci	.prepare =	snd_trident_si7018_capture_prepare,
20628c2ecf20Sopenharmony_ci	.trigger =	snd_trident_trigger,
20638c2ecf20Sopenharmony_ci	.pointer =	snd_trident_playback_pointer,
20648c2ecf20Sopenharmony_ci};
20658c2ecf20Sopenharmony_ci
20668c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_foldback_ops = {
20678c2ecf20Sopenharmony_ci	.open =		snd_trident_foldback_open,
20688c2ecf20Sopenharmony_ci	.close =	snd_trident_foldback_close,
20698c2ecf20Sopenharmony_ci	.hw_params =	snd_trident_hw_params,
20708c2ecf20Sopenharmony_ci	.hw_free =	snd_trident_hw_free,
20718c2ecf20Sopenharmony_ci	.prepare =	snd_trident_foldback_prepare,
20728c2ecf20Sopenharmony_ci	.trigger =	snd_trident_trigger,
20738c2ecf20Sopenharmony_ci	.pointer =	snd_trident_playback_pointer,
20748c2ecf20Sopenharmony_ci};
20758c2ecf20Sopenharmony_ci
20768c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_nx_foldback_ops = {
20778c2ecf20Sopenharmony_ci	.open =		snd_trident_foldback_open,
20788c2ecf20Sopenharmony_ci	.close =	snd_trident_foldback_close,
20798c2ecf20Sopenharmony_ci	.hw_params =	snd_trident_hw_params,
20808c2ecf20Sopenharmony_ci	.hw_free =	snd_trident_hw_free,
20818c2ecf20Sopenharmony_ci	.prepare =	snd_trident_foldback_prepare,
20828c2ecf20Sopenharmony_ci	.trigger =	snd_trident_trigger,
20838c2ecf20Sopenharmony_ci	.pointer =	snd_trident_playback_pointer,
20848c2ecf20Sopenharmony_ci};
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_spdif_ops = {
20878c2ecf20Sopenharmony_ci	.open =		snd_trident_spdif_open,
20888c2ecf20Sopenharmony_ci	.close =	snd_trident_spdif_close,
20898c2ecf20Sopenharmony_ci	.hw_params =	snd_trident_spdif_hw_params,
20908c2ecf20Sopenharmony_ci	.hw_free =	snd_trident_hw_free,
20918c2ecf20Sopenharmony_ci	.prepare =	snd_trident_spdif_prepare,
20928c2ecf20Sopenharmony_ci	.trigger =	snd_trident_trigger,
20938c2ecf20Sopenharmony_ci	.pointer =	snd_trident_spdif_pointer,
20948c2ecf20Sopenharmony_ci};
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_trident_spdif_7018_ops = {
20978c2ecf20Sopenharmony_ci	.open =		snd_trident_spdif_open,
20988c2ecf20Sopenharmony_ci	.close =	snd_trident_spdif_close,
20998c2ecf20Sopenharmony_ci	.hw_params =	snd_trident_spdif_hw_params,
21008c2ecf20Sopenharmony_ci	.hw_free =	snd_trident_hw_free,
21018c2ecf20Sopenharmony_ci	.prepare =	snd_trident_spdif_prepare,
21028c2ecf20Sopenharmony_ci	.trigger =	snd_trident_trigger,
21038c2ecf20Sopenharmony_ci	.pointer =	snd_trident_playback_pointer,
21048c2ecf20Sopenharmony_ci};
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
21078c2ecf20Sopenharmony_ci   snd_trident_pcm
21088c2ecf20Sopenharmony_ci
21098c2ecf20Sopenharmony_ci   Description: This routine registers the 4DWave device for PCM support.
21108c2ecf20Sopenharmony_ci
21118c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci   Returns:     None
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ciint snd_trident_pcm(struct snd_trident *trident, int device)
21188c2ecf20Sopenharmony_ci{
21198c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
21208c2ecf20Sopenharmony_ci	int err;
21218c2ecf20Sopenharmony_ci
21228c2ecf20Sopenharmony_ci	if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, trident->ChanPCM, 1, &pcm)) < 0)
21238c2ecf20Sopenharmony_ci		return err;
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci	pcm->private_data = trident;
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	if (trident->tlb.entries) {
21288c2ecf20Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_nx_playback_ops);
21298c2ecf20Sopenharmony_ci	} else {
21308c2ecf20Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops);
21318c2ecf20Sopenharmony_ci	}
21328c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
21338c2ecf20Sopenharmony_ci			trident->device != TRIDENT_DEVICE_ID_SI7018 ?
21348c2ecf20Sopenharmony_ci			&snd_trident_capture_ops :
21358c2ecf20Sopenharmony_ci			&snd_trident_si7018_capture_ops);
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
21388c2ecf20Sopenharmony_ci	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
21398c2ecf20Sopenharmony_ci	strcpy(pcm->name, "Trident 4DWave");
21408c2ecf20Sopenharmony_ci	trident->pcm = pcm;
21418c2ecf20Sopenharmony_ci
21428c2ecf20Sopenharmony_ci	if (trident->tlb.entries) {
21438c2ecf20Sopenharmony_ci		struct snd_pcm_substream *substream;
21448c2ecf20Sopenharmony_ci		for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
21458c2ecf20Sopenharmony_ci			snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV_SG,
21468c2ecf20Sopenharmony_ci						   &trident->pci->dev,
21478c2ecf20Sopenharmony_ci						   64*1024, 128*1024);
21488c2ecf20Sopenharmony_ci		snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
21498c2ecf20Sopenharmony_ci					   SNDRV_DMA_TYPE_DEV,
21508c2ecf20Sopenharmony_ci					   &trident->pci->dev,
21518c2ecf20Sopenharmony_ci					   64*1024, 128*1024);
21528c2ecf20Sopenharmony_ci	} else {
21538c2ecf20Sopenharmony_ci		snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
21548c2ecf20Sopenharmony_ci					       &trident->pci->dev,
21558c2ecf20Sopenharmony_ci					       64*1024, 128*1024);
21568c2ecf20Sopenharmony_ci	}
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_ci	return 0;
21598c2ecf20Sopenharmony_ci}
21608c2ecf20Sopenharmony_ci
21618c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
21628c2ecf20Sopenharmony_ci   snd_trident_foldback_pcm
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci   Description: This routine registers the 4DWave device for foldback PCM support.
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_ci   Returns:     None
21698c2ecf20Sopenharmony_ci
21708c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
21718c2ecf20Sopenharmony_ci
21728c2ecf20Sopenharmony_ciint snd_trident_foldback_pcm(struct snd_trident *trident, int device)
21738c2ecf20Sopenharmony_ci{
21748c2ecf20Sopenharmony_ci	struct snd_pcm *foldback;
21758c2ecf20Sopenharmony_ci	int err;
21768c2ecf20Sopenharmony_ci	int num_chan = 3;
21778c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_NX)
21808c2ecf20Sopenharmony_ci		num_chan = 4;
21818c2ecf20Sopenharmony_ci	if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, 0, num_chan, &foldback)) < 0)
21828c2ecf20Sopenharmony_ci		return err;
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_ci	foldback->private_data = trident;
21858c2ecf20Sopenharmony_ci	if (trident->tlb.entries)
21868c2ecf20Sopenharmony_ci		snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_nx_foldback_ops);
21878c2ecf20Sopenharmony_ci	else
21888c2ecf20Sopenharmony_ci		snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops);
21898c2ecf20Sopenharmony_ci	foldback->info_flags = 0;
21908c2ecf20Sopenharmony_ci	strcpy(foldback->name, "Trident 4DWave");
21918c2ecf20Sopenharmony_ci	substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
21928c2ecf20Sopenharmony_ci	strcpy(substream->name, "Front Mixer");
21938c2ecf20Sopenharmony_ci	substream = substream->next;
21948c2ecf20Sopenharmony_ci	strcpy(substream->name, "Reverb Mixer");
21958c2ecf20Sopenharmony_ci	substream = substream->next;
21968c2ecf20Sopenharmony_ci	strcpy(substream->name, "Chorus Mixer");
21978c2ecf20Sopenharmony_ci	if (num_chan == 4) {
21988c2ecf20Sopenharmony_ci		substream = substream->next;
21998c2ecf20Sopenharmony_ci		strcpy(substream->name, "Second AC'97 ADC");
22008c2ecf20Sopenharmony_ci	}
22018c2ecf20Sopenharmony_ci	trident->foldback = foldback;
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_ci	if (trident->tlb.entries)
22048c2ecf20Sopenharmony_ci		snd_pcm_set_managed_buffer_all(foldback, SNDRV_DMA_TYPE_DEV_SG,
22058c2ecf20Sopenharmony_ci					       &trident->pci->dev,
22068c2ecf20Sopenharmony_ci					       0, 128*1024);
22078c2ecf20Sopenharmony_ci	else
22088c2ecf20Sopenharmony_ci		snd_pcm_set_managed_buffer_all(foldback, SNDRV_DMA_TYPE_DEV,
22098c2ecf20Sopenharmony_ci					       &trident->pci->dev,
22108c2ecf20Sopenharmony_ci					       64*1024, 128*1024);
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	return 0;
22138c2ecf20Sopenharmony_ci}
22148c2ecf20Sopenharmony_ci
22158c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
22168c2ecf20Sopenharmony_ci   snd_trident_spdif
22178c2ecf20Sopenharmony_ci
22188c2ecf20Sopenharmony_ci   Description: This routine registers the 4DWave-NX device for SPDIF support.
22198c2ecf20Sopenharmony_ci
22208c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave-NX.
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_ci   Returns:     None
22238c2ecf20Sopenharmony_ci
22248c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ciint snd_trident_spdif_pcm(struct snd_trident *trident, int device)
22278c2ecf20Sopenharmony_ci{
22288c2ecf20Sopenharmony_ci	struct snd_pcm *spdif;
22298c2ecf20Sopenharmony_ci	int err;
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci	if ((err = snd_pcm_new(trident->card, "trident_dx_nx IEC958", device, 1, 0, &spdif)) < 0)
22328c2ecf20Sopenharmony_ci		return err;
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci	spdif->private_data = trident;
22358c2ecf20Sopenharmony_ci	if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
22368c2ecf20Sopenharmony_ci		snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops);
22378c2ecf20Sopenharmony_ci	} else {
22388c2ecf20Sopenharmony_ci		snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_7018_ops);
22398c2ecf20Sopenharmony_ci	}
22408c2ecf20Sopenharmony_ci	spdif->info_flags = 0;
22418c2ecf20Sopenharmony_ci	strcpy(spdif->name, "Trident 4DWave IEC958");
22428c2ecf20Sopenharmony_ci	trident->spdif = spdif;
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(spdif, SNDRV_DMA_TYPE_DEV,
22458c2ecf20Sopenharmony_ci				       &trident->pci->dev, 64*1024, 128*1024);
22468c2ecf20Sopenharmony_ci
22478c2ecf20Sopenharmony_ci	return 0;
22488c2ecf20Sopenharmony_ci}
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci/*
22518c2ecf20Sopenharmony_ci *  Mixer part
22528c2ecf20Sopenharmony_ci */
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
22568c2ecf20Sopenharmony_ci    snd_trident_spdif_control
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci    Description: enable/disable S/PDIF out from ac97 mixer
22598c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_ci#define snd_trident_spdif_control_info	snd_ctl_boolean_mono_info
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_cistatic int snd_trident_spdif_control_get(struct snd_kcontrol *kcontrol,
22648c2ecf20Sopenharmony_ci					 struct snd_ctl_elem_value *ucontrol)
22658c2ecf20Sopenharmony_ci{
22668c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
22678c2ecf20Sopenharmony_ci	unsigned char val;
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
22708c2ecf20Sopenharmony_ci	val = trident->spdif_ctrl;
22718c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = val == kcontrol->private_value;
22728c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
22738c2ecf20Sopenharmony_ci	return 0;
22748c2ecf20Sopenharmony_ci}
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_cistatic int snd_trident_spdif_control_put(struct snd_kcontrol *kcontrol,
22778c2ecf20Sopenharmony_ci					 struct snd_ctl_elem_value *ucontrol)
22788c2ecf20Sopenharmony_ci{
22798c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
22808c2ecf20Sopenharmony_ci	unsigned char val;
22818c2ecf20Sopenharmony_ci	int change;
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_ci	val = ucontrol->value.integer.value[0] ? (unsigned char) kcontrol->private_value : 0x00;
22848c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
22858c2ecf20Sopenharmony_ci	/* S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled */
22868c2ecf20Sopenharmony_ci	change = trident->spdif_ctrl != val;
22878c2ecf20Sopenharmony_ci	trident->spdif_ctrl = val;
22888c2ecf20Sopenharmony_ci	if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
22898c2ecf20Sopenharmony_ci		if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) {
22908c2ecf20Sopenharmony_ci			outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
22918c2ecf20Sopenharmony_ci			outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
22928c2ecf20Sopenharmony_ci		}
22938c2ecf20Sopenharmony_ci	} else {
22948c2ecf20Sopenharmony_ci		if (trident->spdif == NULL) {
22958c2ecf20Sopenharmony_ci			unsigned int temp;
22968c2ecf20Sopenharmony_ci			outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
22978c2ecf20Sopenharmony_ci			temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & ~SPDIF_EN;
22988c2ecf20Sopenharmony_ci			if (val)
22998c2ecf20Sopenharmony_ci				temp |= SPDIF_EN;
23008c2ecf20Sopenharmony_ci			outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
23018c2ecf20Sopenharmony_ci		}
23028c2ecf20Sopenharmony_ci	}
23038c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
23048c2ecf20Sopenharmony_ci	return change;
23058c2ecf20Sopenharmony_ci}
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_spdif_control =
23088c2ecf20Sopenharmony_ci{
23098c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
23108c2ecf20Sopenharmony_ci	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
23118c2ecf20Sopenharmony_ci	.info =		snd_trident_spdif_control_info,
23128c2ecf20Sopenharmony_ci	.get =		snd_trident_spdif_control_get,
23138c2ecf20Sopenharmony_ci	.put =		snd_trident_spdif_control_put,
23148c2ecf20Sopenharmony_ci	.private_value = 0x28,
23158c2ecf20Sopenharmony_ci};
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
23188c2ecf20Sopenharmony_ci    snd_trident_spdif_default
23198c2ecf20Sopenharmony_ci
23208c2ecf20Sopenharmony_ci    Description: put/get the S/PDIF default settings
23218c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_cistatic int snd_trident_spdif_default_info(struct snd_kcontrol *kcontrol,
23248c2ecf20Sopenharmony_ci					  struct snd_ctl_elem_info *uinfo)
23258c2ecf20Sopenharmony_ci{
23268c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
23278c2ecf20Sopenharmony_ci	uinfo->count = 1;
23288c2ecf20Sopenharmony_ci	return 0;
23298c2ecf20Sopenharmony_ci}
23308c2ecf20Sopenharmony_ci
23318c2ecf20Sopenharmony_cistatic int snd_trident_spdif_default_get(struct snd_kcontrol *kcontrol,
23328c2ecf20Sopenharmony_ci					 struct snd_ctl_elem_value *ucontrol)
23338c2ecf20Sopenharmony_ci{
23348c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
23358c2ecf20Sopenharmony_ci
23368c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
23378c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[0] = (trident->spdif_bits >> 0) & 0xff;
23388c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[1] = (trident->spdif_bits >> 8) & 0xff;
23398c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[2] = (trident->spdif_bits >> 16) & 0xff;
23408c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[3] = (trident->spdif_bits >> 24) & 0xff;
23418c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
23428c2ecf20Sopenharmony_ci	return 0;
23438c2ecf20Sopenharmony_ci}
23448c2ecf20Sopenharmony_ci
23458c2ecf20Sopenharmony_cistatic int snd_trident_spdif_default_put(struct snd_kcontrol *kcontrol,
23468c2ecf20Sopenharmony_ci					 struct snd_ctl_elem_value *ucontrol)
23478c2ecf20Sopenharmony_ci{
23488c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
23498c2ecf20Sopenharmony_ci	unsigned int val;
23508c2ecf20Sopenharmony_ci	int change;
23518c2ecf20Sopenharmony_ci
23528c2ecf20Sopenharmony_ci	val = (ucontrol->value.iec958.status[0] << 0) |
23538c2ecf20Sopenharmony_ci	      (ucontrol->value.iec958.status[1] << 8) |
23548c2ecf20Sopenharmony_ci	      (ucontrol->value.iec958.status[2] << 16) |
23558c2ecf20Sopenharmony_ci	      (ucontrol->value.iec958.status[3] << 24);
23568c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
23578c2ecf20Sopenharmony_ci	change = trident->spdif_bits != val;
23588c2ecf20Sopenharmony_ci	trident->spdif_bits = val;
23598c2ecf20Sopenharmony_ci	if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
23608c2ecf20Sopenharmony_ci		if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0)
23618c2ecf20Sopenharmony_ci			outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
23628c2ecf20Sopenharmony_ci	} else {
23638c2ecf20Sopenharmony_ci		if (trident->spdif == NULL)
23648c2ecf20Sopenharmony_ci			outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
23658c2ecf20Sopenharmony_ci	}
23668c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
23678c2ecf20Sopenharmony_ci	return change;
23688c2ecf20Sopenharmony_ci}
23698c2ecf20Sopenharmony_ci
23708c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_spdif_default =
23718c2ecf20Sopenharmony_ci{
23728c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
23738c2ecf20Sopenharmony_ci	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
23748c2ecf20Sopenharmony_ci	.info =		snd_trident_spdif_default_info,
23758c2ecf20Sopenharmony_ci	.get =		snd_trident_spdif_default_get,
23768c2ecf20Sopenharmony_ci	.put =		snd_trident_spdif_default_put
23778c2ecf20Sopenharmony_ci};
23788c2ecf20Sopenharmony_ci
23798c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
23808c2ecf20Sopenharmony_ci    snd_trident_spdif_mask
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_ci    Description: put/get the S/PDIF mask
23838c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
23848c2ecf20Sopenharmony_ci
23858c2ecf20Sopenharmony_cistatic int snd_trident_spdif_mask_info(struct snd_kcontrol *kcontrol,
23868c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
23878c2ecf20Sopenharmony_ci{
23888c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
23898c2ecf20Sopenharmony_ci	uinfo->count = 1;
23908c2ecf20Sopenharmony_ci	return 0;
23918c2ecf20Sopenharmony_ci}
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_cistatic int snd_trident_spdif_mask_get(struct snd_kcontrol *kcontrol,
23948c2ecf20Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
23958c2ecf20Sopenharmony_ci{
23968c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[0] = 0xff;
23978c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[1] = 0xff;
23988c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[2] = 0xff;
23998c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[3] = 0xff;
24008c2ecf20Sopenharmony_ci	return 0;
24018c2ecf20Sopenharmony_ci}
24028c2ecf20Sopenharmony_ci
24038c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_spdif_mask =
24048c2ecf20Sopenharmony_ci{
24058c2ecf20Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
24068c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
24078c2ecf20Sopenharmony_ci	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
24088c2ecf20Sopenharmony_ci	.info =		snd_trident_spdif_mask_info,
24098c2ecf20Sopenharmony_ci	.get =		snd_trident_spdif_mask_get,
24108c2ecf20Sopenharmony_ci};
24118c2ecf20Sopenharmony_ci
24128c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
24138c2ecf20Sopenharmony_ci    snd_trident_spdif_stream
24148c2ecf20Sopenharmony_ci
24158c2ecf20Sopenharmony_ci    Description: put/get the S/PDIF stream settings
24168c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
24178c2ecf20Sopenharmony_ci
24188c2ecf20Sopenharmony_cistatic int snd_trident_spdif_stream_info(struct snd_kcontrol *kcontrol,
24198c2ecf20Sopenharmony_ci					 struct snd_ctl_elem_info *uinfo)
24208c2ecf20Sopenharmony_ci{
24218c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
24228c2ecf20Sopenharmony_ci	uinfo->count = 1;
24238c2ecf20Sopenharmony_ci	return 0;
24248c2ecf20Sopenharmony_ci}
24258c2ecf20Sopenharmony_ci
24268c2ecf20Sopenharmony_cistatic int snd_trident_spdif_stream_get(struct snd_kcontrol *kcontrol,
24278c2ecf20Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
24288c2ecf20Sopenharmony_ci{
24298c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
24308c2ecf20Sopenharmony_ci
24318c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
24328c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[0] = (trident->spdif_pcm_bits >> 0) & 0xff;
24338c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[1] = (trident->spdif_pcm_bits >> 8) & 0xff;
24348c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[2] = (trident->spdif_pcm_bits >> 16) & 0xff;
24358c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[3] = (trident->spdif_pcm_bits >> 24) & 0xff;
24368c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
24378c2ecf20Sopenharmony_ci	return 0;
24388c2ecf20Sopenharmony_ci}
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_cistatic int snd_trident_spdif_stream_put(struct snd_kcontrol *kcontrol,
24418c2ecf20Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
24428c2ecf20Sopenharmony_ci{
24438c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
24448c2ecf20Sopenharmony_ci	unsigned int val;
24458c2ecf20Sopenharmony_ci	int change;
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_ci	val = (ucontrol->value.iec958.status[0] << 0) |
24488c2ecf20Sopenharmony_ci	      (ucontrol->value.iec958.status[1] << 8) |
24498c2ecf20Sopenharmony_ci	      (ucontrol->value.iec958.status[2] << 16) |
24508c2ecf20Sopenharmony_ci	      (ucontrol->value.iec958.status[3] << 24);
24518c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
24528c2ecf20Sopenharmony_ci	change = trident->spdif_pcm_bits != val;
24538c2ecf20Sopenharmony_ci	trident->spdif_pcm_bits = val;
24548c2ecf20Sopenharmony_ci	if (trident->spdif != NULL) {
24558c2ecf20Sopenharmony_ci		if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
24568c2ecf20Sopenharmony_ci			outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
24578c2ecf20Sopenharmony_ci		} else {
24588c2ecf20Sopenharmony_ci			outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
24598c2ecf20Sopenharmony_ci		}
24608c2ecf20Sopenharmony_ci	}
24618c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
24628c2ecf20Sopenharmony_ci	return change;
24638c2ecf20Sopenharmony_ci}
24648c2ecf20Sopenharmony_ci
24658c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_spdif_stream =
24668c2ecf20Sopenharmony_ci{
24678c2ecf20Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
24688c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
24698c2ecf20Sopenharmony_ci	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
24708c2ecf20Sopenharmony_ci	.info =		snd_trident_spdif_stream_info,
24718c2ecf20Sopenharmony_ci	.get =		snd_trident_spdif_stream_get,
24728c2ecf20Sopenharmony_ci	.put =		snd_trident_spdif_stream_put
24738c2ecf20Sopenharmony_ci};
24748c2ecf20Sopenharmony_ci
24758c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
24768c2ecf20Sopenharmony_ci    snd_trident_ac97_control
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_ci    Description: enable/disable rear path for ac97
24798c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
24808c2ecf20Sopenharmony_ci
24818c2ecf20Sopenharmony_ci#define snd_trident_ac97_control_info	snd_ctl_boolean_mono_info
24828c2ecf20Sopenharmony_ci
24838c2ecf20Sopenharmony_cistatic int snd_trident_ac97_control_get(struct snd_kcontrol *kcontrol,
24848c2ecf20Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
24858c2ecf20Sopenharmony_ci{
24868c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
24878c2ecf20Sopenharmony_ci	unsigned char val;
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
24908c2ecf20Sopenharmony_ci	val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
24918c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = (val & (1 << kcontrol->private_value)) ? 1 : 0;
24928c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
24938c2ecf20Sopenharmony_ci	return 0;
24948c2ecf20Sopenharmony_ci}
24958c2ecf20Sopenharmony_ci
24968c2ecf20Sopenharmony_cistatic int snd_trident_ac97_control_put(struct snd_kcontrol *kcontrol,
24978c2ecf20Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
24988c2ecf20Sopenharmony_ci{
24998c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
25008c2ecf20Sopenharmony_ci	unsigned char val;
25018c2ecf20Sopenharmony_ci	int change = 0;
25028c2ecf20Sopenharmony_ci
25038c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
25048c2ecf20Sopenharmony_ci	val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
25058c2ecf20Sopenharmony_ci	val &= ~(1 << kcontrol->private_value);
25068c2ecf20Sopenharmony_ci	if (ucontrol->value.integer.value[0])
25078c2ecf20Sopenharmony_ci		val |= 1 << kcontrol->private_value;
25088c2ecf20Sopenharmony_ci	change = val != trident->ac97_ctrl;
25098c2ecf20Sopenharmony_ci	trident->ac97_ctrl = val;
25108c2ecf20Sopenharmony_ci	outl(trident->ac97_ctrl = val, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
25118c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
25128c2ecf20Sopenharmony_ci	return change;
25138c2ecf20Sopenharmony_ci}
25148c2ecf20Sopenharmony_ci
25158c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_ac97_rear_control =
25168c2ecf20Sopenharmony_ci{
25178c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
25188c2ecf20Sopenharmony_ci	.name =         "Rear Path",
25198c2ecf20Sopenharmony_ci	.info =		snd_trident_ac97_control_info,
25208c2ecf20Sopenharmony_ci	.get =		snd_trident_ac97_control_get,
25218c2ecf20Sopenharmony_ci	.put =		snd_trident_ac97_control_put,
25228c2ecf20Sopenharmony_ci	.private_value = 4,
25238c2ecf20Sopenharmony_ci};
25248c2ecf20Sopenharmony_ci
25258c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
25268c2ecf20Sopenharmony_ci    snd_trident_vol_control
25278c2ecf20Sopenharmony_ci
25288c2ecf20Sopenharmony_ci    Description: wave & music volume control
25298c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
25308c2ecf20Sopenharmony_ci
25318c2ecf20Sopenharmony_cistatic int snd_trident_vol_control_info(struct snd_kcontrol *kcontrol,
25328c2ecf20Sopenharmony_ci					struct snd_ctl_elem_info *uinfo)
25338c2ecf20Sopenharmony_ci{
25348c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
25358c2ecf20Sopenharmony_ci	uinfo->count = 2;
25368c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
25378c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 255;
25388c2ecf20Sopenharmony_ci	return 0;
25398c2ecf20Sopenharmony_ci}
25408c2ecf20Sopenharmony_ci
25418c2ecf20Sopenharmony_cistatic int snd_trident_vol_control_get(struct snd_kcontrol *kcontrol,
25428c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
25438c2ecf20Sopenharmony_ci{
25448c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
25458c2ecf20Sopenharmony_ci	unsigned int val;
25468c2ecf20Sopenharmony_ci
25478c2ecf20Sopenharmony_ci	val = trident->musicvol_wavevol;
25488c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = 255 - ((val >> kcontrol->private_value) & 0xff);
25498c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[1] = 255 - ((val >> (kcontrol->private_value + 8)) & 0xff);
25508c2ecf20Sopenharmony_ci	return 0;
25518c2ecf20Sopenharmony_ci}
25528c2ecf20Sopenharmony_ci
25538c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_gvol, -6375, 25, 0);
25548c2ecf20Sopenharmony_ci
25558c2ecf20Sopenharmony_cistatic int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol,
25568c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
25578c2ecf20Sopenharmony_ci{
25588c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
25598c2ecf20Sopenharmony_ci	unsigned int val;
25608c2ecf20Sopenharmony_ci	int change = 0;
25618c2ecf20Sopenharmony_ci
25628c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
25638c2ecf20Sopenharmony_ci	val = trident->musicvol_wavevol;
25648c2ecf20Sopenharmony_ci	val &= ~(0xffff << kcontrol->private_value);
25658c2ecf20Sopenharmony_ci	val |= ((255 - (ucontrol->value.integer.value[0] & 0xff)) |
25668c2ecf20Sopenharmony_ci	        ((255 - (ucontrol->value.integer.value[1] & 0xff)) << 8)) << kcontrol->private_value;
25678c2ecf20Sopenharmony_ci	change = val != trident->musicvol_wavevol;
25688c2ecf20Sopenharmony_ci	outl(trident->musicvol_wavevol = val, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
25698c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
25708c2ecf20Sopenharmony_ci	return change;
25718c2ecf20Sopenharmony_ci}
25728c2ecf20Sopenharmony_ci
25738c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_vol_music_control =
25748c2ecf20Sopenharmony_ci{
25758c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
25768c2ecf20Sopenharmony_ci	.name =         "Music Playback Volume",
25778c2ecf20Sopenharmony_ci	.info =		snd_trident_vol_control_info,
25788c2ecf20Sopenharmony_ci	.get =		snd_trident_vol_control_get,
25798c2ecf20Sopenharmony_ci	.put =		snd_trident_vol_control_put,
25808c2ecf20Sopenharmony_ci	.private_value = 16,
25818c2ecf20Sopenharmony_ci	.tlv = { .p = db_scale_gvol },
25828c2ecf20Sopenharmony_ci};
25838c2ecf20Sopenharmony_ci
25848c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_vol_wave_control =
25858c2ecf20Sopenharmony_ci{
25868c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
25878c2ecf20Sopenharmony_ci	.name =         "Wave Playback Volume",
25888c2ecf20Sopenharmony_ci	.info =		snd_trident_vol_control_info,
25898c2ecf20Sopenharmony_ci	.get =		snd_trident_vol_control_get,
25908c2ecf20Sopenharmony_ci	.put =		snd_trident_vol_control_put,
25918c2ecf20Sopenharmony_ci	.private_value = 0,
25928c2ecf20Sopenharmony_ci	.tlv = { .p = db_scale_gvol },
25938c2ecf20Sopenharmony_ci};
25948c2ecf20Sopenharmony_ci
25958c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
25968c2ecf20Sopenharmony_ci    snd_trident_pcm_vol_control
25978c2ecf20Sopenharmony_ci
25988c2ecf20Sopenharmony_ci    Description: PCM front volume control
25998c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
26008c2ecf20Sopenharmony_ci
26018c2ecf20Sopenharmony_cistatic int snd_trident_pcm_vol_control_info(struct snd_kcontrol *kcontrol,
26028c2ecf20Sopenharmony_ci					    struct snd_ctl_elem_info *uinfo)
26038c2ecf20Sopenharmony_ci{
26048c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
26058c2ecf20Sopenharmony_ci
26068c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
26078c2ecf20Sopenharmony_ci	uinfo->count = 1;
26088c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
26098c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 255;
26108c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_SI7018)
26118c2ecf20Sopenharmony_ci		uinfo->value.integer.max = 1023;
26128c2ecf20Sopenharmony_ci	return 0;
26138c2ecf20Sopenharmony_ci}
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_cistatic int snd_trident_pcm_vol_control_get(struct snd_kcontrol *kcontrol,
26168c2ecf20Sopenharmony_ci					   struct snd_ctl_elem_value *ucontrol)
26178c2ecf20Sopenharmony_ci{
26188c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
26198c2ecf20Sopenharmony_ci	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
26208c2ecf20Sopenharmony_ci
26218c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
26228c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] = 1023 - mix->vol;
26238c2ecf20Sopenharmony_ci	} else {
26248c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] = 255 - (mix->vol>>2);
26258c2ecf20Sopenharmony_ci	}
26268c2ecf20Sopenharmony_ci	return 0;
26278c2ecf20Sopenharmony_ci}
26288c2ecf20Sopenharmony_ci
26298c2ecf20Sopenharmony_cistatic int snd_trident_pcm_vol_control_put(struct snd_kcontrol *kcontrol,
26308c2ecf20Sopenharmony_ci					   struct snd_ctl_elem_value *ucontrol)
26318c2ecf20Sopenharmony_ci{
26328c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
26338c2ecf20Sopenharmony_ci	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
26348c2ecf20Sopenharmony_ci	unsigned int val;
26358c2ecf20Sopenharmony_ci	int change = 0;
26368c2ecf20Sopenharmony_ci
26378c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
26388c2ecf20Sopenharmony_ci		val = 1023 - (ucontrol->value.integer.value[0] & 1023);
26398c2ecf20Sopenharmony_ci	} else {
26408c2ecf20Sopenharmony_ci		val = (255 - (ucontrol->value.integer.value[0] & 255)) << 2;
26418c2ecf20Sopenharmony_ci	}
26428c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
26438c2ecf20Sopenharmony_ci	change = val != mix->vol;
26448c2ecf20Sopenharmony_ci	mix->vol = val;
26458c2ecf20Sopenharmony_ci	if (mix->voice != NULL)
26468c2ecf20Sopenharmony_ci		snd_trident_write_vol_reg(trident, mix->voice, val);
26478c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
26488c2ecf20Sopenharmony_ci	return change;
26498c2ecf20Sopenharmony_ci}
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_pcm_vol_control =
26528c2ecf20Sopenharmony_ci{
26538c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
26548c2ecf20Sopenharmony_ci	.name =         "PCM Front Playback Volume",
26558c2ecf20Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
26568c2ecf20Sopenharmony_ci	.count =	32,
26578c2ecf20Sopenharmony_ci	.info =		snd_trident_pcm_vol_control_info,
26588c2ecf20Sopenharmony_ci	.get =		snd_trident_pcm_vol_control_get,
26598c2ecf20Sopenharmony_ci	.put =		snd_trident_pcm_vol_control_put,
26608c2ecf20Sopenharmony_ci	/* FIXME: no tlv yet */
26618c2ecf20Sopenharmony_ci};
26628c2ecf20Sopenharmony_ci
26638c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
26648c2ecf20Sopenharmony_ci    snd_trident_pcm_pan_control
26658c2ecf20Sopenharmony_ci
26668c2ecf20Sopenharmony_ci    Description: PCM front pan control
26678c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
26688c2ecf20Sopenharmony_ci
26698c2ecf20Sopenharmony_cistatic int snd_trident_pcm_pan_control_info(struct snd_kcontrol *kcontrol,
26708c2ecf20Sopenharmony_ci					    struct snd_ctl_elem_info *uinfo)
26718c2ecf20Sopenharmony_ci{
26728c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
26738c2ecf20Sopenharmony_ci	uinfo->count = 1;
26748c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
26758c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 127;
26768c2ecf20Sopenharmony_ci	return 0;
26778c2ecf20Sopenharmony_ci}
26788c2ecf20Sopenharmony_ci
26798c2ecf20Sopenharmony_cistatic int snd_trident_pcm_pan_control_get(struct snd_kcontrol *kcontrol,
26808c2ecf20Sopenharmony_ci					   struct snd_ctl_elem_value *ucontrol)
26818c2ecf20Sopenharmony_ci{
26828c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
26838c2ecf20Sopenharmony_ci	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
26848c2ecf20Sopenharmony_ci
26858c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = mix->pan;
26868c2ecf20Sopenharmony_ci	if (ucontrol->value.integer.value[0] & 0x40) {
26878c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] = (0x3f - (ucontrol->value.integer.value[0] & 0x3f));
26888c2ecf20Sopenharmony_ci	} else {
26898c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] |= 0x40;
26908c2ecf20Sopenharmony_ci	}
26918c2ecf20Sopenharmony_ci	return 0;
26928c2ecf20Sopenharmony_ci}
26938c2ecf20Sopenharmony_ci
26948c2ecf20Sopenharmony_cistatic int snd_trident_pcm_pan_control_put(struct snd_kcontrol *kcontrol,
26958c2ecf20Sopenharmony_ci					   struct snd_ctl_elem_value *ucontrol)
26968c2ecf20Sopenharmony_ci{
26978c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
26988c2ecf20Sopenharmony_ci	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
26998c2ecf20Sopenharmony_ci	unsigned char val;
27008c2ecf20Sopenharmony_ci	int change = 0;
27018c2ecf20Sopenharmony_ci
27028c2ecf20Sopenharmony_ci	if (ucontrol->value.integer.value[0] & 0x40)
27038c2ecf20Sopenharmony_ci		val = ucontrol->value.integer.value[0] & 0x3f;
27048c2ecf20Sopenharmony_ci	else
27058c2ecf20Sopenharmony_ci		val = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)) | 0x40;
27068c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
27078c2ecf20Sopenharmony_ci	change = val != mix->pan;
27088c2ecf20Sopenharmony_ci	mix->pan = val;
27098c2ecf20Sopenharmony_ci	if (mix->voice != NULL)
27108c2ecf20Sopenharmony_ci		snd_trident_write_pan_reg(trident, mix->voice, val);
27118c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
27128c2ecf20Sopenharmony_ci	return change;
27138c2ecf20Sopenharmony_ci}
27148c2ecf20Sopenharmony_ci
27158c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_pcm_pan_control =
27168c2ecf20Sopenharmony_ci{
27178c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
27188c2ecf20Sopenharmony_ci	.name =         "PCM Pan Playback Control",
27198c2ecf20Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
27208c2ecf20Sopenharmony_ci	.count =	32,
27218c2ecf20Sopenharmony_ci	.info =		snd_trident_pcm_pan_control_info,
27228c2ecf20Sopenharmony_ci	.get =		snd_trident_pcm_pan_control_get,
27238c2ecf20Sopenharmony_ci	.put =		snd_trident_pcm_pan_control_put,
27248c2ecf20Sopenharmony_ci};
27258c2ecf20Sopenharmony_ci
27268c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
27278c2ecf20Sopenharmony_ci    snd_trident_pcm_rvol_control
27288c2ecf20Sopenharmony_ci
27298c2ecf20Sopenharmony_ci    Description: PCM reverb volume control
27308c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
27318c2ecf20Sopenharmony_ci
27328c2ecf20Sopenharmony_cistatic int snd_trident_pcm_rvol_control_info(struct snd_kcontrol *kcontrol,
27338c2ecf20Sopenharmony_ci					     struct snd_ctl_elem_info *uinfo)
27348c2ecf20Sopenharmony_ci{
27358c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
27368c2ecf20Sopenharmony_ci	uinfo->count = 1;
27378c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
27388c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 127;
27398c2ecf20Sopenharmony_ci	return 0;
27408c2ecf20Sopenharmony_ci}
27418c2ecf20Sopenharmony_ci
27428c2ecf20Sopenharmony_cistatic int snd_trident_pcm_rvol_control_get(struct snd_kcontrol *kcontrol,
27438c2ecf20Sopenharmony_ci					    struct snd_ctl_elem_value *ucontrol)
27448c2ecf20Sopenharmony_ci{
27458c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
27468c2ecf20Sopenharmony_ci	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
27478c2ecf20Sopenharmony_ci
27488c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = 127 - mix->rvol;
27498c2ecf20Sopenharmony_ci	return 0;
27508c2ecf20Sopenharmony_ci}
27518c2ecf20Sopenharmony_ci
27528c2ecf20Sopenharmony_cistatic int snd_trident_pcm_rvol_control_put(struct snd_kcontrol *kcontrol,
27538c2ecf20Sopenharmony_ci					    struct snd_ctl_elem_value *ucontrol)
27548c2ecf20Sopenharmony_ci{
27558c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
27568c2ecf20Sopenharmony_ci	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
27578c2ecf20Sopenharmony_ci	unsigned short val;
27588c2ecf20Sopenharmony_ci	int change = 0;
27598c2ecf20Sopenharmony_ci
27608c2ecf20Sopenharmony_ci	val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f);
27618c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
27628c2ecf20Sopenharmony_ci	change = val != mix->rvol;
27638c2ecf20Sopenharmony_ci	mix->rvol = val;
27648c2ecf20Sopenharmony_ci	if (mix->voice != NULL)
27658c2ecf20Sopenharmony_ci		snd_trident_write_rvol_reg(trident, mix->voice, val);
27668c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
27678c2ecf20Sopenharmony_ci	return change;
27688c2ecf20Sopenharmony_ci}
27698c2ecf20Sopenharmony_ci
27708c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1);
27718c2ecf20Sopenharmony_ci
27728c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_pcm_rvol_control =
27738c2ecf20Sopenharmony_ci{
27748c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
27758c2ecf20Sopenharmony_ci	.name =         "PCM Reverb Playback Volume",
27768c2ecf20Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
27778c2ecf20Sopenharmony_ci	.count = 	32,
27788c2ecf20Sopenharmony_ci	.info =		snd_trident_pcm_rvol_control_info,
27798c2ecf20Sopenharmony_ci	.get =		snd_trident_pcm_rvol_control_get,
27808c2ecf20Sopenharmony_ci	.put =		snd_trident_pcm_rvol_control_put,
27818c2ecf20Sopenharmony_ci	.tlv = { .p = db_scale_crvol },
27828c2ecf20Sopenharmony_ci};
27838c2ecf20Sopenharmony_ci
27848c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
27858c2ecf20Sopenharmony_ci    snd_trident_pcm_cvol_control
27868c2ecf20Sopenharmony_ci
27878c2ecf20Sopenharmony_ci    Description: PCM chorus volume control
27888c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
27898c2ecf20Sopenharmony_ci
27908c2ecf20Sopenharmony_cistatic int snd_trident_pcm_cvol_control_info(struct snd_kcontrol *kcontrol,
27918c2ecf20Sopenharmony_ci					     struct snd_ctl_elem_info *uinfo)
27928c2ecf20Sopenharmony_ci{
27938c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
27948c2ecf20Sopenharmony_ci	uinfo->count = 1;
27958c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
27968c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 127;
27978c2ecf20Sopenharmony_ci	return 0;
27988c2ecf20Sopenharmony_ci}
27998c2ecf20Sopenharmony_ci
28008c2ecf20Sopenharmony_cistatic int snd_trident_pcm_cvol_control_get(struct snd_kcontrol *kcontrol,
28018c2ecf20Sopenharmony_ci					    struct snd_ctl_elem_value *ucontrol)
28028c2ecf20Sopenharmony_ci{
28038c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
28048c2ecf20Sopenharmony_ci	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
28058c2ecf20Sopenharmony_ci
28068c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = 127 - mix->cvol;
28078c2ecf20Sopenharmony_ci	return 0;
28088c2ecf20Sopenharmony_ci}
28098c2ecf20Sopenharmony_ci
28108c2ecf20Sopenharmony_cistatic int snd_trident_pcm_cvol_control_put(struct snd_kcontrol *kcontrol,
28118c2ecf20Sopenharmony_ci					    struct snd_ctl_elem_value *ucontrol)
28128c2ecf20Sopenharmony_ci{
28138c2ecf20Sopenharmony_ci	struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
28148c2ecf20Sopenharmony_ci	struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
28158c2ecf20Sopenharmony_ci	unsigned short val;
28168c2ecf20Sopenharmony_ci	int change = 0;
28178c2ecf20Sopenharmony_ci
28188c2ecf20Sopenharmony_ci	val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f);
28198c2ecf20Sopenharmony_ci	spin_lock_irq(&trident->reg_lock);
28208c2ecf20Sopenharmony_ci	change = val != mix->cvol;
28218c2ecf20Sopenharmony_ci	mix->cvol = val;
28228c2ecf20Sopenharmony_ci	if (mix->voice != NULL)
28238c2ecf20Sopenharmony_ci		snd_trident_write_cvol_reg(trident, mix->voice, val);
28248c2ecf20Sopenharmony_ci	spin_unlock_irq(&trident->reg_lock);
28258c2ecf20Sopenharmony_ci	return change;
28268c2ecf20Sopenharmony_ci}
28278c2ecf20Sopenharmony_ci
28288c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_trident_pcm_cvol_control =
28298c2ecf20Sopenharmony_ci{
28308c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
28318c2ecf20Sopenharmony_ci	.name =         "PCM Chorus Playback Volume",
28328c2ecf20Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
28338c2ecf20Sopenharmony_ci	.count =	32,
28348c2ecf20Sopenharmony_ci	.info =		snd_trident_pcm_cvol_control_info,
28358c2ecf20Sopenharmony_ci	.get =		snd_trident_pcm_cvol_control_get,
28368c2ecf20Sopenharmony_ci	.put =		snd_trident_pcm_cvol_control_put,
28378c2ecf20Sopenharmony_ci	.tlv = { .p = db_scale_crvol },
28388c2ecf20Sopenharmony_ci};
28398c2ecf20Sopenharmony_ci
28408c2ecf20Sopenharmony_cistatic void snd_trident_notify_pcm_change1(struct snd_card *card,
28418c2ecf20Sopenharmony_ci					   struct snd_kcontrol *kctl,
28428c2ecf20Sopenharmony_ci					   int num, int activate)
28438c2ecf20Sopenharmony_ci{
28448c2ecf20Sopenharmony_ci	struct snd_ctl_elem_id id;
28458c2ecf20Sopenharmony_ci
28468c2ecf20Sopenharmony_ci	if (! kctl)
28478c2ecf20Sopenharmony_ci		return;
28488c2ecf20Sopenharmony_ci	if (activate)
28498c2ecf20Sopenharmony_ci		kctl->vd[num].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
28508c2ecf20Sopenharmony_ci	else
28518c2ecf20Sopenharmony_ci		kctl->vd[num].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
28528c2ecf20Sopenharmony_ci	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
28538c2ecf20Sopenharmony_ci		       SNDRV_CTL_EVENT_MASK_INFO,
28548c2ecf20Sopenharmony_ci		       snd_ctl_build_ioff(&id, kctl, num));
28558c2ecf20Sopenharmony_ci}
28568c2ecf20Sopenharmony_ci
28578c2ecf20Sopenharmony_cistatic void snd_trident_notify_pcm_change(struct snd_trident *trident,
28588c2ecf20Sopenharmony_ci					  struct snd_trident_pcm_mixer *tmix,
28598c2ecf20Sopenharmony_ci					  int num, int activate)
28608c2ecf20Sopenharmony_ci{
28618c2ecf20Sopenharmony_ci	snd_trident_notify_pcm_change1(trident->card, trident->ctl_vol, num, activate);
28628c2ecf20Sopenharmony_ci	snd_trident_notify_pcm_change1(trident->card, trident->ctl_pan, num, activate);
28638c2ecf20Sopenharmony_ci	snd_trident_notify_pcm_change1(trident->card, trident->ctl_rvol, num, activate);
28648c2ecf20Sopenharmony_ci	snd_trident_notify_pcm_change1(trident->card, trident->ctl_cvol, num, activate);
28658c2ecf20Sopenharmony_ci}
28668c2ecf20Sopenharmony_ci
28678c2ecf20Sopenharmony_cistatic int snd_trident_pcm_mixer_build(struct snd_trident *trident,
28688c2ecf20Sopenharmony_ci				       struct snd_trident_voice *voice,
28698c2ecf20Sopenharmony_ci				       struct snd_pcm_substream *substream)
28708c2ecf20Sopenharmony_ci{
28718c2ecf20Sopenharmony_ci	struct snd_trident_pcm_mixer *tmix;
28728c2ecf20Sopenharmony_ci
28738c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!trident || !voice || !substream))
28748c2ecf20Sopenharmony_ci		return -EINVAL;
28758c2ecf20Sopenharmony_ci	tmix = &trident->pcm_mixer[substream->number];
28768c2ecf20Sopenharmony_ci	tmix->voice = voice;
28778c2ecf20Sopenharmony_ci	tmix->vol = T4D_DEFAULT_PCM_VOL;
28788c2ecf20Sopenharmony_ci	tmix->pan = T4D_DEFAULT_PCM_PAN;
28798c2ecf20Sopenharmony_ci	tmix->rvol = T4D_DEFAULT_PCM_RVOL;
28808c2ecf20Sopenharmony_ci	tmix->cvol = T4D_DEFAULT_PCM_CVOL;
28818c2ecf20Sopenharmony_ci	snd_trident_notify_pcm_change(trident, tmix, substream->number, 1);
28828c2ecf20Sopenharmony_ci	return 0;
28838c2ecf20Sopenharmony_ci}
28848c2ecf20Sopenharmony_ci
28858c2ecf20Sopenharmony_cistatic int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_trident_voice *voice, struct snd_pcm_substream *substream)
28868c2ecf20Sopenharmony_ci{
28878c2ecf20Sopenharmony_ci	struct snd_trident_pcm_mixer *tmix;
28888c2ecf20Sopenharmony_ci
28898c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!trident || !substream))
28908c2ecf20Sopenharmony_ci		return -EINVAL;
28918c2ecf20Sopenharmony_ci	tmix = &trident->pcm_mixer[substream->number];
28928c2ecf20Sopenharmony_ci	tmix->voice = NULL;
28938c2ecf20Sopenharmony_ci	snd_trident_notify_pcm_change(trident, tmix, substream->number, 0);
28948c2ecf20Sopenharmony_ci	return 0;
28958c2ecf20Sopenharmony_ci}
28968c2ecf20Sopenharmony_ci
28978c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
28988c2ecf20Sopenharmony_ci   snd_trident_mixer
28998c2ecf20Sopenharmony_ci
29008c2ecf20Sopenharmony_ci   Description: This routine registers the 4DWave device for mixer support.
29018c2ecf20Sopenharmony_ci
29028c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
29038c2ecf20Sopenharmony_ci
29048c2ecf20Sopenharmony_ci   Returns:     None
29058c2ecf20Sopenharmony_ci
29068c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
29078c2ecf20Sopenharmony_ci
29088c2ecf20Sopenharmony_cistatic int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device)
29098c2ecf20Sopenharmony_ci{
29108c2ecf20Sopenharmony_ci	struct snd_ac97_template _ac97;
29118c2ecf20Sopenharmony_ci	struct snd_card *card = trident->card;
29128c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
29138c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *uctl;
29148c2ecf20Sopenharmony_ci	int idx, err, retries = 2;
29158c2ecf20Sopenharmony_ci	static const struct snd_ac97_bus_ops ops = {
29168c2ecf20Sopenharmony_ci		.write = snd_trident_codec_write,
29178c2ecf20Sopenharmony_ci		.read = snd_trident_codec_read,
29188c2ecf20Sopenharmony_ci	};
29198c2ecf20Sopenharmony_ci
29208c2ecf20Sopenharmony_ci	uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
29218c2ecf20Sopenharmony_ci	if (!uctl)
29228c2ecf20Sopenharmony_ci		return -ENOMEM;
29238c2ecf20Sopenharmony_ci
29248c2ecf20Sopenharmony_ci	if ((err = snd_ac97_bus(trident->card, 0, &ops, NULL, &trident->ac97_bus)) < 0)
29258c2ecf20Sopenharmony_ci		goto __out;
29268c2ecf20Sopenharmony_ci
29278c2ecf20Sopenharmony_ci	memset(&_ac97, 0, sizeof(_ac97));
29288c2ecf20Sopenharmony_ci	_ac97.private_data = trident;
29298c2ecf20Sopenharmony_ci	trident->ac97_detect = 1;
29308c2ecf20Sopenharmony_ci
29318c2ecf20Sopenharmony_ci      __again:
29328c2ecf20Sopenharmony_ci	if ((err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97)) < 0) {
29338c2ecf20Sopenharmony_ci		if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
29348c2ecf20Sopenharmony_ci			if ((err = snd_trident_sis_reset(trident)) < 0)
29358c2ecf20Sopenharmony_ci				goto __out;
29368c2ecf20Sopenharmony_ci			if (retries-- > 0)
29378c2ecf20Sopenharmony_ci				goto __again;
29388c2ecf20Sopenharmony_ci			err = -EIO;
29398c2ecf20Sopenharmony_ci		}
29408c2ecf20Sopenharmony_ci		goto __out;
29418c2ecf20Sopenharmony_ci	}
29428c2ecf20Sopenharmony_ci
29438c2ecf20Sopenharmony_ci	/* secondary codec? */
29448c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_SI7018 &&
29458c2ecf20Sopenharmony_ci	    (inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) {
29468c2ecf20Sopenharmony_ci		_ac97.num = 1;
29478c2ecf20Sopenharmony_ci		err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97_sec);
29488c2ecf20Sopenharmony_ci		if (err < 0)
29498c2ecf20Sopenharmony_ci			dev_err(trident->card->dev,
29508c2ecf20Sopenharmony_ci				"SI7018: the secondary codec - invalid access\n");
29518c2ecf20Sopenharmony_ci#if 0	// only for my testing purpose --jk
29528c2ecf20Sopenharmony_ci		{
29538c2ecf20Sopenharmony_ci			struct snd_ac97 *mc97;
29548c2ecf20Sopenharmony_ci			err = snd_ac97_modem(trident->card, &_ac97, &mc97);
29558c2ecf20Sopenharmony_ci			if (err < 0)
29568c2ecf20Sopenharmony_ci				dev_err(trident->card->dev,
29578c2ecf20Sopenharmony_ci					"snd_ac97_modem returned error %i\n", err);
29588c2ecf20Sopenharmony_ci		}
29598c2ecf20Sopenharmony_ci#endif
29608c2ecf20Sopenharmony_ci	}
29618c2ecf20Sopenharmony_ci
29628c2ecf20Sopenharmony_ci	trident->ac97_detect = 0;
29638c2ecf20Sopenharmony_ci
29648c2ecf20Sopenharmony_ci	if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
29658c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_wave_control, trident))) < 0)
29668c2ecf20Sopenharmony_ci			goto __out;
29678c2ecf20Sopenharmony_ci		kctl->put(kctl, uctl);
29688c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_music_control, trident))) < 0)
29698c2ecf20Sopenharmony_ci			goto __out;
29708c2ecf20Sopenharmony_ci		kctl->put(kctl, uctl);
29718c2ecf20Sopenharmony_ci		outl(trident->musicvol_wavevol = 0x00000000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
29728c2ecf20Sopenharmony_ci	} else {
29738c2ecf20Sopenharmony_ci		outl(trident->musicvol_wavevol = 0xffff0000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
29748c2ecf20Sopenharmony_ci	}
29758c2ecf20Sopenharmony_ci
29768c2ecf20Sopenharmony_ci	for (idx = 0; idx < 32; idx++) {
29778c2ecf20Sopenharmony_ci		struct snd_trident_pcm_mixer *tmix;
29788c2ecf20Sopenharmony_ci
29798c2ecf20Sopenharmony_ci		tmix = &trident->pcm_mixer[idx];
29808c2ecf20Sopenharmony_ci		tmix->voice = NULL;
29818c2ecf20Sopenharmony_ci	}
29828c2ecf20Sopenharmony_ci	if ((trident->ctl_vol = snd_ctl_new1(&snd_trident_pcm_vol_control, trident)) == NULL)
29838c2ecf20Sopenharmony_ci		goto __nomem;
29848c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(card, trident->ctl_vol)))
29858c2ecf20Sopenharmony_ci		goto __out;
29868c2ecf20Sopenharmony_ci
29878c2ecf20Sopenharmony_ci	if ((trident->ctl_pan = snd_ctl_new1(&snd_trident_pcm_pan_control, trident)) == NULL)
29888c2ecf20Sopenharmony_ci		goto __nomem;
29898c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(card, trident->ctl_pan)))
29908c2ecf20Sopenharmony_ci		goto __out;
29918c2ecf20Sopenharmony_ci
29928c2ecf20Sopenharmony_ci	if ((trident->ctl_rvol = snd_ctl_new1(&snd_trident_pcm_rvol_control, trident)) == NULL)
29938c2ecf20Sopenharmony_ci		goto __nomem;
29948c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(card, trident->ctl_rvol)))
29958c2ecf20Sopenharmony_ci		goto __out;
29968c2ecf20Sopenharmony_ci
29978c2ecf20Sopenharmony_ci	if ((trident->ctl_cvol = snd_ctl_new1(&snd_trident_pcm_cvol_control, trident)) == NULL)
29988c2ecf20Sopenharmony_ci		goto __nomem;
29998c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(card, trident->ctl_cvol)))
30008c2ecf20Sopenharmony_ci		goto __out;
30018c2ecf20Sopenharmony_ci
30028c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_NX) {
30038c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_ac97_rear_control, trident))) < 0)
30048c2ecf20Sopenharmony_ci			goto __out;
30058c2ecf20Sopenharmony_ci		kctl->put(kctl, uctl);
30068c2ecf20Sopenharmony_ci	}
30078c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) {
30088c2ecf20Sopenharmony_ci
30098c2ecf20Sopenharmony_ci		kctl = snd_ctl_new1(&snd_trident_spdif_control, trident);
30108c2ecf20Sopenharmony_ci		if (kctl == NULL) {
30118c2ecf20Sopenharmony_ci			err = -ENOMEM;
30128c2ecf20Sopenharmony_ci			goto __out;
30138c2ecf20Sopenharmony_ci		}
30148c2ecf20Sopenharmony_ci		if (trident->ac97->ext_id & AC97_EI_SPDIF)
30158c2ecf20Sopenharmony_ci			kctl->id.index++;
30168c2ecf20Sopenharmony_ci		if (trident->ac97_sec && (trident->ac97_sec->ext_id & AC97_EI_SPDIF))
30178c2ecf20Sopenharmony_ci			kctl->id.index++;
30188c2ecf20Sopenharmony_ci		idx = kctl->id.index;
30198c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(card, kctl)) < 0)
30208c2ecf20Sopenharmony_ci			goto __out;
30218c2ecf20Sopenharmony_ci		kctl->put(kctl, uctl);
30228c2ecf20Sopenharmony_ci
30238c2ecf20Sopenharmony_ci		kctl = snd_ctl_new1(&snd_trident_spdif_default, trident);
30248c2ecf20Sopenharmony_ci		if (kctl == NULL) {
30258c2ecf20Sopenharmony_ci			err = -ENOMEM;
30268c2ecf20Sopenharmony_ci			goto __out;
30278c2ecf20Sopenharmony_ci		}
30288c2ecf20Sopenharmony_ci		kctl->id.index = idx;
30298c2ecf20Sopenharmony_ci		kctl->id.device = pcm_spdif_device;
30308c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(card, kctl)) < 0)
30318c2ecf20Sopenharmony_ci			goto __out;
30328c2ecf20Sopenharmony_ci
30338c2ecf20Sopenharmony_ci		kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident);
30348c2ecf20Sopenharmony_ci		if (kctl == NULL) {
30358c2ecf20Sopenharmony_ci			err = -ENOMEM;
30368c2ecf20Sopenharmony_ci			goto __out;
30378c2ecf20Sopenharmony_ci		}
30388c2ecf20Sopenharmony_ci		kctl->id.index = idx;
30398c2ecf20Sopenharmony_ci		kctl->id.device = pcm_spdif_device;
30408c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(card, kctl)) < 0)
30418c2ecf20Sopenharmony_ci			goto __out;
30428c2ecf20Sopenharmony_ci
30438c2ecf20Sopenharmony_ci		kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident);
30448c2ecf20Sopenharmony_ci		if (kctl == NULL) {
30458c2ecf20Sopenharmony_ci			err = -ENOMEM;
30468c2ecf20Sopenharmony_ci			goto __out;
30478c2ecf20Sopenharmony_ci		}
30488c2ecf20Sopenharmony_ci		kctl->id.index = idx;
30498c2ecf20Sopenharmony_ci		kctl->id.device = pcm_spdif_device;
30508c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(card, kctl)) < 0)
30518c2ecf20Sopenharmony_ci			goto __out;
30528c2ecf20Sopenharmony_ci		trident->spdif_pcm_ctl = kctl;
30538c2ecf20Sopenharmony_ci	}
30548c2ecf20Sopenharmony_ci
30558c2ecf20Sopenharmony_ci	err = 0;
30568c2ecf20Sopenharmony_ci	goto __out;
30578c2ecf20Sopenharmony_ci
30588c2ecf20Sopenharmony_ci __nomem:
30598c2ecf20Sopenharmony_ci	err = -ENOMEM;
30608c2ecf20Sopenharmony_ci
30618c2ecf20Sopenharmony_ci __out:
30628c2ecf20Sopenharmony_ci	kfree(uctl);
30638c2ecf20Sopenharmony_ci
30648c2ecf20Sopenharmony_ci	return err;
30658c2ecf20Sopenharmony_ci}
30668c2ecf20Sopenharmony_ci
30678c2ecf20Sopenharmony_ci/*
30688c2ecf20Sopenharmony_ci * gameport interface
30698c2ecf20Sopenharmony_ci */
30708c2ecf20Sopenharmony_ci
30718c2ecf20Sopenharmony_ci#if IS_REACHABLE(CONFIG_GAMEPORT)
30728c2ecf20Sopenharmony_ci
30738c2ecf20Sopenharmony_cistatic unsigned char snd_trident_gameport_read(struct gameport *gameport)
30748c2ecf20Sopenharmony_ci{
30758c2ecf20Sopenharmony_ci	struct snd_trident *chip = gameport_get_port_data(gameport);
30768c2ecf20Sopenharmony_ci
30778c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!chip))
30788c2ecf20Sopenharmony_ci		return 0;
30798c2ecf20Sopenharmony_ci	return inb(TRID_REG(chip, GAMEPORT_LEGACY));
30808c2ecf20Sopenharmony_ci}
30818c2ecf20Sopenharmony_ci
30828c2ecf20Sopenharmony_cistatic void snd_trident_gameport_trigger(struct gameport *gameport)
30838c2ecf20Sopenharmony_ci{
30848c2ecf20Sopenharmony_ci	struct snd_trident *chip = gameport_get_port_data(gameport);
30858c2ecf20Sopenharmony_ci
30868c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!chip))
30878c2ecf20Sopenharmony_ci		return;
30888c2ecf20Sopenharmony_ci	outb(0xff, TRID_REG(chip, GAMEPORT_LEGACY));
30898c2ecf20Sopenharmony_ci}
30908c2ecf20Sopenharmony_ci
30918c2ecf20Sopenharmony_cistatic int snd_trident_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
30928c2ecf20Sopenharmony_ci{
30938c2ecf20Sopenharmony_ci	struct snd_trident *chip = gameport_get_port_data(gameport);
30948c2ecf20Sopenharmony_ci	int i;
30958c2ecf20Sopenharmony_ci
30968c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!chip))
30978c2ecf20Sopenharmony_ci		return 0;
30988c2ecf20Sopenharmony_ci
30998c2ecf20Sopenharmony_ci	*buttons = (~inb(TRID_REG(chip, GAMEPORT_LEGACY)) >> 4) & 0xf;
31008c2ecf20Sopenharmony_ci
31018c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
31028c2ecf20Sopenharmony_ci		axes[i] = inw(TRID_REG(chip, GAMEPORT_AXES + i * 2));
31038c2ecf20Sopenharmony_ci		if (axes[i] == 0xffff) axes[i] = -1;
31048c2ecf20Sopenharmony_ci	}
31058c2ecf20Sopenharmony_ci
31068c2ecf20Sopenharmony_ci        return 0;
31078c2ecf20Sopenharmony_ci}
31088c2ecf20Sopenharmony_ci
31098c2ecf20Sopenharmony_cistatic int snd_trident_gameport_open(struct gameport *gameport, int mode)
31108c2ecf20Sopenharmony_ci{
31118c2ecf20Sopenharmony_ci	struct snd_trident *chip = gameport_get_port_data(gameport);
31128c2ecf20Sopenharmony_ci
31138c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!chip))
31148c2ecf20Sopenharmony_ci		return 0;
31158c2ecf20Sopenharmony_ci
31168c2ecf20Sopenharmony_ci	switch (mode) {
31178c2ecf20Sopenharmony_ci		case GAMEPORT_MODE_COOKED:
31188c2ecf20Sopenharmony_ci			outb(GAMEPORT_MODE_ADC, TRID_REG(chip, GAMEPORT_GCR));
31198c2ecf20Sopenharmony_ci			msleep(20);
31208c2ecf20Sopenharmony_ci			return 0;
31218c2ecf20Sopenharmony_ci		case GAMEPORT_MODE_RAW:
31228c2ecf20Sopenharmony_ci			outb(0, TRID_REG(chip, GAMEPORT_GCR));
31238c2ecf20Sopenharmony_ci			return 0;
31248c2ecf20Sopenharmony_ci		default:
31258c2ecf20Sopenharmony_ci			return -1;
31268c2ecf20Sopenharmony_ci	}
31278c2ecf20Sopenharmony_ci}
31288c2ecf20Sopenharmony_ci
31298c2ecf20Sopenharmony_ciint snd_trident_create_gameport(struct snd_trident *chip)
31308c2ecf20Sopenharmony_ci{
31318c2ecf20Sopenharmony_ci	struct gameport *gp;
31328c2ecf20Sopenharmony_ci
31338c2ecf20Sopenharmony_ci	chip->gameport = gp = gameport_allocate_port();
31348c2ecf20Sopenharmony_ci	if (!gp) {
31358c2ecf20Sopenharmony_ci		dev_err(chip->card->dev,
31368c2ecf20Sopenharmony_ci			"cannot allocate memory for gameport\n");
31378c2ecf20Sopenharmony_ci		return -ENOMEM;
31388c2ecf20Sopenharmony_ci	}
31398c2ecf20Sopenharmony_ci
31408c2ecf20Sopenharmony_ci	gameport_set_name(gp, "Trident 4DWave");
31418c2ecf20Sopenharmony_ci	gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
31428c2ecf20Sopenharmony_ci	gameport_set_dev_parent(gp, &chip->pci->dev);
31438c2ecf20Sopenharmony_ci
31448c2ecf20Sopenharmony_ci	gameport_set_port_data(gp, chip);
31458c2ecf20Sopenharmony_ci	gp->fuzz = 64;
31468c2ecf20Sopenharmony_ci	gp->read = snd_trident_gameport_read;
31478c2ecf20Sopenharmony_ci	gp->trigger = snd_trident_gameport_trigger;
31488c2ecf20Sopenharmony_ci	gp->cooked_read = snd_trident_gameport_cooked_read;
31498c2ecf20Sopenharmony_ci	gp->open = snd_trident_gameport_open;
31508c2ecf20Sopenharmony_ci
31518c2ecf20Sopenharmony_ci	gameport_register_port(gp);
31528c2ecf20Sopenharmony_ci
31538c2ecf20Sopenharmony_ci	return 0;
31548c2ecf20Sopenharmony_ci}
31558c2ecf20Sopenharmony_ci
31568c2ecf20Sopenharmony_cistatic inline void snd_trident_free_gameport(struct snd_trident *chip)
31578c2ecf20Sopenharmony_ci{
31588c2ecf20Sopenharmony_ci	if (chip->gameport) {
31598c2ecf20Sopenharmony_ci		gameport_unregister_port(chip->gameport);
31608c2ecf20Sopenharmony_ci		chip->gameport = NULL;
31618c2ecf20Sopenharmony_ci	}
31628c2ecf20Sopenharmony_ci}
31638c2ecf20Sopenharmony_ci#else
31648c2ecf20Sopenharmony_ciint snd_trident_create_gameport(struct snd_trident *chip) { return -ENOSYS; }
31658c2ecf20Sopenharmony_cistatic inline void snd_trident_free_gameport(struct snd_trident *chip) { }
31668c2ecf20Sopenharmony_ci#endif /* CONFIG_GAMEPORT */
31678c2ecf20Sopenharmony_ci
31688c2ecf20Sopenharmony_ci/*
31698c2ecf20Sopenharmony_ci * delay for 1 tick
31708c2ecf20Sopenharmony_ci */
31718c2ecf20Sopenharmony_cistatic inline void do_delay(struct snd_trident *chip)
31728c2ecf20Sopenharmony_ci{
31738c2ecf20Sopenharmony_ci	schedule_timeout_uninterruptible(1);
31748c2ecf20Sopenharmony_ci}
31758c2ecf20Sopenharmony_ci
31768c2ecf20Sopenharmony_ci/*
31778c2ecf20Sopenharmony_ci *  SiS reset routine
31788c2ecf20Sopenharmony_ci */
31798c2ecf20Sopenharmony_ci
31808c2ecf20Sopenharmony_cistatic int snd_trident_sis_reset(struct snd_trident *trident)
31818c2ecf20Sopenharmony_ci{
31828c2ecf20Sopenharmony_ci	unsigned long end_time;
31838c2ecf20Sopenharmony_ci	unsigned int i;
31848c2ecf20Sopenharmony_ci	int r;
31858c2ecf20Sopenharmony_ci
31868c2ecf20Sopenharmony_ci	r = trident->in_suspend ? 0 : 2;	/* count of retries */
31878c2ecf20Sopenharmony_ci      __si7018_retry:
31888c2ecf20Sopenharmony_ci	pci_write_config_byte(trident->pci, 0x46, 0x04);	/* SOFTWARE RESET */
31898c2ecf20Sopenharmony_ci	udelay(100);
31908c2ecf20Sopenharmony_ci	pci_write_config_byte(trident->pci, 0x46, 0x00);
31918c2ecf20Sopenharmony_ci	udelay(100);
31928c2ecf20Sopenharmony_ci	/* disable AC97 GPIO interrupt */
31938c2ecf20Sopenharmony_ci	outb(0x00, TRID_REG(trident, SI_AC97_GPIO));
31948c2ecf20Sopenharmony_ci	/* initialize serial interface, force cold reset */
31958c2ecf20Sopenharmony_ci	i = PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID|COLD_RESET;
31968c2ecf20Sopenharmony_ci	outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
31978c2ecf20Sopenharmony_ci	udelay(1000);
31988c2ecf20Sopenharmony_ci	/* remove cold reset */
31998c2ecf20Sopenharmony_ci	i &= ~COLD_RESET;
32008c2ecf20Sopenharmony_ci	outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
32018c2ecf20Sopenharmony_ci	udelay(2000);
32028c2ecf20Sopenharmony_ci	/* wait, until the codec is ready */
32038c2ecf20Sopenharmony_ci	end_time = (jiffies + (HZ * 3) / 4) + 1;
32048c2ecf20Sopenharmony_ci	do {
32058c2ecf20Sopenharmony_ci		if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0)
32068c2ecf20Sopenharmony_ci			goto __si7018_ok;
32078c2ecf20Sopenharmony_ci		do_delay(trident);
32088c2ecf20Sopenharmony_ci	} while (time_after_eq(end_time, jiffies));
32098c2ecf20Sopenharmony_ci	dev_err(trident->card->dev, "AC'97 codec ready error [0x%x]\n",
32108c2ecf20Sopenharmony_ci		inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)));
32118c2ecf20Sopenharmony_ci	if (r-- > 0) {
32128c2ecf20Sopenharmony_ci		end_time = jiffies + HZ;
32138c2ecf20Sopenharmony_ci		do {
32148c2ecf20Sopenharmony_ci			do_delay(trident);
32158c2ecf20Sopenharmony_ci		} while (time_after_eq(end_time, jiffies));
32168c2ecf20Sopenharmony_ci		goto __si7018_retry;
32178c2ecf20Sopenharmony_ci	}
32188c2ecf20Sopenharmony_ci      __si7018_ok:
32198c2ecf20Sopenharmony_ci	/* wait for the second codec */
32208c2ecf20Sopenharmony_ci	do {
32218c2ecf20Sopenharmony_ci		if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_SECONDARY_READY) != 0)
32228c2ecf20Sopenharmony_ci			break;
32238c2ecf20Sopenharmony_ci		do_delay(trident);
32248c2ecf20Sopenharmony_ci	} while (time_after_eq(end_time, jiffies));
32258c2ecf20Sopenharmony_ci	/* enable 64 channel mode */
32268c2ecf20Sopenharmony_ci	outl(BANK_B_EN, TRID_REG(trident, T4D_LFO_GC_CIR));
32278c2ecf20Sopenharmony_ci	return 0;
32288c2ecf20Sopenharmony_ci}
32298c2ecf20Sopenharmony_ci
32308c2ecf20Sopenharmony_ci/*
32318c2ecf20Sopenharmony_ci *  /proc interface
32328c2ecf20Sopenharmony_ci */
32338c2ecf20Sopenharmony_ci
32348c2ecf20Sopenharmony_cistatic void snd_trident_proc_read(struct snd_info_entry *entry,
32358c2ecf20Sopenharmony_ci				  struct snd_info_buffer *buffer)
32368c2ecf20Sopenharmony_ci{
32378c2ecf20Sopenharmony_ci	struct snd_trident *trident = entry->private_data;
32388c2ecf20Sopenharmony_ci	char *s;
32398c2ecf20Sopenharmony_ci
32408c2ecf20Sopenharmony_ci	switch (trident->device) {
32418c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_SI7018:
32428c2ecf20Sopenharmony_ci		s = "SiS 7018 Audio";
32438c2ecf20Sopenharmony_ci		break;
32448c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_DX:
32458c2ecf20Sopenharmony_ci		s = "Trident 4DWave PCI DX";
32468c2ecf20Sopenharmony_ci		break;
32478c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_NX:
32488c2ecf20Sopenharmony_ci		s = "Trident 4DWave PCI NX";
32498c2ecf20Sopenharmony_ci		break;
32508c2ecf20Sopenharmony_ci	default:
32518c2ecf20Sopenharmony_ci		s = "???";
32528c2ecf20Sopenharmony_ci	}
32538c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "%s\n\n", s);
32548c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "Spurious IRQs    : %d\n", trident->spurious_irq_count);
32558c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "Spurious IRQ dlta: %d\n", trident->spurious_irq_max_delta);
32568c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018)
32578c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", trident->spdif_ctrl == 0x28 ? "on" : "off");
32588c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_NX) {
32598c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "Rear Speakers    : %s\n", trident->ac97_ctrl & 0x00000010 ? "on" : "off");
32608c2ecf20Sopenharmony_ci		if (trident->tlb.entries) {
32618c2ecf20Sopenharmony_ci			snd_iprintf(buffer,"\nVirtual Memory\n");
32628c2ecf20Sopenharmony_ci			snd_iprintf(buffer, "Memory Maximum : %d\n", trident->tlb.memhdr->size);
32638c2ecf20Sopenharmony_ci			snd_iprintf(buffer, "Memory Used    : %d\n", trident->tlb.memhdr->used);
32648c2ecf20Sopenharmony_ci			snd_iprintf(buffer, "Memory Free    : %d\n", snd_util_mem_avail(trident->tlb.memhdr));
32658c2ecf20Sopenharmony_ci		}
32668c2ecf20Sopenharmony_ci	}
32678c2ecf20Sopenharmony_ci}
32688c2ecf20Sopenharmony_ci
32698c2ecf20Sopenharmony_cistatic void snd_trident_proc_init(struct snd_trident *trident)
32708c2ecf20Sopenharmony_ci{
32718c2ecf20Sopenharmony_ci	const char *s = "trident";
32728c2ecf20Sopenharmony_ci
32738c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_SI7018)
32748c2ecf20Sopenharmony_ci		s = "sis7018";
32758c2ecf20Sopenharmony_ci	snd_card_ro_proc_new(trident->card, s, trident, snd_trident_proc_read);
32768c2ecf20Sopenharmony_ci}
32778c2ecf20Sopenharmony_ci
32788c2ecf20Sopenharmony_cistatic int snd_trident_dev_free(struct snd_device *device)
32798c2ecf20Sopenharmony_ci{
32808c2ecf20Sopenharmony_ci	struct snd_trident *trident = device->device_data;
32818c2ecf20Sopenharmony_ci	return snd_trident_free(trident);
32828c2ecf20Sopenharmony_ci}
32838c2ecf20Sopenharmony_ci
32848c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
32858c2ecf20Sopenharmony_ci   snd_trident_tlb_alloc
32868c2ecf20Sopenharmony_ci
32878c2ecf20Sopenharmony_ci   Description: Allocate and set up the TLB page table on 4D NX.
32888c2ecf20Sopenharmony_ci		Each entry has 4 bytes (physical PCI address).
32898c2ecf20Sopenharmony_ci
32908c2ecf20Sopenharmony_ci   Parameters:  trident - pointer to target device class for 4DWave.
32918c2ecf20Sopenharmony_ci
32928c2ecf20Sopenharmony_ci   Returns:     0 or negative error code
32938c2ecf20Sopenharmony_ci
32948c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
32958c2ecf20Sopenharmony_ci
32968c2ecf20Sopenharmony_cistatic int snd_trident_tlb_alloc(struct snd_trident *trident)
32978c2ecf20Sopenharmony_ci{
32988c2ecf20Sopenharmony_ci	int i;
32998c2ecf20Sopenharmony_ci
33008c2ecf20Sopenharmony_ci	/* TLB array must be aligned to 16kB !!! so we allocate
33018c2ecf20Sopenharmony_ci	   32kB region and correct offset when necessary */
33028c2ecf20Sopenharmony_ci
33038c2ecf20Sopenharmony_ci	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &trident->pci->dev,
33048c2ecf20Sopenharmony_ci				2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer) < 0) {
33058c2ecf20Sopenharmony_ci		dev_err(trident->card->dev, "unable to allocate TLB buffer\n");
33068c2ecf20Sopenharmony_ci		return -ENOMEM;
33078c2ecf20Sopenharmony_ci	}
33088c2ecf20Sopenharmony_ci	trident->tlb.entries = (__le32 *)ALIGN((unsigned long)trident->tlb.buffer.area, SNDRV_TRIDENT_MAX_PAGES * 4);
33098c2ecf20Sopenharmony_ci	trident->tlb.entries_dmaaddr = ALIGN(trident->tlb.buffer.addr, SNDRV_TRIDENT_MAX_PAGES * 4);
33108c2ecf20Sopenharmony_ci	/* allocate shadow TLB page table (virtual addresses) */
33118c2ecf20Sopenharmony_ci	trident->tlb.shadow_entries =
33128c2ecf20Sopenharmony_ci		vmalloc(array_size(SNDRV_TRIDENT_MAX_PAGES,
33138c2ecf20Sopenharmony_ci				   sizeof(unsigned long)));
33148c2ecf20Sopenharmony_ci	if (!trident->tlb.shadow_entries)
33158c2ecf20Sopenharmony_ci		return -ENOMEM;
33168c2ecf20Sopenharmony_ci
33178c2ecf20Sopenharmony_ci	/* allocate and setup silent page and initialise TLB entries */
33188c2ecf20Sopenharmony_ci	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &trident->pci->dev,
33198c2ecf20Sopenharmony_ci				SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page) < 0) {
33208c2ecf20Sopenharmony_ci		dev_err(trident->card->dev, "unable to allocate silent page\n");
33218c2ecf20Sopenharmony_ci		return -ENOMEM;
33228c2ecf20Sopenharmony_ci	}
33238c2ecf20Sopenharmony_ci	memset(trident->tlb.silent_page.area, 0, SNDRV_TRIDENT_PAGE_SIZE);
33248c2ecf20Sopenharmony_ci	for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) {
33258c2ecf20Sopenharmony_ci		trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page.addr & ~(SNDRV_TRIDENT_PAGE_SIZE-1));
33268c2ecf20Sopenharmony_ci		trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page.area;
33278c2ecf20Sopenharmony_ci	}
33288c2ecf20Sopenharmony_ci
33298c2ecf20Sopenharmony_ci	/* use emu memory block manager code to manage tlb page allocation */
33308c2ecf20Sopenharmony_ci	trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES);
33318c2ecf20Sopenharmony_ci	if (trident->tlb.memhdr == NULL)
33328c2ecf20Sopenharmony_ci		return -ENOMEM;
33338c2ecf20Sopenharmony_ci
33348c2ecf20Sopenharmony_ci	trident->tlb.memhdr->block_extra_size = sizeof(struct snd_trident_memblk_arg);
33358c2ecf20Sopenharmony_ci	return 0;
33368c2ecf20Sopenharmony_ci}
33378c2ecf20Sopenharmony_ci
33388c2ecf20Sopenharmony_ci/*
33398c2ecf20Sopenharmony_ci * initialize 4D DX chip
33408c2ecf20Sopenharmony_ci */
33418c2ecf20Sopenharmony_ci
33428c2ecf20Sopenharmony_cistatic void snd_trident_stop_all_voices(struct snd_trident *trident)
33438c2ecf20Sopenharmony_ci{
33448c2ecf20Sopenharmony_ci	outl(0xffffffff, TRID_REG(trident, T4D_STOP_A));
33458c2ecf20Sopenharmony_ci	outl(0xffffffff, TRID_REG(trident, T4D_STOP_B));
33468c2ecf20Sopenharmony_ci	outl(0, TRID_REG(trident, T4D_AINTEN_A));
33478c2ecf20Sopenharmony_ci	outl(0, TRID_REG(trident, T4D_AINTEN_B));
33488c2ecf20Sopenharmony_ci}
33498c2ecf20Sopenharmony_ci
33508c2ecf20Sopenharmony_cistatic int snd_trident_4d_dx_init(struct snd_trident *trident)
33518c2ecf20Sopenharmony_ci{
33528c2ecf20Sopenharmony_ci	struct pci_dev *pci = trident->pci;
33538c2ecf20Sopenharmony_ci	unsigned long end_time;
33548c2ecf20Sopenharmony_ci
33558c2ecf20Sopenharmony_ci	/* reset the legacy configuration and whole audio/wavetable block */
33568c2ecf20Sopenharmony_ci	pci_write_config_dword(pci, 0x40, 0);	/* DDMA */
33578c2ecf20Sopenharmony_ci	pci_write_config_byte(pci, 0x44, 0);	/* ports */
33588c2ecf20Sopenharmony_ci	pci_write_config_byte(pci, 0x45, 0);	/* Legacy DMA */
33598c2ecf20Sopenharmony_ci	pci_write_config_byte(pci, 0x46, 4); /* reset */
33608c2ecf20Sopenharmony_ci	udelay(100);
33618c2ecf20Sopenharmony_ci	pci_write_config_byte(pci, 0x46, 0); /* release reset */
33628c2ecf20Sopenharmony_ci	udelay(100);
33638c2ecf20Sopenharmony_ci
33648c2ecf20Sopenharmony_ci	/* warm reset of the AC'97 codec */
33658c2ecf20Sopenharmony_ci	outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
33668c2ecf20Sopenharmony_ci	udelay(100);
33678c2ecf20Sopenharmony_ci	outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
33688c2ecf20Sopenharmony_ci	/* DAC on, disable SB IRQ and try to force ADC valid signal */
33698c2ecf20Sopenharmony_ci	trident->ac97_ctrl = 0x0000004a;
33708c2ecf20Sopenharmony_ci	outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
33718c2ecf20Sopenharmony_ci	/* wait, until the codec is ready */
33728c2ecf20Sopenharmony_ci	end_time = (jiffies + (HZ * 3) / 4) + 1;
33738c2ecf20Sopenharmony_ci	do {
33748c2ecf20Sopenharmony_ci		if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0)
33758c2ecf20Sopenharmony_ci			goto __dx_ok;
33768c2ecf20Sopenharmony_ci		do_delay(trident);
33778c2ecf20Sopenharmony_ci	} while (time_after_eq(end_time, jiffies));
33788c2ecf20Sopenharmony_ci	dev_err(trident->card->dev, "AC'97 codec ready error\n");
33798c2ecf20Sopenharmony_ci	return -EIO;
33808c2ecf20Sopenharmony_ci
33818c2ecf20Sopenharmony_ci __dx_ok:
33828c2ecf20Sopenharmony_ci	snd_trident_stop_all_voices(trident);
33838c2ecf20Sopenharmony_ci
33848c2ecf20Sopenharmony_ci	return 0;
33858c2ecf20Sopenharmony_ci}
33868c2ecf20Sopenharmony_ci
33878c2ecf20Sopenharmony_ci/*
33888c2ecf20Sopenharmony_ci * initialize 4D NX chip
33898c2ecf20Sopenharmony_ci */
33908c2ecf20Sopenharmony_cistatic int snd_trident_4d_nx_init(struct snd_trident *trident)
33918c2ecf20Sopenharmony_ci{
33928c2ecf20Sopenharmony_ci	struct pci_dev *pci = trident->pci;
33938c2ecf20Sopenharmony_ci	unsigned long end_time;
33948c2ecf20Sopenharmony_ci
33958c2ecf20Sopenharmony_ci	/* reset the legacy configuration and whole audio/wavetable block */
33968c2ecf20Sopenharmony_ci	pci_write_config_dword(pci, 0x40, 0);	/* DDMA */
33978c2ecf20Sopenharmony_ci	pci_write_config_byte(pci, 0x44, 0);	/* ports */
33988c2ecf20Sopenharmony_ci	pci_write_config_byte(pci, 0x45, 0);	/* Legacy DMA */
33998c2ecf20Sopenharmony_ci
34008c2ecf20Sopenharmony_ci	pci_write_config_byte(pci, 0x46, 1); /* reset */
34018c2ecf20Sopenharmony_ci	udelay(100);
34028c2ecf20Sopenharmony_ci	pci_write_config_byte(pci, 0x46, 0); /* release reset */
34038c2ecf20Sopenharmony_ci	udelay(100);
34048c2ecf20Sopenharmony_ci
34058c2ecf20Sopenharmony_ci	/* warm reset of the AC'97 codec */
34068c2ecf20Sopenharmony_ci	outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
34078c2ecf20Sopenharmony_ci	udelay(100);
34088c2ecf20Sopenharmony_ci	outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
34098c2ecf20Sopenharmony_ci	/* wait, until the codec is ready */
34108c2ecf20Sopenharmony_ci	end_time = (jiffies + (HZ * 3) / 4) + 1;
34118c2ecf20Sopenharmony_ci	do {
34128c2ecf20Sopenharmony_ci		if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0)
34138c2ecf20Sopenharmony_ci			goto __nx_ok;
34148c2ecf20Sopenharmony_ci		do_delay(trident);
34158c2ecf20Sopenharmony_ci	} while (time_after_eq(end_time, jiffies));
34168c2ecf20Sopenharmony_ci	dev_err(trident->card->dev, "AC'97 codec ready error [0x%x]\n",
34178c2ecf20Sopenharmony_ci		inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)));
34188c2ecf20Sopenharmony_ci	return -EIO;
34198c2ecf20Sopenharmony_ci
34208c2ecf20Sopenharmony_ci __nx_ok:
34218c2ecf20Sopenharmony_ci	/* DAC on */
34228c2ecf20Sopenharmony_ci	trident->ac97_ctrl = 0x00000002;
34238c2ecf20Sopenharmony_ci	outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
34248c2ecf20Sopenharmony_ci	/* disable SB IRQ */
34258c2ecf20Sopenharmony_ci	outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT));
34268c2ecf20Sopenharmony_ci
34278c2ecf20Sopenharmony_ci	snd_trident_stop_all_voices(trident);
34288c2ecf20Sopenharmony_ci
34298c2ecf20Sopenharmony_ci	if (trident->tlb.entries != NULL) {
34308c2ecf20Sopenharmony_ci		unsigned int i;
34318c2ecf20Sopenharmony_ci		/* enable virtual addressing via TLB */
34328c2ecf20Sopenharmony_ci		i = trident->tlb.entries_dmaaddr;
34338c2ecf20Sopenharmony_ci		i |= 0x00000001;
34348c2ecf20Sopenharmony_ci		outl(i, TRID_REG(trident, NX_TLBC));
34358c2ecf20Sopenharmony_ci	} else {
34368c2ecf20Sopenharmony_ci		outl(0, TRID_REG(trident, NX_TLBC));
34378c2ecf20Sopenharmony_ci	}
34388c2ecf20Sopenharmony_ci	/* initialize S/PDIF */
34398c2ecf20Sopenharmony_ci	outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
34408c2ecf20Sopenharmony_ci	outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
34418c2ecf20Sopenharmony_ci
34428c2ecf20Sopenharmony_ci	return 0;
34438c2ecf20Sopenharmony_ci}
34448c2ecf20Sopenharmony_ci
34458c2ecf20Sopenharmony_ci/*
34468c2ecf20Sopenharmony_ci * initialize sis7018 chip
34478c2ecf20Sopenharmony_ci */
34488c2ecf20Sopenharmony_cistatic int snd_trident_sis_init(struct snd_trident *trident)
34498c2ecf20Sopenharmony_ci{
34508c2ecf20Sopenharmony_ci	int err;
34518c2ecf20Sopenharmony_ci
34528c2ecf20Sopenharmony_ci	if ((err = snd_trident_sis_reset(trident)) < 0)
34538c2ecf20Sopenharmony_ci		return err;
34548c2ecf20Sopenharmony_ci
34558c2ecf20Sopenharmony_ci	snd_trident_stop_all_voices(trident);
34568c2ecf20Sopenharmony_ci
34578c2ecf20Sopenharmony_ci	/* initialize S/PDIF */
34588c2ecf20Sopenharmony_ci	outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
34598c2ecf20Sopenharmony_ci
34608c2ecf20Sopenharmony_ci	return 0;
34618c2ecf20Sopenharmony_ci}
34628c2ecf20Sopenharmony_ci
34638c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
34648c2ecf20Sopenharmony_ci   snd_trident_create
34658c2ecf20Sopenharmony_ci
34668c2ecf20Sopenharmony_ci   Description: This routine will create the device specific class for
34678c2ecf20Sopenharmony_ci                the 4DWave card. It will also perform basic initialization.
34688c2ecf20Sopenharmony_ci
34698c2ecf20Sopenharmony_ci   Parameters:  card  - which card to create
34708c2ecf20Sopenharmony_ci                pci   - interface to PCI bus resource info
34718c2ecf20Sopenharmony_ci                dma1ptr - playback dma buffer
34728c2ecf20Sopenharmony_ci                dma2ptr - capture dma buffer
34738c2ecf20Sopenharmony_ci                irqptr  -  interrupt resource info
34748c2ecf20Sopenharmony_ci
34758c2ecf20Sopenharmony_ci   Returns:     4DWave device class private data
34768c2ecf20Sopenharmony_ci
34778c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
34788c2ecf20Sopenharmony_ci
34798c2ecf20Sopenharmony_ciint snd_trident_create(struct snd_card *card,
34808c2ecf20Sopenharmony_ci		       struct pci_dev *pci,
34818c2ecf20Sopenharmony_ci		       int pcm_streams,
34828c2ecf20Sopenharmony_ci		       int pcm_spdif_device,
34838c2ecf20Sopenharmony_ci		       int max_wavetable_size,
34848c2ecf20Sopenharmony_ci		       struct snd_trident ** rtrident)
34858c2ecf20Sopenharmony_ci{
34868c2ecf20Sopenharmony_ci	struct snd_trident *trident;
34878c2ecf20Sopenharmony_ci	int i, err;
34888c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice;
34898c2ecf20Sopenharmony_ci	struct snd_trident_pcm_mixer *tmix;
34908c2ecf20Sopenharmony_ci	static const struct snd_device_ops ops = {
34918c2ecf20Sopenharmony_ci		.dev_free =	snd_trident_dev_free,
34928c2ecf20Sopenharmony_ci	};
34938c2ecf20Sopenharmony_ci
34948c2ecf20Sopenharmony_ci	*rtrident = NULL;
34958c2ecf20Sopenharmony_ci
34968c2ecf20Sopenharmony_ci	/* enable PCI device */
34978c2ecf20Sopenharmony_ci	if ((err = pci_enable_device(pci)) < 0)
34988c2ecf20Sopenharmony_ci		return err;
34998c2ecf20Sopenharmony_ci	/* check, if we can restrict PCI DMA transfers to 30 bits */
35008c2ecf20Sopenharmony_ci	if (dma_set_mask(&pci->dev, DMA_BIT_MASK(30)) < 0 ||
35018c2ecf20Sopenharmony_ci	    dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(30)) < 0) {
35028c2ecf20Sopenharmony_ci		dev_err(card->dev,
35038c2ecf20Sopenharmony_ci			"architecture does not support 30bit PCI busmaster DMA\n");
35048c2ecf20Sopenharmony_ci		pci_disable_device(pci);
35058c2ecf20Sopenharmony_ci		return -ENXIO;
35068c2ecf20Sopenharmony_ci	}
35078c2ecf20Sopenharmony_ci
35088c2ecf20Sopenharmony_ci	trident = kzalloc(sizeof(*trident), GFP_KERNEL);
35098c2ecf20Sopenharmony_ci	if (trident == NULL) {
35108c2ecf20Sopenharmony_ci		pci_disable_device(pci);
35118c2ecf20Sopenharmony_ci		return -ENOMEM;
35128c2ecf20Sopenharmony_ci	}
35138c2ecf20Sopenharmony_ci	trident->device = (pci->vendor << 16) | pci->device;
35148c2ecf20Sopenharmony_ci	trident->card = card;
35158c2ecf20Sopenharmony_ci	trident->pci = pci;
35168c2ecf20Sopenharmony_ci	spin_lock_init(&trident->reg_lock);
35178c2ecf20Sopenharmony_ci	spin_lock_init(&trident->event_lock);
35188c2ecf20Sopenharmony_ci	spin_lock_init(&trident->voice_alloc);
35198c2ecf20Sopenharmony_ci	if (pcm_streams < 1)
35208c2ecf20Sopenharmony_ci		pcm_streams = 1;
35218c2ecf20Sopenharmony_ci	if (pcm_streams > 32)
35228c2ecf20Sopenharmony_ci		pcm_streams = 32;
35238c2ecf20Sopenharmony_ci	trident->ChanPCM = pcm_streams;
35248c2ecf20Sopenharmony_ci	if (max_wavetable_size < 0 )
35258c2ecf20Sopenharmony_ci		max_wavetable_size = 0;
35268c2ecf20Sopenharmony_ci	trident->synth.max_size = max_wavetable_size * 1024;
35278c2ecf20Sopenharmony_ci	trident->irq = -1;
35288c2ecf20Sopenharmony_ci
35298c2ecf20Sopenharmony_ci	trident->midi_port = TRID_REG(trident, T4D_MPU401_BASE);
35308c2ecf20Sopenharmony_ci	pci_set_master(pci);
35318c2ecf20Sopenharmony_ci
35328c2ecf20Sopenharmony_ci	if ((err = pci_request_regions(pci, "Trident Audio")) < 0) {
35338c2ecf20Sopenharmony_ci		kfree(trident);
35348c2ecf20Sopenharmony_ci		pci_disable_device(pci);
35358c2ecf20Sopenharmony_ci		return err;
35368c2ecf20Sopenharmony_ci	}
35378c2ecf20Sopenharmony_ci	trident->port = pci_resource_start(pci, 0);
35388c2ecf20Sopenharmony_ci
35398c2ecf20Sopenharmony_ci	if (request_irq(pci->irq, snd_trident_interrupt, IRQF_SHARED,
35408c2ecf20Sopenharmony_ci			KBUILD_MODNAME, trident)) {
35418c2ecf20Sopenharmony_ci		dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
35428c2ecf20Sopenharmony_ci		snd_trident_free(trident);
35438c2ecf20Sopenharmony_ci		return -EBUSY;
35448c2ecf20Sopenharmony_ci	}
35458c2ecf20Sopenharmony_ci	trident->irq = pci->irq;
35468c2ecf20Sopenharmony_ci	card->sync_irq = trident->irq;
35478c2ecf20Sopenharmony_ci
35488c2ecf20Sopenharmony_ci	/* allocate 16k-aligned TLB for NX cards */
35498c2ecf20Sopenharmony_ci	trident->tlb.entries = NULL;
35508c2ecf20Sopenharmony_ci	trident->tlb.buffer.area = NULL;
35518c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_NX) {
35528c2ecf20Sopenharmony_ci		if ((err = snd_trident_tlb_alloc(trident)) < 0) {
35538c2ecf20Sopenharmony_ci			snd_trident_free(trident);
35548c2ecf20Sopenharmony_ci			return err;
35558c2ecf20Sopenharmony_ci		}
35568c2ecf20Sopenharmony_ci	}
35578c2ecf20Sopenharmony_ci
35588c2ecf20Sopenharmony_ci	trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF;
35598c2ecf20Sopenharmony_ci
35608c2ecf20Sopenharmony_ci	/* initialize chip */
35618c2ecf20Sopenharmony_ci	switch (trident->device) {
35628c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_DX:
35638c2ecf20Sopenharmony_ci		err = snd_trident_4d_dx_init(trident);
35648c2ecf20Sopenharmony_ci		break;
35658c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_NX:
35668c2ecf20Sopenharmony_ci		err = snd_trident_4d_nx_init(trident);
35678c2ecf20Sopenharmony_ci		break;
35688c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_SI7018:
35698c2ecf20Sopenharmony_ci		err = snd_trident_sis_init(trident);
35708c2ecf20Sopenharmony_ci		break;
35718c2ecf20Sopenharmony_ci	default:
35728c2ecf20Sopenharmony_ci		snd_BUG();
35738c2ecf20Sopenharmony_ci		break;
35748c2ecf20Sopenharmony_ci	}
35758c2ecf20Sopenharmony_ci	if (err < 0) {
35768c2ecf20Sopenharmony_ci		snd_trident_free(trident);
35778c2ecf20Sopenharmony_ci		return err;
35788c2ecf20Sopenharmony_ci	}
35798c2ecf20Sopenharmony_ci
35808c2ecf20Sopenharmony_ci	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, trident, &ops)) < 0) {
35818c2ecf20Sopenharmony_ci		snd_trident_free(trident);
35828c2ecf20Sopenharmony_ci		return err;
35838c2ecf20Sopenharmony_ci	}
35848c2ecf20Sopenharmony_ci
35858c2ecf20Sopenharmony_ci	if ((err = snd_trident_mixer(trident, pcm_spdif_device)) < 0)
35868c2ecf20Sopenharmony_ci		return err;
35878c2ecf20Sopenharmony_ci
35888c2ecf20Sopenharmony_ci	/* initialise synth voices */
35898c2ecf20Sopenharmony_ci	for (i = 0; i < 64; i++) {
35908c2ecf20Sopenharmony_ci		voice = &trident->synth.voices[i];
35918c2ecf20Sopenharmony_ci		voice->number = i;
35928c2ecf20Sopenharmony_ci		voice->trident = trident;
35938c2ecf20Sopenharmony_ci	}
35948c2ecf20Sopenharmony_ci	/* initialize pcm mixer entries */
35958c2ecf20Sopenharmony_ci	for (i = 0; i < 32; i++) {
35968c2ecf20Sopenharmony_ci		tmix = &trident->pcm_mixer[i];
35978c2ecf20Sopenharmony_ci		tmix->vol = T4D_DEFAULT_PCM_VOL;
35988c2ecf20Sopenharmony_ci		tmix->pan = T4D_DEFAULT_PCM_PAN;
35998c2ecf20Sopenharmony_ci		tmix->rvol = T4D_DEFAULT_PCM_RVOL;
36008c2ecf20Sopenharmony_ci		tmix->cvol = T4D_DEFAULT_PCM_CVOL;
36018c2ecf20Sopenharmony_ci	}
36028c2ecf20Sopenharmony_ci
36038c2ecf20Sopenharmony_ci	snd_trident_enable_eso(trident);
36048c2ecf20Sopenharmony_ci
36058c2ecf20Sopenharmony_ci	snd_trident_proc_init(trident);
36068c2ecf20Sopenharmony_ci	*rtrident = trident;
36078c2ecf20Sopenharmony_ci	return 0;
36088c2ecf20Sopenharmony_ci}
36098c2ecf20Sopenharmony_ci
36108c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
36118c2ecf20Sopenharmony_ci   snd_trident_free
36128c2ecf20Sopenharmony_ci
36138c2ecf20Sopenharmony_ci   Description: This routine will free the device specific class for
36148c2ecf20Sopenharmony_ci                the 4DWave card.
36158c2ecf20Sopenharmony_ci
36168c2ecf20Sopenharmony_ci   Parameters:  trident  - device specific private data for 4DWave card
36178c2ecf20Sopenharmony_ci
36188c2ecf20Sopenharmony_ci   Returns:     None.
36198c2ecf20Sopenharmony_ci
36208c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
36218c2ecf20Sopenharmony_ci
36228c2ecf20Sopenharmony_cistatic int snd_trident_free(struct snd_trident *trident)
36238c2ecf20Sopenharmony_ci{
36248c2ecf20Sopenharmony_ci	snd_trident_free_gameport(trident);
36258c2ecf20Sopenharmony_ci	snd_trident_disable_eso(trident);
36268c2ecf20Sopenharmony_ci	// Disable S/PDIF out
36278c2ecf20Sopenharmony_ci	if (trident->device == TRIDENT_DEVICE_ID_NX)
36288c2ecf20Sopenharmony_ci		outb(0x00, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
36298c2ecf20Sopenharmony_ci	else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
36308c2ecf20Sopenharmony_ci		outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
36318c2ecf20Sopenharmony_ci	}
36328c2ecf20Sopenharmony_ci	if (trident->irq >= 0)
36338c2ecf20Sopenharmony_ci		free_irq(trident->irq, trident);
36348c2ecf20Sopenharmony_ci	if (trident->tlb.buffer.area) {
36358c2ecf20Sopenharmony_ci		outl(0, TRID_REG(trident, NX_TLBC));
36368c2ecf20Sopenharmony_ci		snd_util_memhdr_free(trident->tlb.memhdr);
36378c2ecf20Sopenharmony_ci		if (trident->tlb.silent_page.area)
36388c2ecf20Sopenharmony_ci			snd_dma_free_pages(&trident->tlb.silent_page);
36398c2ecf20Sopenharmony_ci		vfree(trident->tlb.shadow_entries);
36408c2ecf20Sopenharmony_ci		snd_dma_free_pages(&trident->tlb.buffer);
36418c2ecf20Sopenharmony_ci	}
36428c2ecf20Sopenharmony_ci	pci_release_regions(trident->pci);
36438c2ecf20Sopenharmony_ci	pci_disable_device(trident->pci);
36448c2ecf20Sopenharmony_ci	kfree(trident);
36458c2ecf20Sopenharmony_ci	return 0;
36468c2ecf20Sopenharmony_ci}
36478c2ecf20Sopenharmony_ci
36488c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------
36498c2ecf20Sopenharmony_ci   snd_trident_interrupt
36508c2ecf20Sopenharmony_ci
36518c2ecf20Sopenharmony_ci   Description: ISR for Trident 4DWave device
36528c2ecf20Sopenharmony_ci
36538c2ecf20Sopenharmony_ci   Parameters:  trident  - device specific private data for 4DWave card
36548c2ecf20Sopenharmony_ci
36558c2ecf20Sopenharmony_ci   Problems:    It seems that Trident chips generates interrupts more than
36568c2ecf20Sopenharmony_ci                one time in special cases. The spurious interrupts are
36578c2ecf20Sopenharmony_ci                detected via sample timer (T4D_STIMER) and computing
36588c2ecf20Sopenharmony_ci                corresponding delta value. The limits are detected with
36598c2ecf20Sopenharmony_ci                the method try & fail so it is possible that it won't
36608c2ecf20Sopenharmony_ci                work on all computers. [jaroslav]
36618c2ecf20Sopenharmony_ci
36628c2ecf20Sopenharmony_ci   Returns:     None.
36638c2ecf20Sopenharmony_ci
36648c2ecf20Sopenharmony_ci  ---------------------------------------------------------------------------*/
36658c2ecf20Sopenharmony_ci
36668c2ecf20Sopenharmony_cistatic irqreturn_t snd_trident_interrupt(int irq, void *dev_id)
36678c2ecf20Sopenharmony_ci{
36688c2ecf20Sopenharmony_ci	struct snd_trident *trident = dev_id;
36698c2ecf20Sopenharmony_ci	unsigned int audio_int, chn_int, stimer, channel, mask, tmp;
36708c2ecf20Sopenharmony_ci	int delta;
36718c2ecf20Sopenharmony_ci	struct snd_trident_voice *voice;
36728c2ecf20Sopenharmony_ci
36738c2ecf20Sopenharmony_ci	audio_int = inl(TRID_REG(trident, T4D_MISCINT));
36748c2ecf20Sopenharmony_ci	if ((audio_int & (ADDRESS_IRQ|MPU401_IRQ)) == 0)
36758c2ecf20Sopenharmony_ci		return IRQ_NONE;
36768c2ecf20Sopenharmony_ci	if (audio_int & ADDRESS_IRQ) {
36778c2ecf20Sopenharmony_ci		// get interrupt status for all channels
36788c2ecf20Sopenharmony_ci		spin_lock(&trident->reg_lock);
36798c2ecf20Sopenharmony_ci		stimer = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
36808c2ecf20Sopenharmony_ci		chn_int = inl(TRID_REG(trident, T4D_AINT_A));
36818c2ecf20Sopenharmony_ci		if (chn_int == 0)
36828c2ecf20Sopenharmony_ci			goto __skip1;
36838c2ecf20Sopenharmony_ci		outl(chn_int, TRID_REG(trident, T4D_AINT_A));	/* ack */
36848c2ecf20Sopenharmony_ci	      __skip1:
36858c2ecf20Sopenharmony_ci		chn_int = inl(TRID_REG(trident, T4D_AINT_B));
36868c2ecf20Sopenharmony_ci		if (chn_int == 0)
36878c2ecf20Sopenharmony_ci			goto __skip2;
36888c2ecf20Sopenharmony_ci		for (channel = 63; channel >= 32; channel--) {
36898c2ecf20Sopenharmony_ci			mask = 1 << (channel&0x1f);
36908c2ecf20Sopenharmony_ci			if ((chn_int & mask) == 0)
36918c2ecf20Sopenharmony_ci				continue;
36928c2ecf20Sopenharmony_ci			voice = &trident->synth.voices[channel];
36938c2ecf20Sopenharmony_ci			if (!voice->pcm || voice->substream == NULL) {
36948c2ecf20Sopenharmony_ci				outl(mask, TRID_REG(trident, T4D_STOP_B));
36958c2ecf20Sopenharmony_ci				continue;
36968c2ecf20Sopenharmony_ci			}
36978c2ecf20Sopenharmony_ci			delta = (int)stimer - (int)voice->stimer;
36988c2ecf20Sopenharmony_ci			if (delta < 0)
36998c2ecf20Sopenharmony_ci				delta = -delta;
37008c2ecf20Sopenharmony_ci			if ((unsigned int)delta < voice->spurious_threshold) {
37018c2ecf20Sopenharmony_ci				/* do some statistics here */
37028c2ecf20Sopenharmony_ci				trident->spurious_irq_count++;
37038c2ecf20Sopenharmony_ci				if (trident->spurious_irq_max_delta < (unsigned int)delta)
37048c2ecf20Sopenharmony_ci					trident->spurious_irq_max_delta = delta;
37058c2ecf20Sopenharmony_ci				continue;
37068c2ecf20Sopenharmony_ci			}
37078c2ecf20Sopenharmony_ci			voice->stimer = stimer;
37088c2ecf20Sopenharmony_ci			if (voice->isync) {
37098c2ecf20Sopenharmony_ci				if (!voice->isync3) {
37108c2ecf20Sopenharmony_ci					tmp = inw(TRID_REG(trident, T4D_SBBL_SBCL));
37118c2ecf20Sopenharmony_ci					if (trident->bDMAStart & 0x40)
37128c2ecf20Sopenharmony_ci						tmp >>= 1;
37138c2ecf20Sopenharmony_ci					if (tmp > 0)
37148c2ecf20Sopenharmony_ci						tmp = voice->isync_max - tmp;
37158c2ecf20Sopenharmony_ci				} else {
37168c2ecf20Sopenharmony_ci					tmp = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff;
37178c2ecf20Sopenharmony_ci				}
37188c2ecf20Sopenharmony_ci				if (tmp < voice->isync_mark) {
37198c2ecf20Sopenharmony_ci					if (tmp > 0x10)
37208c2ecf20Sopenharmony_ci						tmp = voice->isync_ESO - 7;
37218c2ecf20Sopenharmony_ci					else
37228c2ecf20Sopenharmony_ci						tmp = voice->isync_ESO + 2;
37238c2ecf20Sopenharmony_ci					/* update ESO for IRQ voice to preserve sync */
37248c2ecf20Sopenharmony_ci					snd_trident_stop_voice(trident, voice->number);
37258c2ecf20Sopenharmony_ci					snd_trident_write_eso_reg(trident, voice, tmp);
37268c2ecf20Sopenharmony_ci					snd_trident_start_voice(trident, voice->number);
37278c2ecf20Sopenharmony_ci				}
37288c2ecf20Sopenharmony_ci			} else if (voice->isync2) {
37298c2ecf20Sopenharmony_ci				voice->isync2 = 0;
37308c2ecf20Sopenharmony_ci				/* write original ESO and update CSO for IRQ voice to preserve sync */
37318c2ecf20Sopenharmony_ci				snd_trident_stop_voice(trident, voice->number);
37328c2ecf20Sopenharmony_ci				snd_trident_write_cso_reg(trident, voice, voice->isync_mark);
37338c2ecf20Sopenharmony_ci				snd_trident_write_eso_reg(trident, voice, voice->ESO);
37348c2ecf20Sopenharmony_ci				snd_trident_start_voice(trident, voice->number);
37358c2ecf20Sopenharmony_ci			}
37368c2ecf20Sopenharmony_ci#if 0
37378c2ecf20Sopenharmony_ci			if (voice->extra) {
37388c2ecf20Sopenharmony_ci				/* update CSO for extra voice to preserve sync */
37398c2ecf20Sopenharmony_ci				snd_trident_stop_voice(trident, voice->extra->number);
37408c2ecf20Sopenharmony_ci				snd_trident_write_cso_reg(trident, voice->extra, 0);
37418c2ecf20Sopenharmony_ci				snd_trident_start_voice(trident, voice->extra->number);
37428c2ecf20Sopenharmony_ci			}
37438c2ecf20Sopenharmony_ci#endif
37448c2ecf20Sopenharmony_ci			spin_unlock(&trident->reg_lock);
37458c2ecf20Sopenharmony_ci			snd_pcm_period_elapsed(voice->substream);
37468c2ecf20Sopenharmony_ci			spin_lock(&trident->reg_lock);
37478c2ecf20Sopenharmony_ci		}
37488c2ecf20Sopenharmony_ci		outl(chn_int, TRID_REG(trident, T4D_AINT_B));	/* ack */
37498c2ecf20Sopenharmony_ci	      __skip2:
37508c2ecf20Sopenharmony_ci		spin_unlock(&trident->reg_lock);
37518c2ecf20Sopenharmony_ci	}
37528c2ecf20Sopenharmony_ci	if (audio_int & MPU401_IRQ) {
37538c2ecf20Sopenharmony_ci		if (trident->rmidi) {
37548c2ecf20Sopenharmony_ci			snd_mpu401_uart_interrupt(irq, trident->rmidi->private_data);
37558c2ecf20Sopenharmony_ci		} else {
37568c2ecf20Sopenharmony_ci			inb(TRID_REG(trident, T4D_MPUR0));
37578c2ecf20Sopenharmony_ci		}
37588c2ecf20Sopenharmony_ci	}
37598c2ecf20Sopenharmony_ci	// outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(trident, T4D_MISCINT));
37608c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
37618c2ecf20Sopenharmony_ci}
37628c2ecf20Sopenharmony_ci
37638c2ecf20Sopenharmony_cistruct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port)
37648c2ecf20Sopenharmony_ci{
37658c2ecf20Sopenharmony_ci	struct snd_trident_voice *pvoice;
37668c2ecf20Sopenharmony_ci	unsigned long flags;
37678c2ecf20Sopenharmony_ci	int idx;
37688c2ecf20Sopenharmony_ci
37698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&trident->voice_alloc, flags);
37708c2ecf20Sopenharmony_ci	if (type == SNDRV_TRIDENT_VOICE_TYPE_PCM) {
37718c2ecf20Sopenharmony_ci		idx = snd_trident_allocate_pcm_channel(trident);
37728c2ecf20Sopenharmony_ci		if(idx < 0) {
37738c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&trident->voice_alloc, flags);
37748c2ecf20Sopenharmony_ci			return NULL;
37758c2ecf20Sopenharmony_ci		}
37768c2ecf20Sopenharmony_ci		pvoice = &trident->synth.voices[idx];
37778c2ecf20Sopenharmony_ci		pvoice->use = 1;
37788c2ecf20Sopenharmony_ci		pvoice->pcm = 1;
37798c2ecf20Sopenharmony_ci		pvoice->capture = 0;
37808c2ecf20Sopenharmony_ci		pvoice->spdif = 0;
37818c2ecf20Sopenharmony_ci		pvoice->memblk = NULL;
37828c2ecf20Sopenharmony_ci		pvoice->substream = NULL;
37838c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&trident->voice_alloc, flags);
37848c2ecf20Sopenharmony_ci		return pvoice;
37858c2ecf20Sopenharmony_ci	}
37868c2ecf20Sopenharmony_ci	if (type == SNDRV_TRIDENT_VOICE_TYPE_SYNTH) {
37878c2ecf20Sopenharmony_ci		idx = snd_trident_allocate_synth_channel(trident);
37888c2ecf20Sopenharmony_ci		if(idx < 0) {
37898c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&trident->voice_alloc, flags);
37908c2ecf20Sopenharmony_ci			return NULL;
37918c2ecf20Sopenharmony_ci		}
37928c2ecf20Sopenharmony_ci		pvoice = &trident->synth.voices[idx];
37938c2ecf20Sopenharmony_ci		pvoice->use = 1;
37948c2ecf20Sopenharmony_ci		pvoice->synth = 1;
37958c2ecf20Sopenharmony_ci		pvoice->client = client;
37968c2ecf20Sopenharmony_ci		pvoice->port = port;
37978c2ecf20Sopenharmony_ci		pvoice->memblk = NULL;
37988c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&trident->voice_alloc, flags);
37998c2ecf20Sopenharmony_ci		return pvoice;
38008c2ecf20Sopenharmony_ci	}
38018c2ecf20Sopenharmony_ci	if (type == SNDRV_TRIDENT_VOICE_TYPE_MIDI) {
38028c2ecf20Sopenharmony_ci	}
38038c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&trident->voice_alloc, flags);
38048c2ecf20Sopenharmony_ci	return NULL;
38058c2ecf20Sopenharmony_ci}
38068c2ecf20Sopenharmony_ci
38078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_trident_alloc_voice);
38088c2ecf20Sopenharmony_ci
38098c2ecf20Sopenharmony_civoid snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice)
38108c2ecf20Sopenharmony_ci{
38118c2ecf20Sopenharmony_ci	unsigned long flags;
38128c2ecf20Sopenharmony_ci	void (*private_free)(struct snd_trident_voice *);
38138c2ecf20Sopenharmony_ci
38148c2ecf20Sopenharmony_ci	if (voice == NULL || !voice->use)
38158c2ecf20Sopenharmony_ci		return;
38168c2ecf20Sopenharmony_ci	snd_trident_clear_voices(trident, voice->number, voice->number);
38178c2ecf20Sopenharmony_ci	spin_lock_irqsave(&trident->voice_alloc, flags);
38188c2ecf20Sopenharmony_ci	private_free = voice->private_free;
38198c2ecf20Sopenharmony_ci	voice->private_free = NULL;
38208c2ecf20Sopenharmony_ci	voice->private_data = NULL;
38218c2ecf20Sopenharmony_ci	if (voice->pcm)
38228c2ecf20Sopenharmony_ci		snd_trident_free_pcm_channel(trident, voice->number);
38238c2ecf20Sopenharmony_ci	if (voice->synth)
38248c2ecf20Sopenharmony_ci		snd_trident_free_synth_channel(trident, voice->number);
38258c2ecf20Sopenharmony_ci	voice->use = voice->pcm = voice->synth = voice->midi = 0;
38268c2ecf20Sopenharmony_ci	voice->capture = voice->spdif = 0;
38278c2ecf20Sopenharmony_ci	voice->sample_ops = NULL;
38288c2ecf20Sopenharmony_ci	voice->substream = NULL;
38298c2ecf20Sopenharmony_ci	voice->extra = NULL;
38308c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&trident->voice_alloc, flags);
38318c2ecf20Sopenharmony_ci	if (private_free)
38328c2ecf20Sopenharmony_ci		private_free(voice);
38338c2ecf20Sopenharmony_ci}
38348c2ecf20Sopenharmony_ci
38358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_trident_free_voice);
38368c2ecf20Sopenharmony_ci
38378c2ecf20Sopenharmony_cistatic void snd_trident_clear_voices(struct snd_trident * trident, unsigned short v_min, unsigned short v_max)
38388c2ecf20Sopenharmony_ci{
38398c2ecf20Sopenharmony_ci	unsigned int i, val, mask[2] = { 0, 0 };
38408c2ecf20Sopenharmony_ci
38418c2ecf20Sopenharmony_ci	if (snd_BUG_ON(v_min > 63 || v_max > 63))
38428c2ecf20Sopenharmony_ci		return;
38438c2ecf20Sopenharmony_ci	for (i = v_min; i <= v_max; i++)
38448c2ecf20Sopenharmony_ci		mask[i >> 5] |= 1 << (i & 0x1f);
38458c2ecf20Sopenharmony_ci	if (mask[0]) {
38468c2ecf20Sopenharmony_ci		outl(mask[0], TRID_REG(trident, T4D_STOP_A));
38478c2ecf20Sopenharmony_ci		val = inl(TRID_REG(trident, T4D_AINTEN_A));
38488c2ecf20Sopenharmony_ci		outl(val & ~mask[0], TRID_REG(trident, T4D_AINTEN_A));
38498c2ecf20Sopenharmony_ci	}
38508c2ecf20Sopenharmony_ci	if (mask[1]) {
38518c2ecf20Sopenharmony_ci		outl(mask[1], TRID_REG(trident, T4D_STOP_B));
38528c2ecf20Sopenharmony_ci		val = inl(TRID_REG(trident, T4D_AINTEN_B));
38538c2ecf20Sopenharmony_ci		outl(val & ~mask[1], TRID_REG(trident, T4D_AINTEN_B));
38548c2ecf20Sopenharmony_ci	}
38558c2ecf20Sopenharmony_ci}
38568c2ecf20Sopenharmony_ci
38578c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
38588c2ecf20Sopenharmony_cistatic int snd_trident_suspend(struct device *dev)
38598c2ecf20Sopenharmony_ci{
38608c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
38618c2ecf20Sopenharmony_ci	struct snd_trident *trident = card->private_data;
38628c2ecf20Sopenharmony_ci
38638c2ecf20Sopenharmony_ci	trident->in_suspend = 1;
38648c2ecf20Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
38658c2ecf20Sopenharmony_ci	snd_ac97_suspend(trident->ac97);
38668c2ecf20Sopenharmony_ci	snd_ac97_suspend(trident->ac97_sec);
38678c2ecf20Sopenharmony_ci	return 0;
38688c2ecf20Sopenharmony_ci}
38698c2ecf20Sopenharmony_ci
38708c2ecf20Sopenharmony_cistatic int snd_trident_resume(struct device *dev)
38718c2ecf20Sopenharmony_ci{
38728c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
38738c2ecf20Sopenharmony_ci	struct snd_trident *trident = card->private_data;
38748c2ecf20Sopenharmony_ci
38758c2ecf20Sopenharmony_ci	switch (trident->device) {
38768c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_DX:
38778c2ecf20Sopenharmony_ci		snd_trident_4d_dx_init(trident);
38788c2ecf20Sopenharmony_ci		break;
38798c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_NX:
38808c2ecf20Sopenharmony_ci		snd_trident_4d_nx_init(trident);
38818c2ecf20Sopenharmony_ci		break;
38828c2ecf20Sopenharmony_ci	case TRIDENT_DEVICE_ID_SI7018:
38838c2ecf20Sopenharmony_ci		snd_trident_sis_init(trident);
38848c2ecf20Sopenharmony_ci		break;
38858c2ecf20Sopenharmony_ci	}
38868c2ecf20Sopenharmony_ci
38878c2ecf20Sopenharmony_ci	snd_ac97_resume(trident->ac97);
38888c2ecf20Sopenharmony_ci	snd_ac97_resume(trident->ac97_sec);
38898c2ecf20Sopenharmony_ci
38908c2ecf20Sopenharmony_ci	/* restore some registers */
38918c2ecf20Sopenharmony_ci	outl(trident->musicvol_wavevol, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
38928c2ecf20Sopenharmony_ci
38938c2ecf20Sopenharmony_ci	snd_trident_enable_eso(trident);
38948c2ecf20Sopenharmony_ci
38958c2ecf20Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
38968c2ecf20Sopenharmony_ci	trident->in_suspend = 0;
38978c2ecf20Sopenharmony_ci	return 0;
38988c2ecf20Sopenharmony_ci}
38998c2ecf20Sopenharmony_ci
39008c2ecf20Sopenharmony_ciSIMPLE_DEV_PM_OPS(snd_trident_pm, snd_trident_suspend, snd_trident_resume);
39018c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
3902