Add SoundWire BPT DMA helpers as a separate module to avoid circular dependencies. For now this assumes no link DMA, only coupled mode. Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@xxxxxxxxxxxxxxx> --- include/sound/hda-sdw-bpt.h | 76 +++++++ sound/soc/sof/intel/Kconfig | 8 +- sound/soc/sof/intel/Makefile | 4 + sound/soc/sof/intel/hda-sdw-bpt.c | 328 ++++++++++++++++++++++++++++++ 4 files changed, 415 insertions(+), 1 deletion(-) create mode 100644 include/sound/hda-sdw-bpt.h create mode 100644 sound/soc/sof/intel/hda-sdw-bpt.c diff --git a/include/sound/hda-sdw-bpt.h b/include/sound/hda-sdw-bpt.h new file mode 100644 index 000000000000..163c4513c26f --- /dev/null +++ b/include/sound/hda-sdw-bpt.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2023 Intel Corporation. All rights reserved. + */ + +#include <linux/device.h> + +struct hdac_ext_stream; +struct snd_dma_buffer; + +#if IS_ENABLED(CONFIG_SND_SOF_SOF_HDA_SDW_BPT) +int hda_sdw_bpt_open(struct device *dev, + int link_id, + struct hdac_ext_stream **bpt_tx_stream, + struct snd_dma_buffer *dmab_tx_bdl, + u32 bpt_tx_num_bytes, + u32 tx_dma_bandwidth, + struct hdac_ext_stream **bpt_rx_stream, + struct snd_dma_buffer *dmab_rx_bdl, + u32 bpt_rx_num_bytes, + u32 rx_dma_bandwidth); + +int hda_sdw_bpt_send_async(struct device *dev, + struct hdac_ext_stream *bpt_tx_stream, + struct hdac_ext_stream *bpt_rx_stream); + +int hda_sdw_bpt_wait(struct device *dev, + struct hdac_ext_stream *bpt_tx_stream, + struct hdac_ext_stream *bpt_rx_stream); + +int hda_sdw_bpt_close(struct device *dev, + struct hdac_ext_stream *bpt_tx_stream, + struct snd_dma_buffer *dmab_tx_bdl, + struct hdac_ext_stream *bpt_rx_stream, + struct snd_dma_buffer *dmab_rx_bdl); +#else +static inline int hda_sdw_bpt_open(struct device *dev, + int link_id, + struct hdac_ext_stream **bpt_tx_stream, + struct snd_dma_buffer *dmab_tx_bdl, + u32 bpt_tx_num_bytes, + u32 tx_dma_bandwidth, + struct hdac_ext_stream **bpt_rx_stream, + struct snd_dma_buffer *dmab_rx_bdl, + u32 bpt_rx_num_bytes, + u32 rx_dma_bandwidth) +{ + return 0; +} + +static inline int hda_sdw_bpt_send_async(struct device *dev, + struct hdac_ext_stream *bpt_tx_stream, + struct hdac_ext_stream *bpt_rx_stream) +{ + return 0; +} + +static inline int hda_sdw_bpt_wait(struct device *dev, + struct hdac_ext_stream *bpt_tx_stream, + struct hdac_ext_stream *bpt_rx_stream) +{ + return 0; +} + +static inline int hda_sdw_bpt_close(struct device *dev, + struct hdac_ext_stream *bpt_tx_stream, + struct snd_dma_buffer *dmab_tx_bdl, + struct hdac_ext_stream *bpt_rx_stream, + struct snd_dma_buffer *dmab_rx_bdl) +{ + return 0; +} +#endif diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 3396bd46b778..d2650348987b 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -268,6 +268,7 @@ config SND_SOC_SOF_INTEL_LNL tristate select SND_SOC_SOF_HDA_GENERIC select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE + select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_SOUNDWIRE select SND_SOC_SOF_IPC4 select SND_SOC_SOF_INTEL_MTL @@ -325,12 +326,17 @@ config SND_SOC_SOF_HDA_AUDIO_CODEC endif ## SND_SOC_SOF_HDA_GENERIC +config SND_SOF_SOF_HDA_SDW_BPT + tristate + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level. + config SND_SOC_SOF_HDA_LINK_BASELINE tristate select SND_SOC_SOF_HDA if SND_SOC_SOF_HDA_LINK help This option is not user-selectable but automagically handled by - 'select' statements at a higher level. config SND_SOC_SOF_HDA tristate diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index 806df08e3fd5..de1e37a76c29 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -12,6 +12,8 @@ snd-sof-intel-hda-generic-objs := hda.o hda-common-ops.o snd-sof-intel-hda-mlink-objs := hda-mlink.o +snd-sof-intel-hda-sdw-bpt-objs := hda-sdw-bpt.o + snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o snd-sof-intel-hda-objs := hda-codec.o @@ -26,6 +28,8 @@ obj-$(CONFIG_SND_SOC_SOF_HDA_GENERIC) += snd-sof-intel-hda-generic.o obj-$(CONFIG_SND_SOC_SOF_HDA_MLINK) += snd-sof-intel-hda-mlink.o obj-$(CONFIG_SND_SOC_SOF_HDA) += snd-sof-intel-hda.o +obj-$(CONFIG_SND_SOF_SOF_HDA_SDW_BPT) += snd-sof-intel-hda-sdw-bpt.o + snd-sof-pci-intel-tng-objs := pci-tng.o snd-sof-pci-intel-skl-objs := pci-skl.o skl.o hda-loader-skl.o snd-sof-pci-intel-apl-objs := pci-apl.o apl.o diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c new file mode 100644 index 000000000000..980c75c3a6bd --- /dev/null +++ b/sound/soc/sof/intel/hda-sdw-bpt.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2024 Intel Corporation. All rights reserved. +// + +/* + * Hardware interface for SoundWire BPT support with HDA DMA + */ + +#include <sound/hdaudio_ext.h> +#include <sound/hda-mlink.h> +#include <sound/hda-sdw-bpt.h> +#include <sound/sof.h> +#include "../ops.h" +#include "../sof-priv.h" +#include "hda.h" + +static int hda_sdw_bpt_dma_prepare(struct device *dev, + struct hdac_ext_stream **sdw_bpt_stream, + struct snd_dma_buffer *dmab_bdl, + u32 bpt_num_bytes, + unsigned int num_channels, + int direction) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct hdac_ext_stream *bpt_stream; + unsigned int format = HDA_CL_STREAM_FORMAT; + + /* + * the baseline format needs to be adjusted to + * bandwidth requirements + */ + format |= (num_channels - 1); + + dev_dbg(dev, "direction %d format_val %x\n", direction, format); + + bpt_stream = hda_cl_prepare(dev, format, + bpt_num_bytes, + dmab_bdl, + direction, + false); + if (IS_ERR(bpt_stream)) { + dev_err(sdev->dev, "%s: SDW BPT DMA prepare failed: dir %d\n", + __func__, direction); + return PTR_ERR(bpt_stream); + } + *sdw_bpt_stream = bpt_stream; + + if (hdac_stream(bpt_stream)->direction == SNDRV_PCM_STREAM_PLAYBACK) { + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_ext_link *hlink; + int stream_tag; + + stream_tag = hdac_stream(bpt_stream)->stream_tag; + hlink = hdac_bus_eml_sdw_get_hlink(bus); + + snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag); + } + return 0; +} + +static int hda_sdw_bpt_dma_deprepare(struct device *dev, + struct hdac_ext_stream *sdw_bpt_stream, + struct snd_dma_buffer *dmab_bdl) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + int ret; + + ret = hda_cl_cleanup(sdev->dev, dmab_bdl, sdw_bpt_stream); + if (ret < 0) { + dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n", + __func__); + return ret; + } + + if (hdac_stream(sdw_bpt_stream)->direction == SNDRV_PCM_STREAM_PLAYBACK) { + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_ext_link *hlink; + int stream_tag; + + stream_tag = hdac_stream(sdw_bpt_stream)->stream_tag; + hlink = hdac_bus_eml_sdw_get_hlink(bus); + + snd_hdac_ext_bus_link_clear_stream_id(hlink, stream_tag); + } + + return 0; +} + +static int hda_sdw_bpt_dma_enable(struct device *dev, + struct hdac_ext_stream *sdw_bpt_stream) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + int ret; + + ret = hda_cl_trigger(sdev->dev, sdw_bpt_stream, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(sdev->dev, "%s: SDW BPT DMA trigger start failed\n", __func__); + return ret; + } + return 0; +} + +static int hda_sdw_bpt_dma_disable(struct device *dev, + struct hdac_ext_stream *sdw_bpt_stream) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + int ret; + + ret = hda_cl_trigger(sdev->dev, sdw_bpt_stream, SNDRV_PCM_TRIGGER_STOP); + if (ret < 0) { + dev_err(sdev->dev, "%s: SDW BPT DMA trigger stop failed\n", __func__); + return ret; + } + return 0; +} + +int hda_sdw_bpt_open(struct device *dev, + int link_id, + struct hdac_ext_stream **bpt_tx_stream, + struct snd_dma_buffer *dmab_tx_bdl, + u32 bpt_tx_num_bytes, + u32 tx_dma_bandwidth, + struct hdac_ext_stream **bpt_rx_stream, + struct snd_dma_buffer *dmab_rx_bdl, + u32 bpt_rx_num_bytes, + u32 rx_dma_bandwidth) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + unsigned int num_channels_tx; + unsigned int num_channels_rx; + int ret2; + int ret1; + int ret; + + num_channels_tx = DIV_ROUND_UP(tx_dma_bandwidth, 48000 * 32); + + ret = hda_sdw_bpt_dma_prepare(dev, + bpt_tx_stream, + dmab_tx_bdl, + bpt_tx_num_bytes, + num_channels_tx, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret < 0) { + dev_err(dev, "%s: hda_sdw_bpt_dma_prepare failed for TX: %d\n", + __func__, ret); + return ret; + } + + num_channels_rx = DIV_ROUND_UP(rx_dma_bandwidth, 48000 * 32); + + ret = hda_sdw_bpt_dma_prepare(dev, + bpt_rx_stream, + dmab_rx_bdl, + bpt_rx_num_bytes, + num_channels_rx, + SNDRV_PCM_STREAM_CAPTURE); + if (ret < 0) { + dev_err(dev, "%s: hda_sdw_bpt_dma_prepare failed for RX: %d\n", + __func__, ret); + + ret1 = hda_sdw_bpt_dma_deprepare(dev, + *bpt_tx_stream, + dmab_tx_bdl); + if (ret1 < 0) + dev_err(dev, "%s: hda_sdw_bpt_dma_deprepare failed for TX: %d\n", + __func__, ret1); + return ret; + } + + /* we need to map the channels in PCMSyCM registers */ + ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), + link_id, + 0, /* cpu_dai->id -> PDI0 */ + GENMASK(num_channels_tx - 1, 0), + hdac_stream(*bpt_tx_stream)->stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret < 0) + dev_err(dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed for TX: %d\n", + __func__, ret); + + ret1 = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), + link_id, + 1, /* cpu_dai->id -> PDI1 */ + GENMASK(num_channels_rx - 1, 0), + hdac_stream(*bpt_rx_stream)->stream_tag, + SNDRV_PCM_STREAM_CAPTURE); + if (ret1 < 0) + dev_err(dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed for RX: %d\n", + __func__, ret1); + + if (!ret) + ret = ret1; + + if (ret < 0) { + ret2 = hda_sdw_bpt_close(dev, + *bpt_tx_stream, + dmab_tx_bdl, + *bpt_rx_stream, + dmab_rx_bdl); + if (ret2 < 0) + dev_err(dev, "%s: hda_sdw_bpt_close failed: %d\n", + __func__, ret2); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS(hda_sdw_bpt_open, SND_SOC_SOF_INTEL_HDA_SDW_BPT); + +int hda_sdw_bpt_send_async(struct device *dev, + struct hdac_ext_stream *bpt_tx_stream, + struct hdac_ext_stream *bpt_rx_stream) +{ + int ret1; + int ret; + + ret = hda_sdw_bpt_dma_enable(dev, bpt_tx_stream); + if (ret < 0) { + dev_err(dev, "%s: hda_sdw_bpt_dma_enable failed for TX: %d\n", + __func__, ret); + return ret; + } + + ret = hda_sdw_bpt_dma_enable(dev, bpt_rx_stream); + if (ret < 0) { + dev_err(dev, "%s: hda_sdw_bpt_dma_enable failed for RX: %d\n", + __func__, ret); + + ret1 = hda_sdw_bpt_dma_disable(dev, bpt_tx_stream); + if (ret1 < 0) + dev_err(dev, "%s: hda_sdw_bpt_dma_disable failed for TX: %d\n", + __func__, ret1); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS(hda_sdw_bpt_send_async, SND_SOC_SOF_INTEL_HDA_SDW_BPT); + +/* + * 3s is several orders of magnitude larger than what is needed for a + * typical firmware download. + */ +#define HDA_BPT_IOC_TIMEOUT_MS 3000 + +int hda_sdw_bpt_wait(struct device *dev, + struct hdac_ext_stream *bpt_tx_stream, + struct hdac_ext_stream *bpt_rx_stream) +{ + struct sof_intel_hda_stream *hda_tx_stream; + struct sof_intel_hda_stream *hda_rx_stream; + unsigned long time_tx_left; + unsigned long time_rx_left; + snd_pcm_uframes_t tx_position; + snd_pcm_uframes_t rx_position; + int ret = 0; + int ret1; + + hda_tx_stream = container_of(bpt_tx_stream, struct sof_intel_hda_stream, + hext_stream); + hda_rx_stream = container_of(bpt_rx_stream, struct sof_intel_hda_stream, + hext_stream); + + time_tx_left = wait_for_completion_timeout(&hda_tx_stream->ioc, + msecs_to_jiffies(HDA_BPT_IOC_TIMEOUT_MS)); + if (!time_tx_left) { + tx_position = hda_dsp_stream_get_position(hdac_stream(bpt_tx_stream), + SNDRV_PCM_STREAM_PLAYBACK, + false); + dev_err(dev, "%s: SDW BPT TX DMA did not complete: %ld\n", + __func__, tx_position); + ret = -ETIMEDOUT; + } + + /* the wait should be minimal here */ + time_rx_left = wait_for_completion_timeout(&hda_rx_stream->ioc, + msecs_to_jiffies(HDA_BPT_IOC_TIMEOUT_MS)); + if (!time_rx_left) { + rx_position = hda_dsp_stream_get_position(hdac_stream(bpt_rx_stream), + SNDRV_PCM_STREAM_CAPTURE, + false); + dev_err(dev, "%s: SDW BPT RX DMA did not complete: %ld\n", + __func__, rx_position); + ret = -ETIMEDOUT; + } + + ret1 = hda_sdw_bpt_dma_disable(dev, bpt_rx_stream); + if (!ret) + ret = ret1; + + ret1 = hda_sdw_bpt_dma_disable(dev, bpt_tx_stream); + if (!ret) + ret = ret1; + + return ret; +} +EXPORT_SYMBOL_NS(hda_sdw_bpt_wait, SND_SOC_SOF_INTEL_HDA_SDW_BPT); + +int hda_sdw_bpt_close(struct device *dev, + struct hdac_ext_stream *bpt_tx_stream, + struct snd_dma_buffer *dmab_tx_bdl, + struct hdac_ext_stream *bpt_rx_stream, + struct snd_dma_buffer *dmab_rx_bdl) +{ + int ret; + int ret1; + + ret = hda_sdw_bpt_dma_deprepare(dev, + bpt_tx_stream, + dmab_tx_bdl); + + ret1 = hda_sdw_bpt_dma_deprepare(dev, + bpt_rx_stream, + dmab_rx_bdl); + if (!ret) + ret = ret1; + + return ret; +} +EXPORT_SYMBOL_NS(hda_sdw_bpt_close, SND_SOC_SOF_INTEL_HDA_SDW_BPT); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_HDA_COMMON); +MODULE_IMPORT_NS(SND_SOC_SOF_HDA_MLINK); -- 2.39.2