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