11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
4*6d68d9cbSOswald Buddenhagen * Lee Revell <rlrevell@joe-job.com>
5*6d68d9cbSOswald Buddenhagen * James Courtier-Dutton <James@superbug.co.uk>
6*6d68d9cbSOswald Buddenhagen * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
71da177e4SLinus Torvalds * Creative Labs, Inc.
8*6d68d9cbSOswald Buddenhagen *
91da177e4SLinus Torvalds * Routines for control of EMU10K1 chips
101da177e4SLinus Torvalds */
111da177e4SLinus Torvalds
121da177e4SLinus Torvalds #include <linux/time.h>
131da177e4SLinus Torvalds #include <sound/core.h>
141da177e4SLinus Torvalds #include <sound/emu10k1.h>
1527fe864eSJames Courtier-Dutton #include <linux/delay.h>
16d81a6d71SPaul Gortmaker #include <linux/export.h>
17184c1e2cSJames Courtier-Dutton #include "p17v.h"
181da177e4SLinus Torvalds
check_ptr_reg(struct snd_emu10k1 * emu,unsigned int reg)193676cd4bSOswald Buddenhagen static inline bool check_ptr_reg(struct snd_emu10k1 *emu, unsigned int reg)
203676cd4bSOswald Buddenhagen {
213676cd4bSOswald Buddenhagen if (snd_BUG_ON(!emu))
223676cd4bSOswald Buddenhagen return false;
233676cd4bSOswald Buddenhagen if (snd_BUG_ON(reg & (emu->audigy ? (0xffff0000 & ~A_PTR_ADDRESS_MASK)
243676cd4bSOswald Buddenhagen : (0xffff0000 & ~PTR_ADDRESS_MASK))))
253676cd4bSOswald Buddenhagen return false;
263676cd4bSOswald Buddenhagen if (snd_BUG_ON(reg & 0x0000ffff & ~PTR_CHANNELNUM_MASK))
273676cd4bSOswald Buddenhagen return false;
283676cd4bSOswald Buddenhagen return true;
293676cd4bSOswald Buddenhagen }
303676cd4bSOswald Buddenhagen
snd_emu10k1_ptr_read(struct snd_emu10k1 * emu,unsigned int reg,unsigned int chn)31eb4698f3STakashi Iwai unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
321da177e4SLinus Torvalds {
331da177e4SLinus Torvalds unsigned long flags;
341da177e4SLinus Torvalds unsigned int regptr, val;
351da177e4SLinus Torvalds unsigned int mask;
361da177e4SLinus Torvalds
373676cd4bSOswald Buddenhagen regptr = (reg << 16) | chn;
383676cd4bSOswald Buddenhagen if (!check_ptr_reg(emu, regptr))
393676cd4bSOswald Buddenhagen return 0;
401da177e4SLinus Torvalds
412093dcfcSOswald Buddenhagen spin_lock_irqsave(&emu->emu_lock, flags);
422093dcfcSOswald Buddenhagen outl(regptr, emu->port + PTR);
432093dcfcSOswald Buddenhagen val = inl(emu->port + DATA);
442093dcfcSOswald Buddenhagen spin_unlock_irqrestore(&emu->emu_lock, flags);
452093dcfcSOswald Buddenhagen
461da177e4SLinus Torvalds if (reg & 0xff000000) {
471da177e4SLinus Torvalds unsigned char size, offset;
481da177e4SLinus Torvalds
491da177e4SLinus Torvalds size = (reg >> 24) & 0x3f;
501da177e4SLinus Torvalds offset = (reg >> 16) & 0x1f;
512e9bd50fSOswald Buddenhagen mask = (1 << size) - 1;
521da177e4SLinus Torvalds
532e9bd50fSOswald Buddenhagen return (val >> offset) & mask;
541da177e4SLinus Torvalds } else {
551da177e4SLinus Torvalds return val;
561da177e4SLinus Torvalds }
571da177e4SLinus Torvalds }
581da177e4SLinus Torvalds
592dd31deeSTakashi Iwai EXPORT_SYMBOL(snd_emu10k1_ptr_read);
602dd31deeSTakashi Iwai
snd_emu10k1_ptr_write(struct snd_emu10k1 * emu,unsigned int reg,unsigned int chn,unsigned int data)61eb4698f3STakashi Iwai void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
621da177e4SLinus Torvalds {
631da177e4SLinus Torvalds unsigned int regptr;
641da177e4SLinus Torvalds unsigned long flags;
651da177e4SLinus Torvalds unsigned int mask;
661da177e4SLinus Torvalds
673676cd4bSOswald Buddenhagen regptr = (reg << 16) | chn;
683676cd4bSOswald Buddenhagen if (!check_ptr_reg(emu, regptr))
69c94fa4c9SJames Courtier-Dutton return;
701da177e4SLinus Torvalds
711da177e4SLinus Torvalds if (reg & 0xff000000) {
721da177e4SLinus Torvalds unsigned char size, offset;
731da177e4SLinus Torvalds
741da177e4SLinus Torvalds size = (reg >> 24) & 0x3f;
751da177e4SLinus Torvalds offset = (reg >> 16) & 0x1f;
763676cd4bSOswald Buddenhagen mask = (1 << size) - 1;
773676cd4bSOswald Buddenhagen if (snd_BUG_ON(data & ~mask))
783676cd4bSOswald Buddenhagen return;
793676cd4bSOswald Buddenhagen mask <<= offset;
803676cd4bSOswald Buddenhagen data <<= offset;
811da177e4SLinus Torvalds
821da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
831da177e4SLinus Torvalds outl(regptr, emu->port + PTR);
841da177e4SLinus Torvalds data |= inl(emu->port + DATA) & ~mask;
851da177e4SLinus Torvalds } else {
861da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
871da177e4SLinus Torvalds outl(regptr, emu->port + PTR);
882093dcfcSOswald Buddenhagen }
891da177e4SLinus Torvalds outl(data, emu->port + DATA);
901da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds
932dd31deeSTakashi Iwai EXPORT_SYMBOL(snd_emu10k1_ptr_write);
942dd31deeSTakashi Iwai
snd_emu10k1_ptr_write_multiple(struct snd_emu10k1 * emu,unsigned int chn,...)9546055699SOswald Buddenhagen void snd_emu10k1_ptr_write_multiple(struct snd_emu10k1 *emu, unsigned int chn, ...)
9646055699SOswald Buddenhagen {
9746055699SOswald Buddenhagen va_list va;
9846055699SOswald Buddenhagen u32 addr_mask;
9946055699SOswald Buddenhagen unsigned long flags;
10046055699SOswald Buddenhagen
10146055699SOswald Buddenhagen if (snd_BUG_ON(!emu))
10246055699SOswald Buddenhagen return;
10346055699SOswald Buddenhagen if (snd_BUG_ON(chn & ~PTR_CHANNELNUM_MASK))
10446055699SOswald Buddenhagen return;
10546055699SOswald Buddenhagen addr_mask = ~((emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK) >> 16);
10646055699SOswald Buddenhagen
10746055699SOswald Buddenhagen va_start(va, chn);
10846055699SOswald Buddenhagen spin_lock_irqsave(&emu->emu_lock, flags);
10946055699SOswald Buddenhagen for (;;) {
11046055699SOswald Buddenhagen u32 data;
11146055699SOswald Buddenhagen u32 reg = va_arg(va, u32);
11246055699SOswald Buddenhagen if (reg == REGLIST_END)
11346055699SOswald Buddenhagen break;
11446055699SOswald Buddenhagen data = va_arg(va, u32);
11546055699SOswald Buddenhagen if (snd_BUG_ON(reg & addr_mask)) // Only raw registers supported here
11646055699SOswald Buddenhagen continue;
11746055699SOswald Buddenhagen outl((reg << 16) | chn, emu->port + PTR);
11846055699SOswald Buddenhagen outl(data, emu->port + DATA);
11946055699SOswald Buddenhagen }
12046055699SOswald Buddenhagen spin_unlock_irqrestore(&emu->emu_lock, flags);
12146055699SOswald Buddenhagen va_end(va);
12246055699SOswald Buddenhagen }
12346055699SOswald Buddenhagen
12446055699SOswald Buddenhagen EXPORT_SYMBOL(snd_emu10k1_ptr_write_multiple);
12546055699SOswald Buddenhagen
snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,unsigned int reg,unsigned int chn)126eb4698f3STakashi Iwai unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
1271da177e4SLinus Torvalds unsigned int reg,
1281da177e4SLinus Torvalds unsigned int chn)
1291da177e4SLinus Torvalds {
1301da177e4SLinus Torvalds unsigned long flags;
1311da177e4SLinus Torvalds unsigned int regptr, val;
1321da177e4SLinus Torvalds
1331da177e4SLinus Torvalds regptr = (reg << 16) | chn;
1341da177e4SLinus Torvalds
1351da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
1369d2f3863SOswald Buddenhagen outl(regptr, emu->port + PTR2);
1379d2f3863SOswald Buddenhagen val = inl(emu->port + DATA2);
1381da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
1391da177e4SLinus Torvalds return val;
1401da177e4SLinus Torvalds }
1411da177e4SLinus Torvalds
snd_emu10k1_ptr20_write(struct snd_emu10k1 * emu,unsigned int reg,unsigned int chn,unsigned int data)142eb4698f3STakashi Iwai void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
1431da177e4SLinus Torvalds unsigned int reg,
1441da177e4SLinus Torvalds unsigned int chn,
1451da177e4SLinus Torvalds unsigned int data)
1461da177e4SLinus Torvalds {
1471da177e4SLinus Torvalds unsigned int regptr;
1481da177e4SLinus Torvalds unsigned long flags;
1491da177e4SLinus Torvalds
1501da177e4SLinus Torvalds regptr = (reg << 16) | chn;
1511da177e4SLinus Torvalds
1521da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
1539d2f3863SOswald Buddenhagen outl(regptr, emu->port + PTR2);
1549d2f3863SOswald Buddenhagen outl(data, emu->port + DATA2);
1551da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
1561da177e4SLinus Torvalds }
1571da177e4SLinus Torvalds
snd_emu10k1_spi_write(struct snd_emu10k1 * emu,unsigned int data)15827fe864eSJames Courtier-Dutton int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
15927fe864eSJames Courtier-Dutton unsigned int data)
16027fe864eSJames Courtier-Dutton {
16127fe864eSJames Courtier-Dutton unsigned int reset, set;
16227fe864eSJames Courtier-Dutton unsigned int reg, tmp;
16327fe864eSJames Courtier-Dutton int n, result;
164c94fa4c9SJames Courtier-Dutton int err = 0;
165c94fa4c9SJames Courtier-Dutton
166c94fa4c9SJames Courtier-Dutton /* This function is not re-entrant, so protect against it. */
167c94fa4c9SJames Courtier-Dutton spin_lock(&emu->spi_lock);
16828bcbdddSJames Courtier-Dutton if (emu->card_capabilities->ca0108_chip)
1699d2f3863SOswald Buddenhagen reg = P17V_SPI;
17028bcbdddSJames Courtier-Dutton else {
17128bcbdddSJames Courtier-Dutton /* For other chip types the SPI register
17228bcbdddSJames Courtier-Dutton * is currently unknown. */
173c94fa4c9SJames Courtier-Dutton err = 1;
174c94fa4c9SJames Courtier-Dutton goto spi_write_exit;
17527fe864eSJames Courtier-Dutton }
176c94fa4c9SJames Courtier-Dutton if (data > 0xffff) {
177c94fa4c9SJames Courtier-Dutton /* Only 16bit values allowed */
178c94fa4c9SJames Courtier-Dutton err = 1;
179c94fa4c9SJames Courtier-Dutton goto spi_write_exit;
180c94fa4c9SJames Courtier-Dutton }
18127fe864eSJames Courtier-Dutton
18227fe864eSJames Courtier-Dutton tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
18327fe864eSJames Courtier-Dutton reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
18427fe864eSJames Courtier-Dutton set = reset | 0x10000; /* Set xxx1xxxx */
18527fe864eSJames Courtier-Dutton snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
18627fe864eSJames Courtier-Dutton tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */
18727fe864eSJames Courtier-Dutton snd_emu10k1_ptr20_write(emu, reg, 0, set | data);
18827fe864eSJames Courtier-Dutton result = 1;
18927fe864eSJames Courtier-Dutton /* Wait for status bit to return to 0 */
19027fe864eSJames Courtier-Dutton for (n = 0; n < 100; n++) {
19127fe864eSJames Courtier-Dutton udelay(10);
19227fe864eSJames Courtier-Dutton tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
19327fe864eSJames Courtier-Dutton if (!(tmp & 0x10000)) {
19427fe864eSJames Courtier-Dutton result = 0;
19527fe864eSJames Courtier-Dutton break;
19627fe864eSJames Courtier-Dutton }
19727fe864eSJames Courtier-Dutton }
198c94fa4c9SJames Courtier-Dutton if (result) {
199c94fa4c9SJames Courtier-Dutton /* Timed out */
200c94fa4c9SJames Courtier-Dutton err = 1;
201c94fa4c9SJames Courtier-Dutton goto spi_write_exit;
202c94fa4c9SJames Courtier-Dutton }
20327fe864eSJames Courtier-Dutton snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
20427fe864eSJames Courtier-Dutton tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
205c94fa4c9SJames Courtier-Dutton err = 0;
206c94fa4c9SJames Courtier-Dutton spi_write_exit:
207c94fa4c9SJames Courtier-Dutton spin_unlock(&emu->spi_lock);
208c94fa4c9SJames Courtier-Dutton return err;
20927fe864eSJames Courtier-Dutton }
21027fe864eSJames Courtier-Dutton
211184c1e2cSJames Courtier-Dutton /* The ADC does not support i2c read, so only write is implemented */
snd_emu10k1_i2c_write(struct snd_emu10k1 * emu,u32 reg,u32 value)212184c1e2cSJames Courtier-Dutton int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
213184c1e2cSJames Courtier-Dutton u32 reg,
214184c1e2cSJames Courtier-Dutton u32 value)
215184c1e2cSJames Courtier-Dutton {
216184c1e2cSJames Courtier-Dutton u32 tmp;
217184c1e2cSJames Courtier-Dutton int timeout = 0;
218184c1e2cSJames Courtier-Dutton int status;
219184c1e2cSJames Courtier-Dutton int retry;
220c94fa4c9SJames Courtier-Dutton int err = 0;
221c94fa4c9SJames Courtier-Dutton
222184c1e2cSJames Courtier-Dutton if ((reg > 0x7f) || (value > 0x1ff)) {
2236f002b02STakashi Iwai dev_err(emu->card->dev, "i2c_write: invalid values.\n");
224184c1e2cSJames Courtier-Dutton return -EINVAL;
225184c1e2cSJames Courtier-Dutton }
226184c1e2cSJames Courtier-Dutton
227c94fa4c9SJames Courtier-Dutton /* This function is not re-entrant, so protect against it. */
228c94fa4c9SJames Courtier-Dutton spin_lock(&emu->i2c_lock);
229c94fa4c9SJames Courtier-Dutton
230184c1e2cSJames Courtier-Dutton tmp = reg << 25 | value << 16;
231184c1e2cSJames Courtier-Dutton
232184c1e2cSJames Courtier-Dutton /* This controls the I2C connected to the WM8775 ADC Codec */
233184c1e2cSJames Courtier-Dutton snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
234184c1e2cSJames Courtier-Dutton tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */
235184c1e2cSJames Courtier-Dutton
236184c1e2cSJames Courtier-Dutton for (retry = 0; retry < 10; retry++) {
237184c1e2cSJames Courtier-Dutton /* Send the data to i2c */
238184c1e2cSJames Courtier-Dutton tmp = 0;
239184c1e2cSJames Courtier-Dutton tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
240184c1e2cSJames Courtier-Dutton snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
241184c1e2cSJames Courtier-Dutton
242184c1e2cSJames Courtier-Dutton /* Wait till the transaction ends */
243184c1e2cSJames Courtier-Dutton while (1) {
244c94fa4c9SJames Courtier-Dutton mdelay(1);
245184c1e2cSJames Courtier-Dutton status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
246184c1e2cSJames Courtier-Dutton timeout++;
247184c1e2cSJames Courtier-Dutton if ((status & I2C_A_ADC_START) == 0)
248184c1e2cSJames Courtier-Dutton break;
249184c1e2cSJames Courtier-Dutton
250184c1e2cSJames Courtier-Dutton if (timeout > 1000) {
2516f002b02STakashi Iwai dev_warn(emu->card->dev,
25228a97c19STakashi Iwai "emu10k1:I2C:timeout status=0x%x\n",
25328a97c19STakashi Iwai status);
254184c1e2cSJames Courtier-Dutton break;
255184c1e2cSJames Courtier-Dutton }
256184c1e2cSJames Courtier-Dutton }
257184c1e2cSJames Courtier-Dutton //Read back and see if the transaction is successful
258184c1e2cSJames Courtier-Dutton if ((status & I2C_A_ADC_ABORT) == 0)
259184c1e2cSJames Courtier-Dutton break;
260184c1e2cSJames Courtier-Dutton }
261184c1e2cSJames Courtier-Dutton
262184c1e2cSJames Courtier-Dutton if (retry == 10) {
2636f002b02STakashi Iwai dev_err(emu->card->dev, "Writing to ADC failed!\n");
2646f002b02STakashi Iwai dev_err(emu->card->dev, "status=0x%x, reg=%d, value=%d\n",
265c94fa4c9SJames Courtier-Dutton status, reg, value);
266c94fa4c9SJames Courtier-Dutton /* dump_stack(); */
267c94fa4c9SJames Courtier-Dutton err = -EINVAL;
268184c1e2cSJames Courtier-Dutton }
269184c1e2cSJames Courtier-Dutton
270c94fa4c9SJames Courtier-Dutton spin_unlock(&emu->i2c_lock);
271c94fa4c9SJames Courtier-Dutton return err;
272184c1e2cSJames Courtier-Dutton }
273184c1e2cSJames Courtier-Dutton
snd_emu1010_fpga_write_locked(struct snd_emu10k1 * emu,u32 reg,u32 value)27435d1d582SOswald Buddenhagen static void snd_emu1010_fpga_write_locked(struct snd_emu10k1 *emu, u32 reg, u32 value)
2759f4bd5ddSJames Courtier-Dutton {
27610f212bdSOswald Buddenhagen if (snd_BUG_ON(reg > 0x3f))
27710f212bdSOswald Buddenhagen return;
2789f4bd5ddSJames Courtier-Dutton reg += 0x40; /* 0x40 upwards are registers. */
27910f212bdSOswald Buddenhagen if (snd_BUG_ON(value > 0x3f)) /* 0 to 0x3f are values */
28010f212bdSOswald Buddenhagen return;
281a1c87c0bSOswald Buddenhagen outw(reg, emu->port + A_GPIO);
2829f4bd5ddSJames Courtier-Dutton udelay(10);
283a1c87c0bSOswald Buddenhagen outw(reg | 0x80, emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
2849f4bd5ddSJames Courtier-Dutton udelay(10);
285a1c87c0bSOswald Buddenhagen outw(value, emu->port + A_GPIO);
2869f4bd5ddSJames Courtier-Dutton udelay(10);
287a1c87c0bSOswald Buddenhagen outw(value | 0x80 , emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
28835d1d582SOswald Buddenhagen }
28935d1d582SOswald Buddenhagen
snd_emu1010_fpga_write(struct snd_emu10k1 * emu,u32 reg,u32 value)29035d1d582SOswald Buddenhagen void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value)
29135d1d582SOswald Buddenhagen {
29235d1d582SOswald Buddenhagen unsigned long flags;
29335d1d582SOswald Buddenhagen
29435d1d582SOswald Buddenhagen spin_lock_irqsave(&emu->emu_lock, flags);
29535d1d582SOswald Buddenhagen snd_emu1010_fpga_write_locked(emu, reg, value);
296190d2c46SJames Courtier-Dutton spin_unlock_irqrestore(&emu->emu_lock, flags);
2979f4bd5ddSJames Courtier-Dutton }
2989f4bd5ddSJames Courtier-Dutton
snd_emu1010_fpga_read_locked(struct snd_emu10k1 * emu,u32 reg,u32 * value)2996ab13291SOswald Buddenhagen static void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 *value)
3009f4bd5ddSJames Courtier-Dutton {
3016fb861bbSOswald Buddenhagen // The higest input pin is used as the designated interrupt trigger,
3026fb861bbSOswald Buddenhagen // so it needs to be masked out.
303fbb64eedSOswald Buddenhagen // But note that any other input pin change will also cause an IRQ,
304fbb64eedSOswald Buddenhagen // so using this function often causes an IRQ as a side effect.
3056fb861bbSOswald Buddenhagen u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f;
30610f212bdSOswald Buddenhagen if (snd_BUG_ON(reg > 0x3f))
30710f212bdSOswald Buddenhagen return;
3089f4bd5ddSJames Courtier-Dutton reg += 0x40; /* 0x40 upwards are registers. */
309a1c87c0bSOswald Buddenhagen outw(reg, emu->port + A_GPIO);
3109f4bd5ddSJames Courtier-Dutton udelay(10);
311a1c87c0bSOswald Buddenhagen outw(reg | 0x80, emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
3129f4bd5ddSJames Courtier-Dutton udelay(10);
3136fb861bbSOswald Buddenhagen *value = ((inw(emu->port + A_GPIO) >> 8) & mask);
3146ab13291SOswald Buddenhagen }
3156ab13291SOswald Buddenhagen
snd_emu1010_fpga_read(struct snd_emu10k1 * emu,u32 reg,u32 * value)3166ab13291SOswald Buddenhagen void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
3176ab13291SOswald Buddenhagen {
3186ab13291SOswald Buddenhagen unsigned long flags;
3196ab13291SOswald Buddenhagen
3206ab13291SOswald Buddenhagen spin_lock_irqsave(&emu->emu_lock, flags);
3216ab13291SOswald Buddenhagen snd_emu1010_fpga_read_locked(emu, reg, value);
322190d2c46SJames Courtier-Dutton spin_unlock_irqrestore(&emu->emu_lock, flags);
3239f4bd5ddSJames Courtier-Dutton }
3249f4bd5ddSJames Courtier-Dutton
3259f4bd5ddSJames Courtier-Dutton /* Each Destination has one and only one Source,
3269f4bd5ddSJames Courtier-Dutton * but one Source can feed any number of Destinations simultaneously.
3279f4bd5ddSJames Courtier-Dutton */
snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu,u32 dst,u32 src)32810f212bdSOswald Buddenhagen void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src)
3299f4bd5ddSJames Courtier-Dutton {
33035d1d582SOswald Buddenhagen unsigned long flags;
33135d1d582SOswald Buddenhagen
33210f212bdSOswald Buddenhagen if (snd_BUG_ON(dst & ~0x71f))
33310f212bdSOswald Buddenhagen return;
33410f212bdSOswald Buddenhagen if (snd_BUG_ON(src & ~0x71f))
33510f212bdSOswald Buddenhagen return;
33635d1d582SOswald Buddenhagen spin_lock_irqsave(&emu->emu_lock, flags);
33735d1d582SOswald Buddenhagen snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8);
33835d1d582SOswald Buddenhagen snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f);
33935d1d582SOswald Buddenhagen snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCHI, src >> 8);
34035d1d582SOswald Buddenhagen snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCLO, src & 0x1f);
34135d1d582SOswald Buddenhagen spin_unlock_irqrestore(&emu->emu_lock, flags);
3429f4bd5ddSJames Courtier-Dutton }
3439f4bd5ddSJames Courtier-Dutton
snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 * emu,u32 dst)3446ab13291SOswald Buddenhagen u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst)
3456ab13291SOswald Buddenhagen {
3466ab13291SOswald Buddenhagen unsigned long flags;
3476ab13291SOswald Buddenhagen u32 hi, lo;
3486ab13291SOswald Buddenhagen
3496ab13291SOswald Buddenhagen if (snd_BUG_ON(dst & ~0x71f))
3506ab13291SOswald Buddenhagen return 0;
3516ab13291SOswald Buddenhagen spin_lock_irqsave(&emu->emu_lock, flags);
3526ab13291SOswald Buddenhagen snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8);
3536ab13291SOswald Buddenhagen snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f);
3546ab13291SOswald Buddenhagen snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCHI, &hi);
3556ab13291SOswald Buddenhagen snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCLO, &lo);
3566ab13291SOswald Buddenhagen spin_unlock_irqrestore(&emu->emu_lock, flags);
3576ab13291SOswald Buddenhagen return (hi << 8) | lo;
3586ab13291SOswald Buddenhagen }
3596ab13291SOswald Buddenhagen
snd_emu1010_get_raw_rate(struct snd_emu10k1 * emu,u8 src)360e73b597eSOswald Buddenhagen int snd_emu1010_get_raw_rate(struct snd_emu10k1 *emu, u8 src)
361e73b597eSOswald Buddenhagen {
362e73b597eSOswald Buddenhagen u32 reg_lo, reg_hi, value, value2;
363e73b597eSOswald Buddenhagen
364e73b597eSOswald Buddenhagen switch (src) {
365e73b597eSOswald Buddenhagen case EMU_HANA_WCLOCK_HANA_SPDIF_IN:
366e73b597eSOswald Buddenhagen snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &value);
367e73b597eSOswald Buddenhagen if (value & EMU_HANA_SPDIF_MODE_RX_INVALID)
368e73b597eSOswald Buddenhagen return 0;
369e73b597eSOswald Buddenhagen reg_lo = EMU_HANA_WC_SPDIF_LO;
370e73b597eSOswald Buddenhagen reg_hi = EMU_HANA_WC_SPDIF_HI;
371e73b597eSOswald Buddenhagen break;
372e73b597eSOswald Buddenhagen case EMU_HANA_WCLOCK_HANA_ADAT_IN:
373e73b597eSOswald Buddenhagen reg_lo = EMU_HANA_WC_ADAT_LO;
374e73b597eSOswald Buddenhagen reg_hi = EMU_HANA_WC_ADAT_HI;
375e73b597eSOswald Buddenhagen break;
376e73b597eSOswald Buddenhagen case EMU_HANA_WCLOCK_SYNC_BNC:
377e73b597eSOswald Buddenhagen reg_lo = EMU_HANA_WC_BNC_LO;
378e73b597eSOswald Buddenhagen reg_hi = EMU_HANA_WC_BNC_HI;
379e73b597eSOswald Buddenhagen break;
380e73b597eSOswald Buddenhagen case EMU_HANA_WCLOCK_2ND_HANA:
381e73b597eSOswald Buddenhagen reg_lo = EMU_HANA2_WC_SPDIF_LO;
382e73b597eSOswald Buddenhagen reg_hi = EMU_HANA2_WC_SPDIF_HI;
383e73b597eSOswald Buddenhagen break;
384e73b597eSOswald Buddenhagen default:
385e73b597eSOswald Buddenhagen return 0;
386e73b597eSOswald Buddenhagen }
387e73b597eSOswald Buddenhagen snd_emu1010_fpga_read(emu, reg_hi, &value);
388e73b597eSOswald Buddenhagen snd_emu1010_fpga_read(emu, reg_lo, &value2);
389e73b597eSOswald Buddenhagen // FIXME: The /4 is valid for 0404b, but contradicts all other info.
390e73b597eSOswald Buddenhagen return 0x1770000 / 4 / (((value << 5) | value2) + 1);
391e73b597eSOswald Buddenhagen }
392e73b597eSOswald Buddenhagen
snd_emu1010_update_clock(struct snd_emu10k1 * emu)39360985241SOswald Buddenhagen void snd_emu1010_update_clock(struct snd_emu10k1 *emu)
39460985241SOswald Buddenhagen {
395e73b597eSOswald Buddenhagen int clock;
39660985241SOswald Buddenhagen u32 leds;
39760985241SOswald Buddenhagen
39860985241SOswald Buddenhagen switch (emu->emu1010.wclock) {
39960985241SOswald Buddenhagen case EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X:
400e73b597eSOswald Buddenhagen clock = 44100;
40160985241SOswald Buddenhagen leds = EMU_HANA_DOCK_LEDS_2_44K;
40260985241SOswald Buddenhagen break;
40360985241SOswald Buddenhagen case EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_1X:
404e73b597eSOswald Buddenhagen clock = 48000;
40560985241SOswald Buddenhagen leds = EMU_HANA_DOCK_LEDS_2_48K;
40660985241SOswald Buddenhagen break;
40760985241SOswald Buddenhagen default:
408e73b597eSOswald Buddenhagen clock = snd_emu1010_get_raw_rate(
409e73b597eSOswald Buddenhagen emu, emu->emu1010.wclock & EMU_HANA_WCLOCK_SRC_MASK);
410e73b597eSOswald Buddenhagen // The raw rate reading is rather coarse (it cannot accurately
411e73b597eSOswald Buddenhagen // represent 44.1 kHz) and fluctuates slightly. Luckily, the
412e73b597eSOswald Buddenhagen // clock comes from digital inputs, which use standardized rates.
413e73b597eSOswald Buddenhagen // So we round to the closest standard rate and ignore discrepancies.
414e73b597eSOswald Buddenhagen if (clock < 46000) {
415e73b597eSOswald Buddenhagen clock = 44100;
416e73b597eSOswald Buddenhagen leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_44K;
417e73b597eSOswald Buddenhagen } else {
418e73b597eSOswald Buddenhagen clock = 48000;
419e73b597eSOswald Buddenhagen leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_48K;
420e73b597eSOswald Buddenhagen }
42160985241SOswald Buddenhagen break;
42260985241SOswald Buddenhagen }
423e73b597eSOswald Buddenhagen emu->emu1010.word_clock = clock;
42460985241SOswald Buddenhagen
42560985241SOswald Buddenhagen // FIXME: this should probably represent the AND of all currently
42660985241SOswald Buddenhagen // used sources' lock status. But we don't know how to get that ...
42760985241SOswald Buddenhagen leds |= EMU_HANA_DOCK_LEDS_2_LOCK;
42860985241SOswald Buddenhagen
42960985241SOswald Buddenhagen snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, leds);
43060985241SOswald Buddenhagen }
43160985241SOswald Buddenhagen
snd_emu10k1_intr_enable(struct snd_emu10k1 * emu,unsigned int intrenb)432eb4698f3STakashi Iwai void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
4331da177e4SLinus Torvalds {
4341da177e4SLinus Torvalds unsigned long flags;
4351da177e4SLinus Torvalds unsigned int enable;
4361da177e4SLinus Torvalds
4371da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
4381da177e4SLinus Torvalds enable = inl(emu->port + INTE) | intrenb;
4391da177e4SLinus Torvalds outl(enable, emu->port + INTE);
4401da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
4411da177e4SLinus Torvalds }
4421da177e4SLinus Torvalds
snd_emu10k1_intr_disable(struct snd_emu10k1 * emu,unsigned int intrenb)443eb4698f3STakashi Iwai void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
4441da177e4SLinus Torvalds {
4451da177e4SLinus Torvalds unsigned long flags;
4461da177e4SLinus Torvalds unsigned int enable;
4471da177e4SLinus Torvalds
4481da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
4491da177e4SLinus Torvalds enable = inl(emu->port + INTE) & ~intrenb;
4501da177e4SLinus Torvalds outl(enable, emu->port + INTE);
4511da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
4521da177e4SLinus Torvalds }
4531da177e4SLinus Torvalds
snd_emu10k1_voice_intr_enable(struct snd_emu10k1 * emu,unsigned int voicenum)454eb4698f3STakashi Iwai void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
4551da177e4SLinus Torvalds {
4561da177e4SLinus Torvalds unsigned long flags;
4571da177e4SLinus Torvalds unsigned int val;
4581da177e4SLinus Torvalds
4591da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
4601da177e4SLinus Torvalds if (voicenum >= 32) {
4611da177e4SLinus Torvalds outl(CLIEH << 16, emu->port + PTR);
4621da177e4SLinus Torvalds val = inl(emu->port + DATA);
4631da177e4SLinus Torvalds val |= 1 << (voicenum - 32);
4641da177e4SLinus Torvalds } else {
4651da177e4SLinus Torvalds outl(CLIEL << 16, emu->port + PTR);
4661da177e4SLinus Torvalds val = inl(emu->port + DATA);
4671da177e4SLinus Torvalds val |= 1 << voicenum;
4681da177e4SLinus Torvalds }
4691da177e4SLinus Torvalds outl(val, emu->port + DATA);
4701da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
4711da177e4SLinus Torvalds }
4721da177e4SLinus Torvalds
snd_emu10k1_voice_intr_disable(struct snd_emu10k1 * emu,unsigned int voicenum)473eb4698f3STakashi Iwai void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
4741da177e4SLinus Torvalds {
4751da177e4SLinus Torvalds unsigned long flags;
4761da177e4SLinus Torvalds unsigned int val;
4771da177e4SLinus Torvalds
4781da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
4791da177e4SLinus Torvalds if (voicenum >= 32) {
4801da177e4SLinus Torvalds outl(CLIEH << 16, emu->port + PTR);
4811da177e4SLinus Torvalds val = inl(emu->port + DATA);
4821da177e4SLinus Torvalds val &= ~(1 << (voicenum - 32));
4831da177e4SLinus Torvalds } else {
4841da177e4SLinus Torvalds outl(CLIEL << 16, emu->port + PTR);
4851da177e4SLinus Torvalds val = inl(emu->port + DATA);
4861da177e4SLinus Torvalds val &= ~(1 << voicenum);
4871da177e4SLinus Torvalds }
4881da177e4SLinus Torvalds outl(val, emu->port + DATA);
4891da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
4901da177e4SLinus Torvalds }
4911da177e4SLinus Torvalds
snd_emu10k1_voice_intr_ack(struct snd_emu10k1 * emu,unsigned int voicenum)492eb4698f3STakashi Iwai void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
4931da177e4SLinus Torvalds {
4941da177e4SLinus Torvalds unsigned long flags;
4951da177e4SLinus Torvalds
4961da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
4971da177e4SLinus Torvalds if (voicenum >= 32) {
4981da177e4SLinus Torvalds outl(CLIPH << 16, emu->port + PTR);
4991da177e4SLinus Torvalds voicenum = 1 << (voicenum - 32);
5001da177e4SLinus Torvalds } else {
5011da177e4SLinus Torvalds outl(CLIPL << 16, emu->port + PTR);
5021da177e4SLinus Torvalds voicenum = 1 << voicenum;
5031da177e4SLinus Torvalds }
5041da177e4SLinus Torvalds outl(voicenum, emu->port + DATA);
5051da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
5061da177e4SLinus Torvalds }
5071da177e4SLinus Torvalds
snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 * emu,unsigned int voicenum)508eb4698f3STakashi Iwai void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
5091da177e4SLinus Torvalds {
5101da177e4SLinus Torvalds unsigned long flags;
5111da177e4SLinus Torvalds unsigned int val;
5121da177e4SLinus Torvalds
5131da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
5141da177e4SLinus Torvalds if (voicenum >= 32) {
5151da177e4SLinus Torvalds outl(HLIEH << 16, emu->port + PTR);
5161da177e4SLinus Torvalds val = inl(emu->port + DATA);
5171da177e4SLinus Torvalds val |= 1 << (voicenum - 32);
5181da177e4SLinus Torvalds } else {
5191da177e4SLinus Torvalds outl(HLIEL << 16, emu->port + PTR);
5201da177e4SLinus Torvalds val = inl(emu->port + DATA);
5211da177e4SLinus Torvalds val |= 1 << voicenum;
5221da177e4SLinus Torvalds }
5231da177e4SLinus Torvalds outl(val, emu->port + DATA);
5241da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
5251da177e4SLinus Torvalds }
5261da177e4SLinus Torvalds
snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 * emu,unsigned int voicenum)527eb4698f3STakashi Iwai void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
5281da177e4SLinus Torvalds {
5291da177e4SLinus Torvalds unsigned long flags;
5301da177e4SLinus Torvalds unsigned int val;
5311da177e4SLinus Torvalds
5321da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
5331da177e4SLinus Torvalds if (voicenum >= 32) {
5341da177e4SLinus Torvalds outl(HLIEH << 16, emu->port + PTR);
5351da177e4SLinus Torvalds val = inl(emu->port + DATA);
5361da177e4SLinus Torvalds val &= ~(1 << (voicenum - 32));
5371da177e4SLinus Torvalds } else {
5381da177e4SLinus Torvalds outl(HLIEL << 16, emu->port + PTR);
5391da177e4SLinus Torvalds val = inl(emu->port + DATA);
5401da177e4SLinus Torvalds val &= ~(1 << voicenum);
5411da177e4SLinus Torvalds }
5421da177e4SLinus Torvalds outl(val, emu->port + DATA);
5431da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
5441da177e4SLinus Torvalds }
5451da177e4SLinus Torvalds
snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 * emu,unsigned int voicenum)546eb4698f3STakashi Iwai void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
5471da177e4SLinus Torvalds {
5481da177e4SLinus Torvalds unsigned long flags;
5491da177e4SLinus Torvalds
5501da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
5511da177e4SLinus Torvalds if (voicenum >= 32) {
5521da177e4SLinus Torvalds outl(HLIPH << 16, emu->port + PTR);
5531da177e4SLinus Torvalds voicenum = 1 << (voicenum - 32);
5541da177e4SLinus Torvalds } else {
5551da177e4SLinus Torvalds outl(HLIPL << 16, emu->port + PTR);
5561da177e4SLinus Torvalds voicenum = 1 << voicenum;
5571da177e4SLinus Torvalds }
5581da177e4SLinus Torvalds outl(voicenum, emu->port + DATA);
5591da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
5601da177e4SLinus Torvalds }
5611da177e4SLinus Torvalds
562a61c695aSOswald Buddenhagen #if 0
563eb4698f3STakashi Iwai void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
5641da177e4SLinus Torvalds {
5651da177e4SLinus Torvalds unsigned long flags;
5661da177e4SLinus Torvalds unsigned int sol;
5671da177e4SLinus Torvalds
5681da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
5691da177e4SLinus Torvalds if (voicenum >= 32) {
5701da177e4SLinus Torvalds outl(SOLEH << 16, emu->port + PTR);
5711da177e4SLinus Torvalds sol = inl(emu->port + DATA);
5721da177e4SLinus Torvalds sol |= 1 << (voicenum - 32);
5731da177e4SLinus Torvalds } else {
5741da177e4SLinus Torvalds outl(SOLEL << 16, emu->port + PTR);
5751da177e4SLinus Torvalds sol = inl(emu->port + DATA);
5761da177e4SLinus Torvalds sol |= 1 << voicenum;
5771da177e4SLinus Torvalds }
5781da177e4SLinus Torvalds outl(sol, emu->port + DATA);
5791da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
5801da177e4SLinus Torvalds }
5811da177e4SLinus Torvalds
582eb4698f3STakashi Iwai void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
5831da177e4SLinus Torvalds {
5841da177e4SLinus Torvalds unsigned long flags;
5851da177e4SLinus Torvalds unsigned int sol;
5861da177e4SLinus Torvalds
5871da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
5881da177e4SLinus Torvalds if (voicenum >= 32) {
5891da177e4SLinus Torvalds outl(SOLEH << 16, emu->port + PTR);
5901da177e4SLinus Torvalds sol = inl(emu->port + DATA);
5911da177e4SLinus Torvalds sol &= ~(1 << (voicenum - 32));
5921da177e4SLinus Torvalds } else {
5931da177e4SLinus Torvalds outl(SOLEL << 16, emu->port + PTR);
5941da177e4SLinus Torvalds sol = inl(emu->port + DATA);
5951da177e4SLinus Torvalds sol &= ~(1 << voicenum);
5961da177e4SLinus Torvalds }
5971da177e4SLinus Torvalds outl(sol, emu->port + DATA);
5981da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
5991da177e4SLinus Torvalds }
600a61c695aSOswald Buddenhagen #endif
6011da177e4SLinus Torvalds
snd_emu10k1_voice_set_loop_stop_multiple(struct snd_emu10k1 * emu,u64 voices)60211ee59bdSOswald Buddenhagen void snd_emu10k1_voice_set_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices)
60311ee59bdSOswald Buddenhagen {
60411ee59bdSOswald Buddenhagen unsigned long flags;
60511ee59bdSOswald Buddenhagen
60611ee59bdSOswald Buddenhagen spin_lock_irqsave(&emu->emu_lock, flags);
60711ee59bdSOswald Buddenhagen outl(SOLEL << 16, emu->port + PTR);
60811ee59bdSOswald Buddenhagen outl(inl(emu->port + DATA) | (u32)voices, emu->port + DATA);
60911ee59bdSOswald Buddenhagen outl(SOLEH << 16, emu->port + PTR);
61011ee59bdSOswald Buddenhagen outl(inl(emu->port + DATA) | (u32)(voices >> 32), emu->port + DATA);
61111ee59bdSOswald Buddenhagen spin_unlock_irqrestore(&emu->emu_lock, flags);
61211ee59bdSOswald Buddenhagen }
61311ee59bdSOswald Buddenhagen
snd_emu10k1_voice_clear_loop_stop_multiple(struct snd_emu10k1 * emu,u64 voices)61411ee59bdSOswald Buddenhagen void snd_emu10k1_voice_clear_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices)
61511ee59bdSOswald Buddenhagen {
61611ee59bdSOswald Buddenhagen unsigned long flags;
61711ee59bdSOswald Buddenhagen
61811ee59bdSOswald Buddenhagen spin_lock_irqsave(&emu->emu_lock, flags);
61911ee59bdSOswald Buddenhagen outl(SOLEL << 16, emu->port + PTR);
62011ee59bdSOswald Buddenhagen outl(inl(emu->port + DATA) & (u32)~voices, emu->port + DATA);
62111ee59bdSOswald Buddenhagen outl(SOLEH << 16, emu->port + PTR);
62211ee59bdSOswald Buddenhagen outl(inl(emu->port + DATA) & (u32)(~voices >> 32), emu->port + DATA);
62311ee59bdSOswald Buddenhagen spin_unlock_irqrestore(&emu->emu_lock, flags);
62411ee59bdSOswald Buddenhagen }
62511ee59bdSOswald Buddenhagen
snd_emu10k1_voice_clear_loop_stop_multiple_atomic(struct snd_emu10k1 * emu,u64 voices)62611ee59bdSOswald Buddenhagen int snd_emu10k1_voice_clear_loop_stop_multiple_atomic(struct snd_emu10k1 *emu, u64 voices)
62711ee59bdSOswald Buddenhagen {
62811ee59bdSOswald Buddenhagen unsigned long flags;
62911ee59bdSOswald Buddenhagen u32 soll, solh;
63011ee59bdSOswald Buddenhagen int ret = -EIO;
63111ee59bdSOswald Buddenhagen
63211ee59bdSOswald Buddenhagen spin_lock_irqsave(&emu->emu_lock, flags);
63311ee59bdSOswald Buddenhagen
63411ee59bdSOswald Buddenhagen outl(SOLEL << 16, emu->port + PTR);
63511ee59bdSOswald Buddenhagen soll = inl(emu->port + DATA);
63611ee59bdSOswald Buddenhagen outl(SOLEH << 16, emu->port + PTR);
63711ee59bdSOswald Buddenhagen solh = inl(emu->port + DATA);
63811ee59bdSOswald Buddenhagen
63911ee59bdSOswald Buddenhagen soll &= (u32)~voices;
64011ee59bdSOswald Buddenhagen solh &= (u32)(~voices >> 32);
64111ee59bdSOswald Buddenhagen
64211ee59bdSOswald Buddenhagen for (int tries = 0; tries < 1000; tries++) {
64311ee59bdSOswald Buddenhagen const u32 quart = 1U << (REG_SIZE(WC_CURRENTCHANNEL) - 2);
64411ee59bdSOswald Buddenhagen // First we wait for the third quarter of the sample cycle ...
64511ee59bdSOswald Buddenhagen u32 wc = inl(emu->port + WC);
64611ee59bdSOswald Buddenhagen u32 cc = REG_VAL_GET(WC_CURRENTCHANNEL, wc);
64711ee59bdSOswald Buddenhagen if (cc >= quart * 2 && cc < quart * 3) {
64811ee59bdSOswald Buddenhagen // ... and release the low voices, while the high ones are serviced.
64911ee59bdSOswald Buddenhagen outl(SOLEL << 16, emu->port + PTR);
65011ee59bdSOswald Buddenhagen outl(soll, emu->port + DATA);
65111ee59bdSOswald Buddenhagen // Then we wait for the first quarter of the next sample cycle ...
65211ee59bdSOswald Buddenhagen for (; tries < 1000; tries++) {
65311ee59bdSOswald Buddenhagen cc = REG_VAL_GET(WC_CURRENTCHANNEL, inl(emu->port + WC));
65411ee59bdSOswald Buddenhagen if (cc < quart)
65511ee59bdSOswald Buddenhagen goto good;
65611ee59bdSOswald Buddenhagen // We will block for 10+ us with interrupts disabled. This is
65711ee59bdSOswald Buddenhagen // not nice at all, but necessary for reasonable reliability.
65811ee59bdSOswald Buddenhagen udelay(1);
65911ee59bdSOswald Buddenhagen }
66011ee59bdSOswald Buddenhagen break;
66111ee59bdSOswald Buddenhagen good:
66211ee59bdSOswald Buddenhagen // ... and release the high voices, while the low ones are serviced.
66311ee59bdSOswald Buddenhagen outl(SOLEH << 16, emu->port + PTR);
66411ee59bdSOswald Buddenhagen outl(solh, emu->port + DATA);
66511ee59bdSOswald Buddenhagen // Finally we verify that nothing interfered in fact.
66611ee59bdSOswald Buddenhagen if (REG_VAL_GET(WC_SAMPLECOUNTER, inl(emu->port + WC)) ==
66711ee59bdSOswald Buddenhagen ((REG_VAL_GET(WC_SAMPLECOUNTER, wc) + 1) & REG_MASK0(WC_SAMPLECOUNTER))) {
66811ee59bdSOswald Buddenhagen ret = 0;
66911ee59bdSOswald Buddenhagen } else {
67011ee59bdSOswald Buddenhagen ret = -EAGAIN;
67111ee59bdSOswald Buddenhagen }
67211ee59bdSOswald Buddenhagen break;
67311ee59bdSOswald Buddenhagen }
67411ee59bdSOswald Buddenhagen // Don't block for too long
67511ee59bdSOswald Buddenhagen spin_unlock_irqrestore(&emu->emu_lock, flags);
67611ee59bdSOswald Buddenhagen udelay(1);
67711ee59bdSOswald Buddenhagen spin_lock_irqsave(&emu->emu_lock, flags);
67811ee59bdSOswald Buddenhagen }
67911ee59bdSOswald Buddenhagen
68011ee59bdSOswald Buddenhagen spin_unlock_irqrestore(&emu->emu_lock, flags);
68111ee59bdSOswald Buddenhagen return ret;
68211ee59bdSOswald Buddenhagen }
68311ee59bdSOswald Buddenhagen
snd_emu10k1_wait(struct snd_emu10k1 * emu,unsigned int wait)684eb4698f3STakashi Iwai void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
6851da177e4SLinus Torvalds {
6861da177e4SLinus Torvalds volatile unsigned count;
6871da177e4SLinus Torvalds unsigned int newtime = 0, curtime;
6881da177e4SLinus Torvalds
6891da177e4SLinus Torvalds curtime = inl(emu->port + WC) >> 6;
6901da177e4SLinus Torvalds while (wait-- > 0) {
6911da177e4SLinus Torvalds count = 0;
6921da177e4SLinus Torvalds while (count++ < 16384) {
6931da177e4SLinus Torvalds newtime = inl(emu->port + WC) >> 6;
6941da177e4SLinus Torvalds if (newtime != curtime)
6951da177e4SLinus Torvalds break;
6961da177e4SLinus Torvalds }
6975a47fa3dSRoel Kluin if (count > 16384)
6981da177e4SLinus Torvalds break;
6991da177e4SLinus Torvalds curtime = newtime;
7001da177e4SLinus Torvalds }
7011da177e4SLinus Torvalds }
7021da177e4SLinus Torvalds
snd_emu10k1_ac97_read(struct snd_ac97 * ac97,unsigned short reg)703eb4698f3STakashi Iwai unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
7041da177e4SLinus Torvalds {
705eb4698f3STakashi Iwai struct snd_emu10k1 *emu = ac97->private_data;
7061da177e4SLinus Torvalds unsigned long flags;
7071da177e4SLinus Torvalds unsigned short val;
7081da177e4SLinus Torvalds
7091da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
7101da177e4SLinus Torvalds outb(reg, emu->port + AC97ADDRESS);
7111da177e4SLinus Torvalds val = inw(emu->port + AC97DATA);
7121da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
7131da177e4SLinus Torvalds return val;
7141da177e4SLinus Torvalds }
7151da177e4SLinus Torvalds
snd_emu10k1_ac97_write(struct snd_ac97 * ac97,unsigned short reg,unsigned short data)716eb4698f3STakashi Iwai void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
7171da177e4SLinus Torvalds {
718eb4698f3STakashi Iwai struct snd_emu10k1 *emu = ac97->private_data;
7191da177e4SLinus Torvalds unsigned long flags;
7201da177e4SLinus Torvalds
7211da177e4SLinus Torvalds spin_lock_irqsave(&emu->emu_lock, flags);
7221da177e4SLinus Torvalds outb(reg, emu->port + AC97ADDRESS);
7231da177e4SLinus Torvalds outw(data, emu->port + AC97DATA);
7241da177e4SLinus Torvalds spin_unlock_irqrestore(&emu->emu_lock, flags);
7251da177e4SLinus Torvalds }
726