ALSA: emu10k1 - delay the PCM interrupts (add pcm_irq_delay parameter)
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> Cc: <stable@kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
a5ba6beb83
commit
56385a12d9
|
@ -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 */
|
||||
|
|
|
@ -978,6 +978,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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -310,8 +310,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;
|
||||
|
|
Loading…
Reference in New Issue