Files
linux/sound/soc/qcom/sdw.c
Srinivas Kandagatla 8fdb030fe2 ASoC: qcom: sc7280: make use of common helpers
sc7280 machine driver can make use of common sdw functions to do most of
the soundwire related operations. Remove such redundant code from sc7280
driver.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Tested-by: Steev Klimaszewski <threeway@gmail.com> # Thinkpad X13s
Link: https://patch.msgid.link/20251022143349.1081513-5-srinivas.kandagatla@oss.qualcomm.com
Signed-off-by: Mark Brown <broonie@kernel.org>
2025-10-29 14:54:40 +00:00

217 lines
5.5 KiB
C

// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018-2023, Linaro Limited.
// Copyright (c) 2018, The Linux Foundation. All rights reserved.
#include <dt-bindings/sound/qcom,lpass.h>
#include <dt-bindings/sound/qcom,q6afe.h>
#include <linux/module.h>
#include <sound/soc.h>
#include "sdw.h"
static bool qcom_snd_is_sdw_dai(int id)
{
switch (id) {
case WSA_CODEC_DMA_RX_0:
case WSA_CODEC_DMA_TX_0:
case WSA_CODEC_DMA_RX_1:
case WSA_CODEC_DMA_TX_1:
case WSA_CODEC_DMA_TX_2:
case RX_CODEC_DMA_RX_0:
case TX_CODEC_DMA_TX_0:
case RX_CODEC_DMA_RX_1:
case TX_CODEC_DMA_TX_1:
case RX_CODEC_DMA_RX_2:
case TX_CODEC_DMA_TX_2:
case RX_CODEC_DMA_RX_3:
case TX_CODEC_DMA_TX_3:
case RX_CODEC_DMA_RX_4:
case TX_CODEC_DMA_TX_4:
case RX_CODEC_DMA_RX_5:
case TX_CODEC_DMA_TX_5:
case RX_CODEC_DMA_RX_6:
case RX_CODEC_DMA_RX_7:
case SLIMBUS_0_RX...SLIMBUS_6_TX:
return true;
default:
break;
}
/* DSP Bypass usecase, cpu dai index overlaps with DSP dai ids,
* DO NOT MERGE into top switch case */
switch (id) {
case LPASS_CDC_DMA_TX3:
case LPASS_CDC_DMA_RX0:
return true;
default:
break;
}
return false;
}
/**
* qcom_snd_sdw_startup() - Helper to start Soundwire stream for SoC audio card
* @substream: The PCM substream from audio, as passed to snd_soc_ops->startup()
*
* Helper for the SoC audio card (snd_soc_ops->startup()) to allocate and set
* Soundwire stream runtime to each codec DAI.
*
* The shutdown() callback should call sdw_release_stream() on the same
* sdw_stream_runtime.
*
* Return: 0 or errno
*/
int qcom_snd_sdw_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
u32 rx_ch[SDW_MAX_PORTS], tx_ch[SDW_MAX_PORTS];
struct sdw_stream_runtime *sruntime;
struct snd_soc_dai *codec_dai;
u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
int ret, i, j;
if (!qcom_snd_is_sdw_dai(cpu_dai->id))
return 0;
sruntime = sdw_alloc_stream(cpu_dai->name, SDW_STREAM_PCM);
if (!sruntime)
return -ENOMEM;
for_each_rtd_codec_dais(rtd, i, codec_dai) {
ret = snd_soc_dai_set_stream(codec_dai, sruntime,
substream->stream);
if (ret < 0 && ret != -ENOTSUPP) {
dev_err(rtd->dev, "Failed to set sdw stream on %s\n", codec_dai->name);
goto err_set_stream;
} else if (ret == -ENOTSUPP) {
/* Ignore unsupported */
continue;
}
ret = snd_soc_dai_get_channel_map(codec_dai, &tx_ch_cnt, tx_ch,
&rx_ch_cnt, rx_ch);
if (ret != 0 && ret != -ENOTSUPP) {
dev_err(rtd->dev, "Failed to get codec chan map %s\n", codec_dai->name);
goto err_set_stream;
} else if (ret == -ENOTSUPP) {
/* Ignore unsupported */
continue;
}
}
switch (cpu_dai->id) {
case RX_CODEC_DMA_RX_0:
case TX_CODEC_DMA_TX_3:
if (tx_ch_cnt || rx_ch_cnt) {
for_each_rtd_codec_dais(rtd, j, codec_dai) {
ret = snd_soc_dai_set_channel_map(codec_dai,
tx_ch_cnt, tx_ch,
rx_ch_cnt, rx_ch);
if (ret != 0 && ret != -ENOTSUPP)
goto err_set_stream;
}
}
}
return 0;
err_set_stream:
sdw_release_stream(sruntime);
return ret;
}
EXPORT_SYMBOL_GPL(qcom_snd_sdw_startup);
int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
bool *stream_prepared)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct sdw_stream_runtime *sruntime;
int ret;
if (!qcom_snd_is_sdw_dai(cpu_dai->id))
return 0;
sruntime = qcom_snd_sdw_get_stream(substream);
if (!sruntime)
return 0;
if (*stream_prepared)
return 0;
ret = sdw_prepare_stream(sruntime);
if (ret)
return ret;
/**
* NOTE: there is a strict hw requirement about the ordering of port
* enables and actual WSA881x PA enable. PA enable should only happen
* after soundwire ports are enabled if not DC on the line is
* accumulated resulting in Click/Pop Noise
* PA enable/mute are handled as part of codec DAPM and digital mute.
*/
ret = sdw_enable_stream(sruntime);
if (ret) {
sdw_deprepare_stream(sruntime);
return ret;
}
*stream_prepared = true;
return ret;
}
EXPORT_SYMBOL_GPL(qcom_snd_sdw_prepare);
struct sdw_stream_runtime *qcom_snd_sdw_get_stream(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct sdw_stream_runtime *sruntime;
int i;
if (!qcom_snd_is_sdw_dai(cpu_dai->id))
return NULL;
for_each_rtd_codec_dais(rtd, i, codec_dai) {
sruntime = snd_soc_dai_get_stream(codec_dai, substream->stream);
if (sruntime != ERR_PTR(-ENOTSUPP))
return sruntime;
}
return NULL;
}
EXPORT_SYMBOL_GPL(qcom_snd_sdw_get_stream);
void qcom_snd_sdw_shutdown(struct snd_pcm_substream *substream)
{
struct sdw_stream_runtime *sruntime = qcom_snd_sdw_get_stream(substream);
sdw_release_stream(sruntime);
}
EXPORT_SYMBOL_GPL(qcom_snd_sdw_shutdown);
int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream, bool *stream_prepared)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct sdw_stream_runtime *sruntime;
if (!qcom_snd_is_sdw_dai(cpu_dai->id))
return 0;
sruntime = qcom_snd_sdw_get_stream(substream);
if (sruntime && *stream_prepared) {
sdw_disable_stream(sruntime);
sdw_deprepare_stream(sruntime);
*stream_prepared = false;
}
return 0;
}
EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_free);
MODULE_DESCRIPTION("Qualcomm ASoC SoundWire helper functions");
MODULE_LICENSE("GPL");