[alsa-devel] [PATCH 1/2] ALSA: hda - Add fixed codec delay to runtime delay.
Allow a codec to specify a delay in addition to the delay before samples are passed through DMA. Some codecs have large processing delays because of built-in DSPs that require codec-side buffers of many milliseconds. Default to zero delay if the codec doesn't specify a callback to get the processing delay.
Signed-off-by: Dylan Reid dgreid@chromium.org --- sound/pci/hda/hda_codec.c | 17 +++++++++++++++++ sound/pci/hda/hda_codec.h | 4 ++++ sound/pci/hda/hda_intel.c | 2 ++ 3 files changed, 23 insertions(+)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 17286b3..1d792dd 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3724,6 +3724,23 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, } EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);
+/** + * snd_hda_codec_processing_delay - get the fixed delay through the codec + * @codec: HD-audio codec + * @stream: playback or capture + * + * This is needed for codecs that contain DSPs that cause long delays. + */ +unsigned int snd_hda_codec_processing_delay(struct hda_codec *codec, + unsigned int stream) +{ + if (codec->patch_ops.get_processing_delay) + return codec->patch_ops.get_processing_delay(codec, stream); + + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_codec_processing_delay); + /* * supported power states check */ diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 23ca172..23bd1e7 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -725,6 +725,8 @@ struct hda_codec_ops { int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid); #endif void (*reboot_notify)(struct hda_codec *codec); + unsigned int (*get_processing_delay)(struct hda_codec *codec, + unsigned int stream); };
/* record for amp information cache */ @@ -1063,6 +1065,8 @@ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); void snd_hda_bus_reboot_notify(struct hda_bus *bus); void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state); +unsigned int snd_hda_codec_processing_delay(struct hda_codec *codec, + unsigned int stream);
int snd_hda_lock_devices(struct hda_bus *bus); void snd_hda_unlock_devices(struct hda_bus *bus); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 418bfc0..422bfaf 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2349,6 +2349,7 @@ static unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev, bool with_check) { + struct azx_pcm *apcm = snd_pcm_substream_chip(azx_dev->substream); unsigned int pos; int stream = azx_dev->substream->stream; int delay = 0; @@ -2400,6 +2401,7 @@ static unsigned int azx_get_position(struct azx *chip, chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY; } azx_dev->substream->runtime->delay = + snd_hda_codec_processing_delay(apcm->codec, stream) + bytes_to_frames(azx_dev->substream->runtime, delay); } trace_azx_get_position(chip, azx_dev, pos, delay);
The DSP in the CA0132 codec adds a variable latency to audio depending on what processing is being done. Add a new patch op to return that latency for capture and playback streams. The latency is determined by which blocks are enabled and knowing how much latency is added by each block.
Signed-off-by: Dylan Reid dgreid@chromium.org --- sound/pci/hda/patch_ca0132.c | 52 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 12eb21a..dbecb03 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -131,6 +131,13 @@ enum { /* Effects values size*/ #define EFFECT_VALS_MAX_COUNT 12
+/* Latency introduced by DSP blocks in frames. */ +#define DSP_CAPTURE_INIT_LATENCY 0 +#define DSP_CRYSTAL_VOICE_LATENCY 124 +#define DSP_PLAYBACK_INIT_LATENCY 13 +#define DSP_PLAY_ENHANCEMENT_LATENCY 30 +#define DSP_SPEAKER_OUT_LATENCY 7 + struct ct_effect { char name[44]; hda_nid_t nid; @@ -4440,6 +4447,50 @@ static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res) } }
+static unsigned int ca0132_get_playback_latency(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int latency = DSP_PLAYBACK_INIT_LATENCY; + + /* Add latency if playback enhancement and either effect is enabled. */ + if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) { + if ((spec->effects_switch[SURROUND - EFFECT_START_NID]) || + (spec->effects_switch[DIALOG_PLUS - EFFECT_START_NID])) + latency += DSP_PLAY_ENHANCEMENT_LATENCY; + } + + /* Applying Speaker EQ adds latency as well. */ + if (spec->cur_out_type == SPEAKER_OUT) + latency += DSP_SPEAKER_OUT_LATENCY; + + return latency; +} + +static unsigned int ca0132_get_capture_latency(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int latency = DSP_CAPTURE_INIT_LATENCY; + + if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]) + latency += DSP_CRYSTAL_VOICE_LATENCY; + + return latency; +} + +static unsigned int ca0132_get_processing_delay(struct hda_codec *codec, + unsigned int stream) +{ + struct ca0132_spec *spec = codec->spec; + + if (spec->dsp_state != DSP_DOWNLOADED) + return 0; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + return ca0132_get_playback_latency(codec); + + return ca0132_get_capture_latency(codec); +} + /* * Verbs tables. */ @@ -4617,6 +4668,7 @@ static struct hda_codec_ops ca0132_patch_ops = { .init = ca0132_init, .free = ca0132_free, .unsol_event = ca0132_unsol_event, + .get_processing_delay = ca0132_get_processing_delay, };
static void ca0132_config(struct hda_codec *codec)
At Wed, 3 Apr 2013 08:30:03 -0700, Dylan Reid wrote:
Allow a codec to specify a delay in addition to the delay before samples are passed through DMA. Some codecs have large processing delays because of built-in DSPs that require codec-side buffers of many milliseconds. Default to zero delay if the codec doesn't specify a callback to get the processing delay.
Signed-off-by: Dylan Reid dgreid@chromium.org
sound/pci/hda/hda_codec.c | 17 +++++++++++++++++ sound/pci/hda/hda_codec.h | 4 ++++ sound/pci/hda/hda_intel.c | 2 ++ 3 files changed, 23 insertions(+)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 17286b3..1d792dd 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3724,6 +3724,23 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, } EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);
+/**
- snd_hda_codec_processing_delay - get the fixed delay through the codec
- @codec: HD-audio codec
- @stream: playback or capture
- This is needed for codecs that contain DSPs that cause long delays.
- */
+unsigned int snd_hda_codec_processing_delay(struct hda_codec *codec,
unsigned int stream)
+{
- if (codec->patch_ops.get_processing_delay)
return codec->patch_ops.get_processing_delay(codec, stream);
- return 0;
+} +EXPORT_SYMBOL_HDA(snd_hda_codec_processing_delay);
/*
- supported power states check
*/ diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 23ca172..23bd1e7 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -725,6 +725,8 @@ struct hda_codec_ops { int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid); #endif void (*reboot_notify)(struct hda_codec *codec);
- unsigned int (*get_processing_delay)(struct hda_codec *codec,
unsigned int stream);
};
/* record for amp information cache */ @@ -1063,6 +1065,8 @@ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); void snd_hda_bus_reboot_notify(struct hda_bus *bus); void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state); +unsigned int snd_hda_codec_processing_delay(struct hda_codec *codec,
unsigned int stream);
int snd_hda_lock_devices(struct hda_bus *bus); void snd_hda_unlock_devices(struct hda_bus *bus); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 418bfc0..422bfaf 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2349,6 +2349,7 @@ static unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev, bool with_check) {
- struct azx_pcm *apcm = snd_pcm_substream_chip(azx_dev->substream); unsigned int pos; int stream = azx_dev->substream->stream; int delay = 0;
@@ -2400,6 +2401,7 @@ static unsigned int azx_get_position(struct azx *chip, chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY; } azx_dev->substream->runtime->delay =
snd_hda_codec_processing_delay(apcm->codec, stream) + bytes_to_frames(azx_dev->substream->runtime, delay);
Isn't this a code path where CA0132 never takes? It's in the conditional block of AZX_DCAPS_COUNT_LPIB_DELAY.
You need a patch like:
index 418bfc0..5d53eea 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2399,9 +2399,12 @@ static unsigned int azx_get_position(struct azx *chip, delay = 0; chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY; } - azx_dev->substream->runtime->delay = - bytes_to_frames(azx_dev->substream->runtime, delay); + delay = bytes_to_frames(azx_dev->substream->runtime, delay); } + + azx_dev->substream->runtime->delay = + delay + snd_hda_codec_processing_delay(apcm->codec, stream); + trace_azx_get_position(chip, azx_dev, pos, delay); return pos; }
Takashi
At Wed, 3 Apr 2013 08:30:03 -0700, Dylan Reid wrote:
Allow a codec to specify a delay in addition to the delay before samples are passed through DMA. Some codecs have large processing delays because of built-in DSPs that require codec-side buffers of many milliseconds. Default to zero delay if the codec doesn't specify a callback to get the processing delay.
Signed-off-by: Dylan Reid dgreid@chromium.org
sound/pci/hda/hda_codec.c | 17 +++++++++++++++++ sound/pci/hda/hda_codec.h | 4 ++++ sound/pci/hda/hda_intel.c | 2 ++ 3 files changed, 23 insertions(+)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 17286b3..1d792dd 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3724,6 +3724,23 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, } EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);
+/**
- snd_hda_codec_processing_delay - get the fixed delay through the codec
- @codec: HD-audio codec
- @stream: playback or capture
- This is needed for codecs that contain DSPs that cause long delays.
- */
+unsigned int snd_hda_codec_processing_delay(struct hda_codec *codec,
unsigned int stream)
+{
- if (codec->patch_ops.get_processing_delay)
return codec->patch_ops.get_processing_delay(codec, stream);
- return 0;
+} +EXPORT_SYMBOL_HDA(snd_hda_codec_processing_delay);
In the second thought, it would fit more better to pcm_ops than codec patch_ops. That is, the change would be like below.
Takashi
--- diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 23ca172..c93f902 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -757,6 +757,9 @@ struct hda_pcm_ops { struct snd_pcm_substream *substream); int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec, struct snd_pcm_substream *substream); + unsigned int (*get_delay)(struct hda_pcm_stream *info, + struct hda_codec *codec, + struct snd_pcm_substream *substream); };
/* PCM information for each substream */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 418bfc0..a88f016 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2349,8 +2349,11 @@ static unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev, bool with_check) { + struct snd_pcm_substream *substream = azx_dev->substream; + struct azx_pcm *apcm = snd_pcm_substream_chip(substream); unsigned int pos; - int stream = azx_dev->substream->stream; + int stream = substream->stream; + struct hda_pcm_stream *hinfo = apcm->hinfo[stream]; int delay = 0;
switch (chip->position_fix[stream]) { @@ -2399,9 +2402,13 @@ static unsigned int azx_get_position(struct azx *chip, delay = 0; chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY; } - azx_dev->substream->runtime->delay = - bytes_to_frames(azx_dev->substream->runtime, delay); + delay = bytes_to_frames(azx_dev->substream->runtime, delay); } + + if (hinfo->ops.get_delay) + delay += hinfo->ops.get_delay(hinfo, apcm->codec, substream); + azx_dev->substream->runtime->delay = delay; + trace_azx_get_position(chip, azx_dev, pos, delay); return pos; }
On Thu, Apr 4, 2013 at 12:10 AM, Takashi Iwai tiwai@suse.de wrote:
At Wed, 3 Apr 2013 08:30:03 -0700, Dylan Reid wrote:
Allow a codec to specify a delay in addition to the delay before samples are passed through DMA. Some codecs have large processing delays because of built-in DSPs that require codec-side buffers of many milliseconds. Default to zero delay if the codec doesn't specify a callback to get the processing delay.
Signed-off-by: Dylan Reid dgreid@chromium.org
sound/pci/hda/hda_codec.c | 17 +++++++++++++++++ sound/pci/hda/hda_codec.h | 4 ++++ sound/pci/hda/hda_intel.c | 2 ++ 3 files changed, 23 insertions(+)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 17286b3..1d792dd 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3724,6 +3724,23 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, } EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);
+/**
- snd_hda_codec_processing_delay - get the fixed delay through the codec
- @codec: HD-audio codec
- @stream: playback or capture
- This is needed for codecs that contain DSPs that cause long delays.
- */
+unsigned int snd_hda_codec_processing_delay(struct hda_codec *codec,
unsigned int stream)
+{
if (codec->patch_ops.get_processing_delay)
return codec->patch_ops.get_processing_delay(codec, stream);
return 0;
+} +EXPORT_SYMBOL_HDA(snd_hda_codec_processing_delay);
In the second thought, it would fit more better to pcm_ops than codec patch_ops. That is, the change would be like below.
Yes this looks better. I'll modify the subsequent patch and run our latency tests on it to make sure it works then resend based on this.
Thanks!
-dg
Takashi
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 23ca172..c93f902 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -757,6 +757,9 @@ struct hda_pcm_ops { struct snd_pcm_substream *substream); int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec, struct snd_pcm_substream *substream);
unsigned int (*get_delay)(struct hda_pcm_stream *info,
struct hda_codec *codec,
struct snd_pcm_substream *substream);
};
/* PCM information for each substream */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 418bfc0..a88f016 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2349,8 +2349,11 @@ static unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev, bool with_check) {
struct snd_pcm_substream *substream = azx_dev->substream;
struct azx_pcm *apcm = snd_pcm_substream_chip(substream); unsigned int pos;
int stream = azx_dev->substream->stream;
int stream = substream->stream;
struct hda_pcm_stream *hinfo = apcm->hinfo[stream]; int delay = 0; switch (chip->position_fix[stream]) {
@@ -2399,9 +2402,13 @@ static unsigned int azx_get_position(struct azx *chip, delay = 0; chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY; }
azx_dev->substream->runtime->delay =
bytes_to_frames(azx_dev->substream->runtime, delay);
delay = bytes_to_frames(azx_dev->substream->runtime, delay); }
if (hinfo->ops.get_delay)
delay += hinfo->ops.get_delay(hinfo, apcm->codec, substream);
azx_dev->substream->runtime->delay = delay;
trace_azx_get_position(chip, azx_dev, pos, delay); return pos;
}
At Thu, 4 Apr 2013 09:21:34 -0700, Dylan Reid wrote:
On Thu, Apr 4, 2013 at 12:10 AM, Takashi Iwai tiwai@suse.de wrote:
At Wed, 3 Apr 2013 08:30:03 -0700, Dylan Reid wrote:
Allow a codec to specify a delay in addition to the delay before samples are passed through DMA. Some codecs have large processing delays because of built-in DSPs that require codec-side buffers of many milliseconds. Default to zero delay if the codec doesn't specify a callback to get the processing delay.
Signed-off-by: Dylan Reid dgreid@chromium.org
sound/pci/hda/hda_codec.c | 17 +++++++++++++++++ sound/pci/hda/hda_codec.h | 4 ++++ sound/pci/hda/hda_intel.c | 2 ++ 3 files changed, 23 insertions(+)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 17286b3..1d792dd 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3724,6 +3724,23 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, } EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);
+/**
- snd_hda_codec_processing_delay - get the fixed delay through the codec
- @codec: HD-audio codec
- @stream: playback or capture
- This is needed for codecs that contain DSPs that cause long delays.
- */
+unsigned int snd_hda_codec_processing_delay(struct hda_codec *codec,
unsigned int stream)
+{
if (codec->patch_ops.get_processing_delay)
return codec->patch_ops.get_processing_delay(codec, stream);
return 0;
+} +EXPORT_SYMBOL_HDA(snd_hda_codec_processing_delay);
In the second thought, it would fit more better to pcm_ops than codec patch_ops. That is, the change would be like below.
Yes this looks better. I'll modify the subsequent patch and run our latency tests on it to make sure it works then resend based on this.
I found a missing substream->runtime check (the function may be called even without running the PCM). The revised patch below was committed to for-next branch in the end.
thanks,
Takashi
--- From: Takashi Iwai tiwai@suse.de Subject: [PATCH] ALSA: hda - Introduce get_delay codec PCM ops
Add a new codec PCM ops, get_delay(), to obtain the codec/stream- specific PCM delay count. When it's NULL, nothing changes.
This new feature was requested for CA0132, which has significant delays in the path depending on the running DSP code.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.h | 3 +++ sound/pci/hda/hda_intel.c | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 23ca172..c93f902 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -757,6 +757,9 @@ struct hda_pcm_ops { struct snd_pcm_substream *substream); int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec, struct snd_pcm_substream *substream); + unsigned int (*get_delay)(struct hda_pcm_stream *info, + struct hda_codec *codec, + struct snd_pcm_substream *substream); };
/* PCM information for each substream */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 418bfc0..735567e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2349,8 +2349,11 @@ static unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev, bool with_check) { + struct snd_pcm_substream *substream = azx_dev->substream; + struct azx_pcm *apcm = snd_pcm_substream_chip(substream); unsigned int pos; - int stream = azx_dev->substream->stream; + int stream = substream->stream; + struct hda_pcm_stream *hinfo = apcm->hinfo[stream]; int delay = 0;
switch (chip->position_fix[stream]) { @@ -2381,7 +2384,7 @@ static unsigned int azx_get_position(struct azx *chip, pos = 0;
/* calculate runtime delay from LPIB */ - if (azx_dev->substream->runtime && + if (substream->runtime && chip->position_fix[stream] == POS_FIX_POSBUF && (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) { unsigned int lpib_pos = azx_sd_readl(azx_dev, SD_LPIB); @@ -2399,9 +2402,16 @@ static unsigned int azx_get_position(struct azx *chip, delay = 0; chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY; } - azx_dev->substream->runtime->delay = - bytes_to_frames(azx_dev->substream->runtime, delay); + delay = bytes_to_frames(substream->runtime, delay); } + + if (substream->runtime) { + if (hinfo->ops.get_delay) + delay += hinfo->ops.get_delay(hinfo, apcm->codec, + substream); + substream->runtime->delay = delay; + } + trace_azx_get_position(chip, azx_dev, pos, delay); return pos; }
participants (2)
-
Dylan Reid
-
Takashi Iwai