Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: SOF: Intel: reserve link DMA for sdw bpt stream

Merge series from Bard Liao <yung-chuan.liao@linux.intel.com>:

Currently, hda_sdw_bpt_dma_prepare() get a HDA stream and use the link
DMA but doesn't reserve it. It works fine because we assume the
SwoundWire BPT will not run with audio streams simultaneously. Create
and use the new helpers to reserve the link DMA and allow running BPT
and audio stream simultaneously.

Pierre adds:

For the record this solution has two issues not documented in any commit
message:

a) this will not work in 'dspless' mode, where the link DMA is not
enabled. That's probably fine given that no one used that mode in
production, but that's a software restriction that you will not be able
to undo.

b) this raise the question of how bandwidth will be managed. The premise
of BPT is that it uses all the bus bandwidth to guarantee predictable
firmware download times. If the available bandwidth is restricted by
other audio streams, then mechanically the startup latency will be
increased and vary - or you will have to run the bus at a higher
frequency to provision enough bandwidth for BPT but that means higher
power consumption. Or you will have to change the bus clock dynamically
which is possible at the hardware level for SDCA parts but not legacy
ones.

I am not going to lay on the tracks for this low-level set of changes,
but you'll have to address the b) opens for future contributions.

+171 -96
+3 -91
sound/soc/sof/intel/hda-loader.c
··· 53 53 struct snd_dma_buffer *dmab, bool persistent_buffer, int direction, 54 54 bool is_iccmax) 55 55 { 56 - struct snd_sof_dev *sdev = dev_get_drvdata(dev); 57 - struct hdac_ext_stream *hext_stream; 58 - struct hdac_stream *hstream; 59 - int ret; 60 - 61 - hext_stream = hda_dsp_stream_get(sdev, direction, 0); 62 - 63 - if (!hext_stream) { 64 - dev_err(sdev->dev, "error: no stream available\n"); 65 - return ERR_PTR(-ENODEV); 66 - } 67 - hstream = &hext_stream->hstream; 68 - hstream->substream = NULL; 69 - 70 - /* 71 - * Allocate DMA buffer if it is temporary or if the buffer is intended 72 - * to be persistent but not yet allocated. 73 - * We cannot rely solely on !dmab->area as caller might use a struct on 74 - * stack (when it is temporary) without clearing it to 0. 75 - */ 76 - if (!persistent_buffer || !dmab->area) { 77 - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab); 78 - if (ret < 0) { 79 - dev_err(sdev->dev, "%s: memory alloc failed: %d\n", 80 - __func__, ret); 81 - goto out_put; 82 - } 83 - } 84 - 85 - hstream->period_bytes = 0;/* initialize period_bytes */ 86 - hstream->format_val = format; 87 - hstream->bufsize = size; 88 - 89 - if (is_iccmax) { 90 - ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL); 91 - if (ret < 0) { 92 - dev_err(sdev->dev, "error: iccmax stream prepare failed: %d\n", ret); 93 - goto out_free; 94 - } 95 - } else { 96 - ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL); 97 - if (ret < 0) { 98 - dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret); 99 - goto out_free; 100 - } 101 - hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size); 102 - } 103 - 104 - return hext_stream; 105 - 106 - out_free: 107 - snd_dma_free_pages(dmab); 108 - dmab->area = NULL; 109 - dmab->bytes = 0; 110 - hstream->bufsize = 0; 111 - hstream->format_val = 0; 112 - out_put: 113 - hda_dsp_stream_put(sdev, direction, hstream->stream_tag); 114 - return ERR_PTR(ret); 56 + return hda_data_stream_prepare(dev, format, size, dmab, persistent_buffer, 57 + direction, is_iccmax, false); 115 58 } 116 59 EXPORT_SYMBOL_NS(hda_cl_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON"); 117 60 ··· 218 275 int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, 219 276 bool persistent_buffer, struct hdac_ext_stream *hext_stream) 220 277 { 221 - struct snd_sof_dev *sdev = dev_get_drvdata(dev); 222 - struct hdac_stream *hstream = &hext_stream->hstream; 223 - int sd_offset = SOF_STREAM_SD_OFFSET(hstream); 224 - int ret = 0; 225 - 226 - if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) 227 - ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); 228 - else 229 - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 230 - SOF_HDA_SD_CTL_DMA_START, 0); 231 - 232 - hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag); 233 - hstream->running = 0; 234 - hstream->substream = NULL; 235 - 236 - /* reset BDL address */ 237 - snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 238 - sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0); 239 - snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 240 - sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0); 241 - 242 - snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0); 243 - 244 - if (!persistent_buffer) { 245 - snd_dma_free_pages(dmab); 246 - dmab->area = NULL; 247 - dmab->bytes = 0; 248 - hstream->bufsize = 0; 249 - hstream->format_val = 0; 250 - } 251 - 252 - return ret; 278 + return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, false); 253 279 } 254 280 EXPORT_SYMBOL_NS(hda_cl_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON"); 255 281
+3 -2
sound/soc/sof/intel/hda-sdw-bpt.c
··· 118 118 119 119 dev_dbg(dev, "direction %d format_val %#x\n", direction, format); 120 120 121 - bpt_stream = hda_cl_prepare(dev, format, bpt_num_bytes, dmab_bdl, false, direction, false); 121 + bpt_stream = hda_data_stream_prepare(dev, format, bpt_num_bytes, dmab_bdl, 122 + false, direction, false, true); 122 123 if (IS_ERR(bpt_stream)) { 123 124 dev_err(sdev->dev, "%s: SDW BPT DMA prepare failed: dir %d\n", 124 125 __func__, direction); ··· 163 162 u32 mask; 164 163 int ret; 165 164 166 - ret = hda_cl_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream); 165 + ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, true); 167 166 if (ret < 0) { 168 167 dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n", 169 168 __func__);
+154 -3
sound/soc/sof/intel/hda-stream.c
··· 210 210 } 211 211 212 212 /* get next unused stream */ 213 - struct hdac_ext_stream * 214 - hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) 213 + static struct hdac_ext_stream * 214 + _hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags, bool pair) 215 215 { 216 216 const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata); 217 217 struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; ··· 233 233 if (hda_stream->host_reserved) 234 234 continue; 235 235 236 + if (pair && hext_stream->link_locked) 237 + continue; 238 + 236 239 s->opened = true; 240 + 241 + if (pair) 242 + hext_stream->link_locked = true; 243 + 237 244 break; 238 245 } 239 246 } ··· 271 264 return hext_stream; 272 265 } 273 266 267 + struct hdac_ext_stream * 268 + hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) 269 + { 270 + return _hda_dsp_stream_get(sdev, direction, flags, false); 271 + } 272 + 273 + struct hdac_ext_stream * 274 + hda_dsp_stream_pair_get(struct snd_sof_dev *sdev, int direction, u32 flags) 275 + { 276 + return _hda_dsp_stream_get(sdev, direction, flags, true); 277 + } 278 + 274 279 /* free a stream */ 275 - int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) 280 + static int _hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag, bool pair) 276 281 { 277 282 const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata); 278 283 struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 279 284 struct hdac_bus *bus = sof_to_bus(sdev); 280 285 struct sof_intel_hda_stream *hda_stream; 281 286 struct hdac_ext_stream *hext_stream; 287 + struct hdac_ext_stream *link_stream; 282 288 struct hdac_stream *s; 283 289 bool dmi_l1_enable = true; 284 290 bool found = false; ··· 312 292 if (s->direction == direction && s->stream_tag == stream_tag) { 313 293 s->opened = false; 314 294 found = true; 295 + if (pair) 296 + link_stream = hext_stream; 315 297 } else if (!(hda_stream->flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) { 316 298 dmi_l1_enable = false; 317 299 } ··· 334 312 return -ENODEV; 335 313 } 336 314 315 + if (pair) 316 + snd_hdac_ext_stream_release(link_stream, HDAC_EXT_STREAM_TYPE_LINK); 317 + 337 318 return 0; 319 + } 320 + 321 + int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) 322 + { 323 + return _hda_dsp_stream_put(sdev, direction, stream_tag, false); 324 + } 325 + 326 + int hda_dsp_stream_pair_put(struct snd_sof_dev *sdev, int direction, int stream_tag) 327 + { 328 + return _hda_dsp_stream_put(sdev, direction, stream_tag, true); 338 329 } 339 330 340 331 static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream) ··· 1243 1208 return ((u64)ldp_u << 32) | ldp_l; 1244 1209 } 1245 1210 EXPORT_SYMBOL_NS(hda_dsp_get_stream_ldp, "SND_SOC_SOF_INTEL_HDA_COMMON"); 1211 + 1212 + struct hdac_ext_stream * 1213 + hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int size, 1214 + struct snd_dma_buffer *dmab, bool persistent_buffer, int direction, 1215 + bool is_iccmax, bool pair) 1216 + { 1217 + struct snd_sof_dev *sdev = dev_get_drvdata(dev); 1218 + struct hdac_ext_stream *hext_stream; 1219 + struct hdac_stream *hstream; 1220 + int ret; 1221 + 1222 + if (pair) 1223 + hext_stream = hda_dsp_stream_pair_get(sdev, direction, 0); 1224 + else 1225 + hext_stream = hda_dsp_stream_get(sdev, direction, 0); 1226 + 1227 + if (!hext_stream) { 1228 + dev_err(sdev->dev, "%s: no stream available\n", __func__); 1229 + return ERR_PTR(-ENODEV); 1230 + } 1231 + hstream = &hext_stream->hstream; 1232 + hstream->substream = NULL; 1233 + 1234 + /* 1235 + * Allocate DMA buffer if it is temporary or if the buffer is intended 1236 + * to be persistent but not yet allocated. 1237 + * We cannot rely solely on !dmab->area as caller might use a struct on 1238 + * stack (when it is temporary) without clearing it to 0. 1239 + */ 1240 + if (!persistent_buffer || !dmab->area) { 1241 + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab); 1242 + if (ret < 0) { 1243 + dev_err(sdev->dev, "%s: memory alloc failed: %d\n", 1244 + __func__, ret); 1245 + goto out_put; 1246 + } 1247 + } 1248 + 1249 + hstream->period_bytes = 0; /* initialize period_bytes */ 1250 + hstream->format_val = format; 1251 + hstream->bufsize = size; 1252 + 1253 + if (is_iccmax) { 1254 + ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL); 1255 + if (ret < 0) { 1256 + dev_err(sdev->dev, "%s: iccmax stream prepare failed: %d\n", 1257 + __func__, ret); 1258 + goto out_free; 1259 + } 1260 + } else { 1261 + ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL); 1262 + if (ret < 0) { 1263 + dev_err(sdev->dev, "%s: hdac prepare failed: %d\n", __func__, ret); 1264 + goto out_free; 1265 + } 1266 + hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size); 1267 + } 1268 + 1269 + return hext_stream; 1270 + 1271 + out_free: 1272 + snd_dma_free_pages(dmab); 1273 + dmab->area = NULL; 1274 + dmab->bytes = 0; 1275 + hstream->bufsize = 0; 1276 + hstream->format_val = 0; 1277 + out_put: 1278 + if (pair) 1279 + hda_dsp_stream_pair_put(sdev, direction, hstream->stream_tag); 1280 + else 1281 + hda_dsp_stream_put(sdev, direction, hstream->stream_tag); 1282 + return ERR_PTR(ret); 1283 + } 1284 + EXPORT_SYMBOL_NS(hda_data_stream_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON"); 1285 + 1286 + int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, 1287 + bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair) 1288 + { 1289 + struct snd_sof_dev *sdev = dev_get_drvdata(dev); 1290 + struct hdac_stream *hstream = hdac_stream(hext_stream); 1291 + int sd_offset = SOF_STREAM_SD_OFFSET(hstream); 1292 + int ret = 0; 1293 + 1294 + if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) 1295 + ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); 1296 + else 1297 + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 1298 + SOF_HDA_SD_CTL_DMA_START, 0); 1299 + 1300 + if (pair) 1301 + hda_dsp_stream_pair_put(sdev, hstream->direction, hstream->stream_tag); 1302 + else 1303 + hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag); 1304 + 1305 + hstream->running = 0; 1306 + hstream->substream = NULL; 1307 + 1308 + /* reset BDL address */ 1309 + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 1310 + sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0); 1311 + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, 1312 + sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0); 1313 + 1314 + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0); 1315 + 1316 + if (!persistent_buffer) { 1317 + snd_dma_free_pages(dmab); 1318 + dmab->area = NULL; 1319 + dmab->bytes = 0; 1320 + hstream->bufsize = 0; 1321 + hstream->format_val = 0; 1322 + } 1323 + 1324 + return ret; 1325 + } 1326 + EXPORT_SYMBOL_NS(hda_data_stream_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON");
+11
sound/soc/sof/intel/hda.h
··· 694 694 695 695 struct hdac_ext_stream * 696 696 hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags); 697 + struct hdac_ext_stream * 698 + hda_dsp_stream_pair_get(struct snd_sof_dev *sdev, int direction, u32 flags); 697 699 int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag); 700 + int hda_dsp_stream_pair_put(struct snd_sof_dev *sdev, int direction, int stream_tag); 698 701 int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, 699 702 struct hdac_ext_stream *hext_stream, 700 703 int enable, u32 size); ··· 904 901 905 902 int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, 906 903 struct snd_soc_dai *cpu_dai); 904 + 905 + struct hdac_ext_stream * 906 + hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int size, 907 + struct snd_dma_buffer *dmab, bool persistent_buffer, int direction, 908 + bool is_iccmax, bool pair); 909 + 910 + int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, 911 + bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair); 907 912 908 913 /* common dai driver */ 909 914 extern struct snd_soc_dai_driver skl_dai[];