mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 04:04:43 +01:00
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.
This commit is contained in:
commit
dcf0470aa3
4 changed files with 171 additions and 96 deletions
|
|
@ -53,65 +53,8 @@ hda_cl_prepare(struct device *dev, unsigned int format, unsigned int size,
|
|||
struct snd_dma_buffer *dmab, bool persistent_buffer, int direction,
|
||||
bool is_iccmax)
|
||||
{
|
||||
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
|
||||
struct hdac_ext_stream *hext_stream;
|
||||
struct hdac_stream *hstream;
|
||||
int ret;
|
||||
|
||||
hext_stream = hda_dsp_stream_get(sdev, direction, 0);
|
||||
|
||||
if (!hext_stream) {
|
||||
dev_err(sdev->dev, "error: no stream available\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
hstream = &hext_stream->hstream;
|
||||
hstream->substream = NULL;
|
||||
|
||||
/*
|
||||
* Allocate DMA buffer if it is temporary or if the buffer is intended
|
||||
* to be persistent but not yet allocated.
|
||||
* We cannot rely solely on !dmab->area as caller might use a struct on
|
||||
* stack (when it is temporary) without clearing it to 0.
|
||||
*/
|
||||
if (!persistent_buffer || !dmab->area) {
|
||||
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab);
|
||||
if (ret < 0) {
|
||||
dev_err(sdev->dev, "%s: memory alloc failed: %d\n",
|
||||
__func__, ret);
|
||||
goto out_put;
|
||||
}
|
||||
}
|
||||
|
||||
hstream->period_bytes = 0;/* initialize period_bytes */
|
||||
hstream->format_val = format;
|
||||
hstream->bufsize = size;
|
||||
|
||||
if (is_iccmax) {
|
||||
ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(sdev->dev, "error: iccmax stream prepare failed: %d\n", ret);
|
||||
goto out_free;
|
||||
}
|
||||
} else {
|
||||
ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret);
|
||||
goto out_free;
|
||||
}
|
||||
hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size);
|
||||
}
|
||||
|
||||
return hext_stream;
|
||||
|
||||
out_free:
|
||||
snd_dma_free_pages(dmab);
|
||||
dmab->area = NULL;
|
||||
dmab->bytes = 0;
|
||||
hstream->bufsize = 0;
|
||||
hstream->format_val = 0;
|
||||
out_put:
|
||||
hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
|
||||
return ERR_PTR(ret);
|
||||
return hda_data_stream_prepare(dev, format, size, dmab, persistent_buffer,
|
||||
direction, is_iccmax, false);
|
||||
}
|
||||
EXPORT_SYMBOL_NS(hda_cl_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON");
|
||||
|
||||
|
|
@ -275,38 +218,7 @@ EXPORT_SYMBOL_NS(hda_cl_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON");
|
|||
int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
|
||||
bool persistent_buffer, struct hdac_ext_stream *hext_stream)
|
||||
{
|
||||
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
|
||||
struct hdac_stream *hstream = &hext_stream->hstream;
|
||||
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
|
||||
int ret = 0;
|
||||
|
||||
if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
|
||||
else
|
||||
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
|
||||
SOF_HDA_SD_CTL_DMA_START, 0);
|
||||
|
||||
hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag);
|
||||
hstream->running = 0;
|
||||
hstream->substream = NULL;
|
||||
|
||||
/* reset BDL address */
|
||||
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
|
||||
sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0);
|
||||
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
|
||||
sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0);
|
||||
|
||||
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0);
|
||||
|
||||
if (!persistent_buffer) {
|
||||
snd_dma_free_pages(dmab);
|
||||
dmab->area = NULL;
|
||||
dmab->bytes = 0;
|
||||
hstream->bufsize = 0;
|
||||
hstream->format_val = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, false);
|
||||
}
|
||||
EXPORT_SYMBOL_NS(hda_cl_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON");
|
||||
|
||||
|
|
|
|||
|
|
@ -118,7 +118,8 @@ static int hda_sdw_bpt_dma_prepare(struct device *dev, struct hdac_ext_stream **
|
|||
|
||||
dev_dbg(dev, "direction %d format_val %#x\n", direction, format);
|
||||
|
||||
bpt_stream = hda_cl_prepare(dev, format, bpt_num_bytes, dmab_bdl, false, direction, false);
|
||||
bpt_stream = hda_data_stream_prepare(dev, format, bpt_num_bytes, dmab_bdl,
|
||||
false, direction, false, true);
|
||||
if (IS_ERR(bpt_stream)) {
|
||||
dev_err(sdev->dev, "%s: SDW BPT DMA prepare failed: dir %d\n",
|
||||
__func__, direction);
|
||||
|
|
@ -162,7 +163,7 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream
|
|||
u32 mask;
|
||||
int ret;
|
||||
|
||||
ret = hda_cl_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream);
|
||||
ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, true);
|
||||
if (ret < 0) {
|
||||
dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n",
|
||||
__func__);
|
||||
|
|
|
|||
|
|
@ -210,8 +210,8 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
|
|||
}
|
||||
|
||||
/* get next unused stream */
|
||||
struct hdac_ext_stream *
|
||||
hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
|
||||
static struct hdac_ext_stream *
|
||||
_hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags, bool pair)
|
||||
{
|
||||
const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata);
|
||||
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
|
||||
|
|
@ -233,7 +233,14 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
|
|||
if (hda_stream->host_reserved)
|
||||
continue;
|
||||
|
||||
if (pair && hext_stream->link_locked)
|
||||
continue;
|
||||
|
||||
s->opened = true;
|
||||
|
||||
if (pair)
|
||||
hext_stream->link_locked = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -264,14 +271,27 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
|
|||
return hext_stream;
|
||||
}
|
||||
|
||||
struct hdac_ext_stream *
|
||||
hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
|
||||
{
|
||||
return _hda_dsp_stream_get(sdev, direction, flags, false);
|
||||
}
|
||||
|
||||
struct hdac_ext_stream *
|
||||
hda_dsp_stream_pair_get(struct snd_sof_dev *sdev, int direction, u32 flags)
|
||||
{
|
||||
return _hda_dsp_stream_get(sdev, direction, flags, true);
|
||||
}
|
||||
|
||||
/* free a stream */
|
||||
int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
|
||||
static int _hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag, bool pair)
|
||||
{
|
||||
const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata);
|
||||
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
|
||||
struct hdac_bus *bus = sof_to_bus(sdev);
|
||||
struct sof_intel_hda_stream *hda_stream;
|
||||
struct hdac_ext_stream *hext_stream;
|
||||
struct hdac_ext_stream *link_stream;
|
||||
struct hdac_stream *s;
|
||||
bool dmi_l1_enable = true;
|
||||
bool found = false;
|
||||
|
|
@ -292,6 +312,8 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
|
|||
if (s->direction == direction && s->stream_tag == stream_tag) {
|
||||
s->opened = false;
|
||||
found = true;
|
||||
if (pair)
|
||||
link_stream = hext_stream;
|
||||
} else if (!(hda_stream->flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) {
|
||||
dmi_l1_enable = false;
|
||||
}
|
||||
|
|
@ -312,9 +334,22 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (pair)
|
||||
snd_hdac_ext_stream_release(link_stream, HDAC_EXT_STREAM_TYPE_LINK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
|
||||
{
|
||||
return _hda_dsp_stream_put(sdev, direction, stream_tag, false);
|
||||
}
|
||||
|
||||
int hda_dsp_stream_pair_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
|
||||
{
|
||||
return _hda_dsp_stream_put(sdev, direction, stream_tag, true);
|
||||
}
|
||||
|
||||
static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream)
|
||||
{
|
||||
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
|
||||
|
|
@ -1208,3 +1243,119 @@ u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev,
|
|||
return ((u64)ldp_u << 32) | ldp_l;
|
||||
}
|
||||
EXPORT_SYMBOL_NS(hda_dsp_get_stream_ldp, "SND_SOC_SOF_INTEL_HDA_COMMON");
|
||||
|
||||
struct hdac_ext_stream *
|
||||
hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int size,
|
||||
struct snd_dma_buffer *dmab, bool persistent_buffer, int direction,
|
||||
bool is_iccmax, bool pair)
|
||||
{
|
||||
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
|
||||
struct hdac_ext_stream *hext_stream;
|
||||
struct hdac_stream *hstream;
|
||||
int ret;
|
||||
|
||||
if (pair)
|
||||
hext_stream = hda_dsp_stream_pair_get(sdev, direction, 0);
|
||||
else
|
||||
hext_stream = hda_dsp_stream_get(sdev, direction, 0);
|
||||
|
||||
if (!hext_stream) {
|
||||
dev_err(sdev->dev, "%s: no stream available\n", __func__);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
hstream = &hext_stream->hstream;
|
||||
hstream->substream = NULL;
|
||||
|
||||
/*
|
||||
* Allocate DMA buffer if it is temporary or if the buffer is intended
|
||||
* to be persistent but not yet allocated.
|
||||
* We cannot rely solely on !dmab->area as caller might use a struct on
|
||||
* stack (when it is temporary) without clearing it to 0.
|
||||
*/
|
||||
if (!persistent_buffer || !dmab->area) {
|
||||
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab);
|
||||
if (ret < 0) {
|
||||
dev_err(sdev->dev, "%s: memory alloc failed: %d\n",
|
||||
__func__, ret);
|
||||
goto out_put;
|
||||
}
|
||||
}
|
||||
|
||||
hstream->period_bytes = 0; /* initialize period_bytes */
|
||||
hstream->format_val = format;
|
||||
hstream->bufsize = size;
|
||||
|
||||
if (is_iccmax) {
|
||||
ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(sdev->dev, "%s: iccmax stream prepare failed: %d\n",
|
||||
__func__, ret);
|
||||
goto out_free;
|
||||
}
|
||||
} else {
|
||||
ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(sdev->dev, "%s: hdac prepare failed: %d\n", __func__, ret);
|
||||
goto out_free;
|
||||
}
|
||||
hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size);
|
||||
}
|
||||
|
||||
return hext_stream;
|
||||
|
||||
out_free:
|
||||
snd_dma_free_pages(dmab);
|
||||
dmab->area = NULL;
|
||||
dmab->bytes = 0;
|
||||
hstream->bufsize = 0;
|
||||
hstream->format_val = 0;
|
||||
out_put:
|
||||
if (pair)
|
||||
hda_dsp_stream_pair_put(sdev, direction, hstream->stream_tag);
|
||||
else
|
||||
hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_NS(hda_data_stream_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON");
|
||||
|
||||
int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
|
||||
bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair)
|
||||
{
|
||||
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
|
||||
struct hdac_stream *hstream = hdac_stream(hext_stream);
|
||||
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
|
||||
int ret = 0;
|
||||
|
||||
if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
|
||||
else
|
||||
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
|
||||
SOF_HDA_SD_CTL_DMA_START, 0);
|
||||
|
||||
if (pair)
|
||||
hda_dsp_stream_pair_put(sdev, hstream->direction, hstream->stream_tag);
|
||||
else
|
||||
hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag);
|
||||
|
||||
hstream->running = 0;
|
||||
hstream->substream = NULL;
|
||||
|
||||
/* reset BDL address */
|
||||
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
|
||||
sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0);
|
||||
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR,
|
||||
sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0);
|
||||
|
||||
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0);
|
||||
|
||||
if (!persistent_buffer) {
|
||||
snd_dma_free_pages(dmab);
|
||||
dmab->area = NULL;
|
||||
dmab->bytes = 0;
|
||||
hstream->bufsize = 0;
|
||||
hstream->format_val = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_NS(hda_data_stream_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON");
|
||||
|
|
|
|||
|
|
@ -694,7 +694,10 @@ u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev,
|
|||
|
||||
struct hdac_ext_stream *
|
||||
hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags);
|
||||
struct hdac_ext_stream *
|
||||
hda_dsp_stream_pair_get(struct snd_sof_dev *sdev, int direction, u32 flags);
|
||||
int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag);
|
||||
int hda_dsp_stream_pair_put(struct snd_sof_dev *sdev, int direction, int stream_tag);
|
||||
int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
|
||||
struct hdac_ext_stream *hext_stream,
|
||||
int enable, u32 size);
|
||||
|
|
@ -902,6 +905,14 @@ int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream,
|
|||
int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *cpu_dai);
|
||||
|
||||
struct hdac_ext_stream *
|
||||
hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int size,
|
||||
struct snd_dma_buffer *dmab, bool persistent_buffer, int direction,
|
||||
bool is_iccmax, bool pair);
|
||||
|
||||
int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
|
||||
bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair);
|
||||
|
||||
/* common dai driver */
|
||||
extern struct snd_soc_dai_driver skl_dai[];
|
||||
int hda_dsp_dais_suspend(struct snd_sof_dev *sdev);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue