aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorOswald Buddenhagen2023-05-23 22:07:08 +0200
committerTakashi Iwai2023-05-24 12:10:29 +0200
commit11ee59bdac36ae4b500301a6a3ccf586d3968d92 (patch)
tree29b66c384d6df25681e858725eb088efa5af6744 /include
parent6dbecb9b51321bfaf8e7f26fc163d547abcbee86 (diff)
ALSA: emu10k1: add synchronized start of multi-channel playback
We use independent voices for the channels, so we need to make an effort to ensure that they are actually in sync. The hardware doesn't provide atomicity, so we may need to retry a few times, due to NMIs, PCI contention, and the wrong phase of the moon. Solution inspired by kX-project. Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de> Link: https://lore.kernel.org/r/20230523200709.236023-3-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'include')
-rw-r--r--include/sound/emu10k1.h10
1 files changed, 9 insertions, 1 deletions
diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h
index 0780f39f4bb6..164a2374b4c2 100644
--- a/include/sound/emu10k1.h
+++ b/include/sound/emu10k1.h
@@ -422,7 +422,8 @@ SUB_REG(HCFG, LOCKTANKCACHE, 0x00000004) /* 1 = Cancel bustmaster accesses to ta
#define CPF 0x00 /* Current pitch and fraction register */
SUB_REG(CPF, CURRENTPITCH, 0xffff0000) /* Current pitch (linear, 0x4000 == unity pitch shift) */
#define CPF_STEREO_MASK 0x00008000 /* 1 = Even channel interleave, odd channel locked */
-#define CPF_STOP_MASK 0x00004000 /* 1 = Current pitch forced to 0 */
+SUB_REG(CPF, STOP, 0x00004000) /* 1 = Current pitch forced to 0 */
+ /* Can be set only while matching bit in SOLEx is 1 */
#define CPF_FRACADDRESS_MASK 0x00003fff /* Linear fractional address of the current channel */
#define PTRX 0x01 /* Pitch target and send A/B amounts register */
@@ -771,6 +772,9 @@ SUB_REG(PEFE, FILTERAMOUNT, 0x000000ff) /* Filter envlope amount */
#define CLIPL 0x5a /* Channel loop interrupt pending low register */
#define CLIPH 0x5b /* Channel loop interrupt pending high register */
+// These cause CPF_STOP_MASK to be set shortly after CCCA_CURRADDR passes DSL_LOOPENDADDR.
+// Subsequent changes to the address registers don't resume; clearing the bit here or in CPF does.
+// The registers are NOT synchronized; the next serviced channel picks up immediately.
#define SOLEL 0x5c /* Stop on loop enable low register */
#define SOLEH 0x5d /* Stop on loop enable high register */
@@ -1476,6 +1480,7 @@ struct snd_emu10k1_pcm {
struct snd_emu10k1_voice *extra;
unsigned short running;
unsigned short first_ptr;
+ snd_pcm_uframes_t resume_pos;
struct snd_util_memblk *memblk;
unsigned int start_addr;
unsigned int ccca_start_addr;
@@ -1820,6 +1825,9 @@ void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int
void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum);
void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum);
#endif
+void snd_emu10k1_voice_set_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices);
+void snd_emu10k1_voice_clear_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices);
+int snd_emu10k1_voice_clear_loop_stop_multiple_atomic(struct snd_emu10k1 *emu, u64 voices);
void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait);
static inline unsigned int snd_emu10k1_wc(struct snd_emu10k1 *emu) { return (inl(emu->port + WC) >> 6) & 0xfffff; }
unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg);