History

ASoC: SOF: Intel: hda: Fix compressed stream position tracking

Commit 288fad2f71fa ("ASoC: SOF: Intel: hda: add quirks for HDAudio DMA position information") modified the PCM path only, but left the compressed data patch using an obsolete option. Move the functionality in a helper that can be called for both PCM and compressed data.

Peter Ujfalusi committed 4 years ago

Commit ca7ab1d1 parent 427eb3e3 files · +94 −77Browse files

Unified · Split

3 files
  1. sound/soc/sof/intel/hda-pcm.c +1 −73
  2. sound/soc/sof/intel/hda-stream.c +90 −4
  3. sound/soc/sof/intel/hda.h +3 −0
sound/soc/sof/intel/hda-pcm.c+1−73
@@ -192,79 +192,7 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, · 191 unmodified lines
192192 goto found;
193193 }
194194
195− switch (sof_hda_position_quirk) {
196− case SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY:
197− /*
198− * This legacy code, inherited from the Skylake driver,
199− * mixes DPIB registers and DPIB DDR updates and
200− * does not seem to follow any known hardware recommendations.
201− * It's not clear e.g. why there is a different flow
202− * for capture and playback, the only information that matters is
203− * what traffic class is used, and on all SOF-enabled platforms
204− * only VC0 is supported so the work-around was likely not necessary
205− * and quite possibly wrong.
206− */
207−
208− /* DPIB/posbuf position mode:
209− * For Playback, Use DPIB register from HDA space which
210− * reflects the actual data transferred.
211− * For Capture, Use the position buffer for pointer, as DPIB
212− * is not accurate enough, its update may be completed
213− * earlier than the data written to DDR.
214− */
215− if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
216− pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
217− AZX_REG_VS_SDXDPIB_XBASE +
218− (AZX_REG_VS_SDXDPIB_XINTERVAL *
219− hstream->index));
220− } else {
221− /*
222− * For capture stream, we need more workaround to fix the
223− * position incorrect issue:
224− *
225− * 1. Wait at least 20us before reading position buffer after
226− * the interrupt generated(IOC), to make sure position update
227− * happens on frame boundary i.e. 20.833uSec for 48KHz.
228− * 2. Perform a dummy Read to DPIB register to flush DMA
229− * position value.
230− * 3. Read the DMA Position from posbuf. Now the readback
231− * value should be >= period boundary.
232− */
233− usleep_range(20, 21);
234− snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
235− AZX_REG_VS_SDXDPIB_XBASE +
236− (AZX_REG_VS_SDXDPIB_XINTERVAL *
237− hstream->index));
238− pos = snd_hdac_stream_get_pos_posbuf(hstream);
239− }
240− break;
241− case SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS:
242− /*
243− * In case VC1 traffic is disabled this is the recommended option
244− */
245− pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
246− AZX_REG_VS_SDXDPIB_XBASE +
247− (AZX_REG_VS_SDXDPIB_XINTERVAL *
248− hstream->index));
249− break;
250− case SOF_HDA_POSITION_QUIRK_USE_DPIB_DDR_UPDATE:
251− /*
252− * This is the recommended option when VC1 is enabled.
253− * While this isn't needed for SOF platforms it's added for
254− * consistency and debug.
255− */
256− pos = snd_hdac_stream_get_pos_posbuf(hstream);
257− break;
258− default:
259− dev_err_once(sdev->dev, "hda_position_quirk value %d not supported\n",
260− sof_hda_position_quirk);
261− pos = 0;
262− break;
263− }
264−
265− if (pos >= hstream->bufsize)
266− pos = 0;
267−
195+ pos = hda_dsp_stream_get_position(hstream, substream->stream, true);
268196 found:
269197 pos = bytes_to_frames(substream->runtime, pos);
270198
sound/soc/sof/intel/hda-stream.c+90−4
@@ -707,12 +707,13 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev) · 706 unmodified lines
707707 }
708708
709709 static void
710−hda_dsp_set_bytes_transferred(struct hdac_stream *hstream, u64 buffer_size)
710+hda_dsp_compr_bytes_transferred(struct hdac_stream *hstream, int direction)
711711 {
712+ u64 buffer_size = hstream->bufsize;
712713 u64 prev_pos, pos, num_bytes;
713714
714715 div64_u64_rem(hstream->curr_pos, buffer_size, &prev_pos);
715− pos = snd_hdac_stream_get_pos_posbuf(hstream);
716+ pos = hda_dsp_stream_get_position(hstream, direction, false);
716717
717718 if (pos < prev_pos)
718719 num_bytes = (buffer_size - prev_pos) + pos;
@@ -748,8 +749,7 @@ static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status) · 29 unmodified lines
748749 if (s->substream && sof_hda->no_ipc_position) {
749750 snd_sof_pcm_period_elapsed(s->substream);
750751 } else if (s->cstream) {
751− hda_dsp_set_bytes_transferred(s,
752− s->cstream->runtime->buffer_size);
752+ hda_dsp_compr_bytes_transferred(s, s->cstream->direction);
753753 snd_compr_fragment_elapsed(s->cstream);
754754 }
755755 }
@@ -1009,3 +1009,89 @@ void hda_dsp_stream_free(struct snd_sof_dev *sdev) · 253 unmodified lines
10091009 devm_kfree(sdev->dev, hda_stream);
10101010 }
10111011 }
1012+
1013+snd_pcm_uframes_t hda_dsp_stream_get_position(struct hdac_stream *hstream,
1014+ int direction, bool can_sleep)
1015+{
1016+ struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
1017+ struct sof_intel_hda_stream *hda_stream = hstream_to_sof_hda_stream(hext_stream);
1018+ struct snd_sof_dev *sdev = hda_stream->sdev;
1019+ snd_pcm_uframes_t pos;
1020+
1021+ switch (sof_hda_position_quirk) {
1022+ case SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY:
1023+ /*
1024+ * This legacy code, inherited from the Skylake driver,
1025+ * mixes DPIB registers and DPIB DDR updates and
1026+ * does not seem to follow any known hardware recommendations.
1027+ * It's not clear e.g. why there is a different flow
1028+ * for capture and playback, the only information that matters is
1029+ * what traffic class is used, and on all SOF-enabled platforms
1030+ * only VC0 is supported so the work-around was likely not necessary
1031+ * and quite possibly wrong.
1032+ */
1033+
1034+ /* DPIB/posbuf position mode:
1035+ * For Playback, Use DPIB register from HDA space which
1036+ * reflects the actual data transferred.
1037+ * For Capture, Use the position buffer for pointer, as DPIB
1038+ * is not accurate enough, its update may be completed
1039+ * earlier than the data written to DDR.
1040+ */
1041+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
1042+ pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
1043+ AZX_REG_VS_SDXDPIB_XBASE +
1044+ (AZX_REG_VS_SDXDPIB_XINTERVAL *
1045+ hstream->index));
1046+ } else {
1047+ /*
1048+ * For capture stream, we need more workaround to fix the
1049+ * position incorrect issue:
1050+ *
1051+ * 1. Wait at least 20us before reading position buffer after
1052+ * the interrupt generated(IOC), to make sure position update
1053+ * happens on frame boundary i.e. 20.833uSec for 48KHz.
1054+ * 2. Perform a dummy Read to DPIB register to flush DMA
1055+ * position value.
1056+ * 3. Read the DMA Position from posbuf. Now the readback
1057+ * value should be >= period boundary.
1058+ */
1059+ if (can_sleep)
1060+ usleep_range(20, 21);
1061+
1062+ snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
1063+ AZX_REG_VS_SDXDPIB_XBASE +
1064+ (AZX_REG_VS_SDXDPIB_XINTERVAL *
1065+ hstream->index));
1066+ pos = snd_hdac_stream_get_pos_posbuf(hstream);
1067+ }
1068+ break;
1069+ case SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS:
1070+ /*
1071+ * In case VC1 traffic is disabled this is the recommended option
1072+ */
1073+ pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
1074+ AZX_REG_VS_SDXDPIB_XBASE +
1075+ (AZX_REG_VS_SDXDPIB_XINTERVAL *
1076+ hstream->index));
1077+ break;
1078+ case SOF_HDA_POSITION_QUIRK_USE_DPIB_DDR_UPDATE:
1079+ /*
1080+ * This is the recommended option when VC1 is enabled.
1081+ * While this isn't needed for SOF platforms it's added for
1082+ * consistency and debug.
1083+ */
1084+ pos = snd_hdac_stream_get_pos_posbuf(hstream);
1085+ break;
1086+ default:
1087+ dev_err_once(sdev->dev, "hda_position_quirk value %d not supported\n",
1088+ sof_hda_position_quirk);
1089+ pos = 0;
1090+ break;
1091+ }
1092+
1093+ if (pos >= hstream->bufsize)
1094+ pos = 0;
1095+
1096+ return pos;
1097+}
sound/soc/sof/intel/hda.h+3−0
@@ -565,6 +565,9 @@ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, · 564 unmodified lines
565565 bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev);
566566 bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev);
567567
568+snd_pcm_uframes_t hda_dsp_stream_get_position(struct hdac_stream *hstream,
569+ int direction, bool can_sleep);
570+
568571 struct hdac_ext_stream *
569572 hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags);
570573 int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag);