Comment 90 for bug 996906

Revision history for this message
In , Raymond (superquad-vortex2) wrote :

as pulseaudio use snd_pcm_rewind() which require the sound card report accurate hwptr position than those sound card which report hwptr position at period bounary when interrupt occur

the difference between hda-intel and your ca0106 is the granularity of the pointer callback

https://git.kernel.org/cgit/linux/kernel/git/tiwai/sound.git/tree/include/linux/dmaengine.h

/**
 * enum dma_residue_granularity - Granularity of the reported transfer residue
 * @DMA_RESIDUE_GRANULARITY_DESCRIPTOR: Residue reporting is not support. The
 * DMA channel is only able to tell whether a descriptor has been completed or
 * not, which means residue reporting is not supported by this channel. The
 * residue field of the dma_tx_state field will always be 0.
 * @DMA_RESIDUE_GRANULARITY_SEGMENT: Residue is updated after each successfully
 * completed segment of the transfer (For cyclic transfers this is after each
 * period). This is typically implemented by having the hardware generate an
 * interrupt after each transferred segment and then the drivers updates the
 * outstanding residue by the size of the segment. Another possibility is if
 * the hardware supports scatter-gather and the segment descriptor has a field
 * which gets set after the segment has been completed. The driver then counts
 * the number of segments without the flag set to compute the residue.
 * @DMA_RESIDUE_GRANULARITY_BURST: Residue is updated after each transferred
 * burst. This is typically only supported if the hardware has a progress
 * register of some sort (E.g. a register with the current read/write address
 * or a register with the amount of bursts/beats/bytes that have been
 * transferred or still need to be transferred).
 */

hda-intel can report DMA_RESIDUE_GRANULARITY_BURST

seem both read hwptr from hardware register

can your ca0106 report DMA_RESIDUE_GRANULARITY_SEGMENT or DMA_RESIDUE_GRANULARITY_BURST ?

https://git.kernel.org/cgit/linux/kernel/git/tiwai/sound.git/tree/sound/pci/ca0106/ca0106_main.c

static snd_pcm_uframes_t
snd_ca0106_pcm_pointer_playback(struct snd_pcm_substream *substream)
{
 struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_ca0106_pcm *epcm = runtime->private_data;
 unsigned int ptr, prev_ptr;
 int channel = epcm->channel_id;
 int timeout = 10;

 if (!epcm->running)
  return 0;

 prev_ptr = -1;
 do {
  ptr = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
  ptr = (ptr >> 3) * runtime->period_size;
  ptr += bytes_to_frames(runtime,
   snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel));
  if (ptr >= runtime->buffer_size)
   ptr -= runtime->buffer_size;
  if (prev_ptr == ptr)
   return ptr;
  prev_ptr = ptr;
 } while (--timeout);
 dev_warn(emu->card->dev, "ca0106: unstable DMA pointer!\n");
 return 0;
}

https://git.kernel.org/cgit/linux/kernel/git/tiwai/sound.git/tree/sound/pci/hda/hda_controller.c

static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
{
 struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
 struct azx *chip = apcm->chip;
 struct azx_dev *azx_dev = get_azx_dev(substream);
 return bytes_to_frames(substream->runtime,
          azx_get_position(chip, azx_dev));
}