| @@ -707,12 +707,13 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev) · 706 unmodified lines |
| 707 | 707 | } |
| 708 | 708 | |
| 709 | 709 | 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) |
| 711 | 711 | { |
| 712 | + u64 buffer_size = hstream->bufsize; |
| 712 | 713 | u64 prev_pos, pos, num_bytes; |
| 713 | 714 | |
| 714 | 715 | 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); |
| 716 | 717 | |
| 717 | 718 | if (pos < prev_pos) |
| 718 | 719 | 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 |
| 748 | 749 | if (s->substream && sof_hda->no_ipc_position) { |
| 749 | 750 | snd_sof_pcm_period_elapsed(s->substream); |
| 750 | 751 | } 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); |
| 753 | 753 | snd_compr_fragment_elapsed(s->cstream); |
| 754 | 754 | } |
| 755 | 755 | } |
| @@ -1009,3 +1009,89 @@ void hda_dsp_stream_free(struct snd_sof_dev *sdev) · 253 unmodified lines |
| 1009 | 1009 | devm_kfree(sdev->dev, hda_stream); |
| 1010 | 1010 | } |
| 1011 | 1011 | } |
| 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 | +} |