11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
3c1017a4cSJaroslav Kysela * Maintained by Jaroslav Kysela <perex@perex.cz>
41da177e4SLinus Torvalds * Originated by audio@tridentmicro.com
51da177e4SLinus Torvalds * Fri Feb 19 15:55:28 MST 1999
61da177e4SLinus Torvalds * Routines for control of Trident 4DWave (DX and NX) chip
71da177e4SLinus Torvalds *
81da177e4SLinus Torvalds * BUGS:
91da177e4SLinus Torvalds *
101da177e4SLinus Torvalds * TODO:
111da177e4SLinus Torvalds * ---
121da177e4SLinus Torvalds *
131da177e4SLinus Torvalds * SiS7018 S/PDIF support by Thomas Winischhofer <thomas@winischhofer.net>
141da177e4SLinus Torvalds */
151da177e4SLinus Torvalds
161da177e4SLinus Torvalds #include <linux/delay.h>
171da177e4SLinus Torvalds #include <linux/init.h>
181da177e4SLinus Torvalds #include <linux/interrupt.h>
191da177e4SLinus Torvalds #include <linux/pci.h>
201da177e4SLinus Torvalds #include <linux/slab.h>
211da177e4SLinus Torvalds #include <linux/vmalloc.h>
221da177e4SLinus Torvalds #include <linux/gameport.h>
23910638aeSMatthias Gehre #include <linux/dma-mapping.h>
24d81a6d71SPaul Gortmaker #include <linux/export.h>
256cbbfe1cSTakashi Iwai #include <linux/io.h>
261da177e4SLinus Torvalds
271da177e4SLinus Torvalds #include <sound/core.h>
281da177e4SLinus Torvalds #include <sound/info.h>
291da177e4SLinus Torvalds #include <sound/control.h>
30a0aef8edSTakashi Iwai #include <sound/tlv.h>
3181fcb170STakashi Iwai #include "trident.h"
321da177e4SLinus Torvalds #include <sound/asoundef.h>
331da177e4SLinus Torvalds
34bee1a5beSTakashi Iwai static int snd_trident_pcm_mixer_build(struct snd_trident *trident,
35bee1a5beSTakashi Iwai struct snd_trident_voice * voice,
36bee1a5beSTakashi Iwai struct snd_pcm_substream *substream);
37bee1a5beSTakashi Iwai static int snd_trident_pcm_mixer_free(struct snd_trident *trident,
38bee1a5beSTakashi Iwai struct snd_trident_voice * voice,
39bee1a5beSTakashi Iwai struct snd_pcm_substream *substream);
407d12e780SDavid Howells static irqreturn_t snd_trident_interrupt(int irq, void *dev_id);
41bee1a5beSTakashi Iwai static int snd_trident_sis_reset(struct snd_trident *trident);
421da177e4SLinus Torvalds
43bee1a5beSTakashi Iwai static void snd_trident_clear_voices(struct snd_trident * trident,
44bee1a5beSTakashi Iwai unsigned short v_min, unsigned short v_max);
45*5adfd8c2STakashi Iwai static void snd_trident_free(struct snd_card *card);
461da177e4SLinus Torvalds
471da177e4SLinus Torvalds /*
481da177e4SLinus Torvalds * common I/O routines
491da177e4SLinus Torvalds */
501da177e4SLinus Torvalds
511da177e4SLinus Torvalds
521da177e4SLinus Torvalds #if 0
53bee1a5beSTakashi Iwai static void snd_trident_print_voice_regs(struct snd_trident *trident, int voice)
541da177e4SLinus Torvalds {
551da177e4SLinus Torvalds unsigned int val, tmp;
561da177e4SLinus Torvalds
5780c19b75STakashi Iwai dev_dbg(trident->card->dev, "Trident voice %i:\n", voice);
581da177e4SLinus Torvalds outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR));
591da177e4SLinus Torvalds val = inl(TRID_REG(trident, CH_LBA));
6080c19b75STakashi Iwai dev_dbg(trident->card->dev, "LBA: 0x%x\n", val);
611da177e4SLinus Torvalds val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
6280c19b75STakashi Iwai dev_dbg(trident->card->dev, "GVSel: %i\n", val >> 31);
6380c19b75STakashi Iwai dev_dbg(trident->card->dev, "Pan: 0x%x\n", (val >> 24) & 0x7f);
6480c19b75STakashi Iwai dev_dbg(trident->card->dev, "Vol: 0x%x\n", (val >> 16) & 0xff);
6580c19b75STakashi Iwai dev_dbg(trident->card->dev, "CTRL: 0x%x\n", (val >> 12) & 0x0f);
6680c19b75STakashi Iwai dev_dbg(trident->card->dev, "EC: 0x%x\n", val & 0x0fff);
671da177e4SLinus Torvalds if (trident->device != TRIDENT_DEVICE_ID_NX) {
681da177e4SLinus Torvalds val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS));
6980c19b75STakashi Iwai dev_dbg(trident->card->dev, "CSO: 0x%x\n", val >> 16);
7080c19b75STakashi Iwai dev_dbg(trident->card->dev, "Alpha: 0x%x\n", (val >> 4) & 0x0fff);
7180c19b75STakashi Iwai dev_dbg(trident->card->dev, "FMS: 0x%x\n", val & 0x0f);
721da177e4SLinus Torvalds val = inl(TRID_REG(trident, CH_DX_ESO_DELTA));
7380c19b75STakashi Iwai dev_dbg(trident->card->dev, "ESO: 0x%x\n", val >> 16);
7480c19b75STakashi Iwai dev_dbg(trident->card->dev, "Delta: 0x%x\n", val & 0xffff);
751da177e4SLinus Torvalds val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
761da177e4SLinus Torvalds } else { // TRIDENT_DEVICE_ID_NX
771da177e4SLinus Torvalds val = inl(TRID_REG(trident, CH_NX_DELTA_CSO));
781da177e4SLinus Torvalds tmp = (val >> 24) & 0xff;
7980c19b75STakashi Iwai dev_dbg(trident->card->dev, "CSO: 0x%x\n", val & 0x00ffffff);
801da177e4SLinus Torvalds val = inl(TRID_REG(trident, CH_NX_DELTA_ESO));
811da177e4SLinus Torvalds tmp |= (val >> 16) & 0xff00;
8280c19b75STakashi Iwai dev_dbg(trident->card->dev, "Delta: 0x%x\n", tmp);
8380c19b75STakashi Iwai dev_dbg(trident->card->dev, "ESO: 0x%x\n", val & 0x00ffffff);
841da177e4SLinus Torvalds val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL));
8580c19b75STakashi Iwai dev_dbg(trident->card->dev, "Alpha: 0x%x\n", val >> 20);
8680c19b75STakashi Iwai dev_dbg(trident->card->dev, "FMS: 0x%x\n", (val >> 16) & 0x0f);
871da177e4SLinus Torvalds }
8880c19b75STakashi Iwai dev_dbg(trident->card->dev, "FMC: 0x%x\n", (val >> 14) & 3);
8980c19b75STakashi Iwai dev_dbg(trident->card->dev, "RVol: 0x%x\n", (val >> 7) & 0x7f);
9080c19b75STakashi Iwai dev_dbg(trident->card->dev, "CVol: 0x%x\n", val & 0x7f);
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds #endif
931da177e4SLinus Torvalds
941da177e4SLinus Torvalds /*---------------------------------------------------------------------------
95bee1a5beSTakashi Iwai unsigned short snd_trident_codec_read(struct snd_ac97 *ac97, unsigned short reg)
961da177e4SLinus Torvalds
971da177e4SLinus Torvalds Description: This routine will do all of the reading from the external
981da177e4SLinus Torvalds CODEC (AC97).
991da177e4SLinus Torvalds
1001da177e4SLinus Torvalds Parameters: ac97 - ac97 codec structure
1011da177e4SLinus Torvalds reg - CODEC register index, from AC97 Hal.
1021da177e4SLinus Torvalds
1031da177e4SLinus Torvalds returns: 16 bit value read from the AC97.
1041da177e4SLinus Torvalds
1051da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
snd_trident_codec_read(struct snd_ac97 * ac97,unsigned short reg)106bee1a5beSTakashi Iwai static unsigned short snd_trident_codec_read(struct snd_ac97 *ac97, unsigned short reg)
1071da177e4SLinus Torvalds {
1081da177e4SLinus Torvalds unsigned int data = 0, treg;
1091da177e4SLinus Torvalds unsigned short count = 0xffff;
1101da177e4SLinus Torvalds unsigned long flags;
111bee1a5beSTakashi Iwai struct snd_trident *trident = ac97->private_data;
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvalds spin_lock_irqsave(&trident->reg_lock, flags);
1141da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_DX) {
1151da177e4SLinus Torvalds data = (DX_AC97_BUSY_READ | (reg & 0x000000ff));
1161da177e4SLinus Torvalds outl(data, TRID_REG(trident, DX_ACR1_AC97_R));
1171da177e4SLinus Torvalds do {
1181da177e4SLinus Torvalds data = inl(TRID_REG(trident, DX_ACR1_AC97_R));
1191da177e4SLinus Torvalds if ((data & DX_AC97_BUSY_READ) == 0)
1201da177e4SLinus Torvalds break;
1211da177e4SLinus Torvalds } while (--count);
1221da177e4SLinus Torvalds } else if (trident->device == TRIDENT_DEVICE_ID_NX) {
1231da177e4SLinus Torvalds data = (NX_AC97_BUSY_READ | (reg & 0x000000ff));
1241da177e4SLinus Torvalds treg = ac97->num == 0 ? NX_ACR2_AC97_R_PRIMARY : NX_ACR3_AC97_R_SECONDARY;
1251da177e4SLinus Torvalds outl(data, TRID_REG(trident, treg));
1261da177e4SLinus Torvalds do {
1271da177e4SLinus Torvalds data = inl(TRID_REG(trident, treg));
1281da177e4SLinus Torvalds if ((data & 0x00000C00) == 0)
1291da177e4SLinus Torvalds break;
1301da177e4SLinus Torvalds } while (--count);
1311da177e4SLinus Torvalds } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
1321da177e4SLinus Torvalds data = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
1331da177e4SLinus Torvalds if (ac97->num == 1)
1341da177e4SLinus Torvalds data |= SI_AC97_SECONDARY;
1351da177e4SLinus Torvalds outl(data, TRID_REG(trident, SI_AC97_READ));
1361da177e4SLinus Torvalds do {
1371da177e4SLinus Torvalds data = inl(TRID_REG(trident, SI_AC97_READ));
1381da177e4SLinus Torvalds if ((data & (SI_AC97_BUSY_READ)) == 0)
1391da177e4SLinus Torvalds break;
1401da177e4SLinus Torvalds } while (--count);
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds
1431da177e4SLinus Torvalds if (count == 0 && !trident->ac97_detect) {
14480c19b75STakashi Iwai dev_err(trident->card->dev,
14580c19b75STakashi Iwai "ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n",
146bee1a5beSTakashi Iwai reg, data);
1471da177e4SLinus Torvalds data = 0;
1481da177e4SLinus Torvalds }
1491da177e4SLinus Torvalds
1501da177e4SLinus Torvalds spin_unlock_irqrestore(&trident->reg_lock, flags);
1511da177e4SLinus Torvalds return ((unsigned short) (data >> 16));
1521da177e4SLinus Torvalds }
1531da177e4SLinus Torvalds
1541da177e4SLinus Torvalds /*---------------------------------------------------------------------------
155bee1a5beSTakashi Iwai void snd_trident_codec_write(struct snd_ac97 *ac97, unsigned short reg,
156bee1a5beSTakashi Iwai unsigned short wdata)
1571da177e4SLinus Torvalds
1581da177e4SLinus Torvalds Description: This routine will do all of the writing to the external
1591da177e4SLinus Torvalds CODEC (AC97).
1601da177e4SLinus Torvalds
1611da177e4SLinus Torvalds Parameters: ac97 - ac97 codec structure
1621da177e4SLinus Torvalds reg - CODEC register index, from AC97 Hal.
1631da177e4SLinus Torvalds data - Lower 16 bits are the data to write to CODEC.
1641da177e4SLinus Torvalds
1651da177e4SLinus Torvalds returns: TRUE if everything went ok, else FALSE.
1661da177e4SLinus Torvalds
1671da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
snd_trident_codec_write(struct snd_ac97 * ac97,unsigned short reg,unsigned short wdata)168bee1a5beSTakashi Iwai static void snd_trident_codec_write(struct snd_ac97 *ac97, unsigned short reg,
169bee1a5beSTakashi Iwai unsigned short wdata)
1701da177e4SLinus Torvalds {
1711da177e4SLinus Torvalds unsigned int address, data;
1721da177e4SLinus Torvalds unsigned short count = 0xffff;
1731da177e4SLinus Torvalds unsigned long flags;
174bee1a5beSTakashi Iwai struct snd_trident *trident = ac97->private_data;
1751da177e4SLinus Torvalds
1761da177e4SLinus Torvalds data = ((unsigned long) wdata) << 16;
1771da177e4SLinus Torvalds
1781da177e4SLinus Torvalds spin_lock_irqsave(&trident->reg_lock, flags);
1791da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_DX) {
1801da177e4SLinus Torvalds address = DX_ACR0_AC97_W;
1811da177e4SLinus Torvalds
1821da177e4SLinus Torvalds /* read AC-97 write register status */
1831da177e4SLinus Torvalds do {
1841da177e4SLinus Torvalds if ((inw(TRID_REG(trident, address)) & DX_AC97_BUSY_WRITE) == 0)
1851da177e4SLinus Torvalds break;
1861da177e4SLinus Torvalds } while (--count);
1871da177e4SLinus Torvalds
1881da177e4SLinus Torvalds data |= (DX_AC97_BUSY_WRITE | (reg & 0x000000ff));
1891da177e4SLinus Torvalds } else if (trident->device == TRIDENT_DEVICE_ID_NX) {
1901da177e4SLinus Torvalds address = NX_ACR1_AC97_W;
1911da177e4SLinus Torvalds
1921da177e4SLinus Torvalds /* read AC-97 write register status */
1931da177e4SLinus Torvalds do {
1941da177e4SLinus Torvalds if ((inw(TRID_REG(trident, address)) & NX_AC97_BUSY_WRITE) == 0)
1951da177e4SLinus Torvalds break;
1961da177e4SLinus Torvalds } while (--count);
1971da177e4SLinus Torvalds
1981da177e4SLinus Torvalds data |= (NX_AC97_BUSY_WRITE | (ac97->num << 8) | (reg & 0x000000ff));
1991da177e4SLinus Torvalds } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
2001da177e4SLinus Torvalds address = SI_AC97_WRITE;
2011da177e4SLinus Torvalds
2021da177e4SLinus Torvalds /* read AC-97 write register status */
2031da177e4SLinus Torvalds do {
2041da177e4SLinus Torvalds if ((inw(TRID_REG(trident, address)) & (SI_AC97_BUSY_WRITE)) == 0)
2051da177e4SLinus Torvalds break;
2061da177e4SLinus Torvalds } while (--count);
2071da177e4SLinus Torvalds
2081da177e4SLinus Torvalds data |= SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
2091da177e4SLinus Torvalds if (ac97->num == 1)
2101da177e4SLinus Torvalds data |= SI_AC97_SECONDARY;
2111da177e4SLinus Torvalds } else {
2121da177e4SLinus Torvalds address = 0; /* keep GCC happy */
2131da177e4SLinus Torvalds count = 0; /* return */
2141da177e4SLinus Torvalds }
2151da177e4SLinus Torvalds
2161da177e4SLinus Torvalds if (count == 0) {
2171da177e4SLinus Torvalds spin_unlock_irqrestore(&trident->reg_lock, flags);
2181da177e4SLinus Torvalds return;
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds outl(data, TRID_REG(trident, address));
2211da177e4SLinus Torvalds spin_unlock_irqrestore(&trident->reg_lock, flags);
2221da177e4SLinus Torvalds }
2231da177e4SLinus Torvalds
2241da177e4SLinus Torvalds /*---------------------------------------------------------------------------
225bee1a5beSTakashi Iwai void snd_trident_enable_eso(struct snd_trident *trident)
2261da177e4SLinus Torvalds
2271da177e4SLinus Torvalds Description: This routine will enable end of loop interrupts.
2281da177e4SLinus Torvalds End of loop interrupts will occur when a running
2291da177e4SLinus Torvalds channel reaches ESO.
2301da177e4SLinus Torvalds Also enables middle of loop interrupts.
2311da177e4SLinus Torvalds
2321da177e4SLinus Torvalds Parameters: trident - pointer to target device class for 4DWave.
2331da177e4SLinus Torvalds
2341da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
2351da177e4SLinus Torvalds
snd_trident_enable_eso(struct snd_trident * trident)236bee1a5beSTakashi Iwai static void snd_trident_enable_eso(struct snd_trident * trident)
2371da177e4SLinus Torvalds {
2381da177e4SLinus Torvalds unsigned int val;
2391da177e4SLinus Torvalds
2401da177e4SLinus Torvalds val = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
2411da177e4SLinus Torvalds val |= ENDLP_IE;
2421da177e4SLinus Torvalds val |= MIDLP_IE;
2431da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_SI7018)
2441da177e4SLinus Torvalds val |= BANK_B_EN;
2451da177e4SLinus Torvalds outl(val, TRID_REG(trident, T4D_LFO_GC_CIR));
2461da177e4SLinus Torvalds }
2471da177e4SLinus Torvalds
2481da177e4SLinus Torvalds /*---------------------------------------------------------------------------
249bee1a5beSTakashi Iwai void snd_trident_disable_eso(struct snd_trident *trident)
2501da177e4SLinus Torvalds
2511da177e4SLinus Torvalds Description: This routine will disable end of loop interrupts.
2521da177e4SLinus Torvalds End of loop interrupts will occur when a running
2531da177e4SLinus Torvalds channel reaches ESO.
2541da177e4SLinus Torvalds Also disables middle of loop interrupts.
2551da177e4SLinus Torvalds
2561da177e4SLinus Torvalds Parameters:
2571da177e4SLinus Torvalds trident - pointer to target device class for 4DWave.
2581da177e4SLinus Torvalds
2591da177e4SLinus Torvalds returns: TRUE if everything went ok, else FALSE.
2601da177e4SLinus Torvalds
2611da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
2621da177e4SLinus Torvalds
snd_trident_disable_eso(struct snd_trident * trident)263bee1a5beSTakashi Iwai static void snd_trident_disable_eso(struct snd_trident * trident)
2641da177e4SLinus Torvalds {
2651da177e4SLinus Torvalds unsigned int tmp;
2661da177e4SLinus Torvalds
2671da177e4SLinus Torvalds tmp = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
2681da177e4SLinus Torvalds tmp &= ~ENDLP_IE;
2691da177e4SLinus Torvalds tmp &= ~MIDLP_IE;
2701da177e4SLinus Torvalds outl(tmp, TRID_REG(trident, T4D_LFO_GC_CIR));
2711da177e4SLinus Torvalds }
2721da177e4SLinus Torvalds
2731da177e4SLinus Torvalds /*---------------------------------------------------------------------------
274bee1a5beSTakashi Iwai void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice)
2751da177e4SLinus Torvalds
2761da177e4SLinus Torvalds Description: Start a voice, any channel 0 thru 63.
2771da177e4SLinus Torvalds This routine automatically handles the fact that there are
2781da177e4SLinus Torvalds more than 32 channels available.
2791da177e4SLinus Torvalds
2801da177e4SLinus Torvalds Parameters : voice - Voice number 0 thru n.
2811da177e4SLinus Torvalds trident - pointer to target device class for 4DWave.
2821da177e4SLinus Torvalds
2831da177e4SLinus Torvalds Return Value: None.
2841da177e4SLinus Torvalds
2851da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
2861da177e4SLinus Torvalds
snd_trident_start_voice(struct snd_trident * trident,unsigned int voice)287bee1a5beSTakashi Iwai void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice)
2881da177e4SLinus Torvalds {
2891da177e4SLinus Torvalds unsigned int mask = 1 << (voice & 0x1f);
2901da177e4SLinus Torvalds unsigned int reg = (voice & 0x20) ? T4D_START_B : T4D_START_A;
2911da177e4SLinus Torvalds
2921da177e4SLinus Torvalds outl(mask, TRID_REG(trident, reg));
2931da177e4SLinus Torvalds }
2941da177e4SLinus Torvalds
295cbef55f3STakashi Iwai EXPORT_SYMBOL(snd_trident_start_voice);
296cbef55f3STakashi Iwai
2971da177e4SLinus Torvalds /*---------------------------------------------------------------------------
298bee1a5beSTakashi Iwai void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice)
2991da177e4SLinus Torvalds
3001da177e4SLinus Torvalds Description: Stop a voice, any channel 0 thru 63.
3011da177e4SLinus Torvalds This routine automatically handles the fact that there are
3021da177e4SLinus Torvalds more than 32 channels available.
3031da177e4SLinus Torvalds
3041da177e4SLinus Torvalds Parameters : voice - Voice number 0 thru n.
3051da177e4SLinus Torvalds trident - pointer to target device class for 4DWave.
3061da177e4SLinus Torvalds
3071da177e4SLinus Torvalds Return Value: None.
3081da177e4SLinus Torvalds
3091da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
3101da177e4SLinus Torvalds
snd_trident_stop_voice(struct snd_trident * trident,unsigned int voice)311bee1a5beSTakashi Iwai void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice)
3121da177e4SLinus Torvalds {
3131da177e4SLinus Torvalds unsigned int mask = 1 << (voice & 0x1f);
3141da177e4SLinus Torvalds unsigned int reg = (voice & 0x20) ? T4D_STOP_B : T4D_STOP_A;
3151da177e4SLinus Torvalds
3161da177e4SLinus Torvalds outl(mask, TRID_REG(trident, reg));
3171da177e4SLinus Torvalds }
3181da177e4SLinus Torvalds
319cbef55f3STakashi Iwai EXPORT_SYMBOL(snd_trident_stop_voice);
320cbef55f3STakashi Iwai
3211da177e4SLinus Torvalds /*---------------------------------------------------------------------------
322bee1a5beSTakashi Iwai int snd_trident_allocate_pcm_channel(struct snd_trident *trident)
3231da177e4SLinus Torvalds
3241da177e4SLinus Torvalds Description: Allocate hardware channel in Bank B (32-63).
3251da177e4SLinus Torvalds
3261da177e4SLinus Torvalds Parameters : trident - pointer to target device class for 4DWave.
3271da177e4SLinus Torvalds
3281da177e4SLinus Torvalds Return Value: hardware channel - 32-63 or -1 when no channel is available
3291da177e4SLinus Torvalds
3301da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
3311da177e4SLinus Torvalds
snd_trident_allocate_pcm_channel(struct snd_trident * trident)332bee1a5beSTakashi Iwai static int snd_trident_allocate_pcm_channel(struct snd_trident * trident)
3331da177e4SLinus Torvalds {
3341da177e4SLinus Torvalds int idx;
3351da177e4SLinus Torvalds
3361da177e4SLinus Torvalds if (trident->ChanPCMcnt >= trident->ChanPCM)
3371da177e4SLinus Torvalds return -1;
3381da177e4SLinus Torvalds for (idx = 31; idx >= 0; idx--) {
3391da177e4SLinus Torvalds if (!(trident->ChanMap[T4D_BANK_B] & (1 << idx))) {
3401da177e4SLinus Torvalds trident->ChanMap[T4D_BANK_B] |= 1 << idx;
3411da177e4SLinus Torvalds trident->ChanPCMcnt++;
3421da177e4SLinus Torvalds return idx + 32;
3431da177e4SLinus Torvalds }
3441da177e4SLinus Torvalds }
3451da177e4SLinus Torvalds return -1;
3461da177e4SLinus Torvalds }
3471da177e4SLinus Torvalds
3481da177e4SLinus Torvalds /*---------------------------------------------------------------------------
3491da177e4SLinus Torvalds void snd_trident_free_pcm_channel(int channel)
3501da177e4SLinus Torvalds
3511da177e4SLinus Torvalds Description: Free hardware channel in Bank B (32-63)
3521da177e4SLinus Torvalds
3531da177e4SLinus Torvalds Parameters : trident - pointer to target device class for 4DWave.
3541da177e4SLinus Torvalds channel - hardware channel number 0-63
3551da177e4SLinus Torvalds
3561da177e4SLinus Torvalds Return Value: none
3571da177e4SLinus Torvalds
3581da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
3591da177e4SLinus Torvalds
snd_trident_free_pcm_channel(struct snd_trident * trident,int channel)360bee1a5beSTakashi Iwai static void snd_trident_free_pcm_channel(struct snd_trident *trident, int channel)
3611da177e4SLinus Torvalds {
3621da177e4SLinus Torvalds if (channel < 32 || channel > 63)
3631da177e4SLinus Torvalds return;
3641da177e4SLinus Torvalds channel &= 0x1f;
3651da177e4SLinus Torvalds if (trident->ChanMap[T4D_BANK_B] & (1 << channel)) {
3661da177e4SLinus Torvalds trident->ChanMap[T4D_BANK_B] &= ~(1 << channel);
3671da177e4SLinus Torvalds trident->ChanPCMcnt--;
3681da177e4SLinus Torvalds }
3691da177e4SLinus Torvalds }
3701da177e4SLinus Torvalds
3711da177e4SLinus Torvalds /*---------------------------------------------------------------------------
3721da177e4SLinus Torvalds unsigned int snd_trident_allocate_synth_channel(void)
3731da177e4SLinus Torvalds
3741da177e4SLinus Torvalds Description: Allocate hardware channel in Bank A (0-31).
3751da177e4SLinus Torvalds
3761da177e4SLinus Torvalds Parameters : trident - pointer to target device class for 4DWave.
3771da177e4SLinus Torvalds
3781da177e4SLinus Torvalds Return Value: hardware channel - 0-31 or -1 when no channel is available
3791da177e4SLinus Torvalds
3801da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
3811da177e4SLinus Torvalds
snd_trident_allocate_synth_channel(struct snd_trident * trident)382bee1a5beSTakashi Iwai static int snd_trident_allocate_synth_channel(struct snd_trident * trident)
3831da177e4SLinus Torvalds {
3841da177e4SLinus Torvalds int idx;
3851da177e4SLinus Torvalds
3861da177e4SLinus Torvalds for (idx = 31; idx >= 0; idx--) {
3871da177e4SLinus Torvalds if (!(trident->ChanMap[T4D_BANK_A] & (1 << idx))) {
3881da177e4SLinus Torvalds trident->ChanMap[T4D_BANK_A] |= 1 << idx;
3891da177e4SLinus Torvalds trident->synth.ChanSynthCount++;
3901da177e4SLinus Torvalds return idx;
3911da177e4SLinus Torvalds }
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds return -1;
3941da177e4SLinus Torvalds }
3951da177e4SLinus Torvalds
3961da177e4SLinus Torvalds /*---------------------------------------------------------------------------
3971da177e4SLinus Torvalds void snd_trident_free_synth_channel( int channel )
3981da177e4SLinus Torvalds
3991da177e4SLinus Torvalds Description: Free hardware channel in Bank B (0-31).
4001da177e4SLinus Torvalds
4011da177e4SLinus Torvalds Parameters : trident - pointer to target device class for 4DWave.
4021da177e4SLinus Torvalds channel - hardware channel number 0-63
4031da177e4SLinus Torvalds
4041da177e4SLinus Torvalds Return Value: none
4051da177e4SLinus Torvalds
4061da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
4071da177e4SLinus Torvalds
snd_trident_free_synth_channel(struct snd_trident * trident,int channel)408bee1a5beSTakashi Iwai static void snd_trident_free_synth_channel(struct snd_trident *trident, int channel)
4091da177e4SLinus Torvalds {
4101da177e4SLinus Torvalds if (channel < 0 || channel > 31)
4111da177e4SLinus Torvalds return;
4121da177e4SLinus Torvalds channel &= 0x1f;
4131da177e4SLinus Torvalds if (trident->ChanMap[T4D_BANK_A] & (1 << channel)) {
4141da177e4SLinus Torvalds trident->ChanMap[T4D_BANK_A] &= ~(1 << channel);
4151da177e4SLinus Torvalds trident->synth.ChanSynthCount--;
4161da177e4SLinus Torvalds }
4171da177e4SLinus Torvalds }
4181da177e4SLinus Torvalds
4191da177e4SLinus Torvalds /*---------------------------------------------------------------------------
4201da177e4SLinus Torvalds snd_trident_write_voice_regs
4211da177e4SLinus Torvalds
4221da177e4SLinus Torvalds Description: This routine will complete and write the 5 hardware channel
4231da177e4SLinus Torvalds registers to hardware.
4241da177e4SLinus Torvalds
425561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
4261da177e4SLinus Torvalds voice - synthesizer voice structure
4271da177e4SLinus Torvalds Each register field.
4281da177e4SLinus Torvalds
4291da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
4301da177e4SLinus Torvalds
snd_trident_write_voice_regs(struct snd_trident * trident,struct snd_trident_voice * voice)431bee1a5beSTakashi Iwai void snd_trident_write_voice_regs(struct snd_trident * trident,
432bee1a5beSTakashi Iwai struct snd_trident_voice * voice)
4331da177e4SLinus Torvalds {
4341da177e4SLinus Torvalds unsigned int FmcRvolCvol;
4351da177e4SLinus Torvalds unsigned int regs[5];
4361da177e4SLinus Torvalds
4371da177e4SLinus Torvalds regs[1] = voice->LBA;
4381da177e4SLinus Torvalds regs[4] = (voice->GVSel << 31) |
4391da177e4SLinus Torvalds ((voice->Pan & 0x0000007f) << 24) |
4401da177e4SLinus Torvalds ((voice->CTRL & 0x0000000f) << 12);
4411da177e4SLinus Torvalds FmcRvolCvol = ((voice->FMC & 3) << 14) |
4421da177e4SLinus Torvalds ((voice->RVol & 0x7f) << 7) |
4431da177e4SLinus Torvalds (voice->CVol & 0x7f);
4441da177e4SLinus Torvalds
4451da177e4SLinus Torvalds switch (trident->device) {
4461da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_SI7018:
4471da177e4SLinus Torvalds regs[4] |= voice->number > 31 ?
4481da177e4SLinus Torvalds (voice->Vol & 0x000003ff) :
4491da177e4SLinus Torvalds ((voice->Vol & 0x00003fc) << (16-2)) |
4501da177e4SLinus Torvalds (voice->EC & 0x00000fff);
451bee1a5beSTakashi Iwai regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) |
452bee1a5beSTakashi Iwai (voice->FMS & 0x0000000f);
4531da177e4SLinus Torvalds regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff);
4541da177e4SLinus Torvalds regs[3] = (voice->Attribute << 16) | FmcRvolCvol;
4551da177e4SLinus Torvalds break;
4561da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_DX:
4571da177e4SLinus Torvalds regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) |
4581da177e4SLinus Torvalds (voice->EC & 0x00000fff);
459bee1a5beSTakashi Iwai regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) |
460bee1a5beSTakashi Iwai (voice->FMS & 0x0000000f);
4611da177e4SLinus Torvalds regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff);
4621da177e4SLinus Torvalds regs[3] = FmcRvolCvol;
4631da177e4SLinus Torvalds break;
4641da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_NX:
4651da177e4SLinus Torvalds regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) |
4661da177e4SLinus Torvalds (voice->EC & 0x00000fff);
4671da177e4SLinus Torvalds regs[0] = (voice->Delta << 24) | (voice->CSO & 0x00ffffff);
468bee1a5beSTakashi Iwai regs[2] = ((voice->Delta << 16) & 0xff000000) |
469bee1a5beSTakashi Iwai (voice->ESO & 0x00ffffff);
470bee1a5beSTakashi Iwai regs[3] = (voice->Alpha << 20) |
471bee1a5beSTakashi Iwai ((voice->FMS & 0x0000000f) << 16) | FmcRvolCvol;
4721da177e4SLinus Torvalds break;
4731da177e4SLinus Torvalds default:
4741da177e4SLinus Torvalds snd_BUG();
475b95eed7cSTakashi Iwai return;
4761da177e4SLinus Torvalds }
4771da177e4SLinus Torvalds
4781da177e4SLinus Torvalds outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
4791da177e4SLinus Torvalds outl(regs[0], TRID_REG(trident, CH_START + 0));
4801da177e4SLinus Torvalds outl(regs[1], TRID_REG(trident, CH_START + 4));
4811da177e4SLinus Torvalds outl(regs[2], TRID_REG(trident, CH_START + 8));
4821da177e4SLinus Torvalds outl(regs[3], TRID_REG(trident, CH_START + 12));
4831da177e4SLinus Torvalds outl(regs[4], TRID_REG(trident, CH_START + 16));
4841da177e4SLinus Torvalds
4851da177e4SLinus Torvalds #if 0
48680c19b75STakashi Iwai dev_dbg(trident->card->dev, "written %i channel:\n", voice->number);
48780c19b75STakashi Iwai dev_dbg(trident->card->dev, " regs[0] = 0x%x/0x%x\n",
488ee419653STakashi Iwai regs[0], inl(TRID_REG(trident, CH_START + 0)));
48980c19b75STakashi Iwai dev_dbg(trident->card->dev, " regs[1] = 0x%x/0x%x\n",
490ee419653STakashi Iwai regs[1], inl(TRID_REG(trident, CH_START + 4)));
49180c19b75STakashi Iwai dev_dbg(trident->card->dev, " regs[2] = 0x%x/0x%x\n",
492ee419653STakashi Iwai regs[2], inl(TRID_REG(trident, CH_START + 8)));
49380c19b75STakashi Iwai dev_dbg(trident->card->dev, " regs[3] = 0x%x/0x%x\n",
494ee419653STakashi Iwai regs[3], inl(TRID_REG(trident, CH_START + 12)));
49580c19b75STakashi Iwai dev_dbg(trident->card->dev, " regs[4] = 0x%x/0x%x\n",
496ee419653STakashi Iwai regs[4], inl(TRID_REG(trident, CH_START + 16)));
4971da177e4SLinus Torvalds #endif
4981da177e4SLinus Torvalds }
4991da177e4SLinus Torvalds
500cbef55f3STakashi Iwai EXPORT_SYMBOL(snd_trident_write_voice_regs);
501cbef55f3STakashi Iwai
5021da177e4SLinus Torvalds /*---------------------------------------------------------------------------
5031da177e4SLinus Torvalds snd_trident_write_cso_reg
5041da177e4SLinus Torvalds
5051da177e4SLinus Torvalds Description: This routine will write the new CSO offset
5061da177e4SLinus Torvalds register to hardware.
5071da177e4SLinus Torvalds
508561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
5091da177e4SLinus Torvalds voice - synthesizer voice structure
5101da177e4SLinus Torvalds CSO - new CSO value
5111da177e4SLinus Torvalds
5121da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
5131da177e4SLinus Torvalds
snd_trident_write_cso_reg(struct snd_trident * trident,struct snd_trident_voice * voice,unsigned int CSO)514bee1a5beSTakashi Iwai static void snd_trident_write_cso_reg(struct snd_trident * trident,
515bee1a5beSTakashi Iwai struct snd_trident_voice * voice,
516bee1a5beSTakashi Iwai unsigned int CSO)
5171da177e4SLinus Torvalds {
5181da177e4SLinus Torvalds voice->CSO = CSO;
5191da177e4SLinus Torvalds outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
5201da177e4SLinus Torvalds if (trident->device != TRIDENT_DEVICE_ID_NX) {
5211da177e4SLinus Torvalds outw(voice->CSO, TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
5221da177e4SLinus Torvalds } else {
523bee1a5beSTakashi Iwai outl((voice->Delta << 24) |
524bee1a5beSTakashi Iwai (voice->CSO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_CSO));
5251da177e4SLinus Torvalds }
5261da177e4SLinus Torvalds }
5271da177e4SLinus Torvalds
5281da177e4SLinus Torvalds /*---------------------------------------------------------------------------
5291da177e4SLinus Torvalds snd_trident_write_eso_reg
5301da177e4SLinus Torvalds
5311da177e4SLinus Torvalds Description: This routine will write the new ESO offset
5321da177e4SLinus Torvalds register to hardware.
5331da177e4SLinus Torvalds
534561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
5351da177e4SLinus Torvalds voice - synthesizer voice structure
5361da177e4SLinus Torvalds ESO - new ESO value
5371da177e4SLinus Torvalds
5381da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
5391da177e4SLinus Torvalds
snd_trident_write_eso_reg(struct snd_trident * trident,struct snd_trident_voice * voice,unsigned int ESO)540bee1a5beSTakashi Iwai static void snd_trident_write_eso_reg(struct snd_trident * trident,
541bee1a5beSTakashi Iwai struct snd_trident_voice * voice,
542bee1a5beSTakashi Iwai unsigned int ESO)
5431da177e4SLinus Torvalds {
5441da177e4SLinus Torvalds voice->ESO = ESO;
5451da177e4SLinus Torvalds outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
5461da177e4SLinus Torvalds if (trident->device != TRIDENT_DEVICE_ID_NX) {
5471da177e4SLinus Torvalds outw(voice->ESO, TRID_REG(trident, CH_DX_ESO_DELTA) + 2);
5481da177e4SLinus Torvalds } else {
549bee1a5beSTakashi Iwai outl(((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff),
550bee1a5beSTakashi Iwai TRID_REG(trident, CH_NX_DELTA_ESO));
5511da177e4SLinus Torvalds }
5521da177e4SLinus Torvalds }
5531da177e4SLinus Torvalds
5541da177e4SLinus Torvalds /*---------------------------------------------------------------------------
5551da177e4SLinus Torvalds snd_trident_write_vol_reg
5561da177e4SLinus Torvalds
5571da177e4SLinus Torvalds Description: This routine will write the new voice volume
5581da177e4SLinus Torvalds register to hardware.
5591da177e4SLinus Torvalds
560561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
5611da177e4SLinus Torvalds voice - synthesizer voice structure
5621da177e4SLinus Torvalds Vol - new voice volume
5631da177e4SLinus Torvalds
5641da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
5651da177e4SLinus Torvalds
snd_trident_write_vol_reg(struct snd_trident * trident,struct snd_trident_voice * voice,unsigned int Vol)566bee1a5beSTakashi Iwai static void snd_trident_write_vol_reg(struct snd_trident * trident,
567bee1a5beSTakashi Iwai struct snd_trident_voice * voice,
568bee1a5beSTakashi Iwai unsigned int Vol)
5691da177e4SLinus Torvalds {
5701da177e4SLinus Torvalds voice->Vol = Vol;
5711da177e4SLinus Torvalds outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
5721da177e4SLinus Torvalds switch (trident->device) {
5731da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_DX:
5741da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_NX:
5751da177e4SLinus Torvalds outb(voice->Vol >> 2, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 2));
5761da177e4SLinus Torvalds break;
5771da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_SI7018:
57880c19b75STakashi Iwai /* dev_dbg(trident->card->dev, "voice->Vol = 0x%x\n", voice->Vol); */
579bee1a5beSTakashi Iwai outw((voice->CTRL << 12) | voice->Vol,
580bee1a5beSTakashi Iwai TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
5811da177e4SLinus Torvalds break;
5821da177e4SLinus Torvalds }
5831da177e4SLinus Torvalds }
5841da177e4SLinus Torvalds
5851da177e4SLinus Torvalds /*---------------------------------------------------------------------------
5861da177e4SLinus Torvalds snd_trident_write_pan_reg
5871da177e4SLinus Torvalds
5881da177e4SLinus Torvalds Description: This routine will write the new voice pan
5891da177e4SLinus Torvalds register to hardware.
5901da177e4SLinus Torvalds
591561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
5921da177e4SLinus Torvalds voice - synthesizer voice structure
5931da177e4SLinus Torvalds Pan - new pan value
5941da177e4SLinus Torvalds
5951da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
5961da177e4SLinus Torvalds
snd_trident_write_pan_reg(struct snd_trident * trident,struct snd_trident_voice * voice,unsigned int Pan)597bee1a5beSTakashi Iwai static void snd_trident_write_pan_reg(struct snd_trident * trident,
598bee1a5beSTakashi Iwai struct snd_trident_voice * voice,
599bee1a5beSTakashi Iwai unsigned int Pan)
6001da177e4SLinus Torvalds {
6011da177e4SLinus Torvalds voice->Pan = Pan;
6021da177e4SLinus Torvalds outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
603bee1a5beSTakashi Iwai outb(((voice->GVSel & 0x01) << 7) | (voice->Pan & 0x7f),
604bee1a5beSTakashi Iwai TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 3));
6051da177e4SLinus Torvalds }
6061da177e4SLinus Torvalds
6071da177e4SLinus Torvalds /*---------------------------------------------------------------------------
6081da177e4SLinus Torvalds snd_trident_write_rvol_reg
6091da177e4SLinus Torvalds
6101da177e4SLinus Torvalds Description: This routine will write the new reverb volume
6111da177e4SLinus Torvalds register to hardware.
6121da177e4SLinus Torvalds
613561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
6141da177e4SLinus Torvalds voice - synthesizer voice structure
6151da177e4SLinus Torvalds RVol - new reverb volume
6161da177e4SLinus Torvalds
6171da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
6181da177e4SLinus Torvalds
snd_trident_write_rvol_reg(struct snd_trident * trident,struct snd_trident_voice * voice,unsigned int RVol)619bee1a5beSTakashi Iwai static void snd_trident_write_rvol_reg(struct snd_trident * trident,
620bee1a5beSTakashi Iwai struct snd_trident_voice * voice,
621bee1a5beSTakashi Iwai unsigned int RVol)
6221da177e4SLinus Torvalds {
6231da177e4SLinus Torvalds voice->RVol = RVol;
6241da177e4SLinus Torvalds outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
625bee1a5beSTakashi Iwai outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) |
626bee1a5beSTakashi Iwai (voice->CVol & 0x007f),
627bee1a5beSTakashi Iwai TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ?
628bee1a5beSTakashi Iwai CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL));
6291da177e4SLinus Torvalds }
6301da177e4SLinus Torvalds
6311da177e4SLinus Torvalds /*---------------------------------------------------------------------------
6321da177e4SLinus Torvalds snd_trident_write_cvol_reg
6331da177e4SLinus Torvalds
6341da177e4SLinus Torvalds Description: This routine will write the new chorus volume
6351da177e4SLinus Torvalds register to hardware.
6361da177e4SLinus Torvalds
637561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
6381da177e4SLinus Torvalds voice - synthesizer voice structure
6391da177e4SLinus Torvalds CVol - new chorus volume
6401da177e4SLinus Torvalds
6411da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
6421da177e4SLinus Torvalds
snd_trident_write_cvol_reg(struct snd_trident * trident,struct snd_trident_voice * voice,unsigned int CVol)643bee1a5beSTakashi Iwai static void snd_trident_write_cvol_reg(struct snd_trident * trident,
644bee1a5beSTakashi Iwai struct snd_trident_voice * voice,
645bee1a5beSTakashi Iwai unsigned int CVol)
6461da177e4SLinus Torvalds {
6471da177e4SLinus Torvalds voice->CVol = CVol;
6481da177e4SLinus Torvalds outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
649bee1a5beSTakashi Iwai outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) |
650bee1a5beSTakashi Iwai (voice->CVol & 0x007f),
651bee1a5beSTakashi Iwai TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ?
652bee1a5beSTakashi Iwai CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL));
6531da177e4SLinus Torvalds }
6541da177e4SLinus Torvalds
6551da177e4SLinus Torvalds /*---------------------------------------------------------------------------
6561da177e4SLinus Torvalds snd_trident_convert_rate
6571da177e4SLinus Torvalds
6581da177e4SLinus Torvalds Description: This routine converts rate in HZ to hardware delta value.
6591da177e4SLinus Torvalds
660561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
6611da177e4SLinus Torvalds rate - Real or Virtual channel number.
6621da177e4SLinus Torvalds
6631da177e4SLinus Torvalds Returns: Delta value.
6641da177e4SLinus Torvalds
6651da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
snd_trident_convert_rate(unsigned int rate)6661da177e4SLinus Torvalds static unsigned int snd_trident_convert_rate(unsigned int rate)
6671da177e4SLinus Torvalds {
6681da177e4SLinus Torvalds unsigned int delta;
6691da177e4SLinus Torvalds
6701da177e4SLinus Torvalds // We special case 44100 and 8000 since rounding with the equation
6711da177e4SLinus Torvalds // does not give us an accurate enough value. For 11025 and 22050
6721da177e4SLinus Torvalds // the equation gives us the best answer. All other frequencies will
6731da177e4SLinus Torvalds // also use the equation. JDW
6741da177e4SLinus Torvalds if (rate == 44100)
6751da177e4SLinus Torvalds delta = 0xeb3;
6761da177e4SLinus Torvalds else if (rate == 8000)
6771da177e4SLinus Torvalds delta = 0x2ab;
6781da177e4SLinus Torvalds else if (rate == 48000)
6791da177e4SLinus Torvalds delta = 0x1000;
6801da177e4SLinus Torvalds else
681a8667a3fSLars-Peter Clausen delta = DIV_ROUND_CLOSEST(rate << 12, 48000) & 0x0000ffff;
6821da177e4SLinus Torvalds return delta;
6831da177e4SLinus Torvalds }
6841da177e4SLinus Torvalds
6851da177e4SLinus Torvalds /*---------------------------------------------------------------------------
6861da177e4SLinus Torvalds snd_trident_convert_adc_rate
6871da177e4SLinus Torvalds
6881da177e4SLinus Torvalds Description: This routine converts rate in HZ to hardware delta value.
6891da177e4SLinus Torvalds
690561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
6911da177e4SLinus Torvalds rate - Real or Virtual channel number.
6921da177e4SLinus Torvalds
6931da177e4SLinus Torvalds Returns: Delta value.
6941da177e4SLinus Torvalds
6951da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
snd_trident_convert_adc_rate(unsigned int rate)6961da177e4SLinus Torvalds static unsigned int snd_trident_convert_adc_rate(unsigned int rate)
6971da177e4SLinus Torvalds {
6981da177e4SLinus Torvalds unsigned int delta;
6991da177e4SLinus Torvalds
7001da177e4SLinus Torvalds // We special case 44100 and 8000 since rounding with the equation
7011da177e4SLinus Torvalds // does not give us an accurate enough value. For 11025 and 22050
7021da177e4SLinus Torvalds // the equation gives us the best answer. All other frequencies will
7031da177e4SLinus Torvalds // also use the equation. JDW
7041da177e4SLinus Torvalds if (rate == 44100)
7051da177e4SLinus Torvalds delta = 0x116a;
7061da177e4SLinus Torvalds else if (rate == 8000)
7071da177e4SLinus Torvalds delta = 0x6000;
7081da177e4SLinus Torvalds else if (rate == 48000)
7091da177e4SLinus Torvalds delta = 0x1000;
7101da177e4SLinus Torvalds else
7111da177e4SLinus Torvalds delta = ((48000 << 12) / rate) & 0x0000ffff;
7121da177e4SLinus Torvalds return delta;
7131da177e4SLinus Torvalds }
7141da177e4SLinus Torvalds
7151da177e4SLinus Torvalds /*---------------------------------------------------------------------------
7161da177e4SLinus Torvalds snd_trident_spurious_threshold
7171da177e4SLinus Torvalds
7181da177e4SLinus Torvalds Description: This routine converts rate in HZ to spurious threshold.
7191da177e4SLinus Torvalds
720561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
7211da177e4SLinus Torvalds rate - Real or Virtual channel number.
7221da177e4SLinus Torvalds
7231da177e4SLinus Torvalds Returns: Delta value.
7241da177e4SLinus Torvalds
7251da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
snd_trident_spurious_threshold(unsigned int rate,unsigned int period_size)726bee1a5beSTakashi Iwai static unsigned int snd_trident_spurious_threshold(unsigned int rate,
727bee1a5beSTakashi Iwai unsigned int period_size)
7281da177e4SLinus Torvalds {
7291da177e4SLinus Torvalds unsigned int res = (rate * period_size) / 48000;
7301da177e4SLinus Torvalds if (res < 64)
7311da177e4SLinus Torvalds res = res / 2;
7321da177e4SLinus Torvalds else
7331da177e4SLinus Torvalds res -= 32;
7341da177e4SLinus Torvalds return res;
7351da177e4SLinus Torvalds }
7361da177e4SLinus Torvalds
7371da177e4SLinus Torvalds /*---------------------------------------------------------------------------
7381da177e4SLinus Torvalds snd_trident_control_mode
7391da177e4SLinus Torvalds
7401da177e4SLinus Torvalds Description: This routine returns a control mode for a PCM channel.
7411da177e4SLinus Torvalds
742561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
7431da177e4SLinus Torvalds substream - PCM substream
7441da177e4SLinus Torvalds
7451da177e4SLinus Torvalds Returns: Control value.
7461da177e4SLinus Torvalds
7471da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
snd_trident_control_mode(struct snd_pcm_substream * substream)748bee1a5beSTakashi Iwai static unsigned int snd_trident_control_mode(struct snd_pcm_substream *substream)
7491da177e4SLinus Torvalds {
7501da177e4SLinus Torvalds unsigned int CTRL;
751bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
7521da177e4SLinus Torvalds
7531da177e4SLinus Torvalds /* set ctrl mode
7541da177e4SLinus Torvalds CTRL default: 8-bit (unsigned) mono, loop mode enabled
7551da177e4SLinus Torvalds */
7561da177e4SLinus Torvalds CTRL = 0x00000001;
7571da177e4SLinus Torvalds if (snd_pcm_format_width(runtime->format) == 16)
7581da177e4SLinus Torvalds CTRL |= 0x00000008; // 16-bit data
7591da177e4SLinus Torvalds if (snd_pcm_format_signed(runtime->format))
7601da177e4SLinus Torvalds CTRL |= 0x00000002; // signed data
7611da177e4SLinus Torvalds if (runtime->channels > 1)
7621da177e4SLinus Torvalds CTRL |= 0x00000004; // stereo data
7631da177e4SLinus Torvalds return CTRL;
7641da177e4SLinus Torvalds }
7651da177e4SLinus Torvalds
7661da177e4SLinus Torvalds /*
7671da177e4SLinus Torvalds * PCM part
7681da177e4SLinus Torvalds */
7691da177e4SLinus Torvalds
7701da177e4SLinus Torvalds /*---------------------------------------------------------------------------
7711da177e4SLinus Torvalds snd_trident_allocate_pcm_mem
7721da177e4SLinus Torvalds
7731da177e4SLinus Torvalds Description: Allocate PCM ring buffer for given substream
7741da177e4SLinus Torvalds
7751da177e4SLinus Torvalds Parameters: substream - PCM substream class
7761da177e4SLinus Torvalds hw_params - hardware parameters
7771da177e4SLinus Torvalds
7781da177e4SLinus Torvalds Returns: Error status
7791da177e4SLinus Torvalds
7801da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
7811da177e4SLinus Torvalds
snd_trident_allocate_pcm_mem(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)782bee1a5beSTakashi Iwai static int snd_trident_allocate_pcm_mem(struct snd_pcm_substream *substream,
783bee1a5beSTakashi Iwai struct snd_pcm_hw_params *hw_params)
7841da177e4SLinus Torvalds {
785bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
786bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
787bee1a5beSTakashi Iwai struct snd_trident_voice *voice = runtime->private_data;
7881da177e4SLinus Torvalds
7891da177e4SLinus Torvalds if (trident->tlb.entries) {
790c79eafa0STakashi Iwai if (runtime->buffer_changed) {
7911da177e4SLinus Torvalds if (voice->memblk)
7921da177e4SLinus Torvalds snd_trident_free_pages(trident, voice->memblk);
7931da177e4SLinus Torvalds voice->memblk = snd_trident_alloc_pages(trident, substream);
7941da177e4SLinus Torvalds if (voice->memblk == NULL)
7951da177e4SLinus Torvalds return -ENOMEM;
7961da177e4SLinus Torvalds }
7971da177e4SLinus Torvalds }
7981da177e4SLinus Torvalds return 0;
7991da177e4SLinus Torvalds }
8001da177e4SLinus Torvalds
8011da177e4SLinus Torvalds /*---------------------------------------------------------------------------
8021da177e4SLinus Torvalds snd_trident_allocate_evoice
8031da177e4SLinus Torvalds
8041da177e4SLinus Torvalds Description: Allocate extra voice as interrupt generator
8051da177e4SLinus Torvalds
8061da177e4SLinus Torvalds Parameters: substream - PCM substream class
8071da177e4SLinus Torvalds hw_params - hardware parameters
8081da177e4SLinus Torvalds
8091da177e4SLinus Torvalds Returns: Error status
8101da177e4SLinus Torvalds
8111da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
8121da177e4SLinus Torvalds
snd_trident_allocate_evoice(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)813bee1a5beSTakashi Iwai static int snd_trident_allocate_evoice(struct snd_pcm_substream *substream,
814bee1a5beSTakashi Iwai struct snd_pcm_hw_params *hw_params)
8151da177e4SLinus Torvalds {
816bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
817bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
818bee1a5beSTakashi Iwai struct snd_trident_voice *voice = runtime->private_data;
819bee1a5beSTakashi Iwai struct snd_trident_voice *evoice = voice->extra;
8201da177e4SLinus Torvalds
8211da177e4SLinus Torvalds /* voice management */
8221da177e4SLinus Torvalds
8231da177e4SLinus Torvalds if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) {
8241da177e4SLinus Torvalds if (evoice == NULL) {
8251da177e4SLinus Torvalds evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
8261da177e4SLinus Torvalds if (evoice == NULL)
8271da177e4SLinus Torvalds return -ENOMEM;
8281da177e4SLinus Torvalds voice->extra = evoice;
8291da177e4SLinus Torvalds evoice->substream = substream;
8301da177e4SLinus Torvalds }
8311da177e4SLinus Torvalds } else {
8321da177e4SLinus Torvalds if (evoice != NULL) {
8331da177e4SLinus Torvalds snd_trident_free_voice(trident, evoice);
8341da177e4SLinus Torvalds voice->extra = evoice = NULL;
8351da177e4SLinus Torvalds }
8361da177e4SLinus Torvalds }
8371da177e4SLinus Torvalds
8381da177e4SLinus Torvalds return 0;
8391da177e4SLinus Torvalds }
8401da177e4SLinus Torvalds
8411da177e4SLinus Torvalds /*---------------------------------------------------------------------------
8421da177e4SLinus Torvalds snd_trident_hw_params
8431da177e4SLinus Torvalds
8441da177e4SLinus Torvalds Description: Set the hardware parameters for the playback device.
8451da177e4SLinus Torvalds
8461da177e4SLinus Torvalds Parameters: substream - PCM substream class
8471da177e4SLinus Torvalds hw_params - hardware parameters
8481da177e4SLinus Torvalds
8491da177e4SLinus Torvalds Returns: Error status
8501da177e4SLinus Torvalds
8511da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
8521da177e4SLinus Torvalds
snd_trident_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)853bee1a5beSTakashi Iwai static int snd_trident_hw_params(struct snd_pcm_substream *substream,
854bee1a5beSTakashi Iwai struct snd_pcm_hw_params *hw_params)
8551da177e4SLinus Torvalds {
8561da177e4SLinus Torvalds int err;
8571da177e4SLinus Torvalds
8581da177e4SLinus Torvalds err = snd_trident_allocate_pcm_mem(substream, hw_params);
8591da177e4SLinus Torvalds if (err >= 0)
8601da177e4SLinus Torvalds err = snd_trident_allocate_evoice(substream, hw_params);
8611da177e4SLinus Torvalds return err;
8621da177e4SLinus Torvalds }
8631da177e4SLinus Torvalds
8641da177e4SLinus Torvalds /*---------------------------------------------------------------------------
8651da177e4SLinus Torvalds snd_trident_playback_hw_free
8661da177e4SLinus Torvalds
8671da177e4SLinus Torvalds Description: Release the hardware resources for the playback device.
8681da177e4SLinus Torvalds
8691da177e4SLinus Torvalds Parameters: substream - PCM substream class
8701da177e4SLinus Torvalds
8711da177e4SLinus Torvalds Returns: Error status
8721da177e4SLinus Torvalds
8731da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
8741da177e4SLinus Torvalds
snd_trident_hw_free(struct snd_pcm_substream * substream)875bee1a5beSTakashi Iwai static int snd_trident_hw_free(struct snd_pcm_substream *substream)
8761da177e4SLinus Torvalds {
877bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
878bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
879bee1a5beSTakashi Iwai struct snd_trident_voice *voice = runtime->private_data;
880bee1a5beSTakashi Iwai struct snd_trident_voice *evoice = voice ? voice->extra : NULL;
8811da177e4SLinus Torvalds
8821da177e4SLinus Torvalds if (trident->tlb.entries) {
8831da177e4SLinus Torvalds if (voice && voice->memblk) {
8841da177e4SLinus Torvalds snd_trident_free_pages(trident, voice->memblk);
8851da177e4SLinus Torvalds voice->memblk = NULL;
8861da177e4SLinus Torvalds }
8871da177e4SLinus Torvalds }
8881da177e4SLinus Torvalds if (evoice != NULL) {
8891da177e4SLinus Torvalds snd_trident_free_voice(trident, evoice);
8901da177e4SLinus Torvalds voice->extra = NULL;
8911da177e4SLinus Torvalds }
8921da177e4SLinus Torvalds return 0;
8931da177e4SLinus Torvalds }
8941da177e4SLinus Torvalds
8951da177e4SLinus Torvalds /*---------------------------------------------------------------------------
8961da177e4SLinus Torvalds snd_trident_playback_prepare
8971da177e4SLinus Torvalds
8981da177e4SLinus Torvalds Description: Prepare playback device for playback.
8991da177e4SLinus Torvalds
9001da177e4SLinus Torvalds Parameters: substream - PCM substream class
9011da177e4SLinus Torvalds
9021da177e4SLinus Torvalds Returns: Error status
9031da177e4SLinus Torvalds
9041da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
9051da177e4SLinus Torvalds
snd_trident_playback_prepare(struct snd_pcm_substream * substream)906bee1a5beSTakashi Iwai static int snd_trident_playback_prepare(struct snd_pcm_substream *substream)
9071da177e4SLinus Torvalds {
908bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
909bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
910bee1a5beSTakashi Iwai struct snd_trident_voice *voice = runtime->private_data;
911bee1a5beSTakashi Iwai struct snd_trident_voice *evoice = voice->extra;
912bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[substream->number];
9131da177e4SLinus Torvalds
9141da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
9151da177e4SLinus Torvalds
9161da177e4SLinus Torvalds /* set delta (rate) value */
9171da177e4SLinus Torvalds voice->Delta = snd_trident_convert_rate(runtime->rate);
9181da177e4SLinus Torvalds voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
9191da177e4SLinus Torvalds
9201da177e4SLinus Torvalds /* set Loop Begin Address */
9211da177e4SLinus Torvalds if (voice->memblk)
9221da177e4SLinus Torvalds voice->LBA = voice->memblk->offset;
9231da177e4SLinus Torvalds else
9241da177e4SLinus Torvalds voice->LBA = runtime->dma_addr;
9251da177e4SLinus Torvalds
9261da177e4SLinus Torvalds voice->CSO = 0;
9271da177e4SLinus Torvalds voice->ESO = runtime->buffer_size - 1; /* in samples */
9281da177e4SLinus Torvalds voice->CTRL = snd_trident_control_mode(substream);
9291da177e4SLinus Torvalds voice->FMC = 3;
9301da177e4SLinus Torvalds voice->GVSel = 1;
9311da177e4SLinus Torvalds voice->EC = 0;
9321da177e4SLinus Torvalds voice->Alpha = 0;
9331da177e4SLinus Torvalds voice->FMS = 0;
9341da177e4SLinus Torvalds voice->Vol = mix->vol;
9351da177e4SLinus Torvalds voice->RVol = mix->rvol;
9361da177e4SLinus Torvalds voice->CVol = mix->cvol;
9371da177e4SLinus Torvalds voice->Pan = mix->pan;
9381da177e4SLinus Torvalds voice->Attribute = 0;
9391da177e4SLinus Torvalds #if 0
9401da177e4SLinus Torvalds voice->Attribute = (1<<(30-16))|(2<<(26-16))|
9411da177e4SLinus Torvalds (0<<(24-16))|(0x1f<<(19-16));
9421da177e4SLinus Torvalds #else
9431da177e4SLinus Torvalds voice->Attribute = 0;
9441da177e4SLinus Torvalds #endif
9451da177e4SLinus Torvalds
9461da177e4SLinus Torvalds snd_trident_write_voice_regs(trident, voice);
9471da177e4SLinus Torvalds
9481da177e4SLinus Torvalds if (evoice != NULL) {
9491da177e4SLinus Torvalds evoice->Delta = voice->Delta;
9501da177e4SLinus Torvalds evoice->spurious_threshold = voice->spurious_threshold;
9511da177e4SLinus Torvalds evoice->LBA = voice->LBA;
9521da177e4SLinus Torvalds evoice->CSO = 0;
9531da177e4SLinus Torvalds evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
9541da177e4SLinus Torvalds evoice->CTRL = voice->CTRL;
9551da177e4SLinus Torvalds evoice->FMC = 3;
9561da177e4SLinus Torvalds evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
9571da177e4SLinus Torvalds evoice->EC = 0;
9581da177e4SLinus Torvalds evoice->Alpha = 0;
9591da177e4SLinus Torvalds evoice->FMS = 0;
9601da177e4SLinus Torvalds evoice->Vol = 0x3ff; /* mute */
9611da177e4SLinus Torvalds evoice->RVol = evoice->CVol = 0x7f; /* mute */
9621da177e4SLinus Torvalds evoice->Pan = 0x7f; /* mute */
9631da177e4SLinus Torvalds #if 0
9641da177e4SLinus Torvalds evoice->Attribute = (1<<(30-16))|(2<<(26-16))|
9651da177e4SLinus Torvalds (0<<(24-16))|(0x1f<<(19-16));
9661da177e4SLinus Torvalds #else
9671da177e4SLinus Torvalds evoice->Attribute = 0;
9681da177e4SLinus Torvalds #endif
9691da177e4SLinus Torvalds snd_trident_write_voice_regs(trident, evoice);
9701da177e4SLinus Torvalds evoice->isync2 = 1;
9711da177e4SLinus Torvalds evoice->isync_mark = runtime->period_size;
9721da177e4SLinus Torvalds evoice->ESO = (runtime->period_size * 2) - 1;
9731da177e4SLinus Torvalds }
9741da177e4SLinus Torvalds
9751da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
9761da177e4SLinus Torvalds
9771da177e4SLinus Torvalds return 0;
9781da177e4SLinus Torvalds }
9791da177e4SLinus Torvalds
9801da177e4SLinus Torvalds /*---------------------------------------------------------------------------
9811da177e4SLinus Torvalds snd_trident_capture_hw_params
9821da177e4SLinus Torvalds
9831da177e4SLinus Torvalds Description: Set the hardware parameters for the capture device.
9841da177e4SLinus Torvalds
9851da177e4SLinus Torvalds Parameters: substream - PCM substream class
9861da177e4SLinus Torvalds hw_params - hardware parameters
9871da177e4SLinus Torvalds
9881da177e4SLinus Torvalds Returns: Error status
9891da177e4SLinus Torvalds
9901da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
9911da177e4SLinus Torvalds
snd_trident_capture_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)992bee1a5beSTakashi Iwai static int snd_trident_capture_hw_params(struct snd_pcm_substream *substream,
993bee1a5beSTakashi Iwai struct snd_pcm_hw_params *hw_params)
9941da177e4SLinus Torvalds {
9951da177e4SLinus Torvalds return snd_trident_allocate_pcm_mem(substream, hw_params);
9961da177e4SLinus Torvalds }
9971da177e4SLinus Torvalds
9981da177e4SLinus Torvalds /*---------------------------------------------------------------------------
9991da177e4SLinus Torvalds snd_trident_capture_prepare
10001da177e4SLinus Torvalds
10011da177e4SLinus Torvalds Description: Prepare capture device for playback.
10021da177e4SLinus Torvalds
10031da177e4SLinus Torvalds Parameters: substream - PCM substream class
10041da177e4SLinus Torvalds
10051da177e4SLinus Torvalds Returns: Error status
10061da177e4SLinus Torvalds
10071da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
10081da177e4SLinus Torvalds
snd_trident_capture_prepare(struct snd_pcm_substream * substream)1009bee1a5beSTakashi Iwai static int snd_trident_capture_prepare(struct snd_pcm_substream *substream)
10101da177e4SLinus Torvalds {
1011bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
1012bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
1013bee1a5beSTakashi Iwai struct snd_trident_voice *voice = runtime->private_data;
10141da177e4SLinus Torvalds unsigned int val, ESO_bytes;
10151da177e4SLinus Torvalds
10161da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
10171da177e4SLinus Torvalds
1018421f91d2SUwe Kleine-König // Initialize the channel and set channel Mode
10191da177e4SLinus Torvalds outb(0, TRID_REG(trident, LEGACY_DMAR15));
10201da177e4SLinus Torvalds
10211da177e4SLinus Torvalds // Set DMA channel operation mode register
10221da177e4SLinus Torvalds outb(0x54, TRID_REG(trident, LEGACY_DMAR11));
10231da177e4SLinus Torvalds
10241da177e4SLinus Torvalds // Set channel buffer Address, DMAR0 expects contiguous PCI memory area
10251da177e4SLinus Torvalds voice->LBA = runtime->dma_addr;
10261da177e4SLinus Torvalds outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0));
10271da177e4SLinus Torvalds if (voice->memblk)
10281da177e4SLinus Torvalds voice->LBA = voice->memblk->offset;
10291da177e4SLinus Torvalds
10301da177e4SLinus Torvalds // set ESO
10311da177e4SLinus Torvalds ESO_bytes = snd_pcm_lib_buffer_bytes(substream) - 1;
10321da177e4SLinus Torvalds outb((ESO_bytes & 0x00ff0000) >> 16, TRID_REG(trident, LEGACY_DMAR6));
10331da177e4SLinus Torvalds outw((ESO_bytes & 0x0000ffff), TRID_REG(trident, LEGACY_DMAR4));
10341da177e4SLinus Torvalds ESO_bytes++;
10351da177e4SLinus Torvalds
10361da177e4SLinus Torvalds // Set channel sample rate, 4.12 format
1037a8667a3fSLars-Peter Clausen val = DIV_ROUND_CLOSEST(48000U << 12, runtime->rate);
10381da177e4SLinus Torvalds outw(val, TRID_REG(trident, T4D_SBDELTA_DELTA_R));
10391da177e4SLinus Torvalds
10401da177e4SLinus Torvalds // Set channel interrupt blk length
10411da177e4SLinus Torvalds if (snd_pcm_format_width(runtime->format) == 16) {
10421da177e4SLinus Torvalds val = (unsigned short) ((ESO_bytes >> 1) - 1);
10431da177e4SLinus Torvalds } else {
10441da177e4SLinus Torvalds val = (unsigned short) (ESO_bytes - 1);
10451da177e4SLinus Torvalds }
10461da177e4SLinus Torvalds
10471da177e4SLinus Torvalds outl((val << 16) | val, TRID_REG(trident, T4D_SBBL_SBCL));
10481da177e4SLinus Torvalds
10491da177e4SLinus Torvalds // Right now, set format and start to run captureing,
10501da177e4SLinus Torvalds // continuous run loop enable.
10511da177e4SLinus Torvalds trident->bDMAStart = 0x19; // 0001 1001b
10521da177e4SLinus Torvalds
10531da177e4SLinus Torvalds if (snd_pcm_format_width(runtime->format) == 16)
10541da177e4SLinus Torvalds trident->bDMAStart |= 0x80;
10551da177e4SLinus Torvalds if (snd_pcm_format_signed(runtime->format))
10561da177e4SLinus Torvalds trident->bDMAStart |= 0x20;
10571da177e4SLinus Torvalds if (runtime->channels > 1)
10581da177e4SLinus Torvalds trident->bDMAStart |= 0x40;
10591da177e4SLinus Torvalds
10601da177e4SLinus Torvalds // Prepare capture intr channel
10611da177e4SLinus Torvalds
10621da177e4SLinus Torvalds voice->Delta = snd_trident_convert_rate(runtime->rate);
10631da177e4SLinus Torvalds voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
10641da177e4SLinus Torvalds voice->isync = 1;
10651da177e4SLinus Torvalds voice->isync_mark = runtime->period_size;
10661da177e4SLinus Torvalds voice->isync_max = runtime->buffer_size;
10671da177e4SLinus Torvalds
10681da177e4SLinus Torvalds // Set voice parameters
10691da177e4SLinus Torvalds voice->CSO = 0;
10701da177e4SLinus Torvalds voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1;
10711da177e4SLinus Torvalds voice->CTRL = snd_trident_control_mode(substream);
10721da177e4SLinus Torvalds voice->FMC = 3;
10731da177e4SLinus Torvalds voice->RVol = 0x7f;
10741da177e4SLinus Torvalds voice->CVol = 0x7f;
10751da177e4SLinus Torvalds voice->GVSel = 1;
10761da177e4SLinus Torvalds voice->Pan = 0x7f; /* mute */
10771da177e4SLinus Torvalds voice->Vol = 0x3ff; /* mute */
10781da177e4SLinus Torvalds voice->EC = 0;
10791da177e4SLinus Torvalds voice->Alpha = 0;
10801da177e4SLinus Torvalds voice->FMS = 0;
10811da177e4SLinus Torvalds voice->Attribute = 0;
10821da177e4SLinus Torvalds
10831da177e4SLinus Torvalds snd_trident_write_voice_regs(trident, voice);
10841da177e4SLinus Torvalds
10851da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
10861da177e4SLinus Torvalds return 0;
10871da177e4SLinus Torvalds }
10881da177e4SLinus Torvalds
10891da177e4SLinus Torvalds /*---------------------------------------------------------------------------
10901da177e4SLinus Torvalds snd_trident_si7018_capture_hw_params
10911da177e4SLinus Torvalds
10921da177e4SLinus Torvalds Description: Set the hardware parameters for the capture device.
10931da177e4SLinus Torvalds
10941da177e4SLinus Torvalds Parameters: substream - PCM substream class
10951da177e4SLinus Torvalds hw_params - hardware parameters
10961da177e4SLinus Torvalds
10971da177e4SLinus Torvalds Returns: Error status
10981da177e4SLinus Torvalds
10991da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
11001da177e4SLinus Torvalds
snd_trident_si7018_capture_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)1101bee1a5beSTakashi Iwai static int snd_trident_si7018_capture_hw_params(struct snd_pcm_substream *substream,
1102bee1a5beSTakashi Iwai struct snd_pcm_hw_params *hw_params)
11031da177e4SLinus Torvalds {
11041da177e4SLinus Torvalds return snd_trident_allocate_evoice(substream, hw_params);
11051da177e4SLinus Torvalds }
11061da177e4SLinus Torvalds
11071da177e4SLinus Torvalds /*---------------------------------------------------------------------------
11081da177e4SLinus Torvalds snd_trident_si7018_capture_hw_free
11091da177e4SLinus Torvalds
11101da177e4SLinus Torvalds Description: Release the hardware resources for the capture device.
11111da177e4SLinus Torvalds
11121da177e4SLinus Torvalds Parameters: substream - PCM substream class
11131da177e4SLinus Torvalds
11141da177e4SLinus Torvalds Returns: Error status
11151da177e4SLinus Torvalds
11161da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
11171da177e4SLinus Torvalds
snd_trident_si7018_capture_hw_free(struct snd_pcm_substream * substream)1118bee1a5beSTakashi Iwai static int snd_trident_si7018_capture_hw_free(struct snd_pcm_substream *substream)
11191da177e4SLinus Torvalds {
1120bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
1121bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
1122bee1a5beSTakashi Iwai struct snd_trident_voice *voice = runtime->private_data;
1123bee1a5beSTakashi Iwai struct snd_trident_voice *evoice = voice ? voice->extra : NULL;
11241da177e4SLinus Torvalds
11251da177e4SLinus Torvalds if (evoice != NULL) {
11261da177e4SLinus Torvalds snd_trident_free_voice(trident, evoice);
11271da177e4SLinus Torvalds voice->extra = NULL;
11281da177e4SLinus Torvalds }
11291da177e4SLinus Torvalds return 0;
11301da177e4SLinus Torvalds }
11311da177e4SLinus Torvalds
11321da177e4SLinus Torvalds /*---------------------------------------------------------------------------
11331da177e4SLinus Torvalds snd_trident_si7018_capture_prepare
11341da177e4SLinus Torvalds
11351da177e4SLinus Torvalds Description: Prepare capture device for playback.
11361da177e4SLinus Torvalds
11371da177e4SLinus Torvalds Parameters: substream - PCM substream class
11381da177e4SLinus Torvalds
11391da177e4SLinus Torvalds Returns: Error status
11401da177e4SLinus Torvalds
11411da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
11421da177e4SLinus Torvalds
snd_trident_si7018_capture_prepare(struct snd_pcm_substream * substream)1143bee1a5beSTakashi Iwai static int snd_trident_si7018_capture_prepare(struct snd_pcm_substream *substream)
11441da177e4SLinus Torvalds {
1145bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
1146bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
1147bee1a5beSTakashi Iwai struct snd_trident_voice *voice = runtime->private_data;
1148bee1a5beSTakashi Iwai struct snd_trident_voice *evoice = voice->extra;
11491da177e4SLinus Torvalds
11501da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
11511da177e4SLinus Torvalds
11521da177e4SLinus Torvalds voice->LBA = runtime->dma_addr;
11531da177e4SLinus Torvalds voice->Delta = snd_trident_convert_adc_rate(runtime->rate);
11541da177e4SLinus Torvalds voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
11551da177e4SLinus Torvalds
11561da177e4SLinus Torvalds // Set voice parameters
11571da177e4SLinus Torvalds voice->CSO = 0;
11581da177e4SLinus Torvalds voice->ESO = runtime->buffer_size - 1; /* in samples */
11591da177e4SLinus Torvalds voice->CTRL = snd_trident_control_mode(substream);
11601da177e4SLinus Torvalds voice->FMC = 0;
11611da177e4SLinus Torvalds voice->RVol = 0;
11621da177e4SLinus Torvalds voice->CVol = 0;
11631da177e4SLinus Torvalds voice->GVSel = 1;
11641da177e4SLinus Torvalds voice->Pan = T4D_DEFAULT_PCM_PAN;
11651da177e4SLinus Torvalds voice->Vol = 0;
11661da177e4SLinus Torvalds voice->EC = 0;
11671da177e4SLinus Torvalds voice->Alpha = 0;
11681da177e4SLinus Torvalds voice->FMS = 0;
11691da177e4SLinus Torvalds
11701da177e4SLinus Torvalds voice->Attribute = (2 << (30-16)) |
11711da177e4SLinus Torvalds (2 << (26-16)) |
11721da177e4SLinus Torvalds (2 << (24-16)) |
11731da177e4SLinus Torvalds (1 << (23-16));
11741da177e4SLinus Torvalds
11751da177e4SLinus Torvalds snd_trident_write_voice_regs(trident, voice);
11761da177e4SLinus Torvalds
11771da177e4SLinus Torvalds if (evoice != NULL) {
11781da177e4SLinus Torvalds evoice->Delta = snd_trident_convert_rate(runtime->rate);
11791da177e4SLinus Torvalds evoice->spurious_threshold = voice->spurious_threshold;
11801da177e4SLinus Torvalds evoice->LBA = voice->LBA;
11811da177e4SLinus Torvalds evoice->CSO = 0;
11821da177e4SLinus Torvalds evoice->ESO = (runtime->period_size * 2) + 20 - 1; /* in samples, 20 means correction */
11831da177e4SLinus Torvalds evoice->CTRL = voice->CTRL;
11841da177e4SLinus Torvalds evoice->FMC = 3;
11851da177e4SLinus Torvalds evoice->GVSel = 0;
11861da177e4SLinus Torvalds evoice->EC = 0;
11871da177e4SLinus Torvalds evoice->Alpha = 0;
11881da177e4SLinus Torvalds evoice->FMS = 0;
11891da177e4SLinus Torvalds evoice->Vol = 0x3ff; /* mute */
11901da177e4SLinus Torvalds evoice->RVol = evoice->CVol = 0x7f; /* mute */
11911da177e4SLinus Torvalds evoice->Pan = 0x7f; /* mute */
11921da177e4SLinus Torvalds evoice->Attribute = 0;
11931da177e4SLinus Torvalds snd_trident_write_voice_regs(trident, evoice);
11941da177e4SLinus Torvalds evoice->isync2 = 1;
11951da177e4SLinus Torvalds evoice->isync_mark = runtime->period_size;
11961da177e4SLinus Torvalds evoice->ESO = (runtime->period_size * 2) - 1;
11971da177e4SLinus Torvalds }
11981da177e4SLinus Torvalds
11991da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
12001da177e4SLinus Torvalds return 0;
12011da177e4SLinus Torvalds }
12021da177e4SLinus Torvalds
12031da177e4SLinus Torvalds /*---------------------------------------------------------------------------
12041da177e4SLinus Torvalds snd_trident_foldback_prepare
12051da177e4SLinus Torvalds
12061da177e4SLinus Torvalds Description: Prepare foldback capture device for playback.
12071da177e4SLinus Torvalds
12081da177e4SLinus Torvalds Parameters: substream - PCM substream class
12091da177e4SLinus Torvalds
12101da177e4SLinus Torvalds Returns: Error status
12111da177e4SLinus Torvalds
12121da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
12131da177e4SLinus Torvalds
snd_trident_foldback_prepare(struct snd_pcm_substream * substream)1214bee1a5beSTakashi Iwai static int snd_trident_foldback_prepare(struct snd_pcm_substream *substream)
12151da177e4SLinus Torvalds {
1216bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
1217bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
1218bee1a5beSTakashi Iwai struct snd_trident_voice *voice = runtime->private_data;
1219bee1a5beSTakashi Iwai struct snd_trident_voice *evoice = voice->extra;
12201da177e4SLinus Torvalds
12211da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
12221da177e4SLinus Torvalds
12231da177e4SLinus Torvalds /* Set channel buffer Address */
12241da177e4SLinus Torvalds if (voice->memblk)
12251da177e4SLinus Torvalds voice->LBA = voice->memblk->offset;
12261da177e4SLinus Torvalds else
12271da177e4SLinus Torvalds voice->LBA = runtime->dma_addr;
12281da177e4SLinus Torvalds
12291da177e4SLinus Torvalds /* set target ESO for channel */
12301da177e4SLinus Torvalds voice->ESO = runtime->buffer_size - 1; /* in samples */
12311da177e4SLinus Torvalds
12321da177e4SLinus Torvalds /* set sample rate */
12331da177e4SLinus Torvalds voice->Delta = 0x1000;
12341da177e4SLinus Torvalds voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size);
12351da177e4SLinus Torvalds
12361da177e4SLinus Torvalds voice->CSO = 0;
12371da177e4SLinus Torvalds voice->CTRL = snd_trident_control_mode(substream);
12381da177e4SLinus Torvalds voice->FMC = 3;
12391da177e4SLinus Torvalds voice->RVol = 0x7f;
12401da177e4SLinus Torvalds voice->CVol = 0x7f;
12411da177e4SLinus Torvalds voice->GVSel = 1;
12421da177e4SLinus Torvalds voice->Pan = 0x7f; /* mute */
12431da177e4SLinus Torvalds voice->Vol = 0x3ff; /* mute */
12441da177e4SLinus Torvalds voice->EC = 0;
12451da177e4SLinus Torvalds voice->Alpha = 0;
12461da177e4SLinus Torvalds voice->FMS = 0;
12471da177e4SLinus Torvalds voice->Attribute = 0;
12481da177e4SLinus Torvalds
12491da177e4SLinus Torvalds /* set up capture channel */
12501da177e4SLinus Torvalds outb(((voice->number & 0x3f) | 0x80), TRID_REG(trident, T4D_RCI + voice->foldback_chan));
12511da177e4SLinus Torvalds
12521da177e4SLinus Torvalds snd_trident_write_voice_regs(trident, voice);
12531da177e4SLinus Torvalds
12541da177e4SLinus Torvalds if (evoice != NULL) {
12551da177e4SLinus Torvalds evoice->Delta = voice->Delta;
12561da177e4SLinus Torvalds evoice->spurious_threshold = voice->spurious_threshold;
12571da177e4SLinus Torvalds evoice->LBA = voice->LBA;
12581da177e4SLinus Torvalds evoice->CSO = 0;
12591da177e4SLinus Torvalds evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
12601da177e4SLinus Torvalds evoice->CTRL = voice->CTRL;
12611da177e4SLinus Torvalds evoice->FMC = 3;
12621da177e4SLinus Torvalds evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
12631da177e4SLinus Torvalds evoice->EC = 0;
12641da177e4SLinus Torvalds evoice->Alpha = 0;
12651da177e4SLinus Torvalds evoice->FMS = 0;
12661da177e4SLinus Torvalds evoice->Vol = 0x3ff; /* mute */
12671da177e4SLinus Torvalds evoice->RVol = evoice->CVol = 0x7f; /* mute */
12681da177e4SLinus Torvalds evoice->Pan = 0x7f; /* mute */
12691da177e4SLinus Torvalds evoice->Attribute = 0;
12701da177e4SLinus Torvalds snd_trident_write_voice_regs(trident, evoice);
12711da177e4SLinus Torvalds evoice->isync2 = 1;
12721da177e4SLinus Torvalds evoice->isync_mark = runtime->period_size;
12731da177e4SLinus Torvalds evoice->ESO = (runtime->period_size * 2) - 1;
12741da177e4SLinus Torvalds }
12751da177e4SLinus Torvalds
12761da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
12771da177e4SLinus Torvalds return 0;
12781da177e4SLinus Torvalds }
12791da177e4SLinus Torvalds
12801da177e4SLinus Torvalds /*---------------------------------------------------------------------------
12811da177e4SLinus Torvalds snd_trident_spdif_hw_params
12821da177e4SLinus Torvalds
12831da177e4SLinus Torvalds Description: Set the hardware parameters for the spdif device.
12841da177e4SLinus Torvalds
12851da177e4SLinus Torvalds Parameters: substream - PCM substream class
12861da177e4SLinus Torvalds hw_params - hardware parameters
12871da177e4SLinus Torvalds
12881da177e4SLinus Torvalds Returns: Error status
12891da177e4SLinus Torvalds
12901da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
12911da177e4SLinus Torvalds
snd_trident_spdif_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)1292bee1a5beSTakashi Iwai static int snd_trident_spdif_hw_params(struct snd_pcm_substream *substream,
1293bee1a5beSTakashi Iwai struct snd_pcm_hw_params *hw_params)
12941da177e4SLinus Torvalds {
1295bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
12961da177e4SLinus Torvalds unsigned int old_bits = 0, change = 0;
12971da177e4SLinus Torvalds int err;
12981da177e4SLinus Torvalds
12991da177e4SLinus Torvalds err = snd_trident_allocate_pcm_mem(substream, hw_params);
13001da177e4SLinus Torvalds if (err < 0)
13011da177e4SLinus Torvalds return err;
13021da177e4SLinus Torvalds
13031da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
13041da177e4SLinus Torvalds err = snd_trident_allocate_evoice(substream, hw_params);
13051da177e4SLinus Torvalds if (err < 0)
13061da177e4SLinus Torvalds return err;
13071da177e4SLinus Torvalds }
13081da177e4SLinus Torvalds
13091da177e4SLinus Torvalds /* prepare SPDIF channel */
13101da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
13111da177e4SLinus Torvalds old_bits = trident->spdif_pcm_bits;
13121da177e4SLinus Torvalds if (old_bits & IEC958_AES0_PROFESSIONAL)
13131da177e4SLinus Torvalds trident->spdif_pcm_bits &= ~IEC958_AES0_PRO_FS;
13141da177e4SLinus Torvalds else
13151da177e4SLinus Torvalds trident->spdif_pcm_bits &= ~(IEC958_AES3_CON_FS << 24);
13161da177e4SLinus Torvalds if (params_rate(hw_params) >= 48000) {
13171da177e4SLinus Torvalds trident->spdif_pcm_ctrl = 0x3c; // 48000 Hz
13181da177e4SLinus Torvalds trident->spdif_pcm_bits |=
13191da177e4SLinus Torvalds trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
13201da177e4SLinus Torvalds IEC958_AES0_PRO_FS_48000 :
13211da177e4SLinus Torvalds (IEC958_AES3_CON_FS_48000 << 24);
13221da177e4SLinus Torvalds }
13231da177e4SLinus Torvalds else if (params_rate(hw_params) >= 44100) {
13241da177e4SLinus Torvalds trident->spdif_pcm_ctrl = 0x3e; // 44100 Hz
13251da177e4SLinus Torvalds trident->spdif_pcm_bits |=
13261da177e4SLinus Torvalds trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
13271da177e4SLinus Torvalds IEC958_AES0_PRO_FS_44100 :
13281da177e4SLinus Torvalds (IEC958_AES3_CON_FS_44100 << 24);
13291da177e4SLinus Torvalds }
13301da177e4SLinus Torvalds else {
13311da177e4SLinus Torvalds trident->spdif_pcm_ctrl = 0x3d; // 32000 Hz
13321da177e4SLinus Torvalds trident->spdif_pcm_bits |=
13331da177e4SLinus Torvalds trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
13341da177e4SLinus Torvalds IEC958_AES0_PRO_FS_32000 :
13351da177e4SLinus Torvalds (IEC958_AES3_CON_FS_32000 << 24);
13361da177e4SLinus Torvalds }
13371da177e4SLinus Torvalds change = old_bits != trident->spdif_pcm_bits;
13381da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
13391da177e4SLinus Torvalds
13401da177e4SLinus Torvalds if (change)
13411da177e4SLinus Torvalds snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE, &trident->spdif_pcm_ctl->id);
13421da177e4SLinus Torvalds
13431da177e4SLinus Torvalds return 0;
13441da177e4SLinus Torvalds }
13451da177e4SLinus Torvalds
13461da177e4SLinus Torvalds /*---------------------------------------------------------------------------
13471da177e4SLinus Torvalds snd_trident_spdif_prepare
13481da177e4SLinus Torvalds
13491da177e4SLinus Torvalds Description: Prepare SPDIF device for playback.
13501da177e4SLinus Torvalds
13511da177e4SLinus Torvalds Parameters: substream - PCM substream class
13521da177e4SLinus Torvalds
13531da177e4SLinus Torvalds Returns: Error status
13541da177e4SLinus Torvalds
13551da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
13561da177e4SLinus Torvalds
snd_trident_spdif_prepare(struct snd_pcm_substream * substream)1357bee1a5beSTakashi Iwai static int snd_trident_spdif_prepare(struct snd_pcm_substream *substream)
13581da177e4SLinus Torvalds {
1359bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
1360bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
1361bee1a5beSTakashi Iwai struct snd_trident_voice *voice = runtime->private_data;
1362bee1a5beSTakashi Iwai struct snd_trident_voice *evoice = voice->extra;
1363bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[substream->number];
13641da177e4SLinus Torvalds unsigned int RESO, LBAO;
13651da177e4SLinus Torvalds unsigned int temp;
13661da177e4SLinus Torvalds
13671da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
13681da177e4SLinus Torvalds
13691da177e4SLinus Torvalds if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
13701da177e4SLinus Torvalds
13711da177e4SLinus Torvalds /* set delta (rate) value */
13721da177e4SLinus Torvalds voice->Delta = snd_trident_convert_rate(runtime->rate);
13731da177e4SLinus Torvalds voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
13741da177e4SLinus Torvalds
13751da177e4SLinus Torvalds /* set Loop Back Address */
13761da177e4SLinus Torvalds LBAO = runtime->dma_addr;
13771da177e4SLinus Torvalds if (voice->memblk)
13781da177e4SLinus Torvalds voice->LBA = voice->memblk->offset;
13791da177e4SLinus Torvalds else
13801da177e4SLinus Torvalds voice->LBA = LBAO;
13811da177e4SLinus Torvalds
13821da177e4SLinus Torvalds voice->isync = 1;
13831da177e4SLinus Torvalds voice->isync3 = 1;
13841da177e4SLinus Torvalds voice->isync_mark = runtime->period_size;
13851da177e4SLinus Torvalds voice->isync_max = runtime->buffer_size;
13861da177e4SLinus Torvalds
13871da177e4SLinus Torvalds /* set target ESO for channel */
13881da177e4SLinus Torvalds RESO = runtime->buffer_size - 1;
13891da177e4SLinus Torvalds voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1;
13901da177e4SLinus Torvalds
13911da177e4SLinus Torvalds /* set ctrl mode */
13921da177e4SLinus Torvalds voice->CTRL = snd_trident_control_mode(substream);
13931da177e4SLinus Torvalds
13941da177e4SLinus Torvalds voice->FMC = 3;
13951da177e4SLinus Torvalds voice->RVol = 0x7f;
13961da177e4SLinus Torvalds voice->CVol = 0x7f;
13971da177e4SLinus Torvalds voice->GVSel = 1;
13981da177e4SLinus Torvalds voice->Pan = 0x7f;
13991da177e4SLinus Torvalds voice->Vol = 0x3ff;
14001da177e4SLinus Torvalds voice->EC = 0;
14011da177e4SLinus Torvalds voice->CSO = 0;
14021da177e4SLinus Torvalds voice->Alpha = 0;
14031da177e4SLinus Torvalds voice->FMS = 0;
14041da177e4SLinus Torvalds voice->Attribute = 0;
14051da177e4SLinus Torvalds
14061da177e4SLinus Torvalds /* prepare surrogate IRQ channel */
14071da177e4SLinus Torvalds snd_trident_write_voice_regs(trident, voice);
14081da177e4SLinus Torvalds
14091da177e4SLinus Torvalds outw((RESO & 0xffff), TRID_REG(trident, NX_SPESO));
14101da177e4SLinus Torvalds outb((RESO >> 16), TRID_REG(trident, NX_SPESO + 2));
14111da177e4SLinus Torvalds outl((LBAO & 0xfffffffc), TRID_REG(trident, NX_SPLBA));
14121da177e4SLinus Torvalds outw((voice->CSO & 0xffff), TRID_REG(trident, NX_SPCTRL_SPCSO));
14131da177e4SLinus Torvalds outb((voice->CSO >> 16), TRID_REG(trident, NX_SPCTRL_SPCSO + 2));
14141da177e4SLinus Torvalds
14151da177e4SLinus Torvalds /* set SPDIF setting */
14161da177e4SLinus Torvalds outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
14171da177e4SLinus Torvalds outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
14181da177e4SLinus Torvalds
14191da177e4SLinus Torvalds } else { /* SiS */
14201da177e4SLinus Torvalds
14211da177e4SLinus Torvalds /* set delta (rate) value */
14221da177e4SLinus Torvalds voice->Delta = 0x800;
14231da177e4SLinus Torvalds voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size);
14241da177e4SLinus Torvalds
14251da177e4SLinus Torvalds /* set Loop Begin Address */
14261da177e4SLinus Torvalds if (voice->memblk)
14271da177e4SLinus Torvalds voice->LBA = voice->memblk->offset;
14281da177e4SLinus Torvalds else
14291da177e4SLinus Torvalds voice->LBA = runtime->dma_addr;
14301da177e4SLinus Torvalds
14311da177e4SLinus Torvalds voice->CSO = 0;
14321da177e4SLinus Torvalds voice->ESO = runtime->buffer_size - 1; /* in samples */
14331da177e4SLinus Torvalds voice->CTRL = snd_trident_control_mode(substream);
14341da177e4SLinus Torvalds voice->FMC = 3;
14351da177e4SLinus Torvalds voice->GVSel = 1;
14361da177e4SLinus Torvalds voice->EC = 0;
14371da177e4SLinus Torvalds voice->Alpha = 0;
14381da177e4SLinus Torvalds voice->FMS = 0;
14391da177e4SLinus Torvalds voice->Vol = mix->vol;
14401da177e4SLinus Torvalds voice->RVol = mix->rvol;
14411da177e4SLinus Torvalds voice->CVol = mix->cvol;
14421da177e4SLinus Torvalds voice->Pan = mix->pan;
14431da177e4SLinus Torvalds voice->Attribute = (1<<(30-16))|(7<<(26-16))|
14441da177e4SLinus Torvalds (0<<(24-16))|(0<<(19-16));
14451da177e4SLinus Torvalds
14461da177e4SLinus Torvalds snd_trident_write_voice_regs(trident, voice);
14471da177e4SLinus Torvalds
14481da177e4SLinus Torvalds if (evoice != NULL) {
14491da177e4SLinus Torvalds evoice->Delta = voice->Delta;
14501da177e4SLinus Torvalds evoice->spurious_threshold = voice->spurious_threshold;
14511da177e4SLinus Torvalds evoice->LBA = voice->LBA;
14521da177e4SLinus Torvalds evoice->CSO = 0;
14531da177e4SLinus Torvalds evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
14541da177e4SLinus Torvalds evoice->CTRL = voice->CTRL;
14551da177e4SLinus Torvalds evoice->FMC = 3;
14561da177e4SLinus Torvalds evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
14571da177e4SLinus Torvalds evoice->EC = 0;
14581da177e4SLinus Torvalds evoice->Alpha = 0;
14591da177e4SLinus Torvalds evoice->FMS = 0;
14601da177e4SLinus Torvalds evoice->Vol = 0x3ff; /* mute */
14611da177e4SLinus Torvalds evoice->RVol = evoice->CVol = 0x7f; /* mute */
14621da177e4SLinus Torvalds evoice->Pan = 0x7f; /* mute */
14631da177e4SLinus Torvalds evoice->Attribute = 0;
14641da177e4SLinus Torvalds snd_trident_write_voice_regs(trident, evoice);
14651da177e4SLinus Torvalds evoice->isync2 = 1;
14661da177e4SLinus Torvalds evoice->isync_mark = runtime->period_size;
14671da177e4SLinus Torvalds evoice->ESO = (runtime->period_size * 2) - 1;
14681da177e4SLinus Torvalds }
14691da177e4SLinus Torvalds
14701da177e4SLinus Torvalds outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
14711da177e4SLinus Torvalds temp = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
14721da177e4SLinus Torvalds temp &= ~(1<<19);
14731da177e4SLinus Torvalds outl(temp, TRID_REG(trident, T4D_LFO_GC_CIR));
14741da177e4SLinus Torvalds temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL));
14751da177e4SLinus Torvalds temp |= SPDIF_EN;
14761da177e4SLinus Torvalds outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
14771da177e4SLinus Torvalds }
14781da177e4SLinus Torvalds
14791da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
14801da177e4SLinus Torvalds
14811da177e4SLinus Torvalds return 0;
14821da177e4SLinus Torvalds }
14831da177e4SLinus Torvalds
14841da177e4SLinus Torvalds /*---------------------------------------------------------------------------
14851da177e4SLinus Torvalds snd_trident_trigger
14861da177e4SLinus Torvalds
14871da177e4SLinus Torvalds Description: Start/stop devices
14881da177e4SLinus Torvalds
14891da177e4SLinus Torvalds Parameters: substream - PCM substream class
14901da177e4SLinus Torvalds cmd - trigger command (STOP, GO)
14911da177e4SLinus Torvalds
14921da177e4SLinus Torvalds Returns: Error status
14931da177e4SLinus Torvalds
14941da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
14951da177e4SLinus Torvalds
snd_trident_trigger(struct snd_pcm_substream * substream,int cmd)1496bee1a5beSTakashi Iwai static int snd_trident_trigger(struct snd_pcm_substream *substream,
14971da177e4SLinus Torvalds int cmd)
14981da177e4SLinus Torvalds
14991da177e4SLinus Torvalds {
1500bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
1501bee1a5beSTakashi Iwai struct snd_pcm_substream *s;
15021da177e4SLinus Torvalds unsigned int what, whati, capture_flag, spdif_flag;
1503bee1a5beSTakashi Iwai struct snd_trident_voice *voice, *evoice;
15041da177e4SLinus Torvalds unsigned int val, go;
15051da177e4SLinus Torvalds
15061da177e4SLinus Torvalds switch (cmd) {
15071da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_START:
15081da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
15091da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_RESUME:
15101da177e4SLinus Torvalds go = 1;
15111da177e4SLinus Torvalds break;
15121da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_STOP:
15131da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
15141da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_SUSPEND:
15151da177e4SLinus Torvalds go = 0;
15161da177e4SLinus Torvalds break;
15171da177e4SLinus Torvalds default:
15181da177e4SLinus Torvalds return -EINVAL;
15191da177e4SLinus Torvalds }
15201da177e4SLinus Torvalds what = whati = capture_flag = spdif_flag = 0;
15211da177e4SLinus Torvalds spin_lock(&trident->reg_lock);
15221da177e4SLinus Torvalds val = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
1523ef991b95STakashi Iwai snd_pcm_group_for_each_entry(s, substream) {
1524bee1a5beSTakashi Iwai if ((struct snd_trident *) snd_pcm_substream_chip(s) == trident) {
1525bee1a5beSTakashi Iwai voice = s->runtime->private_data;
15261da177e4SLinus Torvalds evoice = voice->extra;
15271da177e4SLinus Torvalds what |= 1 << (voice->number & 0x1f);
15281da177e4SLinus Torvalds if (evoice == NULL) {
15291da177e4SLinus Torvalds whati |= 1 << (voice->number & 0x1f);
15301da177e4SLinus Torvalds } else {
15311da177e4SLinus Torvalds what |= 1 << (evoice->number & 0x1f);
15321da177e4SLinus Torvalds whati |= 1 << (evoice->number & 0x1f);
15331da177e4SLinus Torvalds if (go)
15341da177e4SLinus Torvalds evoice->stimer = val;
15351da177e4SLinus Torvalds }
15361da177e4SLinus Torvalds if (go) {
15371da177e4SLinus Torvalds voice->running = 1;
15381da177e4SLinus Torvalds voice->stimer = val;
15391da177e4SLinus Torvalds } else {
15401da177e4SLinus Torvalds voice->running = 0;
15411da177e4SLinus Torvalds }
15421da177e4SLinus Torvalds snd_pcm_trigger_done(s, substream);
15431da177e4SLinus Torvalds if (voice->capture)
15441da177e4SLinus Torvalds capture_flag = 1;
15451da177e4SLinus Torvalds if (voice->spdif)
15461da177e4SLinus Torvalds spdif_flag = 1;
15471da177e4SLinus Torvalds }
15481da177e4SLinus Torvalds }
15491da177e4SLinus Torvalds if (spdif_flag) {
15501da177e4SLinus Torvalds if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
15511da177e4SLinus Torvalds outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
1552981bceadSPierre Ossman val = trident->spdif_pcm_ctrl;
1553981bceadSPierre Ossman if (!go)
1554981bceadSPierre Ossman val &= ~(0x28);
1555981bceadSPierre Ossman outb(val, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
15561da177e4SLinus Torvalds } else {
15571da177e4SLinus Torvalds outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
15581da177e4SLinus Torvalds val = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) | SPDIF_EN;
15591da177e4SLinus Torvalds outl(val, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
15601da177e4SLinus Torvalds }
15611da177e4SLinus Torvalds }
15621da177e4SLinus Torvalds if (!go)
15631da177e4SLinus Torvalds outl(what, TRID_REG(trident, T4D_STOP_B));
15641da177e4SLinus Torvalds val = inl(TRID_REG(trident, T4D_AINTEN_B));
15651da177e4SLinus Torvalds if (go) {
15661da177e4SLinus Torvalds val |= whati;
15671da177e4SLinus Torvalds } else {
15681da177e4SLinus Torvalds val &= ~whati;
15691da177e4SLinus Torvalds }
15701da177e4SLinus Torvalds outl(val, TRID_REG(trident, T4D_AINTEN_B));
15711da177e4SLinus Torvalds if (go) {
15721da177e4SLinus Torvalds outl(what, TRID_REG(trident, T4D_START_B));
15731da177e4SLinus Torvalds
15741da177e4SLinus Torvalds if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
15751da177e4SLinus Torvalds outb(trident->bDMAStart, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
15761da177e4SLinus Torvalds } else {
15771da177e4SLinus Torvalds if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
15781da177e4SLinus Torvalds outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
15791da177e4SLinus Torvalds }
15801da177e4SLinus Torvalds spin_unlock(&trident->reg_lock);
15811da177e4SLinus Torvalds return 0;
15821da177e4SLinus Torvalds }
15831da177e4SLinus Torvalds
15841da177e4SLinus Torvalds /*---------------------------------------------------------------------------
15851da177e4SLinus Torvalds snd_trident_playback_pointer
15861da177e4SLinus Torvalds
15871da177e4SLinus Torvalds Description: This routine return the playback position
15881da177e4SLinus Torvalds
15891da177e4SLinus Torvalds Parameters: substream - PCM substream class
15901da177e4SLinus Torvalds
15911da177e4SLinus Torvalds Returns: position of buffer
15921da177e4SLinus Torvalds
15931da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
15941da177e4SLinus Torvalds
snd_trident_playback_pointer(struct snd_pcm_substream * substream)1595bee1a5beSTakashi Iwai static snd_pcm_uframes_t snd_trident_playback_pointer(struct snd_pcm_substream *substream)
15961da177e4SLinus Torvalds {
1597bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
1598bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
1599bee1a5beSTakashi Iwai struct snd_trident_voice *voice = runtime->private_data;
16001da177e4SLinus Torvalds unsigned int cso;
16011da177e4SLinus Torvalds
16021da177e4SLinus Torvalds if (!voice->running)
16031da177e4SLinus Torvalds return 0;
16041da177e4SLinus Torvalds
16051da177e4SLinus Torvalds spin_lock(&trident->reg_lock);
16061da177e4SLinus Torvalds
16071da177e4SLinus Torvalds outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
16081da177e4SLinus Torvalds
16091da177e4SLinus Torvalds if (trident->device != TRIDENT_DEVICE_ID_NX) {
16101da177e4SLinus Torvalds cso = inw(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
16111da177e4SLinus Torvalds } else { // ID_4DWAVE_NX
16121da177e4SLinus Torvalds cso = (unsigned int) inl(TRID_REG(trident, CH_NX_DELTA_CSO)) & 0x00ffffff;
16131da177e4SLinus Torvalds }
16141da177e4SLinus Torvalds
16151da177e4SLinus Torvalds spin_unlock(&trident->reg_lock);
16161da177e4SLinus Torvalds
16171da177e4SLinus Torvalds if (cso >= runtime->buffer_size)
16181da177e4SLinus Torvalds cso = 0;
16191da177e4SLinus Torvalds
16201da177e4SLinus Torvalds return cso;
16211da177e4SLinus Torvalds }
16221da177e4SLinus Torvalds
16231da177e4SLinus Torvalds /*---------------------------------------------------------------------------
16241da177e4SLinus Torvalds snd_trident_capture_pointer
16251da177e4SLinus Torvalds
16261da177e4SLinus Torvalds Description: This routine return the capture position
16271da177e4SLinus Torvalds
1628561de31aSJoe Perches Parameters: pcm1 - PCM device class
16291da177e4SLinus Torvalds
16301da177e4SLinus Torvalds Returns: position of buffer
16311da177e4SLinus Torvalds
16321da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
16331da177e4SLinus Torvalds
snd_trident_capture_pointer(struct snd_pcm_substream * substream)1634bee1a5beSTakashi Iwai static snd_pcm_uframes_t snd_trident_capture_pointer(struct snd_pcm_substream *substream)
16351da177e4SLinus Torvalds {
1636bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
1637bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
1638bee1a5beSTakashi Iwai struct snd_trident_voice *voice = runtime->private_data;
16391da177e4SLinus Torvalds unsigned int result;
16401da177e4SLinus Torvalds
16411da177e4SLinus Torvalds if (!voice->running)
16421da177e4SLinus Torvalds return 0;
16431da177e4SLinus Torvalds
16441da177e4SLinus Torvalds result = inw(TRID_REG(trident, T4D_SBBL_SBCL));
16451da177e4SLinus Torvalds if (runtime->channels > 1)
16461da177e4SLinus Torvalds result >>= 1;
16471da177e4SLinus Torvalds if (result > 0)
16481da177e4SLinus Torvalds result = runtime->buffer_size - result;
16491da177e4SLinus Torvalds
16501da177e4SLinus Torvalds return result;
16511da177e4SLinus Torvalds }
16521da177e4SLinus Torvalds
16531da177e4SLinus Torvalds /*---------------------------------------------------------------------------
16541da177e4SLinus Torvalds snd_trident_spdif_pointer
16551da177e4SLinus Torvalds
16561da177e4SLinus Torvalds Description: This routine return the SPDIF playback position
16571da177e4SLinus Torvalds
16581da177e4SLinus Torvalds Parameters: substream - PCM substream class
16591da177e4SLinus Torvalds
16601da177e4SLinus Torvalds Returns: position of buffer
16611da177e4SLinus Torvalds
16621da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
16631da177e4SLinus Torvalds
snd_trident_spdif_pointer(struct snd_pcm_substream * substream)1664bee1a5beSTakashi Iwai static snd_pcm_uframes_t snd_trident_spdif_pointer(struct snd_pcm_substream *substream)
16651da177e4SLinus Torvalds {
1666bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
1667bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
1668bee1a5beSTakashi Iwai struct snd_trident_voice *voice = runtime->private_data;
16691da177e4SLinus Torvalds unsigned int result;
16701da177e4SLinus Torvalds
16711da177e4SLinus Torvalds if (!voice->running)
16721da177e4SLinus Torvalds return 0;
16731da177e4SLinus Torvalds
16741da177e4SLinus Torvalds result = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff;
16751da177e4SLinus Torvalds
16761da177e4SLinus Torvalds return result;
16771da177e4SLinus Torvalds }
16781da177e4SLinus Torvalds
16791da177e4SLinus Torvalds /*
16801da177e4SLinus Torvalds * Playback support device description
16811da177e4SLinus Torvalds */
16821da177e4SLinus Torvalds
1683b57cac28SBhumika Goyal static const struct snd_pcm_hardware snd_trident_playback =
16841da177e4SLinus Torvalds {
16851da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
16861da177e4SLinus Torvalds SNDRV_PCM_INFO_BLOCK_TRANSFER |
16871da177e4SLinus Torvalds SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
168841e4845cSJaroslav Kysela SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
16891da177e4SLinus Torvalds .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
16901da177e4SLinus Torvalds SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
16911da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
16921da177e4SLinus Torvalds .rate_min = 4000,
16931da177e4SLinus Torvalds .rate_max = 48000,
16941da177e4SLinus Torvalds .channels_min = 1,
16951da177e4SLinus Torvalds .channels_max = 2,
16961da177e4SLinus Torvalds .buffer_bytes_max = (256*1024),
16971da177e4SLinus Torvalds .period_bytes_min = 64,
16981da177e4SLinus Torvalds .period_bytes_max = (256*1024),
16991da177e4SLinus Torvalds .periods_min = 1,
17001da177e4SLinus Torvalds .periods_max = 1024,
17011da177e4SLinus Torvalds .fifo_size = 0,
17021da177e4SLinus Torvalds };
17031da177e4SLinus Torvalds
17041da177e4SLinus Torvalds /*
17051da177e4SLinus Torvalds * Capture support device description
17061da177e4SLinus Torvalds */
17071da177e4SLinus Torvalds
1708b57cac28SBhumika Goyal static const struct snd_pcm_hardware snd_trident_capture =
17091da177e4SLinus Torvalds {
17101da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
17111da177e4SLinus Torvalds SNDRV_PCM_INFO_BLOCK_TRANSFER |
17121da177e4SLinus Torvalds SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
171341e4845cSJaroslav Kysela SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
17141da177e4SLinus Torvalds .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
17151da177e4SLinus Torvalds SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
17161da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
17171da177e4SLinus Torvalds .rate_min = 4000,
17181da177e4SLinus Torvalds .rate_max = 48000,
17191da177e4SLinus Torvalds .channels_min = 1,
17201da177e4SLinus Torvalds .channels_max = 2,
17211da177e4SLinus Torvalds .buffer_bytes_max = (128*1024),
17221da177e4SLinus Torvalds .period_bytes_min = 64,
17231da177e4SLinus Torvalds .period_bytes_max = (128*1024),
17241da177e4SLinus Torvalds .periods_min = 1,
17251da177e4SLinus Torvalds .periods_max = 1024,
17261da177e4SLinus Torvalds .fifo_size = 0,
17271da177e4SLinus Torvalds };
17281da177e4SLinus Torvalds
17291da177e4SLinus Torvalds /*
17301da177e4SLinus Torvalds * Foldback capture support device description
17311da177e4SLinus Torvalds */
17321da177e4SLinus Torvalds
1733b57cac28SBhumika Goyal static const struct snd_pcm_hardware snd_trident_foldback =
17341da177e4SLinus Torvalds {
17351da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
17361da177e4SLinus Torvalds SNDRV_PCM_INFO_BLOCK_TRANSFER |
17371da177e4SLinus Torvalds SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
173841e4845cSJaroslav Kysela SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
17391da177e4SLinus Torvalds .formats = SNDRV_PCM_FMTBIT_S16_LE,
17401da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_48000,
17411da177e4SLinus Torvalds .rate_min = 48000,
17421da177e4SLinus Torvalds .rate_max = 48000,
17431da177e4SLinus Torvalds .channels_min = 2,
17441da177e4SLinus Torvalds .channels_max = 2,
17451da177e4SLinus Torvalds .buffer_bytes_max = (128*1024),
17461da177e4SLinus Torvalds .period_bytes_min = 64,
17471da177e4SLinus Torvalds .period_bytes_max = (128*1024),
17481da177e4SLinus Torvalds .periods_min = 1,
17491da177e4SLinus Torvalds .periods_max = 1024,
17501da177e4SLinus Torvalds .fifo_size = 0,
17511da177e4SLinus Torvalds };
17521da177e4SLinus Torvalds
17531da177e4SLinus Torvalds /*
17541da177e4SLinus Torvalds * SPDIF playback support device description
17551da177e4SLinus Torvalds */
17561da177e4SLinus Torvalds
1757b57cac28SBhumika Goyal static const struct snd_pcm_hardware snd_trident_spdif =
17581da177e4SLinus Torvalds {
17591da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
17601da177e4SLinus Torvalds SNDRV_PCM_INFO_BLOCK_TRANSFER |
17611da177e4SLinus Torvalds SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
176241e4845cSJaroslav Kysela SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
17631da177e4SLinus Torvalds .formats = SNDRV_PCM_FMTBIT_S16_LE,
17641da177e4SLinus Torvalds .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
17651da177e4SLinus Torvalds SNDRV_PCM_RATE_48000),
17661da177e4SLinus Torvalds .rate_min = 32000,
17671da177e4SLinus Torvalds .rate_max = 48000,
17681da177e4SLinus Torvalds .channels_min = 2,
17691da177e4SLinus Torvalds .channels_max = 2,
17701da177e4SLinus Torvalds .buffer_bytes_max = (128*1024),
17711da177e4SLinus Torvalds .period_bytes_min = 64,
17721da177e4SLinus Torvalds .period_bytes_max = (128*1024),
17731da177e4SLinus Torvalds .periods_min = 1,
17741da177e4SLinus Torvalds .periods_max = 1024,
17751da177e4SLinus Torvalds .fifo_size = 0,
17761da177e4SLinus Torvalds };
17771da177e4SLinus Torvalds
1778b57cac28SBhumika Goyal static const struct snd_pcm_hardware snd_trident_spdif_7018 =
17791da177e4SLinus Torvalds {
17801da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
17811da177e4SLinus Torvalds SNDRV_PCM_INFO_BLOCK_TRANSFER |
17821da177e4SLinus Torvalds SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
178341e4845cSJaroslav Kysela SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
17841da177e4SLinus Torvalds .formats = SNDRV_PCM_FMTBIT_S16_LE,
17851da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_48000,
17861da177e4SLinus Torvalds .rate_min = 48000,
17871da177e4SLinus Torvalds .rate_max = 48000,
17881da177e4SLinus Torvalds .channels_min = 2,
17891da177e4SLinus Torvalds .channels_max = 2,
17901da177e4SLinus Torvalds .buffer_bytes_max = (128*1024),
17911da177e4SLinus Torvalds .period_bytes_min = 64,
17921da177e4SLinus Torvalds .period_bytes_max = (128*1024),
17931da177e4SLinus Torvalds .periods_min = 1,
17941da177e4SLinus Torvalds .periods_max = 1024,
17951da177e4SLinus Torvalds .fifo_size = 0,
17961da177e4SLinus Torvalds };
17971da177e4SLinus Torvalds
snd_trident_pcm_free_substream(struct snd_pcm_runtime * runtime)1798bee1a5beSTakashi Iwai static void snd_trident_pcm_free_substream(struct snd_pcm_runtime *runtime)
17991da177e4SLinus Torvalds {
1800bee1a5beSTakashi Iwai struct snd_trident_voice *voice = runtime->private_data;
1801bee1a5beSTakashi Iwai struct snd_trident *trident;
18021da177e4SLinus Torvalds
18031da177e4SLinus Torvalds if (voice) {
18041da177e4SLinus Torvalds trident = voice->trident;
18051da177e4SLinus Torvalds snd_trident_free_voice(trident, voice);
18061da177e4SLinus Torvalds }
18071da177e4SLinus Torvalds }
18081da177e4SLinus Torvalds
snd_trident_playback_open(struct snd_pcm_substream * substream)1809bee1a5beSTakashi Iwai static int snd_trident_playback_open(struct snd_pcm_substream *substream)
18101da177e4SLinus Torvalds {
1811bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
1812bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
1813bee1a5beSTakashi Iwai struct snd_trident_voice *voice;
18141da177e4SLinus Torvalds
18151da177e4SLinus Torvalds voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
18161da177e4SLinus Torvalds if (voice == NULL)
18171da177e4SLinus Torvalds return -EAGAIN;
18181da177e4SLinus Torvalds snd_trident_pcm_mixer_build(trident, voice, substream);
18191da177e4SLinus Torvalds voice->substream = substream;
18201da177e4SLinus Torvalds runtime->private_data = voice;
18211da177e4SLinus Torvalds runtime->private_free = snd_trident_pcm_free_substream;
18221da177e4SLinus Torvalds runtime->hw = snd_trident_playback;
18231da177e4SLinus Torvalds snd_pcm_set_sync(substream);
18241da177e4SLinus Torvalds snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
18251da177e4SLinus Torvalds return 0;
18261da177e4SLinus Torvalds }
18271da177e4SLinus Torvalds
18281da177e4SLinus Torvalds /*---------------------------------------------------------------------------
18291da177e4SLinus Torvalds snd_trident_playback_close
18301da177e4SLinus Torvalds
18311da177e4SLinus Torvalds Description: This routine will close the 4DWave playback device. For now
18321da177e4SLinus Torvalds we will simply free the dma transfer buffer.
18331da177e4SLinus Torvalds
18341da177e4SLinus Torvalds Parameters: substream - PCM substream class
18351da177e4SLinus Torvalds
18361da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
snd_trident_playback_close(struct snd_pcm_substream * substream)1837bee1a5beSTakashi Iwai static int snd_trident_playback_close(struct snd_pcm_substream *substream)
18381da177e4SLinus Torvalds {
1839bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
1840bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
1841bee1a5beSTakashi Iwai struct snd_trident_voice *voice = runtime->private_data;
18421da177e4SLinus Torvalds
18431da177e4SLinus Torvalds snd_trident_pcm_mixer_free(trident, voice, substream);
18441da177e4SLinus Torvalds return 0;
18451da177e4SLinus Torvalds }
18461da177e4SLinus Torvalds
18471da177e4SLinus Torvalds /*---------------------------------------------------------------------------
18481da177e4SLinus Torvalds snd_trident_spdif_open
18491da177e4SLinus Torvalds
18501da177e4SLinus Torvalds Description: This routine will open the 4DWave SPDIF device.
18511da177e4SLinus Torvalds
18521da177e4SLinus Torvalds Parameters: substream - PCM substream class
18531da177e4SLinus Torvalds
18541da177e4SLinus Torvalds Returns: status - success or failure flag
18551da177e4SLinus Torvalds
18561da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
18571da177e4SLinus Torvalds
snd_trident_spdif_open(struct snd_pcm_substream * substream)1858bee1a5beSTakashi Iwai static int snd_trident_spdif_open(struct snd_pcm_substream *substream)
18591da177e4SLinus Torvalds {
1860bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
1861bee1a5beSTakashi Iwai struct snd_trident_voice *voice;
1862bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
18631da177e4SLinus Torvalds
18641da177e4SLinus Torvalds voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
18651da177e4SLinus Torvalds if (voice == NULL)
18661da177e4SLinus Torvalds return -EAGAIN;
18671da177e4SLinus Torvalds voice->spdif = 1;
18681da177e4SLinus Torvalds voice->substream = substream;
18691da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
18701da177e4SLinus Torvalds trident->spdif_pcm_bits = trident->spdif_bits;
18711da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
18721da177e4SLinus Torvalds
18731da177e4SLinus Torvalds runtime->private_data = voice;
18741da177e4SLinus Torvalds runtime->private_free = snd_trident_pcm_free_substream;
18751da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
18761da177e4SLinus Torvalds runtime->hw = snd_trident_spdif;
18771da177e4SLinus Torvalds } else {
18781da177e4SLinus Torvalds runtime->hw = snd_trident_spdif_7018;
18791da177e4SLinus Torvalds }
18801da177e4SLinus Torvalds
18811da177e4SLinus Torvalds trident->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
18821da177e4SLinus Torvalds snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
18831da177e4SLinus Torvalds SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);
18841da177e4SLinus Torvalds
18851da177e4SLinus Torvalds snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
18861da177e4SLinus Torvalds return 0;
18871da177e4SLinus Torvalds }
18881da177e4SLinus Torvalds
18891da177e4SLinus Torvalds
18901da177e4SLinus Torvalds /*---------------------------------------------------------------------------
18911da177e4SLinus Torvalds snd_trident_spdif_close
18921da177e4SLinus Torvalds
18931da177e4SLinus Torvalds Description: This routine will close the 4DWave SPDIF device.
18941da177e4SLinus Torvalds
18951da177e4SLinus Torvalds Parameters: substream - PCM substream class
18961da177e4SLinus Torvalds
18971da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
18981da177e4SLinus Torvalds
snd_trident_spdif_close(struct snd_pcm_substream * substream)1899bee1a5beSTakashi Iwai static int snd_trident_spdif_close(struct snd_pcm_substream *substream)
19001da177e4SLinus Torvalds {
1901bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
19021da177e4SLinus Torvalds unsigned int temp;
19031da177e4SLinus Torvalds
19041da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
19051da177e4SLinus Torvalds // restore default SPDIF setting
19061da177e4SLinus Torvalds if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
19071da177e4SLinus Torvalds outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
19081da177e4SLinus Torvalds outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
19091da177e4SLinus Torvalds } else {
19101da177e4SLinus Torvalds outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
19111da177e4SLinus Torvalds temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL));
19121da177e4SLinus Torvalds if (trident->spdif_ctrl) {
19131da177e4SLinus Torvalds temp |= SPDIF_EN;
19141da177e4SLinus Torvalds } else {
19151da177e4SLinus Torvalds temp &= ~SPDIF_EN;
19161da177e4SLinus Torvalds }
19171da177e4SLinus Torvalds outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
19181da177e4SLinus Torvalds }
19191da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
19201da177e4SLinus Torvalds trident->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
19211da177e4SLinus Torvalds snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
19221da177e4SLinus Torvalds SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);
19231da177e4SLinus Torvalds return 0;
19241da177e4SLinus Torvalds }
19251da177e4SLinus Torvalds
19261da177e4SLinus Torvalds /*---------------------------------------------------------------------------
19271da177e4SLinus Torvalds snd_trident_capture_open
19281da177e4SLinus Torvalds
19291da177e4SLinus Torvalds Description: This routine will open the 4DWave capture device.
19301da177e4SLinus Torvalds
19311da177e4SLinus Torvalds Parameters: substream - PCM substream class
19321da177e4SLinus Torvalds
19331da177e4SLinus Torvalds Returns: status - success or failure flag
19341da177e4SLinus Torvalds
19351da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
19361da177e4SLinus Torvalds
snd_trident_capture_open(struct snd_pcm_substream * substream)1937bee1a5beSTakashi Iwai static int snd_trident_capture_open(struct snd_pcm_substream *substream)
19381da177e4SLinus Torvalds {
1939bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
1940bee1a5beSTakashi Iwai struct snd_trident_voice *voice;
1941bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
19421da177e4SLinus Torvalds
19431da177e4SLinus Torvalds voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
19441da177e4SLinus Torvalds if (voice == NULL)
19451da177e4SLinus Torvalds return -EAGAIN;
19461da177e4SLinus Torvalds voice->capture = 1;
19471da177e4SLinus Torvalds voice->substream = substream;
19481da177e4SLinus Torvalds runtime->private_data = voice;
19491da177e4SLinus Torvalds runtime->private_free = snd_trident_pcm_free_substream;
19501da177e4SLinus Torvalds runtime->hw = snd_trident_capture;
19511da177e4SLinus Torvalds snd_pcm_set_sync(substream);
19521da177e4SLinus Torvalds snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
19531da177e4SLinus Torvalds return 0;
19541da177e4SLinus Torvalds }
19551da177e4SLinus Torvalds
19561da177e4SLinus Torvalds /*---------------------------------------------------------------------------
19571da177e4SLinus Torvalds snd_trident_capture_close
19581da177e4SLinus Torvalds
19591da177e4SLinus Torvalds Description: This routine will close the 4DWave capture device. For now
19601da177e4SLinus Torvalds we will simply free the dma transfer buffer.
19611da177e4SLinus Torvalds
19621da177e4SLinus Torvalds Parameters: substream - PCM substream class
19631da177e4SLinus Torvalds
19641da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
snd_trident_capture_close(struct snd_pcm_substream * substream)1965bee1a5beSTakashi Iwai static int snd_trident_capture_close(struct snd_pcm_substream *substream)
19661da177e4SLinus Torvalds {
19671da177e4SLinus Torvalds return 0;
19681da177e4SLinus Torvalds }
19691da177e4SLinus Torvalds
19701da177e4SLinus Torvalds /*---------------------------------------------------------------------------
19711da177e4SLinus Torvalds snd_trident_foldback_open
19721da177e4SLinus Torvalds
19731da177e4SLinus Torvalds Description: This routine will open the 4DWave foldback capture device.
19741da177e4SLinus Torvalds
19751da177e4SLinus Torvalds Parameters: substream - PCM substream class
19761da177e4SLinus Torvalds
19771da177e4SLinus Torvalds Returns: status - success or failure flag
19781da177e4SLinus Torvalds
19791da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
19801da177e4SLinus Torvalds
snd_trident_foldback_open(struct snd_pcm_substream * substream)1981bee1a5beSTakashi Iwai static int snd_trident_foldback_open(struct snd_pcm_substream *substream)
19821da177e4SLinus Torvalds {
1983bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
1984bee1a5beSTakashi Iwai struct snd_trident_voice *voice;
1985bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
19861da177e4SLinus Torvalds
19871da177e4SLinus Torvalds voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
19881da177e4SLinus Torvalds if (voice == NULL)
19891da177e4SLinus Torvalds return -EAGAIN;
19901da177e4SLinus Torvalds voice->foldback_chan = substream->number;
19911da177e4SLinus Torvalds voice->substream = substream;
19921da177e4SLinus Torvalds runtime->private_data = voice;
19931da177e4SLinus Torvalds runtime->private_free = snd_trident_pcm_free_substream;
19941da177e4SLinus Torvalds runtime->hw = snd_trident_foldback;
19951da177e4SLinus Torvalds snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
19961da177e4SLinus Torvalds return 0;
19971da177e4SLinus Torvalds }
19981da177e4SLinus Torvalds
19991da177e4SLinus Torvalds /*---------------------------------------------------------------------------
20001da177e4SLinus Torvalds snd_trident_foldback_close
20011da177e4SLinus Torvalds
20021da177e4SLinus Torvalds Description: This routine will close the 4DWave foldback capture device.
20031da177e4SLinus Torvalds For now we will simply free the dma transfer buffer.
20041da177e4SLinus Torvalds
20051da177e4SLinus Torvalds Parameters: substream - PCM substream class
20061da177e4SLinus Torvalds
20071da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
snd_trident_foldback_close(struct snd_pcm_substream * substream)2008bee1a5beSTakashi Iwai static int snd_trident_foldback_close(struct snd_pcm_substream *substream)
20091da177e4SLinus Torvalds {
2010bee1a5beSTakashi Iwai struct snd_trident *trident = snd_pcm_substream_chip(substream);
2011bee1a5beSTakashi Iwai struct snd_trident_voice *voice;
2012bee1a5beSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
2013bee1a5beSTakashi Iwai voice = runtime->private_data;
20141da177e4SLinus Torvalds
20151da177e4SLinus Torvalds /* stop capture channel */
20161da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
20171da177e4SLinus Torvalds outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan));
20181da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
20191da177e4SLinus Torvalds return 0;
20201da177e4SLinus Torvalds }
20211da177e4SLinus Torvalds
20221da177e4SLinus Torvalds /*---------------------------------------------------------------------------
20231da177e4SLinus Torvalds PCM operations
20241da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
20251da177e4SLinus Torvalds
20266769e988SJulia Lawall static const struct snd_pcm_ops snd_trident_playback_ops = {
20271da177e4SLinus Torvalds .open = snd_trident_playback_open,
20281da177e4SLinus Torvalds .close = snd_trident_playback_close,
20291da177e4SLinus Torvalds .hw_params = snd_trident_hw_params,
20301da177e4SLinus Torvalds .hw_free = snd_trident_hw_free,
20311da177e4SLinus Torvalds .prepare = snd_trident_playback_prepare,
20321da177e4SLinus Torvalds .trigger = snd_trident_trigger,
20331da177e4SLinus Torvalds .pointer = snd_trident_playback_pointer,
20341da177e4SLinus Torvalds };
20351da177e4SLinus Torvalds
20366769e988SJulia Lawall static const struct snd_pcm_ops snd_trident_nx_playback_ops = {
20371da177e4SLinus Torvalds .open = snd_trident_playback_open,
20381da177e4SLinus Torvalds .close = snd_trident_playback_close,
20391da177e4SLinus Torvalds .hw_params = snd_trident_hw_params,
20401da177e4SLinus Torvalds .hw_free = snd_trident_hw_free,
20411da177e4SLinus Torvalds .prepare = snd_trident_playback_prepare,
20421da177e4SLinus Torvalds .trigger = snd_trident_trigger,
20431da177e4SLinus Torvalds .pointer = snd_trident_playback_pointer,
20441da177e4SLinus Torvalds };
20451da177e4SLinus Torvalds
2046841c1ea0SArvind Yadav static const struct snd_pcm_ops snd_trident_capture_ops = {
20471da177e4SLinus Torvalds .open = snd_trident_capture_open,
20481da177e4SLinus Torvalds .close = snd_trident_capture_close,
20491da177e4SLinus Torvalds .hw_params = snd_trident_capture_hw_params,
20501da177e4SLinus Torvalds .hw_free = snd_trident_hw_free,
20511da177e4SLinus Torvalds .prepare = snd_trident_capture_prepare,
20521da177e4SLinus Torvalds .trigger = snd_trident_trigger,
20531da177e4SLinus Torvalds .pointer = snd_trident_capture_pointer,
20541da177e4SLinus Torvalds };
20551da177e4SLinus Torvalds
2056841c1ea0SArvind Yadav static const struct snd_pcm_ops snd_trident_si7018_capture_ops = {
20571da177e4SLinus Torvalds .open = snd_trident_capture_open,
20581da177e4SLinus Torvalds .close = snd_trident_capture_close,
20591da177e4SLinus Torvalds .hw_params = snd_trident_si7018_capture_hw_params,
20601da177e4SLinus Torvalds .hw_free = snd_trident_si7018_capture_hw_free,
20611da177e4SLinus Torvalds .prepare = snd_trident_si7018_capture_prepare,
20621da177e4SLinus Torvalds .trigger = snd_trident_trigger,
20631da177e4SLinus Torvalds .pointer = snd_trident_playback_pointer,
20641da177e4SLinus Torvalds };
20651da177e4SLinus Torvalds
20666769e988SJulia Lawall static const struct snd_pcm_ops snd_trident_foldback_ops = {
20671da177e4SLinus Torvalds .open = snd_trident_foldback_open,
20681da177e4SLinus Torvalds .close = snd_trident_foldback_close,
20691da177e4SLinus Torvalds .hw_params = snd_trident_hw_params,
20701da177e4SLinus Torvalds .hw_free = snd_trident_hw_free,
20711da177e4SLinus Torvalds .prepare = snd_trident_foldback_prepare,
20721da177e4SLinus Torvalds .trigger = snd_trident_trigger,
20731da177e4SLinus Torvalds .pointer = snd_trident_playback_pointer,
20741da177e4SLinus Torvalds };
20751da177e4SLinus Torvalds
20766769e988SJulia Lawall static const struct snd_pcm_ops snd_trident_nx_foldback_ops = {
20771da177e4SLinus Torvalds .open = snd_trident_foldback_open,
20781da177e4SLinus Torvalds .close = snd_trident_foldback_close,
20791da177e4SLinus Torvalds .hw_params = snd_trident_hw_params,
20801da177e4SLinus Torvalds .hw_free = snd_trident_hw_free,
20811da177e4SLinus Torvalds .prepare = snd_trident_foldback_prepare,
20821da177e4SLinus Torvalds .trigger = snd_trident_trigger,
20831da177e4SLinus Torvalds .pointer = snd_trident_playback_pointer,
20841da177e4SLinus Torvalds };
20851da177e4SLinus Torvalds
20866769e988SJulia Lawall static const struct snd_pcm_ops snd_trident_spdif_ops = {
20871da177e4SLinus Torvalds .open = snd_trident_spdif_open,
20881da177e4SLinus Torvalds .close = snd_trident_spdif_close,
20891da177e4SLinus Torvalds .hw_params = snd_trident_spdif_hw_params,
20901da177e4SLinus Torvalds .hw_free = snd_trident_hw_free,
20911da177e4SLinus Torvalds .prepare = snd_trident_spdif_prepare,
20921da177e4SLinus Torvalds .trigger = snd_trident_trigger,
20931da177e4SLinus Torvalds .pointer = snd_trident_spdif_pointer,
20941da177e4SLinus Torvalds };
20951da177e4SLinus Torvalds
20966769e988SJulia Lawall static const struct snd_pcm_ops snd_trident_spdif_7018_ops = {
20971da177e4SLinus Torvalds .open = snd_trident_spdif_open,
20981da177e4SLinus Torvalds .close = snd_trident_spdif_close,
20991da177e4SLinus Torvalds .hw_params = snd_trident_spdif_hw_params,
21001da177e4SLinus Torvalds .hw_free = snd_trident_hw_free,
21011da177e4SLinus Torvalds .prepare = snd_trident_spdif_prepare,
21021da177e4SLinus Torvalds .trigger = snd_trident_trigger,
21031da177e4SLinus Torvalds .pointer = snd_trident_playback_pointer,
21041da177e4SLinus Torvalds };
21051da177e4SLinus Torvalds
21061da177e4SLinus Torvalds /*---------------------------------------------------------------------------
21071da177e4SLinus Torvalds snd_trident_pcm
21081da177e4SLinus Torvalds
21091da177e4SLinus Torvalds Description: This routine registers the 4DWave device for PCM support.
21101da177e4SLinus Torvalds
2111561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
21121da177e4SLinus Torvalds
21131da177e4SLinus Torvalds Returns: None
21141da177e4SLinus Torvalds
21151da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
21161da177e4SLinus Torvalds
snd_trident_pcm(struct snd_trident * trident,int device)21171b16416fSLars-Peter Clausen int snd_trident_pcm(struct snd_trident *trident, int device)
21181da177e4SLinus Torvalds {
2119bee1a5beSTakashi Iwai struct snd_pcm *pcm;
21201da177e4SLinus Torvalds int err;
21211da177e4SLinus Torvalds
212234b946eeSTakashi Iwai err = snd_pcm_new(trident->card, "trident_dx_nx", device, trident->ChanPCM, 1, &pcm);
212334b946eeSTakashi Iwai if (err < 0)
21241da177e4SLinus Torvalds return err;
21251da177e4SLinus Torvalds
21261da177e4SLinus Torvalds pcm->private_data = trident;
21271da177e4SLinus Torvalds
21281da177e4SLinus Torvalds if (trident->tlb.entries) {
21291da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_nx_playback_ops);
21301da177e4SLinus Torvalds } else {
21311da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops);
21321da177e4SLinus Torvalds }
21331da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
21341da177e4SLinus Torvalds trident->device != TRIDENT_DEVICE_ID_SI7018 ?
21351da177e4SLinus Torvalds &snd_trident_capture_ops :
21361da177e4SLinus Torvalds &snd_trident_si7018_capture_ops);
21371da177e4SLinus Torvalds
21381da177e4SLinus Torvalds pcm->info_flags = 0;
21391da177e4SLinus Torvalds pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
21401da177e4SLinus Torvalds strcpy(pcm->name, "Trident 4DWave");
21411da177e4SLinus Torvalds trident->pcm = pcm;
21421da177e4SLinus Torvalds
21431da177e4SLinus Torvalds if (trident->tlb.entries) {
2144bee1a5beSTakashi Iwai struct snd_pcm_substream *substream;
21451da177e4SLinus Torvalds for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
2146c79eafa0STakashi Iwai snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV_SG,
21476974f8adSTakashi Iwai &trident->pci->dev,
21481da177e4SLinus Torvalds 64*1024, 128*1024);
2149c79eafa0STakashi Iwai snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
21506974f8adSTakashi Iwai SNDRV_DMA_TYPE_DEV,
21516974f8adSTakashi Iwai &trident->pci->dev,
21521da177e4SLinus Torvalds 64*1024, 128*1024);
21531da177e4SLinus Torvalds } else {
2154c79eafa0STakashi Iwai snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
21556974f8adSTakashi Iwai &trident->pci->dev,
21566974f8adSTakashi Iwai 64*1024, 128*1024);
21571da177e4SLinus Torvalds }
21581da177e4SLinus Torvalds
21591da177e4SLinus Torvalds return 0;
21601da177e4SLinus Torvalds }
21611da177e4SLinus Torvalds
21621da177e4SLinus Torvalds /*---------------------------------------------------------------------------
21631da177e4SLinus Torvalds snd_trident_foldback_pcm
21641da177e4SLinus Torvalds
21651da177e4SLinus Torvalds Description: This routine registers the 4DWave device for foldback PCM support.
21661da177e4SLinus Torvalds
2167561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
21681da177e4SLinus Torvalds
21691da177e4SLinus Torvalds Returns: None
21701da177e4SLinus Torvalds
21711da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
21721da177e4SLinus Torvalds
snd_trident_foldback_pcm(struct snd_trident * trident,int device)21731b16416fSLars-Peter Clausen int snd_trident_foldback_pcm(struct snd_trident *trident, int device)
21741da177e4SLinus Torvalds {
2175bee1a5beSTakashi Iwai struct snd_pcm *foldback;
21761da177e4SLinus Torvalds int err;
21771da177e4SLinus Torvalds int num_chan = 3;
2178bee1a5beSTakashi Iwai struct snd_pcm_substream *substream;
21791da177e4SLinus Torvalds
21801da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_NX)
21811da177e4SLinus Torvalds num_chan = 4;
218234b946eeSTakashi Iwai err = snd_pcm_new(trident->card, "trident_dx_nx", device, 0, num_chan, &foldback);
218334b946eeSTakashi Iwai if (err < 0)
21841da177e4SLinus Torvalds return err;
21851da177e4SLinus Torvalds
21861da177e4SLinus Torvalds foldback->private_data = trident;
21871da177e4SLinus Torvalds if (trident->tlb.entries)
21881da177e4SLinus Torvalds snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_nx_foldback_ops);
21891da177e4SLinus Torvalds else
21901da177e4SLinus Torvalds snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops);
21911da177e4SLinus Torvalds foldback->info_flags = 0;
21921da177e4SLinus Torvalds strcpy(foldback->name, "Trident 4DWave");
21931da177e4SLinus Torvalds substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
21941da177e4SLinus Torvalds strcpy(substream->name, "Front Mixer");
21951da177e4SLinus Torvalds substream = substream->next;
21961da177e4SLinus Torvalds strcpy(substream->name, "Reverb Mixer");
21971da177e4SLinus Torvalds substream = substream->next;
21981da177e4SLinus Torvalds strcpy(substream->name, "Chorus Mixer");
21991da177e4SLinus Torvalds if (num_chan == 4) {
22001da177e4SLinus Torvalds substream = substream->next;
22011da177e4SLinus Torvalds strcpy(substream->name, "Second AC'97 ADC");
22021da177e4SLinus Torvalds }
22031da177e4SLinus Torvalds trident->foldback = foldback;
22041da177e4SLinus Torvalds
22051da177e4SLinus Torvalds if (trident->tlb.entries)
2206c79eafa0STakashi Iwai snd_pcm_set_managed_buffer_all(foldback, SNDRV_DMA_TYPE_DEV_SG,
22076974f8adSTakashi Iwai &trident->pci->dev,
22086974f8adSTakashi Iwai 0, 128*1024);
22091da177e4SLinus Torvalds else
2210c79eafa0STakashi Iwai snd_pcm_set_managed_buffer_all(foldback, SNDRV_DMA_TYPE_DEV,
22116974f8adSTakashi Iwai &trident->pci->dev,
22126974f8adSTakashi Iwai 64*1024, 128*1024);
22131da177e4SLinus Torvalds
22141da177e4SLinus Torvalds return 0;
22151da177e4SLinus Torvalds }
22161da177e4SLinus Torvalds
22171da177e4SLinus Torvalds /*---------------------------------------------------------------------------
22181da177e4SLinus Torvalds snd_trident_spdif
22191da177e4SLinus Torvalds
22201da177e4SLinus Torvalds Description: This routine registers the 4DWave-NX device for SPDIF support.
22211da177e4SLinus Torvalds
2222561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave-NX.
22231da177e4SLinus Torvalds
22241da177e4SLinus Torvalds Returns: None
22251da177e4SLinus Torvalds
22261da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
22271da177e4SLinus Torvalds
snd_trident_spdif_pcm(struct snd_trident * trident,int device)22281b16416fSLars-Peter Clausen int snd_trident_spdif_pcm(struct snd_trident *trident, int device)
22291da177e4SLinus Torvalds {
2230bee1a5beSTakashi Iwai struct snd_pcm *spdif;
22311da177e4SLinus Torvalds int err;
22321da177e4SLinus Torvalds
223334b946eeSTakashi Iwai err = snd_pcm_new(trident->card, "trident_dx_nx IEC958", device, 1, 0, &spdif);
223434b946eeSTakashi Iwai if (err < 0)
22351da177e4SLinus Torvalds return err;
22361da177e4SLinus Torvalds
22371da177e4SLinus Torvalds spdif->private_data = trident;
22381da177e4SLinus Torvalds if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
22391da177e4SLinus Torvalds snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops);
22401da177e4SLinus Torvalds } else {
22411da177e4SLinus Torvalds snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_7018_ops);
22421da177e4SLinus Torvalds }
22431da177e4SLinus Torvalds spdif->info_flags = 0;
22441da177e4SLinus Torvalds strcpy(spdif->name, "Trident 4DWave IEC958");
22451da177e4SLinus Torvalds trident->spdif = spdif;
22461da177e4SLinus Torvalds
2247c79eafa0STakashi Iwai snd_pcm_set_managed_buffer_all(spdif, SNDRV_DMA_TYPE_DEV,
2248c79eafa0STakashi Iwai &trident->pci->dev, 64*1024, 128*1024);
22491da177e4SLinus Torvalds
22501da177e4SLinus Torvalds return 0;
22511da177e4SLinus Torvalds }
22521da177e4SLinus Torvalds
22531da177e4SLinus Torvalds /*
22541da177e4SLinus Torvalds * Mixer part
22551da177e4SLinus Torvalds */
22561da177e4SLinus Torvalds
22571da177e4SLinus Torvalds
22581da177e4SLinus Torvalds /*---------------------------------------------------------------------------
22591da177e4SLinus Torvalds snd_trident_spdif_control
22601da177e4SLinus Torvalds
22611da177e4SLinus Torvalds Description: enable/disable S/PDIF out from ac97 mixer
22621da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
22631da177e4SLinus Torvalds
2264a5ce8890STakashi Iwai #define snd_trident_spdif_control_info snd_ctl_boolean_mono_info
22651da177e4SLinus Torvalds
snd_trident_spdif_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2266bee1a5beSTakashi Iwai static int snd_trident_spdif_control_get(struct snd_kcontrol *kcontrol,
2267bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
22681da177e4SLinus Torvalds {
2269bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
22701da177e4SLinus Torvalds unsigned char val;
22711da177e4SLinus Torvalds
22721da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
22731da177e4SLinus Torvalds val = trident->spdif_ctrl;
22741da177e4SLinus Torvalds ucontrol->value.integer.value[0] = val == kcontrol->private_value;
22751da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
22761da177e4SLinus Torvalds return 0;
22771da177e4SLinus Torvalds }
22781da177e4SLinus Torvalds
snd_trident_spdif_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2279bee1a5beSTakashi Iwai static int snd_trident_spdif_control_put(struct snd_kcontrol *kcontrol,
2280bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
22811da177e4SLinus Torvalds {
2282bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
22831da177e4SLinus Torvalds unsigned char val;
22841da177e4SLinus Torvalds int change;
22851da177e4SLinus Torvalds
22861da177e4SLinus Torvalds val = ucontrol->value.integer.value[0] ? (unsigned char) kcontrol->private_value : 0x00;
22871da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
22881da177e4SLinus Torvalds /* S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled */
22891da177e4SLinus Torvalds change = trident->spdif_ctrl != val;
22901da177e4SLinus Torvalds trident->spdif_ctrl = val;
22911da177e4SLinus Torvalds if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
22921da177e4SLinus Torvalds if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) {
22931da177e4SLinus Torvalds outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
22941da177e4SLinus Torvalds outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
22951da177e4SLinus Torvalds }
22961da177e4SLinus Torvalds } else {
22971da177e4SLinus Torvalds if (trident->spdif == NULL) {
22981da177e4SLinus Torvalds unsigned int temp;
22991da177e4SLinus Torvalds outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
23001da177e4SLinus Torvalds temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & ~SPDIF_EN;
23011da177e4SLinus Torvalds if (val)
23021da177e4SLinus Torvalds temp |= SPDIF_EN;
23031da177e4SLinus Torvalds outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
23041da177e4SLinus Torvalds }
23051da177e4SLinus Torvalds }
23061da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
23071da177e4SLinus Torvalds return change;
23081da177e4SLinus Torvalds }
23091da177e4SLinus Torvalds
2310f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_trident_spdif_control =
23111da177e4SLinus Torvalds {
23121da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
23131da177e4SLinus Torvalds .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
23141da177e4SLinus Torvalds .info = snd_trident_spdif_control_info,
23151da177e4SLinus Torvalds .get = snd_trident_spdif_control_get,
23161da177e4SLinus Torvalds .put = snd_trident_spdif_control_put,
23171da177e4SLinus Torvalds .private_value = 0x28,
23181da177e4SLinus Torvalds };
23191da177e4SLinus Torvalds
23201da177e4SLinus Torvalds /*---------------------------------------------------------------------------
23211da177e4SLinus Torvalds snd_trident_spdif_default
23221da177e4SLinus Torvalds
23231da177e4SLinus Torvalds Description: put/get the S/PDIF default settings
23241da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
23251da177e4SLinus Torvalds
snd_trident_spdif_default_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2326bee1a5beSTakashi Iwai static int snd_trident_spdif_default_info(struct snd_kcontrol *kcontrol,
2327bee1a5beSTakashi Iwai struct snd_ctl_elem_info *uinfo)
23281da177e4SLinus Torvalds {
23291da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
23301da177e4SLinus Torvalds uinfo->count = 1;
23311da177e4SLinus Torvalds return 0;
23321da177e4SLinus Torvalds }
23331da177e4SLinus Torvalds
snd_trident_spdif_default_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2334bee1a5beSTakashi Iwai static int snd_trident_spdif_default_get(struct snd_kcontrol *kcontrol,
2335bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
23361da177e4SLinus Torvalds {
2337bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
23381da177e4SLinus Torvalds
23391da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
23401da177e4SLinus Torvalds ucontrol->value.iec958.status[0] = (trident->spdif_bits >> 0) & 0xff;
23411da177e4SLinus Torvalds ucontrol->value.iec958.status[1] = (trident->spdif_bits >> 8) & 0xff;
23421da177e4SLinus Torvalds ucontrol->value.iec958.status[2] = (trident->spdif_bits >> 16) & 0xff;
23431da177e4SLinus Torvalds ucontrol->value.iec958.status[3] = (trident->spdif_bits >> 24) & 0xff;
23441da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
23451da177e4SLinus Torvalds return 0;
23461da177e4SLinus Torvalds }
23471da177e4SLinus Torvalds
snd_trident_spdif_default_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2348bee1a5beSTakashi Iwai static int snd_trident_spdif_default_put(struct snd_kcontrol *kcontrol,
2349bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
23501da177e4SLinus Torvalds {
2351bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
23521da177e4SLinus Torvalds unsigned int val;
23531da177e4SLinus Torvalds int change;
23541da177e4SLinus Torvalds
23551da177e4SLinus Torvalds val = (ucontrol->value.iec958.status[0] << 0) |
23561da177e4SLinus Torvalds (ucontrol->value.iec958.status[1] << 8) |
23571da177e4SLinus Torvalds (ucontrol->value.iec958.status[2] << 16) |
23581da177e4SLinus Torvalds (ucontrol->value.iec958.status[3] << 24);
23591da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
23601da177e4SLinus Torvalds change = trident->spdif_bits != val;
23611da177e4SLinus Torvalds trident->spdif_bits = val;
23621da177e4SLinus Torvalds if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
23631da177e4SLinus Torvalds if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0)
23641da177e4SLinus Torvalds outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
23651da177e4SLinus Torvalds } else {
23661da177e4SLinus Torvalds if (trident->spdif == NULL)
23671da177e4SLinus Torvalds outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
23681da177e4SLinus Torvalds }
23691da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
23701da177e4SLinus Torvalds return change;
23711da177e4SLinus Torvalds }
23721da177e4SLinus Torvalds
2373f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_trident_spdif_default =
23741da177e4SLinus Torvalds {
23751da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_PCM,
23761da177e4SLinus Torvalds .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
23771da177e4SLinus Torvalds .info = snd_trident_spdif_default_info,
23781da177e4SLinus Torvalds .get = snd_trident_spdif_default_get,
23791da177e4SLinus Torvalds .put = snd_trident_spdif_default_put
23801da177e4SLinus Torvalds };
23811da177e4SLinus Torvalds
23821da177e4SLinus Torvalds /*---------------------------------------------------------------------------
23831da177e4SLinus Torvalds snd_trident_spdif_mask
23841da177e4SLinus Torvalds
23851da177e4SLinus Torvalds Description: put/get the S/PDIF mask
23861da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
23871da177e4SLinus Torvalds
snd_trident_spdif_mask_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2388bee1a5beSTakashi Iwai static int snd_trident_spdif_mask_info(struct snd_kcontrol *kcontrol,
2389bee1a5beSTakashi Iwai struct snd_ctl_elem_info *uinfo)
23901da177e4SLinus Torvalds {
23911da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
23921da177e4SLinus Torvalds uinfo->count = 1;
23931da177e4SLinus Torvalds return 0;
23941da177e4SLinus Torvalds }
23951da177e4SLinus Torvalds
snd_trident_spdif_mask_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2396bee1a5beSTakashi Iwai static int snd_trident_spdif_mask_get(struct snd_kcontrol *kcontrol,
2397bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
23981da177e4SLinus Torvalds {
23991da177e4SLinus Torvalds ucontrol->value.iec958.status[0] = 0xff;
24001da177e4SLinus Torvalds ucontrol->value.iec958.status[1] = 0xff;
24011da177e4SLinus Torvalds ucontrol->value.iec958.status[2] = 0xff;
24021da177e4SLinus Torvalds ucontrol->value.iec958.status[3] = 0xff;
24031da177e4SLinus Torvalds return 0;
24041da177e4SLinus Torvalds }
24051da177e4SLinus Torvalds
2406f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_trident_spdif_mask =
24071da177e4SLinus Torvalds {
24081da177e4SLinus Torvalds .access = SNDRV_CTL_ELEM_ACCESS_READ,
24091da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_PCM,
24101da177e4SLinus Torvalds .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
24111da177e4SLinus Torvalds .info = snd_trident_spdif_mask_info,
24121da177e4SLinus Torvalds .get = snd_trident_spdif_mask_get,
24131da177e4SLinus Torvalds };
24141da177e4SLinus Torvalds
24151da177e4SLinus Torvalds /*---------------------------------------------------------------------------
24161da177e4SLinus Torvalds snd_trident_spdif_stream
24171da177e4SLinus Torvalds
24181da177e4SLinus Torvalds Description: put/get the S/PDIF stream settings
24191da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
24201da177e4SLinus Torvalds
snd_trident_spdif_stream_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2421bee1a5beSTakashi Iwai static int snd_trident_spdif_stream_info(struct snd_kcontrol *kcontrol,
2422bee1a5beSTakashi Iwai struct snd_ctl_elem_info *uinfo)
24231da177e4SLinus Torvalds {
24241da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
24251da177e4SLinus Torvalds uinfo->count = 1;
24261da177e4SLinus Torvalds return 0;
24271da177e4SLinus Torvalds }
24281da177e4SLinus Torvalds
snd_trident_spdif_stream_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2429bee1a5beSTakashi Iwai static int snd_trident_spdif_stream_get(struct snd_kcontrol *kcontrol,
2430bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
24311da177e4SLinus Torvalds {
2432bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
24331da177e4SLinus Torvalds
24341da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
24351da177e4SLinus Torvalds ucontrol->value.iec958.status[0] = (trident->spdif_pcm_bits >> 0) & 0xff;
24361da177e4SLinus Torvalds ucontrol->value.iec958.status[1] = (trident->spdif_pcm_bits >> 8) & 0xff;
24371da177e4SLinus Torvalds ucontrol->value.iec958.status[2] = (trident->spdif_pcm_bits >> 16) & 0xff;
24381da177e4SLinus Torvalds ucontrol->value.iec958.status[3] = (trident->spdif_pcm_bits >> 24) & 0xff;
24391da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
24401da177e4SLinus Torvalds return 0;
24411da177e4SLinus Torvalds }
24421da177e4SLinus Torvalds
snd_trident_spdif_stream_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2443bee1a5beSTakashi Iwai static int snd_trident_spdif_stream_put(struct snd_kcontrol *kcontrol,
2444bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
24451da177e4SLinus Torvalds {
2446bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
24471da177e4SLinus Torvalds unsigned int val;
24481da177e4SLinus Torvalds int change;
24491da177e4SLinus Torvalds
24501da177e4SLinus Torvalds val = (ucontrol->value.iec958.status[0] << 0) |
24511da177e4SLinus Torvalds (ucontrol->value.iec958.status[1] << 8) |
24521da177e4SLinus Torvalds (ucontrol->value.iec958.status[2] << 16) |
24531da177e4SLinus Torvalds (ucontrol->value.iec958.status[3] << 24);
24541da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
24551da177e4SLinus Torvalds change = trident->spdif_pcm_bits != val;
24561da177e4SLinus Torvalds trident->spdif_pcm_bits = val;
24571da177e4SLinus Torvalds if (trident->spdif != NULL) {
24581da177e4SLinus Torvalds if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
24591da177e4SLinus Torvalds outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
24601da177e4SLinus Torvalds } else {
24611da177e4SLinus Torvalds outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
24621da177e4SLinus Torvalds }
24631da177e4SLinus Torvalds }
24641da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
24651da177e4SLinus Torvalds return change;
24661da177e4SLinus Torvalds }
24671da177e4SLinus Torvalds
2468f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_trident_spdif_stream =
24691da177e4SLinus Torvalds {
24701da177e4SLinus Torvalds .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
24711da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_PCM,
24721da177e4SLinus Torvalds .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
24731da177e4SLinus Torvalds .info = snd_trident_spdif_stream_info,
24741da177e4SLinus Torvalds .get = snd_trident_spdif_stream_get,
24751da177e4SLinus Torvalds .put = snd_trident_spdif_stream_put
24761da177e4SLinus Torvalds };
24771da177e4SLinus Torvalds
24781da177e4SLinus Torvalds /*---------------------------------------------------------------------------
24791da177e4SLinus Torvalds snd_trident_ac97_control
24801da177e4SLinus Torvalds
24811da177e4SLinus Torvalds Description: enable/disable rear path for ac97
24821da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
24831da177e4SLinus Torvalds
2484a5ce8890STakashi Iwai #define snd_trident_ac97_control_info snd_ctl_boolean_mono_info
24851da177e4SLinus Torvalds
snd_trident_ac97_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2486bee1a5beSTakashi Iwai static int snd_trident_ac97_control_get(struct snd_kcontrol *kcontrol,
2487bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
24881da177e4SLinus Torvalds {
2489bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
24901da177e4SLinus Torvalds unsigned char val;
24911da177e4SLinus Torvalds
24921da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
24931da177e4SLinus Torvalds val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
24941da177e4SLinus Torvalds ucontrol->value.integer.value[0] = (val & (1 << kcontrol->private_value)) ? 1 : 0;
24951da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
24961da177e4SLinus Torvalds return 0;
24971da177e4SLinus Torvalds }
24981da177e4SLinus Torvalds
snd_trident_ac97_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2499bee1a5beSTakashi Iwai static int snd_trident_ac97_control_put(struct snd_kcontrol *kcontrol,
2500bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
25011da177e4SLinus Torvalds {
2502bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
25031da177e4SLinus Torvalds unsigned char val;
25041da177e4SLinus Torvalds int change = 0;
25051da177e4SLinus Torvalds
25061da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
25071da177e4SLinus Torvalds val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
25081da177e4SLinus Torvalds val &= ~(1 << kcontrol->private_value);
25091da177e4SLinus Torvalds if (ucontrol->value.integer.value[0])
25101da177e4SLinus Torvalds val |= 1 << kcontrol->private_value;
25111da177e4SLinus Torvalds change = val != trident->ac97_ctrl;
25121da177e4SLinus Torvalds trident->ac97_ctrl = val;
25131da177e4SLinus Torvalds outl(trident->ac97_ctrl = val, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
25141da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
25151da177e4SLinus Torvalds return change;
25161da177e4SLinus Torvalds }
25171da177e4SLinus Torvalds
2518f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_trident_ac97_rear_control =
25191da177e4SLinus Torvalds {
25201da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
25211da177e4SLinus Torvalds .name = "Rear Path",
25221da177e4SLinus Torvalds .info = snd_trident_ac97_control_info,
25231da177e4SLinus Torvalds .get = snd_trident_ac97_control_get,
25241da177e4SLinus Torvalds .put = snd_trident_ac97_control_put,
25251da177e4SLinus Torvalds .private_value = 4,
25261da177e4SLinus Torvalds };
25271da177e4SLinus Torvalds
25281da177e4SLinus Torvalds /*---------------------------------------------------------------------------
25291da177e4SLinus Torvalds snd_trident_vol_control
25301da177e4SLinus Torvalds
25311da177e4SLinus Torvalds Description: wave & music volume control
25321da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
25331da177e4SLinus Torvalds
snd_trident_vol_control_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2534bee1a5beSTakashi Iwai static int snd_trident_vol_control_info(struct snd_kcontrol *kcontrol,
2535bee1a5beSTakashi Iwai struct snd_ctl_elem_info *uinfo)
25361da177e4SLinus Torvalds {
25371da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
25381da177e4SLinus Torvalds uinfo->count = 2;
25391da177e4SLinus Torvalds uinfo->value.integer.min = 0;
25401da177e4SLinus Torvalds uinfo->value.integer.max = 255;
25411da177e4SLinus Torvalds return 0;
25421da177e4SLinus Torvalds }
25431da177e4SLinus Torvalds
snd_trident_vol_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2544bee1a5beSTakashi Iwai static int snd_trident_vol_control_get(struct snd_kcontrol *kcontrol,
2545bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
25461da177e4SLinus Torvalds {
2547bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
25481da177e4SLinus Torvalds unsigned int val;
25491da177e4SLinus Torvalds
25501da177e4SLinus Torvalds val = trident->musicvol_wavevol;
25511da177e4SLinus Torvalds ucontrol->value.integer.value[0] = 255 - ((val >> kcontrol->private_value) & 0xff);
25521da177e4SLinus Torvalds ucontrol->value.integer.value[1] = 255 - ((val >> (kcontrol->private_value + 8)) & 0xff);
25531da177e4SLinus Torvalds return 0;
25541da177e4SLinus Torvalds }
25551da177e4SLinus Torvalds
25560cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(db_scale_gvol, -6375, 25, 0);
2557a0aef8edSTakashi Iwai
snd_trident_vol_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2558bee1a5beSTakashi Iwai static int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol,
2559bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
25601da177e4SLinus Torvalds {
2561bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
25621da177e4SLinus Torvalds unsigned int val;
25631da177e4SLinus Torvalds int change = 0;
25641da177e4SLinus Torvalds
25651da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
25661da177e4SLinus Torvalds val = trident->musicvol_wavevol;
25671da177e4SLinus Torvalds val &= ~(0xffff << kcontrol->private_value);
25681da177e4SLinus Torvalds val |= ((255 - (ucontrol->value.integer.value[0] & 0xff)) |
25691da177e4SLinus Torvalds ((255 - (ucontrol->value.integer.value[1] & 0xff)) << 8)) << kcontrol->private_value;
25701da177e4SLinus Torvalds change = val != trident->musicvol_wavevol;
25711da177e4SLinus Torvalds outl(trident->musicvol_wavevol = val, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
25721da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
25731da177e4SLinus Torvalds return change;
25741da177e4SLinus Torvalds }
25751da177e4SLinus Torvalds
2576f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_trident_vol_music_control =
25771da177e4SLinus Torvalds {
25781da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
25791da177e4SLinus Torvalds .name = "Music Playback Volume",
25801da177e4SLinus Torvalds .info = snd_trident_vol_control_info,
25811da177e4SLinus Torvalds .get = snd_trident_vol_control_get,
25821da177e4SLinus Torvalds .put = snd_trident_vol_control_put,
25831da177e4SLinus Torvalds .private_value = 16,
2584a0aef8edSTakashi Iwai .tlv = { .p = db_scale_gvol },
25851da177e4SLinus Torvalds };
25861da177e4SLinus Torvalds
2587f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_trident_vol_wave_control =
25881da177e4SLinus Torvalds {
25891da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
25901da177e4SLinus Torvalds .name = "Wave Playback Volume",
25911da177e4SLinus Torvalds .info = snd_trident_vol_control_info,
25921da177e4SLinus Torvalds .get = snd_trident_vol_control_get,
25931da177e4SLinus Torvalds .put = snd_trident_vol_control_put,
25941da177e4SLinus Torvalds .private_value = 0,
2595a0aef8edSTakashi Iwai .tlv = { .p = db_scale_gvol },
25961da177e4SLinus Torvalds };
25971da177e4SLinus Torvalds
25981da177e4SLinus Torvalds /*---------------------------------------------------------------------------
25991da177e4SLinus Torvalds snd_trident_pcm_vol_control
26001da177e4SLinus Torvalds
26011da177e4SLinus Torvalds Description: PCM front volume control
26021da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
26031da177e4SLinus Torvalds
snd_trident_pcm_vol_control_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2604bee1a5beSTakashi Iwai static int snd_trident_pcm_vol_control_info(struct snd_kcontrol *kcontrol,
2605bee1a5beSTakashi Iwai struct snd_ctl_elem_info *uinfo)
26061da177e4SLinus Torvalds {
2607bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
26081da177e4SLinus Torvalds
26091da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
26101da177e4SLinus Torvalds uinfo->count = 1;
26111da177e4SLinus Torvalds uinfo->value.integer.min = 0;
26121da177e4SLinus Torvalds uinfo->value.integer.max = 255;
26131da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_SI7018)
26141da177e4SLinus Torvalds uinfo->value.integer.max = 1023;
26151da177e4SLinus Torvalds return 0;
26161da177e4SLinus Torvalds }
26171da177e4SLinus Torvalds
snd_trident_pcm_vol_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2618bee1a5beSTakashi Iwai static int snd_trident_pcm_vol_control_get(struct snd_kcontrol *kcontrol,
2619bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
26201da177e4SLinus Torvalds {
2621bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2622bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
26231da177e4SLinus Torvalds
26241da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
26251da177e4SLinus Torvalds ucontrol->value.integer.value[0] = 1023 - mix->vol;
26261da177e4SLinus Torvalds } else {
26271da177e4SLinus Torvalds ucontrol->value.integer.value[0] = 255 - (mix->vol>>2);
26281da177e4SLinus Torvalds }
26291da177e4SLinus Torvalds return 0;
26301da177e4SLinus Torvalds }
26311da177e4SLinus Torvalds
snd_trident_pcm_vol_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2632bee1a5beSTakashi Iwai static int snd_trident_pcm_vol_control_put(struct snd_kcontrol *kcontrol,
2633bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
26341da177e4SLinus Torvalds {
2635bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2636bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
26371da177e4SLinus Torvalds unsigned int val;
26381da177e4SLinus Torvalds int change = 0;
26391da177e4SLinus Torvalds
26401da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
26411da177e4SLinus Torvalds val = 1023 - (ucontrol->value.integer.value[0] & 1023);
26421da177e4SLinus Torvalds } else {
26431da177e4SLinus Torvalds val = (255 - (ucontrol->value.integer.value[0] & 255)) << 2;
26441da177e4SLinus Torvalds }
26451da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
26461da177e4SLinus Torvalds change = val != mix->vol;
26471da177e4SLinus Torvalds mix->vol = val;
26481da177e4SLinus Torvalds if (mix->voice != NULL)
26491da177e4SLinus Torvalds snd_trident_write_vol_reg(trident, mix->voice, val);
26501da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
26511da177e4SLinus Torvalds return change;
26521da177e4SLinus Torvalds }
26531da177e4SLinus Torvalds
2654f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_trident_pcm_vol_control =
26551da177e4SLinus Torvalds {
26561da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
26571da177e4SLinus Torvalds .name = "PCM Front Playback Volume",
26581da177e4SLinus Torvalds .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
26591da177e4SLinus Torvalds .count = 32,
26601da177e4SLinus Torvalds .info = snd_trident_pcm_vol_control_info,
26611da177e4SLinus Torvalds .get = snd_trident_pcm_vol_control_get,
26621da177e4SLinus Torvalds .put = snd_trident_pcm_vol_control_put,
2663a0aef8edSTakashi Iwai /* FIXME: no tlv yet */
26641da177e4SLinus Torvalds };
26651da177e4SLinus Torvalds
26661da177e4SLinus Torvalds /*---------------------------------------------------------------------------
26671da177e4SLinus Torvalds snd_trident_pcm_pan_control
26681da177e4SLinus Torvalds
26691da177e4SLinus Torvalds Description: PCM front pan control
26701da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
26711da177e4SLinus Torvalds
snd_trident_pcm_pan_control_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2672bee1a5beSTakashi Iwai static int snd_trident_pcm_pan_control_info(struct snd_kcontrol *kcontrol,
2673bee1a5beSTakashi Iwai struct snd_ctl_elem_info *uinfo)
26741da177e4SLinus Torvalds {
26751da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
26761da177e4SLinus Torvalds uinfo->count = 1;
26771da177e4SLinus Torvalds uinfo->value.integer.min = 0;
26781da177e4SLinus Torvalds uinfo->value.integer.max = 127;
26791da177e4SLinus Torvalds return 0;
26801da177e4SLinus Torvalds }
26811da177e4SLinus Torvalds
snd_trident_pcm_pan_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2682bee1a5beSTakashi Iwai static int snd_trident_pcm_pan_control_get(struct snd_kcontrol *kcontrol,
2683bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
26841da177e4SLinus Torvalds {
2685bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2686bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
26871da177e4SLinus Torvalds
26881da177e4SLinus Torvalds ucontrol->value.integer.value[0] = mix->pan;
26891da177e4SLinus Torvalds if (ucontrol->value.integer.value[0] & 0x40) {
26901da177e4SLinus Torvalds ucontrol->value.integer.value[0] = (0x3f - (ucontrol->value.integer.value[0] & 0x3f));
26911da177e4SLinus Torvalds } else {
26921da177e4SLinus Torvalds ucontrol->value.integer.value[0] |= 0x40;
26931da177e4SLinus Torvalds }
26941da177e4SLinus Torvalds return 0;
26951da177e4SLinus Torvalds }
26961da177e4SLinus Torvalds
snd_trident_pcm_pan_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2697bee1a5beSTakashi Iwai static int snd_trident_pcm_pan_control_put(struct snd_kcontrol *kcontrol,
2698bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
26991da177e4SLinus Torvalds {
2700bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2701bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
27021da177e4SLinus Torvalds unsigned char val;
27031da177e4SLinus Torvalds int change = 0;
27041da177e4SLinus Torvalds
27051da177e4SLinus Torvalds if (ucontrol->value.integer.value[0] & 0x40)
27061da177e4SLinus Torvalds val = ucontrol->value.integer.value[0] & 0x3f;
27071da177e4SLinus Torvalds else
27081da177e4SLinus Torvalds val = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)) | 0x40;
27091da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
27101da177e4SLinus Torvalds change = val != mix->pan;
27111da177e4SLinus Torvalds mix->pan = val;
27121da177e4SLinus Torvalds if (mix->voice != NULL)
27131da177e4SLinus Torvalds snd_trident_write_pan_reg(trident, mix->voice, val);
27141da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
27151da177e4SLinus Torvalds return change;
27161da177e4SLinus Torvalds }
27171da177e4SLinus Torvalds
2718f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_trident_pcm_pan_control =
27191da177e4SLinus Torvalds {
27201da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
27211da177e4SLinus Torvalds .name = "PCM Pan Playback Control",
27221da177e4SLinus Torvalds .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
27231da177e4SLinus Torvalds .count = 32,
27241da177e4SLinus Torvalds .info = snd_trident_pcm_pan_control_info,
27251da177e4SLinus Torvalds .get = snd_trident_pcm_pan_control_get,
27261da177e4SLinus Torvalds .put = snd_trident_pcm_pan_control_put,
27271da177e4SLinus Torvalds };
27281da177e4SLinus Torvalds
27291da177e4SLinus Torvalds /*---------------------------------------------------------------------------
27301da177e4SLinus Torvalds snd_trident_pcm_rvol_control
27311da177e4SLinus Torvalds
27321da177e4SLinus Torvalds Description: PCM reverb volume control
27331da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
27341da177e4SLinus Torvalds
snd_trident_pcm_rvol_control_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2735bee1a5beSTakashi Iwai static int snd_trident_pcm_rvol_control_info(struct snd_kcontrol *kcontrol,
2736bee1a5beSTakashi Iwai struct snd_ctl_elem_info *uinfo)
27371da177e4SLinus Torvalds {
27381da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
27391da177e4SLinus Torvalds uinfo->count = 1;
27401da177e4SLinus Torvalds uinfo->value.integer.min = 0;
27411da177e4SLinus Torvalds uinfo->value.integer.max = 127;
27421da177e4SLinus Torvalds return 0;
27431da177e4SLinus Torvalds }
27441da177e4SLinus Torvalds
snd_trident_pcm_rvol_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2745bee1a5beSTakashi Iwai static int snd_trident_pcm_rvol_control_get(struct snd_kcontrol *kcontrol,
2746bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
27471da177e4SLinus Torvalds {
2748bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2749bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
27501da177e4SLinus Torvalds
27511da177e4SLinus Torvalds ucontrol->value.integer.value[0] = 127 - mix->rvol;
27521da177e4SLinus Torvalds return 0;
27531da177e4SLinus Torvalds }
27541da177e4SLinus Torvalds
snd_trident_pcm_rvol_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2755bee1a5beSTakashi Iwai static int snd_trident_pcm_rvol_control_put(struct snd_kcontrol *kcontrol,
2756bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
27571da177e4SLinus Torvalds {
2758bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2759bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
27601da177e4SLinus Torvalds unsigned short val;
27611da177e4SLinus Torvalds int change = 0;
27621da177e4SLinus Torvalds
27631da177e4SLinus Torvalds val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f);
27641da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
27651da177e4SLinus Torvalds change = val != mix->rvol;
27661da177e4SLinus Torvalds mix->rvol = val;
27671da177e4SLinus Torvalds if (mix->voice != NULL)
27681da177e4SLinus Torvalds snd_trident_write_rvol_reg(trident, mix->voice, val);
27691da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
27701da177e4SLinus Torvalds return change;
27711da177e4SLinus Torvalds }
27721da177e4SLinus Torvalds
27730cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1);
2774a0aef8edSTakashi Iwai
2775f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_trident_pcm_rvol_control =
27761da177e4SLinus Torvalds {
27771da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
27781da177e4SLinus Torvalds .name = "PCM Reverb Playback Volume",
27791da177e4SLinus Torvalds .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
27801da177e4SLinus Torvalds .count = 32,
27811da177e4SLinus Torvalds .info = snd_trident_pcm_rvol_control_info,
27821da177e4SLinus Torvalds .get = snd_trident_pcm_rvol_control_get,
27831da177e4SLinus Torvalds .put = snd_trident_pcm_rvol_control_put,
2784a0aef8edSTakashi Iwai .tlv = { .p = db_scale_crvol },
27851da177e4SLinus Torvalds };
27861da177e4SLinus Torvalds
27871da177e4SLinus Torvalds /*---------------------------------------------------------------------------
27881da177e4SLinus Torvalds snd_trident_pcm_cvol_control
27891da177e4SLinus Torvalds
27901da177e4SLinus Torvalds Description: PCM chorus volume control
27911da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
27921da177e4SLinus Torvalds
snd_trident_pcm_cvol_control_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2793bee1a5beSTakashi Iwai static int snd_trident_pcm_cvol_control_info(struct snd_kcontrol *kcontrol,
2794bee1a5beSTakashi Iwai struct snd_ctl_elem_info *uinfo)
27951da177e4SLinus Torvalds {
27961da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
27971da177e4SLinus Torvalds uinfo->count = 1;
27981da177e4SLinus Torvalds uinfo->value.integer.min = 0;
27991da177e4SLinus Torvalds uinfo->value.integer.max = 127;
28001da177e4SLinus Torvalds return 0;
28011da177e4SLinus Torvalds }
28021da177e4SLinus Torvalds
snd_trident_pcm_cvol_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2803bee1a5beSTakashi Iwai static int snd_trident_pcm_cvol_control_get(struct snd_kcontrol *kcontrol,
2804bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
28051da177e4SLinus Torvalds {
2806bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2807bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
28081da177e4SLinus Torvalds
28091da177e4SLinus Torvalds ucontrol->value.integer.value[0] = 127 - mix->cvol;
28101da177e4SLinus Torvalds return 0;
28111da177e4SLinus Torvalds }
28121da177e4SLinus Torvalds
snd_trident_pcm_cvol_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2813bee1a5beSTakashi Iwai static int snd_trident_pcm_cvol_control_put(struct snd_kcontrol *kcontrol,
2814bee1a5beSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
28151da177e4SLinus Torvalds {
2816bee1a5beSTakashi Iwai struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
2817bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
28181da177e4SLinus Torvalds unsigned short val;
28191da177e4SLinus Torvalds int change = 0;
28201da177e4SLinus Torvalds
28211da177e4SLinus Torvalds val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f);
28221da177e4SLinus Torvalds spin_lock_irq(&trident->reg_lock);
28231da177e4SLinus Torvalds change = val != mix->cvol;
28241da177e4SLinus Torvalds mix->cvol = val;
28251da177e4SLinus Torvalds if (mix->voice != NULL)
28261da177e4SLinus Torvalds snd_trident_write_cvol_reg(trident, mix->voice, val);
28271da177e4SLinus Torvalds spin_unlock_irq(&trident->reg_lock);
28281da177e4SLinus Torvalds return change;
28291da177e4SLinus Torvalds }
28301da177e4SLinus Torvalds
2831f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_trident_pcm_cvol_control =
28321da177e4SLinus Torvalds {
28331da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
28341da177e4SLinus Torvalds .name = "PCM Chorus Playback Volume",
28351da177e4SLinus Torvalds .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
28361da177e4SLinus Torvalds .count = 32,
28371da177e4SLinus Torvalds .info = snd_trident_pcm_cvol_control_info,
28381da177e4SLinus Torvalds .get = snd_trident_pcm_cvol_control_get,
28391da177e4SLinus Torvalds .put = snd_trident_pcm_cvol_control_put,
2840a0aef8edSTakashi Iwai .tlv = { .p = db_scale_crvol },
28411da177e4SLinus Torvalds };
28421da177e4SLinus Torvalds
snd_trident_notify_pcm_change1(struct snd_card * card,struct snd_kcontrol * kctl,int num,int activate)2843bee1a5beSTakashi Iwai static void snd_trident_notify_pcm_change1(struct snd_card *card,
2844bee1a5beSTakashi Iwai struct snd_kcontrol *kctl,
2845bee1a5beSTakashi Iwai int num, int activate)
28461da177e4SLinus Torvalds {
2847bee1a5beSTakashi Iwai struct snd_ctl_elem_id id;
28481da177e4SLinus Torvalds
28497c22f1aaSTakashi Iwai if (! kctl)
28507c22f1aaSTakashi Iwai return;
28511da177e4SLinus Torvalds if (activate)
28521da177e4SLinus Torvalds kctl->vd[num].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
28531da177e4SLinus Torvalds else
28541da177e4SLinus Torvalds kctl->vd[num].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
28551da177e4SLinus Torvalds snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
28561da177e4SLinus Torvalds SNDRV_CTL_EVENT_MASK_INFO,
28571da177e4SLinus Torvalds snd_ctl_build_ioff(&id, kctl, num));
28581da177e4SLinus Torvalds }
28591da177e4SLinus Torvalds
snd_trident_notify_pcm_change(struct snd_trident * trident,struct snd_trident_pcm_mixer * tmix,int num,int activate)2860bee1a5beSTakashi Iwai static void snd_trident_notify_pcm_change(struct snd_trident *trident,
2861bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *tmix,
2862bee1a5beSTakashi Iwai int num, int activate)
28631da177e4SLinus Torvalds {
28641da177e4SLinus Torvalds snd_trident_notify_pcm_change1(trident->card, trident->ctl_vol, num, activate);
28651da177e4SLinus Torvalds snd_trident_notify_pcm_change1(trident->card, trident->ctl_pan, num, activate);
28661da177e4SLinus Torvalds snd_trident_notify_pcm_change1(trident->card, trident->ctl_rvol, num, activate);
28671da177e4SLinus Torvalds snd_trident_notify_pcm_change1(trident->card, trident->ctl_cvol, num, activate);
28681da177e4SLinus Torvalds }
28691da177e4SLinus Torvalds
snd_trident_pcm_mixer_build(struct snd_trident * trident,struct snd_trident_voice * voice,struct snd_pcm_substream * substream)2870bee1a5beSTakashi Iwai static int snd_trident_pcm_mixer_build(struct snd_trident *trident,
2871bee1a5beSTakashi Iwai struct snd_trident_voice *voice,
2872bee1a5beSTakashi Iwai struct snd_pcm_substream *substream)
28731da177e4SLinus Torvalds {
2874bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *tmix;
28751da177e4SLinus Torvalds
2876da3cec35STakashi Iwai if (snd_BUG_ON(!trident || !voice || !substream))
2877da3cec35STakashi Iwai return -EINVAL;
28781da177e4SLinus Torvalds tmix = &trident->pcm_mixer[substream->number];
28791da177e4SLinus Torvalds tmix->voice = voice;
28801da177e4SLinus Torvalds tmix->vol = T4D_DEFAULT_PCM_VOL;
28811da177e4SLinus Torvalds tmix->pan = T4D_DEFAULT_PCM_PAN;
28821da177e4SLinus Torvalds tmix->rvol = T4D_DEFAULT_PCM_RVOL;
28831da177e4SLinus Torvalds tmix->cvol = T4D_DEFAULT_PCM_CVOL;
28841da177e4SLinus Torvalds snd_trident_notify_pcm_change(trident, tmix, substream->number, 1);
28851da177e4SLinus Torvalds return 0;
28861da177e4SLinus Torvalds }
28871da177e4SLinus Torvalds
snd_trident_pcm_mixer_free(struct snd_trident * trident,struct snd_trident_voice * voice,struct snd_pcm_substream * substream)2888bee1a5beSTakashi Iwai static int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_trident_voice *voice, struct snd_pcm_substream *substream)
28891da177e4SLinus Torvalds {
2890bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *tmix;
28911da177e4SLinus Torvalds
2892da3cec35STakashi Iwai if (snd_BUG_ON(!trident || !substream))
2893da3cec35STakashi Iwai return -EINVAL;
28941da177e4SLinus Torvalds tmix = &trident->pcm_mixer[substream->number];
28951da177e4SLinus Torvalds tmix->voice = NULL;
28961da177e4SLinus Torvalds snd_trident_notify_pcm_change(trident, tmix, substream->number, 0);
28971da177e4SLinus Torvalds return 0;
28981da177e4SLinus Torvalds }
28991da177e4SLinus Torvalds
29001da177e4SLinus Torvalds /*---------------------------------------------------------------------------
29011da177e4SLinus Torvalds snd_trident_mixer
29021da177e4SLinus Torvalds
29031da177e4SLinus Torvalds Description: This routine registers the 4DWave device for mixer support.
29041da177e4SLinus Torvalds
2905561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
29061da177e4SLinus Torvalds
29071da177e4SLinus Torvalds Returns: None
29081da177e4SLinus Torvalds
29091da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
29101da177e4SLinus Torvalds
snd_trident_mixer(struct snd_trident * trident,int pcm_spdif_device)2911e23e7a14SBill Pemberton static int snd_trident_mixer(struct snd_trident *trident, int pcm_spdif_device)
29121da177e4SLinus Torvalds {
2913bee1a5beSTakashi Iwai struct snd_ac97_template _ac97;
2914bee1a5beSTakashi Iwai struct snd_card *card = trident->card;
2915bee1a5beSTakashi Iwai struct snd_kcontrol *kctl;
2916bee1a5beSTakashi Iwai struct snd_ctl_elem_value *uctl;
29171da177e4SLinus Torvalds int idx, err, retries = 2;
291851055da5STakashi Iwai static const struct snd_ac97_bus_ops ops = {
29191da177e4SLinus Torvalds .write = snd_trident_codec_write,
29201da177e4SLinus Torvalds .read = snd_trident_codec_read,
29211da177e4SLinus Torvalds };
29221da177e4SLinus Torvalds
2923e560d8d8STakashi Iwai uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
29241da177e4SLinus Torvalds if (!uctl)
29251da177e4SLinus Torvalds return -ENOMEM;
29261da177e4SLinus Torvalds
292734b946eeSTakashi Iwai err = snd_ac97_bus(trident->card, 0, &ops, NULL, &trident->ac97_bus);
292834b946eeSTakashi Iwai if (err < 0)
29291da177e4SLinus Torvalds goto __out;
29301da177e4SLinus Torvalds
29311da177e4SLinus Torvalds memset(&_ac97, 0, sizeof(_ac97));
29321da177e4SLinus Torvalds _ac97.private_data = trident;
29331da177e4SLinus Torvalds trident->ac97_detect = 1;
29341da177e4SLinus Torvalds
29351da177e4SLinus Torvalds __again:
293634b946eeSTakashi Iwai err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97);
293734b946eeSTakashi Iwai if (err < 0) {
29381da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
293934b946eeSTakashi Iwai err = snd_trident_sis_reset(trident);
294034b946eeSTakashi Iwai if (err < 0)
29411da177e4SLinus Torvalds goto __out;
29421da177e4SLinus Torvalds if (retries-- > 0)
29431da177e4SLinus Torvalds goto __again;
29441da177e4SLinus Torvalds err = -EIO;
29451da177e4SLinus Torvalds }
29461da177e4SLinus Torvalds goto __out;
29471da177e4SLinus Torvalds }
29481da177e4SLinus Torvalds
29491da177e4SLinus Torvalds /* secondary codec? */
29501da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_SI7018 &&
29511da177e4SLinus Torvalds (inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) {
29521da177e4SLinus Torvalds _ac97.num = 1;
29531da177e4SLinus Torvalds err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97_sec);
29541da177e4SLinus Torvalds if (err < 0)
295580c19b75STakashi Iwai dev_err(trident->card->dev,
295680c19b75STakashi Iwai "SI7018: the secondary codec - invalid access\n");
29571da177e4SLinus Torvalds #if 0 // only for my testing purpose --jk
29581da177e4SLinus Torvalds {
2959bee1a5beSTakashi Iwai struct snd_ac97 *mc97;
29601da177e4SLinus Torvalds err = snd_ac97_modem(trident->card, &_ac97, &mc97);
29611da177e4SLinus Torvalds if (err < 0)
296280c19b75STakashi Iwai dev_err(trident->card->dev,
296380c19b75STakashi Iwai "snd_ac97_modem returned error %i\n", err);
29641da177e4SLinus Torvalds }
29651da177e4SLinus Torvalds #endif
29661da177e4SLinus Torvalds }
29671da177e4SLinus Torvalds
29681da177e4SLinus Torvalds trident->ac97_detect = 0;
29691da177e4SLinus Torvalds
29701da177e4SLinus Torvalds if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
297134b946eeSTakashi Iwai kctl = snd_ctl_new1(&snd_trident_vol_wave_control, trident);
297234b946eeSTakashi Iwai err = snd_ctl_add(card, kctl);
297334b946eeSTakashi Iwai if (err < 0)
29741da177e4SLinus Torvalds goto __out;
29751da177e4SLinus Torvalds kctl->put(kctl, uctl);
297634b946eeSTakashi Iwai kctl = snd_ctl_new1(&snd_trident_vol_music_control, trident);
297734b946eeSTakashi Iwai err = snd_ctl_add(card, kctl);
297834b946eeSTakashi Iwai if (err < 0)
29791da177e4SLinus Torvalds goto __out;
29801da177e4SLinus Torvalds kctl->put(kctl, uctl);
29811da177e4SLinus Torvalds outl(trident->musicvol_wavevol = 0x00000000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
29821da177e4SLinus Torvalds } else {
29831da177e4SLinus Torvalds outl(trident->musicvol_wavevol = 0xffff0000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
29841da177e4SLinus Torvalds }
29851da177e4SLinus Torvalds
29861da177e4SLinus Torvalds for (idx = 0; idx < 32; idx++) {
2987bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *tmix;
29881da177e4SLinus Torvalds
29891da177e4SLinus Torvalds tmix = &trident->pcm_mixer[idx];
29901da177e4SLinus Torvalds tmix->voice = NULL;
29911da177e4SLinus Torvalds }
299234b946eeSTakashi Iwai trident->ctl_vol = snd_ctl_new1(&snd_trident_pcm_vol_control, trident);
299334b946eeSTakashi Iwai if (!trident->ctl_vol)
29941da177e4SLinus Torvalds goto __nomem;
299534b946eeSTakashi Iwai err = snd_ctl_add(card, trident->ctl_vol);
299634b946eeSTakashi Iwai if (err)
29971da177e4SLinus Torvalds goto __out;
29981da177e4SLinus Torvalds
299934b946eeSTakashi Iwai trident->ctl_pan = snd_ctl_new1(&snd_trident_pcm_pan_control, trident);
300034b946eeSTakashi Iwai if (!trident->ctl_pan)
30011da177e4SLinus Torvalds goto __nomem;
300234b946eeSTakashi Iwai err = snd_ctl_add(card, trident->ctl_pan);
300334b946eeSTakashi Iwai if (err)
30041da177e4SLinus Torvalds goto __out;
30051da177e4SLinus Torvalds
300634b946eeSTakashi Iwai trident->ctl_rvol = snd_ctl_new1(&snd_trident_pcm_rvol_control, trident);
300734b946eeSTakashi Iwai if (!trident->ctl_rvol)
30081da177e4SLinus Torvalds goto __nomem;
300934b946eeSTakashi Iwai err = snd_ctl_add(card, trident->ctl_rvol);
301034b946eeSTakashi Iwai if (err)
30111da177e4SLinus Torvalds goto __out;
30121da177e4SLinus Torvalds
301334b946eeSTakashi Iwai trident->ctl_cvol = snd_ctl_new1(&snd_trident_pcm_cvol_control, trident);
301434b946eeSTakashi Iwai if (!trident->ctl_cvol)
30151da177e4SLinus Torvalds goto __nomem;
301634b946eeSTakashi Iwai err = snd_ctl_add(card, trident->ctl_cvol);
301734b946eeSTakashi Iwai if (err)
30181da177e4SLinus Torvalds goto __out;
30191da177e4SLinus Torvalds
30201da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_NX) {
302134b946eeSTakashi Iwai kctl = snd_ctl_new1(&snd_trident_ac97_rear_control, trident);
302234b946eeSTakashi Iwai err = snd_ctl_add(card, kctl);
302334b946eeSTakashi Iwai if (err < 0)
30241da177e4SLinus Torvalds goto __out;
30251da177e4SLinus Torvalds kctl->put(kctl, uctl);
30261da177e4SLinus Torvalds }
30271da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) {
30281da177e4SLinus Torvalds
30291da177e4SLinus Torvalds kctl = snd_ctl_new1(&snd_trident_spdif_control, trident);
30301da177e4SLinus Torvalds if (kctl == NULL) {
30311da177e4SLinus Torvalds err = -ENOMEM;
30321da177e4SLinus Torvalds goto __out;
30331da177e4SLinus Torvalds }
30341da177e4SLinus Torvalds if (trident->ac97->ext_id & AC97_EI_SPDIF)
30351da177e4SLinus Torvalds kctl->id.index++;
30361da177e4SLinus Torvalds if (trident->ac97_sec && (trident->ac97_sec->ext_id & AC97_EI_SPDIF))
30371da177e4SLinus Torvalds kctl->id.index++;
30381da177e4SLinus Torvalds idx = kctl->id.index;
303934b946eeSTakashi Iwai err = snd_ctl_add(card, kctl);
304034b946eeSTakashi Iwai if (err < 0)
30411da177e4SLinus Torvalds goto __out;
30421da177e4SLinus Torvalds kctl->put(kctl, uctl);
30431da177e4SLinus Torvalds
30441da177e4SLinus Torvalds kctl = snd_ctl_new1(&snd_trident_spdif_default, trident);
30451da177e4SLinus Torvalds if (kctl == NULL) {
30461da177e4SLinus Torvalds err = -ENOMEM;
30471da177e4SLinus Torvalds goto __out;
30481da177e4SLinus Torvalds }
30491da177e4SLinus Torvalds kctl->id.index = idx;
30501da177e4SLinus Torvalds kctl->id.device = pcm_spdif_device;
305134b946eeSTakashi Iwai err = snd_ctl_add(card, kctl);
305234b946eeSTakashi Iwai if (err < 0)
30531da177e4SLinus Torvalds goto __out;
30541da177e4SLinus Torvalds
30551da177e4SLinus Torvalds kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident);
30561da177e4SLinus Torvalds if (kctl == NULL) {
30571da177e4SLinus Torvalds err = -ENOMEM;
30581da177e4SLinus Torvalds goto __out;
30591da177e4SLinus Torvalds }
30601da177e4SLinus Torvalds kctl->id.index = idx;
30611da177e4SLinus Torvalds kctl->id.device = pcm_spdif_device;
306234b946eeSTakashi Iwai err = snd_ctl_add(card, kctl);
306334b946eeSTakashi Iwai if (err < 0)
30641da177e4SLinus Torvalds goto __out;
30651da177e4SLinus Torvalds
30661da177e4SLinus Torvalds kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident);
30671da177e4SLinus Torvalds if (kctl == NULL) {
30681da177e4SLinus Torvalds err = -ENOMEM;
30691da177e4SLinus Torvalds goto __out;
30701da177e4SLinus Torvalds }
30711da177e4SLinus Torvalds kctl->id.index = idx;
30721da177e4SLinus Torvalds kctl->id.device = pcm_spdif_device;
307334b946eeSTakashi Iwai err = snd_ctl_add(card, kctl);
307434b946eeSTakashi Iwai if (err < 0)
30751da177e4SLinus Torvalds goto __out;
30761da177e4SLinus Torvalds trident->spdif_pcm_ctl = kctl;
30771da177e4SLinus Torvalds }
30781da177e4SLinus Torvalds
30791da177e4SLinus Torvalds err = 0;
30801da177e4SLinus Torvalds goto __out;
30811da177e4SLinus Torvalds
30821da177e4SLinus Torvalds __nomem:
30831da177e4SLinus Torvalds err = -ENOMEM;
30841da177e4SLinus Torvalds
30851da177e4SLinus Torvalds __out:
30861da177e4SLinus Torvalds kfree(uctl);
30871da177e4SLinus Torvalds
30881da177e4SLinus Torvalds return err;
30891da177e4SLinus Torvalds }
30901da177e4SLinus Torvalds
30911da177e4SLinus Torvalds /*
30921da177e4SLinus Torvalds * gameport interface
30931da177e4SLinus Torvalds */
30941da177e4SLinus Torvalds
3095b2fac073SFabian Frederick #if IS_REACHABLE(CONFIG_GAMEPORT)
30961da177e4SLinus Torvalds
snd_trident_gameport_read(struct gameport * gameport)30971da177e4SLinus Torvalds static unsigned char snd_trident_gameport_read(struct gameport *gameport)
30981da177e4SLinus Torvalds {
3099bee1a5beSTakashi Iwai struct snd_trident *chip = gameport_get_port_data(gameport);
31001da177e4SLinus Torvalds
3101da3cec35STakashi Iwai if (snd_BUG_ON(!chip))
3102da3cec35STakashi Iwai return 0;
31031da177e4SLinus Torvalds return inb(TRID_REG(chip, GAMEPORT_LEGACY));
31041da177e4SLinus Torvalds }
31051da177e4SLinus Torvalds
snd_trident_gameport_trigger(struct gameport * gameport)31061da177e4SLinus Torvalds static void snd_trident_gameport_trigger(struct gameport *gameport)
31071da177e4SLinus Torvalds {
3108bee1a5beSTakashi Iwai struct snd_trident *chip = gameport_get_port_data(gameport);
31091da177e4SLinus Torvalds
3110da3cec35STakashi Iwai if (snd_BUG_ON(!chip))
3111da3cec35STakashi Iwai return;
31121da177e4SLinus Torvalds outb(0xff, TRID_REG(chip, GAMEPORT_LEGACY));
31131da177e4SLinus Torvalds }
31141da177e4SLinus Torvalds
snd_trident_gameport_cooked_read(struct gameport * gameport,int * axes,int * buttons)31151da177e4SLinus Torvalds static int snd_trident_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
31161da177e4SLinus Torvalds {
3117bee1a5beSTakashi Iwai struct snd_trident *chip = gameport_get_port_data(gameport);
31181da177e4SLinus Torvalds int i;
31191da177e4SLinus Torvalds
3120da3cec35STakashi Iwai if (snd_BUG_ON(!chip))
3121da3cec35STakashi Iwai return 0;
31221da177e4SLinus Torvalds
31231da177e4SLinus Torvalds *buttons = (~inb(TRID_REG(chip, GAMEPORT_LEGACY)) >> 4) & 0xf;
31241da177e4SLinus Torvalds
31251da177e4SLinus Torvalds for (i = 0; i < 4; i++) {
31261da177e4SLinus Torvalds axes[i] = inw(TRID_REG(chip, GAMEPORT_AXES + i * 2));
31271da177e4SLinus Torvalds if (axes[i] == 0xffff) axes[i] = -1;
31281da177e4SLinus Torvalds }
31291da177e4SLinus Torvalds
31301da177e4SLinus Torvalds return 0;
31311da177e4SLinus Torvalds }
31321da177e4SLinus Torvalds
snd_trident_gameport_open(struct gameport * gameport,int mode)31331da177e4SLinus Torvalds static int snd_trident_gameport_open(struct gameport *gameport, int mode)
31341da177e4SLinus Torvalds {
3135bee1a5beSTakashi Iwai struct snd_trident *chip = gameport_get_port_data(gameport);
31361da177e4SLinus Torvalds
3137da3cec35STakashi Iwai if (snd_BUG_ON(!chip))
3138da3cec35STakashi Iwai return 0;
31391da177e4SLinus Torvalds
31401da177e4SLinus Torvalds switch (mode) {
31411da177e4SLinus Torvalds case GAMEPORT_MODE_COOKED:
31421da177e4SLinus Torvalds outb(GAMEPORT_MODE_ADC, TRID_REG(chip, GAMEPORT_GCR));
3143ef21ca24SNishanth Aravamudan msleep(20);
31441da177e4SLinus Torvalds return 0;
31451da177e4SLinus Torvalds case GAMEPORT_MODE_RAW:
31461da177e4SLinus Torvalds outb(0, TRID_REG(chip, GAMEPORT_GCR));
31471da177e4SLinus Torvalds return 0;
31481da177e4SLinus Torvalds default:
31491da177e4SLinus Torvalds return -1;
31501da177e4SLinus Torvalds }
31511da177e4SLinus Torvalds }
31521da177e4SLinus Torvalds
snd_trident_create_gameport(struct snd_trident * chip)3153e23e7a14SBill Pemberton int snd_trident_create_gameport(struct snd_trident *chip)
31541da177e4SLinus Torvalds {
31551da177e4SLinus Torvalds struct gameport *gp;
31561da177e4SLinus Torvalds
31571da177e4SLinus Torvalds chip->gameport = gp = gameport_allocate_port();
31581da177e4SLinus Torvalds if (!gp) {
315980c19b75STakashi Iwai dev_err(chip->card->dev,
316080c19b75STakashi Iwai "cannot allocate memory for gameport\n");
31611da177e4SLinus Torvalds return -ENOMEM;
31621da177e4SLinus Torvalds }
31631da177e4SLinus Torvalds
31641da177e4SLinus Torvalds gameport_set_name(gp, "Trident 4DWave");
31651da177e4SLinus Torvalds gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
31661da177e4SLinus Torvalds gameport_set_dev_parent(gp, &chip->pci->dev);
31671da177e4SLinus Torvalds
31681da177e4SLinus Torvalds gameport_set_port_data(gp, chip);
31691da177e4SLinus Torvalds gp->fuzz = 64;
31701da177e4SLinus Torvalds gp->read = snd_trident_gameport_read;
31711da177e4SLinus Torvalds gp->trigger = snd_trident_gameport_trigger;
31721da177e4SLinus Torvalds gp->cooked_read = snd_trident_gameport_cooked_read;
31731da177e4SLinus Torvalds gp->open = snd_trident_gameport_open;
31741da177e4SLinus Torvalds
31751da177e4SLinus Torvalds gameport_register_port(gp);
31761da177e4SLinus Torvalds
31771da177e4SLinus Torvalds return 0;
31781da177e4SLinus Torvalds }
31791da177e4SLinus Torvalds
snd_trident_free_gameport(struct snd_trident * chip)3180bee1a5beSTakashi Iwai static inline void snd_trident_free_gameport(struct snd_trident *chip)
31811da177e4SLinus Torvalds {
31821da177e4SLinus Torvalds if (chip->gameport) {
31831da177e4SLinus Torvalds gameport_unregister_port(chip->gameport);
31841da177e4SLinus Torvalds chip->gameport = NULL;
31851da177e4SLinus Torvalds }
31861da177e4SLinus Torvalds }
31871da177e4SLinus Torvalds #else
snd_trident_create_gameport(struct snd_trident * chip)3188e23e7a14SBill Pemberton int snd_trident_create_gameport(struct snd_trident *chip) { return -ENOSYS; }
snd_trident_free_gameport(struct snd_trident * chip)3189bee1a5beSTakashi Iwai static inline void snd_trident_free_gameport(struct snd_trident *chip) { }
31901da177e4SLinus Torvalds #endif /* CONFIG_GAMEPORT */
31911da177e4SLinus Torvalds
31921da177e4SLinus Torvalds /*
31931da177e4SLinus Torvalds * delay for 1 tick
31941da177e4SLinus Torvalds */
do_delay(struct snd_trident * chip)3195bee1a5beSTakashi Iwai static inline void do_delay(struct snd_trident *chip)
31961da177e4SLinus Torvalds {
31978433a509SNishanth Aravamudan schedule_timeout_uninterruptible(1);
31981da177e4SLinus Torvalds }
31991da177e4SLinus Torvalds
32001da177e4SLinus Torvalds /*
32011da177e4SLinus Torvalds * SiS reset routine
32021da177e4SLinus Torvalds */
32031da177e4SLinus Torvalds
snd_trident_sis_reset(struct snd_trident * trident)3204bee1a5beSTakashi Iwai static int snd_trident_sis_reset(struct snd_trident *trident)
32051da177e4SLinus Torvalds {
32061da177e4SLinus Torvalds unsigned long end_time;
32071da177e4SLinus Torvalds unsigned int i;
32081da177e4SLinus Torvalds int r;
32091da177e4SLinus Torvalds
32101da177e4SLinus Torvalds r = trident->in_suspend ? 0 : 2; /* count of retries */
32111da177e4SLinus Torvalds __si7018_retry:
32121da177e4SLinus Torvalds pci_write_config_byte(trident->pci, 0x46, 0x04); /* SOFTWARE RESET */
32131da177e4SLinus Torvalds udelay(100);
32141da177e4SLinus Torvalds pci_write_config_byte(trident->pci, 0x46, 0x00);
32151da177e4SLinus Torvalds udelay(100);
32161da177e4SLinus Torvalds /* disable AC97 GPIO interrupt */
32171da177e4SLinus Torvalds outb(0x00, TRID_REG(trident, SI_AC97_GPIO));
32181da177e4SLinus Torvalds /* initialize serial interface, force cold reset */
32191da177e4SLinus Torvalds i = PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID|COLD_RESET;
32201da177e4SLinus Torvalds outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
32211da177e4SLinus Torvalds udelay(1000);
32221da177e4SLinus Torvalds /* remove cold reset */
32231da177e4SLinus Torvalds i &= ~COLD_RESET;
32241da177e4SLinus Torvalds outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
32251da177e4SLinus Torvalds udelay(2000);
32261da177e4SLinus Torvalds /* wait, until the codec is ready */
32271da177e4SLinus Torvalds end_time = (jiffies + (HZ * 3) / 4) + 1;
32281da177e4SLinus Torvalds do {
32291da177e4SLinus Torvalds if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0)
32301da177e4SLinus Torvalds goto __si7018_ok;
32311da177e4SLinus Torvalds do_delay(trident);
32321da177e4SLinus Torvalds } while (time_after_eq(end_time, jiffies));
323380c19b75STakashi Iwai dev_err(trident->card->dev, "AC'97 codec ready error [0x%x]\n",
323480c19b75STakashi Iwai inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)));
32351da177e4SLinus Torvalds if (r-- > 0) {
32361da177e4SLinus Torvalds end_time = jiffies + HZ;
32371da177e4SLinus Torvalds do {
32381da177e4SLinus Torvalds do_delay(trident);
32391da177e4SLinus Torvalds } while (time_after_eq(end_time, jiffies));
32401da177e4SLinus Torvalds goto __si7018_retry;
32411da177e4SLinus Torvalds }
32421da177e4SLinus Torvalds __si7018_ok:
32431da177e4SLinus Torvalds /* wait for the second codec */
32441da177e4SLinus Torvalds do {
32451da177e4SLinus Torvalds if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_SECONDARY_READY) != 0)
32461da177e4SLinus Torvalds break;
32471da177e4SLinus Torvalds do_delay(trident);
32481da177e4SLinus Torvalds } while (time_after_eq(end_time, jiffies));
32491da177e4SLinus Torvalds /* enable 64 channel mode */
32501da177e4SLinus Torvalds outl(BANK_B_EN, TRID_REG(trident, T4D_LFO_GC_CIR));
32511da177e4SLinus Torvalds return 0;
32521da177e4SLinus Torvalds }
32531da177e4SLinus Torvalds
32541da177e4SLinus Torvalds /*
32551da177e4SLinus Torvalds * /proc interface
32561da177e4SLinus Torvalds */
32571da177e4SLinus Torvalds
snd_trident_proc_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)3258bee1a5beSTakashi Iwai static void snd_trident_proc_read(struct snd_info_entry *entry,
3259bee1a5beSTakashi Iwai struct snd_info_buffer *buffer)
32601da177e4SLinus Torvalds {
3261bee1a5beSTakashi Iwai struct snd_trident *trident = entry->private_data;
32621da177e4SLinus Torvalds char *s;
32631da177e4SLinus Torvalds
32641da177e4SLinus Torvalds switch (trident->device) {
32651da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_SI7018:
32661da177e4SLinus Torvalds s = "SiS 7018 Audio";
32671da177e4SLinus Torvalds break;
32681da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_DX:
32691da177e4SLinus Torvalds s = "Trident 4DWave PCI DX";
32701da177e4SLinus Torvalds break;
32711da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_NX:
32721da177e4SLinus Torvalds s = "Trident 4DWave PCI NX";
32731da177e4SLinus Torvalds break;
32741da177e4SLinus Torvalds default:
32751da177e4SLinus Torvalds s = "???";
32761da177e4SLinus Torvalds }
32771da177e4SLinus Torvalds snd_iprintf(buffer, "%s\n\n", s);
32781da177e4SLinus Torvalds snd_iprintf(buffer, "Spurious IRQs : %d\n", trident->spurious_irq_count);
32791da177e4SLinus Torvalds snd_iprintf(buffer, "Spurious IRQ dlta: %d\n", trident->spurious_irq_max_delta);
32801da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018)
32811da177e4SLinus Torvalds snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", trident->spdif_ctrl == 0x28 ? "on" : "off");
32821da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_NX) {
32831da177e4SLinus Torvalds snd_iprintf(buffer, "Rear Speakers : %s\n", trident->ac97_ctrl & 0x00000010 ? "on" : "off");
32841da177e4SLinus Torvalds if (trident->tlb.entries) {
32851da177e4SLinus Torvalds snd_iprintf(buffer,"\nVirtual Memory\n");
32861da177e4SLinus Torvalds snd_iprintf(buffer, "Memory Maximum : %d\n", trident->tlb.memhdr->size);
32871da177e4SLinus Torvalds snd_iprintf(buffer, "Memory Used : %d\n", trident->tlb.memhdr->used);
32881da177e4SLinus Torvalds snd_iprintf(buffer, "Memory Free : %d\n", snd_util_mem_avail(trident->tlb.memhdr));
32891da177e4SLinus Torvalds }
32901da177e4SLinus Torvalds }
32911da177e4SLinus Torvalds }
32921da177e4SLinus Torvalds
snd_trident_proc_init(struct snd_trident * trident)3293e23e7a14SBill Pemberton static void snd_trident_proc_init(struct snd_trident *trident)
32941da177e4SLinus Torvalds {
32951da177e4SLinus Torvalds const char *s = "trident";
32961da177e4SLinus Torvalds
32971da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_SI7018)
32981da177e4SLinus Torvalds s = "sis7018";
329947f2769bSTakashi Iwai snd_card_ro_proc_new(trident->card, s, trident, snd_trident_proc_read);
33001da177e4SLinus Torvalds }
33011da177e4SLinus Torvalds
33021da177e4SLinus Torvalds /*---------------------------------------------------------------------------
33031da177e4SLinus Torvalds snd_trident_tlb_alloc
33041da177e4SLinus Torvalds
33051da177e4SLinus Torvalds Description: Allocate and set up the TLB page table on 4D NX.
33061da177e4SLinus Torvalds Each entry has 4 bytes (physical PCI address).
33071da177e4SLinus Torvalds
3308561de31aSJoe Perches Parameters: trident - pointer to target device class for 4DWave.
33091da177e4SLinus Torvalds
33101da177e4SLinus Torvalds Returns: 0 or negative error code
33111da177e4SLinus Torvalds
33121da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
33131da177e4SLinus Torvalds
snd_trident_tlb_alloc(struct snd_trident * trident)3314e23e7a14SBill Pemberton static int snd_trident_tlb_alloc(struct snd_trident *trident)
33151da177e4SLinus Torvalds {
33161da177e4SLinus Torvalds int i;
33171da177e4SLinus Torvalds
33181da177e4SLinus Torvalds /* TLB array must be aligned to 16kB !!! so we allocate
33191da177e4SLinus Torvalds 32kB region and correct offset when necessary */
33201da177e4SLinus Torvalds
3321*5adfd8c2STakashi Iwai trident->tlb.buffer =
3322*5adfd8c2STakashi Iwai snd_devm_alloc_pages(&trident->pci->dev, SNDRV_DMA_TYPE_DEV,
3323*5adfd8c2STakashi Iwai 2 * SNDRV_TRIDENT_MAX_PAGES * 4);
3324*5adfd8c2STakashi Iwai if (!trident->tlb.buffer) {
332580c19b75STakashi Iwai dev_err(trident->card->dev, "unable to allocate TLB buffer\n");
33261da177e4SLinus Torvalds return -ENOMEM;
33271da177e4SLinus Torvalds }
3328*5adfd8c2STakashi Iwai trident->tlb.entries = (__le32 *)ALIGN((unsigned long)trident->tlb.buffer->area, SNDRV_TRIDENT_MAX_PAGES * 4);
3329*5adfd8c2STakashi Iwai trident->tlb.entries_dmaaddr = ALIGN(trident->tlb.buffer->addr, SNDRV_TRIDENT_MAX_PAGES * 4);
3330aef7758fSMarkus Elfring
33311da177e4SLinus Torvalds /* allocate and setup silent page and initialise TLB entries */
3332*5adfd8c2STakashi Iwai trident->tlb.silent_page =
3333*5adfd8c2STakashi Iwai snd_devm_alloc_pages(&trident->pci->dev, SNDRV_DMA_TYPE_DEV,
3334*5adfd8c2STakashi Iwai SNDRV_TRIDENT_PAGE_SIZE);
3335*5adfd8c2STakashi Iwai if (!trident->tlb.silent_page) {
333680c19b75STakashi Iwai dev_err(trident->card->dev, "unable to allocate silent page\n");
33371da177e4SLinus Torvalds return -ENOMEM;
33381da177e4SLinus Torvalds }
3339*5adfd8c2STakashi Iwai memset(trident->tlb.silent_page->area, 0, SNDRV_TRIDENT_PAGE_SIZE);
334074fb9831STakashi Iwai for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++)
3341*5adfd8c2STakashi Iwai trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page->addr & ~(SNDRV_TRIDENT_PAGE_SIZE-1));
33421da177e4SLinus Torvalds
33431da177e4SLinus Torvalds /* use emu memory block manager code to manage tlb page allocation */
33441da177e4SLinus Torvalds trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES);
33451da177e4SLinus Torvalds if (trident->tlb.memhdr == NULL)
33461da177e4SLinus Torvalds return -ENOMEM;
33471da177e4SLinus Torvalds
3348bee1a5beSTakashi Iwai trident->tlb.memhdr->block_extra_size = sizeof(struct snd_trident_memblk_arg);
33491da177e4SLinus Torvalds return 0;
33501da177e4SLinus Torvalds }
33511da177e4SLinus Torvalds
33521da177e4SLinus Torvalds /*
33531da177e4SLinus Torvalds * initialize 4D DX chip
33541da177e4SLinus Torvalds */
33551da177e4SLinus Torvalds
snd_trident_stop_all_voices(struct snd_trident * trident)3356bee1a5beSTakashi Iwai static void snd_trident_stop_all_voices(struct snd_trident *trident)
33571da177e4SLinus Torvalds {
33581da177e4SLinus Torvalds outl(0xffffffff, TRID_REG(trident, T4D_STOP_A));
33591da177e4SLinus Torvalds outl(0xffffffff, TRID_REG(trident, T4D_STOP_B));
33601da177e4SLinus Torvalds outl(0, TRID_REG(trident, T4D_AINTEN_A));
33611da177e4SLinus Torvalds outl(0, TRID_REG(trident, T4D_AINTEN_B));
33621da177e4SLinus Torvalds }
33631da177e4SLinus Torvalds
snd_trident_4d_dx_init(struct snd_trident * trident)3364bee1a5beSTakashi Iwai static int snd_trident_4d_dx_init(struct snd_trident *trident)
33651da177e4SLinus Torvalds {
33661da177e4SLinus Torvalds struct pci_dev *pci = trident->pci;
33671da177e4SLinus Torvalds unsigned long end_time;
33681da177e4SLinus Torvalds
33691da177e4SLinus Torvalds /* reset the legacy configuration and whole audio/wavetable block */
33701da177e4SLinus Torvalds pci_write_config_dword(pci, 0x40, 0); /* DDMA */
33711da177e4SLinus Torvalds pci_write_config_byte(pci, 0x44, 0); /* ports */
33721da177e4SLinus Torvalds pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */
33731da177e4SLinus Torvalds pci_write_config_byte(pci, 0x46, 4); /* reset */
33741da177e4SLinus Torvalds udelay(100);
33751da177e4SLinus Torvalds pci_write_config_byte(pci, 0x46, 0); /* release reset */
33761da177e4SLinus Torvalds udelay(100);
33771da177e4SLinus Torvalds
33781da177e4SLinus Torvalds /* warm reset of the AC'97 codec */
33791da177e4SLinus Torvalds outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
33801da177e4SLinus Torvalds udelay(100);
33811da177e4SLinus Torvalds outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
33821da177e4SLinus Torvalds /* DAC on, disable SB IRQ and try to force ADC valid signal */
33831da177e4SLinus Torvalds trident->ac97_ctrl = 0x0000004a;
33841da177e4SLinus Torvalds outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
33851da177e4SLinus Torvalds /* wait, until the codec is ready */
33861da177e4SLinus Torvalds end_time = (jiffies + (HZ * 3) / 4) + 1;
33871da177e4SLinus Torvalds do {
33881da177e4SLinus Torvalds if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0)
33891da177e4SLinus Torvalds goto __dx_ok;
33901da177e4SLinus Torvalds do_delay(trident);
33911da177e4SLinus Torvalds } while (time_after_eq(end_time, jiffies));
339280c19b75STakashi Iwai dev_err(trident->card->dev, "AC'97 codec ready error\n");
33931da177e4SLinus Torvalds return -EIO;
33941da177e4SLinus Torvalds
33951da177e4SLinus Torvalds __dx_ok:
33961da177e4SLinus Torvalds snd_trident_stop_all_voices(trident);
33971da177e4SLinus Torvalds
33981da177e4SLinus Torvalds return 0;
33991da177e4SLinus Torvalds }
34001da177e4SLinus Torvalds
34011da177e4SLinus Torvalds /*
34021da177e4SLinus Torvalds * initialize 4D NX chip
34031da177e4SLinus Torvalds */
snd_trident_4d_nx_init(struct snd_trident * trident)3404bee1a5beSTakashi Iwai static int snd_trident_4d_nx_init(struct snd_trident *trident)
34051da177e4SLinus Torvalds {
34061da177e4SLinus Torvalds struct pci_dev *pci = trident->pci;
34071da177e4SLinus Torvalds unsigned long end_time;
34081da177e4SLinus Torvalds
34091da177e4SLinus Torvalds /* reset the legacy configuration and whole audio/wavetable block */
34101da177e4SLinus Torvalds pci_write_config_dword(pci, 0x40, 0); /* DDMA */
34111da177e4SLinus Torvalds pci_write_config_byte(pci, 0x44, 0); /* ports */
34121da177e4SLinus Torvalds pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */
34131da177e4SLinus Torvalds
34141da177e4SLinus Torvalds pci_write_config_byte(pci, 0x46, 1); /* reset */
34151da177e4SLinus Torvalds udelay(100);
34161da177e4SLinus Torvalds pci_write_config_byte(pci, 0x46, 0); /* release reset */
34171da177e4SLinus Torvalds udelay(100);
34181da177e4SLinus Torvalds
34191da177e4SLinus Torvalds /* warm reset of the AC'97 codec */
34201da177e4SLinus Torvalds outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
34211da177e4SLinus Torvalds udelay(100);
34221da177e4SLinus Torvalds outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
34231da177e4SLinus Torvalds /* wait, until the codec is ready */
34241da177e4SLinus Torvalds end_time = (jiffies + (HZ * 3) / 4) + 1;
34251da177e4SLinus Torvalds do {
34261da177e4SLinus Torvalds if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0)
34271da177e4SLinus Torvalds goto __nx_ok;
34281da177e4SLinus Torvalds do_delay(trident);
34291da177e4SLinus Torvalds } while (time_after_eq(end_time, jiffies));
343080c19b75STakashi Iwai dev_err(trident->card->dev, "AC'97 codec ready error [0x%x]\n",
343180c19b75STakashi Iwai inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)));
34321da177e4SLinus Torvalds return -EIO;
34331da177e4SLinus Torvalds
34341da177e4SLinus Torvalds __nx_ok:
34351da177e4SLinus Torvalds /* DAC on */
34361da177e4SLinus Torvalds trident->ac97_ctrl = 0x00000002;
34371da177e4SLinus Torvalds outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
34381da177e4SLinus Torvalds /* disable SB IRQ */
34391da177e4SLinus Torvalds outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT));
34401da177e4SLinus Torvalds
34411da177e4SLinus Torvalds snd_trident_stop_all_voices(trident);
34421da177e4SLinus Torvalds
34431da177e4SLinus Torvalds if (trident->tlb.entries != NULL) {
34441da177e4SLinus Torvalds unsigned int i;
34451da177e4SLinus Torvalds /* enable virtual addressing via TLB */
34461da177e4SLinus Torvalds i = trident->tlb.entries_dmaaddr;
34471da177e4SLinus Torvalds i |= 0x00000001;
34481da177e4SLinus Torvalds outl(i, TRID_REG(trident, NX_TLBC));
34491da177e4SLinus Torvalds } else {
34501da177e4SLinus Torvalds outl(0, TRID_REG(trident, NX_TLBC));
34511da177e4SLinus Torvalds }
34521da177e4SLinus Torvalds /* initialize S/PDIF */
34531da177e4SLinus Torvalds outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
34541da177e4SLinus Torvalds outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
34551da177e4SLinus Torvalds
34561da177e4SLinus Torvalds return 0;
34571da177e4SLinus Torvalds }
34581da177e4SLinus Torvalds
34591da177e4SLinus Torvalds /*
34601da177e4SLinus Torvalds * initialize sis7018 chip
34611da177e4SLinus Torvalds */
snd_trident_sis_init(struct snd_trident * trident)3462bee1a5beSTakashi Iwai static int snd_trident_sis_init(struct snd_trident *trident)
34631da177e4SLinus Torvalds {
34641da177e4SLinus Torvalds int err;
34651da177e4SLinus Torvalds
346634b946eeSTakashi Iwai err = snd_trident_sis_reset(trident);
346734b946eeSTakashi Iwai if (err < 0)
34681da177e4SLinus Torvalds return err;
34691da177e4SLinus Torvalds
34701da177e4SLinus Torvalds snd_trident_stop_all_voices(trident);
34711da177e4SLinus Torvalds
34721da177e4SLinus Torvalds /* initialize S/PDIF */
34731da177e4SLinus Torvalds outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
34741da177e4SLinus Torvalds
34751da177e4SLinus Torvalds return 0;
34761da177e4SLinus Torvalds }
34771da177e4SLinus Torvalds
34781da177e4SLinus Torvalds /*---------------------------------------------------------------------------
34791da177e4SLinus Torvalds snd_trident_create
34801da177e4SLinus Torvalds
34811da177e4SLinus Torvalds Description: This routine will create the device specific class for
34821da177e4SLinus Torvalds the 4DWave card. It will also perform basic initialization.
34831da177e4SLinus Torvalds
3484561de31aSJoe Perches Parameters: card - which card to create
34851da177e4SLinus Torvalds pci - interface to PCI bus resource info
34861da177e4SLinus Torvalds dma1ptr - playback dma buffer
34871da177e4SLinus Torvalds dma2ptr - capture dma buffer
34881da177e4SLinus Torvalds irqptr - interrupt resource info
34891da177e4SLinus Torvalds
34901da177e4SLinus Torvalds Returns: 4DWave device class private data
34911da177e4SLinus Torvalds
34921da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
34931da177e4SLinus Torvalds
snd_trident_create(struct snd_card * card,struct pci_dev * pci,int pcm_streams,int pcm_spdif_device,int max_wavetable_size)3494e23e7a14SBill Pemberton int snd_trident_create(struct snd_card *card,
34951da177e4SLinus Torvalds struct pci_dev *pci,
34961da177e4SLinus Torvalds int pcm_streams,
34971da177e4SLinus Torvalds int pcm_spdif_device,
3498*5adfd8c2STakashi Iwai int max_wavetable_size)
34991da177e4SLinus Torvalds {
3500*5adfd8c2STakashi Iwai struct snd_trident *trident = card->private_data;
35011da177e4SLinus Torvalds int i, err;
3502bee1a5beSTakashi Iwai struct snd_trident_voice *voice;
3503bee1a5beSTakashi Iwai struct snd_trident_pcm_mixer *tmix;
35041da177e4SLinus Torvalds
35051da177e4SLinus Torvalds /* enable PCI device */
3506*5adfd8c2STakashi Iwai err = pcim_enable_device(pci);
350734b946eeSTakashi Iwai if (err < 0)
35081da177e4SLinus Torvalds return err;
35091da177e4SLinus Torvalds /* check, if we can restrict PCI DMA transfers to 30 bits */
3510669f65eaSTakashi Iwai if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(30))) {
351180c19b75STakashi Iwai dev_err(card->dev,
351280c19b75STakashi Iwai "architecture does not support 30bit PCI busmaster DMA\n");
35131da177e4SLinus Torvalds return -ENXIO;
35141da177e4SLinus Torvalds }
35151da177e4SLinus Torvalds
35161da177e4SLinus Torvalds trident->device = (pci->vendor << 16) | pci->device;
35171da177e4SLinus Torvalds trident->card = card;
35181da177e4SLinus Torvalds trident->pci = pci;
35191da177e4SLinus Torvalds spin_lock_init(&trident->reg_lock);
35201da177e4SLinus Torvalds spin_lock_init(&trident->event_lock);
35211da177e4SLinus Torvalds spin_lock_init(&trident->voice_alloc);
35221da177e4SLinus Torvalds if (pcm_streams < 1)
35231da177e4SLinus Torvalds pcm_streams = 1;
35241da177e4SLinus Torvalds if (pcm_streams > 32)
35251da177e4SLinus Torvalds pcm_streams = 32;
35261da177e4SLinus Torvalds trident->ChanPCM = pcm_streams;
35271da177e4SLinus Torvalds if (max_wavetable_size < 0 )
35281da177e4SLinus Torvalds max_wavetable_size = 0;
35291da177e4SLinus Torvalds trident->synth.max_size = max_wavetable_size * 1024;
35301da177e4SLinus Torvalds trident->irq = -1;
3531*5adfd8c2STakashi Iwai card->private_free = snd_trident_free;
35321da177e4SLinus Torvalds
35331da177e4SLinus Torvalds trident->midi_port = TRID_REG(trident, T4D_MPU401_BASE);
35341da177e4SLinus Torvalds pci_set_master(pci);
35351da177e4SLinus Torvalds
353634b946eeSTakashi Iwai err = pci_request_regions(pci, "Trident Audio");
3537*5adfd8c2STakashi Iwai if (err < 0)
35381da177e4SLinus Torvalds return err;
35391da177e4SLinus Torvalds trident->port = pci_resource_start(pci, 0);
35401da177e4SLinus Torvalds
3541*5adfd8c2STakashi Iwai if (devm_request_irq(&pci->dev, pci->irq, snd_trident_interrupt,
3542*5adfd8c2STakashi Iwai IRQF_SHARED, KBUILD_MODNAME, trident)) {
354380c19b75STakashi Iwai dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
35441da177e4SLinus Torvalds return -EBUSY;
35451da177e4SLinus Torvalds }
35461da177e4SLinus Torvalds trident->irq = pci->irq;
35474a9ff148STakashi Iwai card->sync_irq = trident->irq;
35481da177e4SLinus Torvalds
35491da177e4SLinus Torvalds /* allocate 16k-aligned TLB for NX cards */
35501da177e4SLinus Torvalds trident->tlb.entries = NULL;
35511da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_NX) {
355234b946eeSTakashi Iwai err = snd_trident_tlb_alloc(trident);
3553*5adfd8c2STakashi Iwai if (err < 0)
35541da177e4SLinus Torvalds return err;
35551da177e4SLinus Torvalds }
35561da177e4SLinus Torvalds
35571da177e4SLinus Torvalds trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF;
35581da177e4SLinus Torvalds
35591da177e4SLinus Torvalds /* initialize chip */
35601da177e4SLinus Torvalds switch (trident->device) {
35611da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_DX:
35621da177e4SLinus Torvalds err = snd_trident_4d_dx_init(trident);
35631da177e4SLinus Torvalds break;
35641da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_NX:
35651da177e4SLinus Torvalds err = snd_trident_4d_nx_init(trident);
35661da177e4SLinus Torvalds break;
35671da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_SI7018:
35681da177e4SLinus Torvalds err = snd_trident_sis_init(trident);
35691da177e4SLinus Torvalds break;
35701da177e4SLinus Torvalds default:
35711da177e4SLinus Torvalds snd_BUG();
35721da177e4SLinus Torvalds break;
35731da177e4SLinus Torvalds }
3574*5adfd8c2STakashi Iwai if (err < 0)
35751da177e4SLinus Torvalds return err;
35761da177e4SLinus Torvalds
357734b946eeSTakashi Iwai err = snd_trident_mixer(trident, pcm_spdif_device);
357834b946eeSTakashi Iwai if (err < 0)
35791da177e4SLinus Torvalds return err;
35801da177e4SLinus Torvalds
35811da177e4SLinus Torvalds /* initialise synth voices */
35821da177e4SLinus Torvalds for (i = 0; i < 64; i++) {
35831da177e4SLinus Torvalds voice = &trident->synth.voices[i];
35841da177e4SLinus Torvalds voice->number = i;
35851da177e4SLinus Torvalds voice->trident = trident;
35861da177e4SLinus Torvalds }
35871da177e4SLinus Torvalds /* initialize pcm mixer entries */
35881da177e4SLinus Torvalds for (i = 0; i < 32; i++) {
35891da177e4SLinus Torvalds tmix = &trident->pcm_mixer[i];
35901da177e4SLinus Torvalds tmix->vol = T4D_DEFAULT_PCM_VOL;
35911da177e4SLinus Torvalds tmix->pan = T4D_DEFAULT_PCM_PAN;
35921da177e4SLinus Torvalds tmix->rvol = T4D_DEFAULT_PCM_RVOL;
35931da177e4SLinus Torvalds tmix->cvol = T4D_DEFAULT_PCM_CVOL;
35941da177e4SLinus Torvalds }
35951da177e4SLinus Torvalds
35961da177e4SLinus Torvalds snd_trident_enable_eso(trident);
35971da177e4SLinus Torvalds
35981da177e4SLinus Torvalds snd_trident_proc_init(trident);
35991da177e4SLinus Torvalds return 0;
36001da177e4SLinus Torvalds }
36011da177e4SLinus Torvalds
36021da177e4SLinus Torvalds /*---------------------------------------------------------------------------
36031da177e4SLinus Torvalds snd_trident_free
36041da177e4SLinus Torvalds
36051da177e4SLinus Torvalds Description: This routine will free the device specific class for
36061da177e4SLinus Torvalds the 4DWave card.
36071da177e4SLinus Torvalds
3608*5adfd8c2STakashi Iwai Parameters: card - card to release
36091da177e4SLinus Torvalds
36101da177e4SLinus Torvalds Returns: None.
36111da177e4SLinus Torvalds
36121da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
36131da177e4SLinus Torvalds
snd_trident_free(struct snd_card * card)3614*5adfd8c2STakashi Iwai static void snd_trident_free(struct snd_card *card)
36151da177e4SLinus Torvalds {
3616*5adfd8c2STakashi Iwai struct snd_trident *trident = card->private_data;
3617*5adfd8c2STakashi Iwai
36181da177e4SLinus Torvalds snd_trident_free_gameport(trident);
36191da177e4SLinus Torvalds snd_trident_disable_eso(trident);
36201da177e4SLinus Torvalds // Disable S/PDIF out
36211da177e4SLinus Torvalds if (trident->device == TRIDENT_DEVICE_ID_NX)
36221da177e4SLinus Torvalds outb(0x00, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
36231da177e4SLinus Torvalds else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
36241da177e4SLinus Torvalds outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
36251da177e4SLinus Torvalds }
3626*5adfd8c2STakashi Iwai if (trident->tlb.buffer) {
36271da177e4SLinus Torvalds outl(0, TRID_REG(trident, NX_TLBC));
36281da177e4SLinus Torvalds snd_util_memhdr_free(trident->tlb.memhdr);
36291da177e4SLinus Torvalds }
36301da177e4SLinus Torvalds }
36311da177e4SLinus Torvalds
36321da177e4SLinus Torvalds /*---------------------------------------------------------------------------
36331da177e4SLinus Torvalds snd_trident_interrupt
36341da177e4SLinus Torvalds
36351da177e4SLinus Torvalds Description: ISR for Trident 4DWave device
36361da177e4SLinus Torvalds
3637561de31aSJoe Perches Parameters: trident - device specific private data for 4DWave card
36381da177e4SLinus Torvalds
36391da177e4SLinus Torvalds Problems: It seems that Trident chips generates interrupts more than
36401da177e4SLinus Torvalds one time in special cases. The spurious interrupts are
36411da177e4SLinus Torvalds detected via sample timer (T4D_STIMER) and computing
36421da177e4SLinus Torvalds corresponding delta value. The limits are detected with
36431da177e4SLinus Torvalds the method try & fail so it is possible that it won't
36441da177e4SLinus Torvalds work on all computers. [jaroslav]
36451da177e4SLinus Torvalds
36461da177e4SLinus Torvalds Returns: None.
36471da177e4SLinus Torvalds
36481da177e4SLinus Torvalds ---------------------------------------------------------------------------*/
36491da177e4SLinus Torvalds
snd_trident_interrupt(int irq,void * dev_id)36507d12e780SDavid Howells static irqreturn_t snd_trident_interrupt(int irq, void *dev_id)
36511da177e4SLinus Torvalds {
3652bee1a5beSTakashi Iwai struct snd_trident *trident = dev_id;
36531da177e4SLinus Torvalds unsigned int audio_int, chn_int, stimer, channel, mask, tmp;
36541da177e4SLinus Torvalds int delta;
3655bee1a5beSTakashi Iwai struct snd_trident_voice *voice;
36561da177e4SLinus Torvalds
36571da177e4SLinus Torvalds audio_int = inl(TRID_REG(trident, T4D_MISCINT));
36581da177e4SLinus Torvalds if ((audio_int & (ADDRESS_IRQ|MPU401_IRQ)) == 0)
36591da177e4SLinus Torvalds return IRQ_NONE;
36601da177e4SLinus Torvalds if (audio_int & ADDRESS_IRQ) {
36611da177e4SLinus Torvalds // get interrupt status for all channels
36621da177e4SLinus Torvalds spin_lock(&trident->reg_lock);
36631da177e4SLinus Torvalds stimer = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
36641da177e4SLinus Torvalds chn_int = inl(TRID_REG(trident, T4D_AINT_A));
36651da177e4SLinus Torvalds if (chn_int == 0)
36661da177e4SLinus Torvalds goto __skip1;
36671da177e4SLinus Torvalds outl(chn_int, TRID_REG(trident, T4D_AINT_A)); /* ack */
36681da177e4SLinus Torvalds __skip1:
36691da177e4SLinus Torvalds chn_int = inl(TRID_REG(trident, T4D_AINT_B));
36701da177e4SLinus Torvalds if (chn_int == 0)
36711da177e4SLinus Torvalds goto __skip2;
36721da177e4SLinus Torvalds for (channel = 63; channel >= 32; channel--) {
36731da177e4SLinus Torvalds mask = 1 << (channel&0x1f);
36741da177e4SLinus Torvalds if ((chn_int & mask) == 0)
36751da177e4SLinus Torvalds continue;
36761da177e4SLinus Torvalds voice = &trident->synth.voices[channel];
36771da177e4SLinus Torvalds if (!voice->pcm || voice->substream == NULL) {
36781da177e4SLinus Torvalds outl(mask, TRID_REG(trident, T4D_STOP_B));
36791da177e4SLinus Torvalds continue;
36801da177e4SLinus Torvalds }
36811da177e4SLinus Torvalds delta = (int)stimer - (int)voice->stimer;
36821da177e4SLinus Torvalds if (delta < 0)
36831da177e4SLinus Torvalds delta = -delta;
36841da177e4SLinus Torvalds if ((unsigned int)delta < voice->spurious_threshold) {
36851da177e4SLinus Torvalds /* do some statistics here */
36861da177e4SLinus Torvalds trident->spurious_irq_count++;
36871da177e4SLinus Torvalds if (trident->spurious_irq_max_delta < (unsigned int)delta)
36881da177e4SLinus Torvalds trident->spurious_irq_max_delta = delta;
36891da177e4SLinus Torvalds continue;
36901da177e4SLinus Torvalds }
36911da177e4SLinus Torvalds voice->stimer = stimer;
36921da177e4SLinus Torvalds if (voice->isync) {
36931da177e4SLinus Torvalds if (!voice->isync3) {
36941da177e4SLinus Torvalds tmp = inw(TRID_REG(trident, T4D_SBBL_SBCL));
36951da177e4SLinus Torvalds if (trident->bDMAStart & 0x40)
36961da177e4SLinus Torvalds tmp >>= 1;
36971da177e4SLinus Torvalds if (tmp > 0)
36981da177e4SLinus Torvalds tmp = voice->isync_max - tmp;
36991da177e4SLinus Torvalds } else {
37001da177e4SLinus Torvalds tmp = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff;
37011da177e4SLinus Torvalds }
37021da177e4SLinus Torvalds if (tmp < voice->isync_mark) {
37031da177e4SLinus Torvalds if (tmp > 0x10)
37041da177e4SLinus Torvalds tmp = voice->isync_ESO - 7;
37051da177e4SLinus Torvalds else
37061da177e4SLinus Torvalds tmp = voice->isync_ESO + 2;
37071da177e4SLinus Torvalds /* update ESO for IRQ voice to preserve sync */
37081da177e4SLinus Torvalds snd_trident_stop_voice(trident, voice->number);
37091da177e4SLinus Torvalds snd_trident_write_eso_reg(trident, voice, tmp);
37101da177e4SLinus Torvalds snd_trident_start_voice(trident, voice->number);
37111da177e4SLinus Torvalds }
37121da177e4SLinus Torvalds } else if (voice->isync2) {
37131da177e4SLinus Torvalds voice->isync2 = 0;
37141da177e4SLinus Torvalds /* write original ESO and update CSO for IRQ voice to preserve sync */
37151da177e4SLinus Torvalds snd_trident_stop_voice(trident, voice->number);
37161da177e4SLinus Torvalds snd_trident_write_cso_reg(trident, voice, voice->isync_mark);
37171da177e4SLinus Torvalds snd_trident_write_eso_reg(trident, voice, voice->ESO);
37181da177e4SLinus Torvalds snd_trident_start_voice(trident, voice->number);
37191da177e4SLinus Torvalds }
37201da177e4SLinus Torvalds #if 0
37211da177e4SLinus Torvalds if (voice->extra) {
37221da177e4SLinus Torvalds /* update CSO for extra voice to preserve sync */
37231da177e4SLinus Torvalds snd_trident_stop_voice(trident, voice->extra->number);
37241da177e4SLinus Torvalds snd_trident_write_cso_reg(trident, voice->extra, 0);
37251da177e4SLinus Torvalds snd_trident_start_voice(trident, voice->extra->number);
37261da177e4SLinus Torvalds }
37271da177e4SLinus Torvalds #endif
37281da177e4SLinus Torvalds spin_unlock(&trident->reg_lock);
37291da177e4SLinus Torvalds snd_pcm_period_elapsed(voice->substream);
37301da177e4SLinus Torvalds spin_lock(&trident->reg_lock);
37311da177e4SLinus Torvalds }
37321da177e4SLinus Torvalds outl(chn_int, TRID_REG(trident, T4D_AINT_B)); /* ack */
37331da177e4SLinus Torvalds __skip2:
37341da177e4SLinus Torvalds spin_unlock(&trident->reg_lock);
37351da177e4SLinus Torvalds }
37361da177e4SLinus Torvalds if (audio_int & MPU401_IRQ) {
37371da177e4SLinus Torvalds if (trident->rmidi) {
37387d12e780SDavid Howells snd_mpu401_uart_interrupt(irq, trident->rmidi->private_data);
37391da177e4SLinus Torvalds } else {
37401da177e4SLinus Torvalds inb(TRID_REG(trident, T4D_MPUR0));
37411da177e4SLinus Torvalds }
37421da177e4SLinus Torvalds }
37431da177e4SLinus Torvalds // outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(trident, T4D_MISCINT));
37441da177e4SLinus Torvalds return IRQ_HANDLED;
37451da177e4SLinus Torvalds }
37461da177e4SLinus Torvalds
snd_trident_alloc_voice(struct snd_trident * trident,int type,int client,int port)3747bee1a5beSTakashi Iwai struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port)
37481da177e4SLinus Torvalds {
3749bee1a5beSTakashi Iwai struct snd_trident_voice *pvoice;
37501da177e4SLinus Torvalds unsigned long flags;
37511da177e4SLinus Torvalds int idx;
37521da177e4SLinus Torvalds
37531da177e4SLinus Torvalds spin_lock_irqsave(&trident->voice_alloc, flags);
37541da177e4SLinus Torvalds if (type == SNDRV_TRIDENT_VOICE_TYPE_PCM) {
37551da177e4SLinus Torvalds idx = snd_trident_allocate_pcm_channel(trident);
37561da177e4SLinus Torvalds if(idx < 0) {
37571da177e4SLinus Torvalds spin_unlock_irqrestore(&trident->voice_alloc, flags);
37581da177e4SLinus Torvalds return NULL;
37591da177e4SLinus Torvalds }
37601da177e4SLinus Torvalds pvoice = &trident->synth.voices[idx];
37611da177e4SLinus Torvalds pvoice->use = 1;
37621da177e4SLinus Torvalds pvoice->pcm = 1;
37631da177e4SLinus Torvalds pvoice->capture = 0;
37641da177e4SLinus Torvalds pvoice->spdif = 0;
37651da177e4SLinus Torvalds pvoice->memblk = NULL;
37661da177e4SLinus Torvalds pvoice->substream = NULL;
37671da177e4SLinus Torvalds spin_unlock_irqrestore(&trident->voice_alloc, flags);
37681da177e4SLinus Torvalds return pvoice;
37691da177e4SLinus Torvalds }
37701da177e4SLinus Torvalds if (type == SNDRV_TRIDENT_VOICE_TYPE_SYNTH) {
37711da177e4SLinus Torvalds idx = snd_trident_allocate_synth_channel(trident);
37721da177e4SLinus Torvalds if(idx < 0) {
37731da177e4SLinus Torvalds spin_unlock_irqrestore(&trident->voice_alloc, flags);
37741da177e4SLinus Torvalds return NULL;
37751da177e4SLinus Torvalds }
37761da177e4SLinus Torvalds pvoice = &trident->synth.voices[idx];
37771da177e4SLinus Torvalds pvoice->use = 1;
37781da177e4SLinus Torvalds pvoice->synth = 1;
37791da177e4SLinus Torvalds pvoice->client = client;
37801da177e4SLinus Torvalds pvoice->port = port;
37811da177e4SLinus Torvalds pvoice->memblk = NULL;
37821da177e4SLinus Torvalds spin_unlock_irqrestore(&trident->voice_alloc, flags);
37831da177e4SLinus Torvalds return pvoice;
37841da177e4SLinus Torvalds }
37851da177e4SLinus Torvalds if (type == SNDRV_TRIDENT_VOICE_TYPE_MIDI) {
37861da177e4SLinus Torvalds }
37871da177e4SLinus Torvalds spin_unlock_irqrestore(&trident->voice_alloc, flags);
37881da177e4SLinus Torvalds return NULL;
37891da177e4SLinus Torvalds }
37901da177e4SLinus Torvalds
3791cbef55f3STakashi Iwai EXPORT_SYMBOL(snd_trident_alloc_voice);
3792cbef55f3STakashi Iwai
snd_trident_free_voice(struct snd_trident * trident,struct snd_trident_voice * voice)3793bee1a5beSTakashi Iwai void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice)
37941da177e4SLinus Torvalds {
37951da177e4SLinus Torvalds unsigned long flags;
3796bee1a5beSTakashi Iwai void (*private_free)(struct snd_trident_voice *);
37971da177e4SLinus Torvalds
37981da177e4SLinus Torvalds if (voice == NULL || !voice->use)
37991da177e4SLinus Torvalds return;
38001da177e4SLinus Torvalds snd_trident_clear_voices(trident, voice->number, voice->number);
38011da177e4SLinus Torvalds spin_lock_irqsave(&trident->voice_alloc, flags);
38021da177e4SLinus Torvalds private_free = voice->private_free;
38031da177e4SLinus Torvalds voice->private_free = NULL;
38041da177e4SLinus Torvalds voice->private_data = NULL;
38051da177e4SLinus Torvalds if (voice->pcm)
38061da177e4SLinus Torvalds snd_trident_free_pcm_channel(trident, voice->number);
38071da177e4SLinus Torvalds if (voice->synth)
38081da177e4SLinus Torvalds snd_trident_free_synth_channel(trident, voice->number);
38091da177e4SLinus Torvalds voice->use = voice->pcm = voice->synth = voice->midi = 0;
38101da177e4SLinus Torvalds voice->capture = voice->spdif = 0;
38111da177e4SLinus Torvalds voice->sample_ops = NULL;
38121da177e4SLinus Torvalds voice->substream = NULL;
38131da177e4SLinus Torvalds voice->extra = NULL;
38141da177e4SLinus Torvalds spin_unlock_irqrestore(&trident->voice_alloc, flags);
38151da177e4SLinus Torvalds if (private_free)
38161da177e4SLinus Torvalds private_free(voice);
38171da177e4SLinus Torvalds }
38181da177e4SLinus Torvalds
3819cbef55f3STakashi Iwai EXPORT_SYMBOL(snd_trident_free_voice);
3820cbef55f3STakashi Iwai
snd_trident_clear_voices(struct snd_trident * trident,unsigned short v_min,unsigned short v_max)3821bee1a5beSTakashi Iwai static void snd_trident_clear_voices(struct snd_trident * trident, unsigned short v_min, unsigned short v_max)
38221da177e4SLinus Torvalds {
38231da177e4SLinus Torvalds unsigned int i, val, mask[2] = { 0, 0 };
38241da177e4SLinus Torvalds
3825da3cec35STakashi Iwai if (snd_BUG_ON(v_min > 63 || v_max > 63))
3826da3cec35STakashi Iwai return;
38271da177e4SLinus Torvalds for (i = v_min; i <= v_max; i++)
38281da177e4SLinus Torvalds mask[i >> 5] |= 1 << (i & 0x1f);
38291da177e4SLinus Torvalds if (mask[0]) {
38301da177e4SLinus Torvalds outl(mask[0], TRID_REG(trident, T4D_STOP_A));
38311da177e4SLinus Torvalds val = inl(TRID_REG(trident, T4D_AINTEN_A));
38321da177e4SLinus Torvalds outl(val & ~mask[0], TRID_REG(trident, T4D_AINTEN_A));
38331da177e4SLinus Torvalds }
38341da177e4SLinus Torvalds if (mask[1]) {
38351da177e4SLinus Torvalds outl(mask[1], TRID_REG(trident, T4D_STOP_B));
38361da177e4SLinus Torvalds val = inl(TRID_REG(trident, T4D_AINTEN_B));
38371da177e4SLinus Torvalds outl(val & ~mask[1], TRID_REG(trident, T4D_AINTEN_B));
38381da177e4SLinus Torvalds }
38391da177e4SLinus Torvalds }
38401da177e4SLinus Torvalds
3841c7561cd8STakashi Iwai #ifdef CONFIG_PM_SLEEP
snd_trident_suspend(struct device * dev)384268cb2b55STakashi Iwai static int snd_trident_suspend(struct device *dev)
38431da177e4SLinus Torvalds {
384468cb2b55STakashi Iwai struct snd_card *card = dev_get_drvdata(dev);
3845fb0700b4STakashi Iwai struct snd_trident *trident = card->private_data;
38461da177e4SLinus Torvalds
38471da177e4SLinus Torvalds trident->in_suspend = 1;
3848fb0700b4STakashi Iwai snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
38491da177e4SLinus Torvalds snd_ac97_suspend(trident->ac97);
38501da177e4SLinus Torvalds snd_ac97_suspend(trident->ac97_sec);
38511da177e4SLinus Torvalds return 0;
38521da177e4SLinus Torvalds }
38531da177e4SLinus Torvalds
snd_trident_resume(struct device * dev)385468cb2b55STakashi Iwai static int snd_trident_resume(struct device *dev)
38551da177e4SLinus Torvalds {
385668cb2b55STakashi Iwai struct snd_card *card = dev_get_drvdata(dev);
3857fb0700b4STakashi Iwai struct snd_trident *trident = card->private_data;
38581da177e4SLinus Torvalds
38591da177e4SLinus Torvalds switch (trident->device) {
38601da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_DX:
38611da177e4SLinus Torvalds snd_trident_4d_dx_init(trident);
38621da177e4SLinus Torvalds break;
38631da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_NX:
38641da177e4SLinus Torvalds snd_trident_4d_nx_init(trident);
38651da177e4SLinus Torvalds break;
38661da177e4SLinus Torvalds case TRIDENT_DEVICE_ID_SI7018:
38671da177e4SLinus Torvalds snd_trident_sis_init(trident);
38681da177e4SLinus Torvalds break;
38691da177e4SLinus Torvalds }
38701da177e4SLinus Torvalds
38711da177e4SLinus Torvalds snd_ac97_resume(trident->ac97);
38721da177e4SLinus Torvalds snd_ac97_resume(trident->ac97_sec);
38731da177e4SLinus Torvalds
38741da177e4SLinus Torvalds /* restore some registers */
38751da177e4SLinus Torvalds outl(trident->musicvol_wavevol, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
38761da177e4SLinus Torvalds
38771da177e4SLinus Torvalds snd_trident_enable_eso(trident);
38781da177e4SLinus Torvalds
3879fb0700b4STakashi Iwai snd_power_change_state(card, SNDRV_CTL_POWER_D0);
38801da177e4SLinus Torvalds trident->in_suspend = 0;
38811da177e4SLinus Torvalds return 0;
38821da177e4SLinus Torvalds }
388368cb2b55STakashi Iwai
388468cb2b55STakashi Iwai SIMPLE_DEV_PM_OPS(snd_trident_pm, snd_trident_suspend, snd_trident_resume);
3885c7561cd8STakashi Iwai #endif /* CONFIG_PM_SLEEP */
3886