ALSA: emu10k1 - delay the PCM interrupts (add pcm_irq_delay parameter)
authorJaroslav Kysela <perex@perex.cz>
Wed, 18 Aug 2010 12:08:17 +0000 (14:08 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 26 Aug 2010 23:41:34 +0000 (16:41 -0700)
commit 56385a12d9bb9e173751f74b6c430742018cafc0 upstream.

With some hardware combinations, the PCM interrupts are acknowledged
before the period boundary from the emu10k1 chip. The midlevel PCM code
gets confused and the playback stream is interrupted.

It seems that the interrupt processing shift by 2 samples is enough
to fix this issue. This default value does not harm other,
non-affected hardware.

More information: Kernel bugzilla bug#16300

[A copmile warning fixed by tiwai]

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
include/sound/emu10k1.h
sound/core/pcm_native.c
sound/pci/emu10k1/emu10k1.c
sound/pci/emu10k1/emupcm.c
sound/pci/emu10k1/memory.c

index 6a664c3f7c1e426dd0b74129ce9cf81cb07fefa0..7dc97d12253c1bdc494b939e5e856b1b1fa6fc2c 100644 (file)
@@ -1707,6 +1707,7 @@ struct snd_emu10k1 {
        unsigned int card_type;                 /* EMU10K1_CARD_* */
        unsigned int ecard_ctrl;                /* ecard control bits */
        unsigned long dma_mask;                 /* PCI DMA mask */
+       unsigned int delay_pcm_irq;             /* in samples */
        int max_cache_pages;                    /* max memory size / PAGE_SIZE */
        struct snd_dma_buffer silent_page;      /* silent page */
        struct snd_dma_buffer ptb_pages;        /* page table pages */
index 7ba779d9e2e797c55e40d629c23e7fc67c2779f9..e6d2d974d23143e42b24fbd1fff56f6cc2e23eb6 100644 (file)
@@ -972,6 +972,10 @@ static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push)
 {
        if (substream->runtime->trigger_master != substream)
                return 0;
+       /* some drivers might use hw_ptr to recover from the pause -
+          update the hw_ptr now */
+       if (push)
+               snd_pcm_update_hw_ptr(substream);
        /* The jiffies check in snd_pcm_update_hw_ptr*() is done by
         * a delta betwen the current jiffies, this gives a large enough
         * delta, effectively to skip the check once.
index 168af67d938e1415993b29eb166763327ec0048b..92626f3aab2dbc9f4c0c7b16f098056f25854ba1 100644 (file)
@@ -52,6 +52,7 @@ static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64};
 static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128};
 static int enable_ir[SNDRV_CARDS];
 static uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */
+static uint delay_pcm_irq[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard.");
@@ -73,6 +74,8 @@ module_param_array(enable_ir, bool, NULL, 0444);
 MODULE_PARM_DESC(enable_ir, "Enable IR.");
 module_param_array(subsystem, uint, NULL, 0444);
 MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
+module_param_array(delay_pcm_irq, uint, NULL, 0444);
+MODULE_PARM_DESC(delay_pcm_irq, "Delay PCM interrupt by specified number of samples (default 0).");
 /*
  * Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value  Model:SB0400
  */
@@ -127,6 +130,7 @@ static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci,
                                      &emu)) < 0)
                goto error;
        card->private_data = emu;
+       emu->delay_pcm_irq = delay_pcm_irq[dev] & 0x1f;
        if ((err = snd_emu10k1_pcm(emu, 0, NULL)) < 0)
                goto error;
        if ((err = snd_emu10k1_pcm_mic(emu, 1, NULL)) < 0)
index 55b83ef73c630e83b2d098a45cce1f570923df7f..622bace148e3c4e5e4de3efa1936b56245004c69 100644 (file)
@@ -332,7 +332,7 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
                evoice->epcm->ccca_start_addr = start_addr + ccis;
                if (extra) {
                        start_addr += ccis;
-                       end_addr += ccis;
+                       end_addr += ccis + emu->delay_pcm_irq;
                }
                if (stereo && !extra) {
                        snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK);
@@ -360,7 +360,9 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
        /* Assumption that PT is already 0 so no harm overwriting */
        snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]);
        snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24));
-       snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24));
+       snd_emu10k1_ptr_write(emu, PSST, voice,
+                       (start_addr + (extra ? emu->delay_pcm_irq : 0)) |
+                       (send_amount[2] << 24));
        if (emu->card_capabilities->emu_model)
                pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
        else 
@@ -732,6 +734,23 @@ static void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu, struct snd_
        snd_emu10k1_ptr_write(emu, IP, voice, 0);
 }
 
+static inline void snd_emu10k1_playback_mangle_extra(struct snd_emu10k1 *emu,
+               struct snd_emu10k1_pcm *epcm,
+               struct snd_pcm_substream *substream,
+               struct snd_pcm_runtime *runtime)
+{
+       unsigned int ptr, period_pos;
+
+       /* try to sychronize the current position for the interrupt
+          source voice */
+       period_pos = runtime->status->hw_ptr - runtime->hw_ptr_interrupt;
+       period_pos %= runtime->period_size;
+       ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->extra->number);
+       ptr &= ~0x00ffffff;
+       ptr |= epcm->ccca_start_addr + period_pos;
+       snd_emu10k1_ptr_write(emu, CCCA, epcm->extra->number, ptr);
+}
+
 static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
                                        int cmd)
 {
@@ -753,6 +772,8 @@ static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
                /* follow thru */
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
        case SNDRV_PCM_TRIGGER_RESUME:
+               if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
+                       snd_emu10k1_playback_mangle_extra(emu, epcm, substream, runtime);
                mix = &emu->pcm_mixer[substream->number];
                snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, 0, mix);
                snd_emu10k1_playback_prepare_voice(emu, epcm->voices[1], 0, 0, mix);
@@ -869,8 +890,9 @@ static snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream *
 #endif
        /*
        printk(KERN_DEBUG
-              "ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n",
-              ptr, runtime->buffer_size, runtime->period_size);
+              "ptr = 0x%lx, buffer_size = 0x%lx, period_size = 0x%lx\n",
+              (long)ptr, (long)runtime->buffer_size,
+              (long)runtime->period_size);
        */
        return ptr;
 }
index 6a47672f930aedfe67163ce56a0e5a726470e734..7c3ce5f379f2ba5b5023170748119a6b0033e763 100644 (file)
@@ -309,8 +309,10 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst
        if (snd_BUG_ON(!hdr))
                return NULL;
 
+       idx = runtime->period_size >= runtime->buffer_size ?
+                                       (emu->delay_pcm_irq * 2) : 0;
        mutex_lock(&hdr->block_mutex);
-       blk = search_empty(emu, runtime->dma_bytes);
+       blk = search_empty(emu, runtime->dma_bytes + idx);
        if (blk == NULL) {
                mutex_unlock(&hdr->block_mutex);
                return NULL;