aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/qcom/sdm845.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sound/soc/qcom/sdm845.c188
1 files changed, 121 insertions, 67 deletions
diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c
index 3b5547a27aad..d8d35563af00 100644
--- a/sound/soc/qcom/sdm845.c
+++ b/sound/soc/qcom/sdm845.c
@@ -11,11 +11,13 @@
#include <sound/pcm_params.h>
#include <sound/jack.h>
#include <sound/soc.h>
+#include <linux/soundwire/sdw.h>
#include <uapi/linux/input-event-codes.h>
#include "common.h"
#include "qdsp6/q6afe.h"
#include "../codecs/rt5663.h"
+#define DRIVER_NAME "sdm845"
#define DEFAULT_SAMPLE_RATE_48K 48000
#define DEFAULT_MCLK_RATE 24576000
#define TDM_BCLK_RATE 6144000
@@ -25,16 +27,19 @@
#define SPK_TDM_RX_MASK 0x03
#define NUM_TDM_SLOTS 8
#define SLIM_MAX_TX_PORTS 16
-#define SLIM_MAX_RX_PORTS 16
+#define SLIM_MAX_RX_PORTS 13
#define WCD934X_DEFAULT_MCLK_RATE 9600000
struct sdm845_snd_data {
struct snd_soc_jack jack;
bool jack_setup;
+ bool slim_port_setup;
+ bool stream_prepared[AFE_PORT_MAX];
struct snd_soc_card *card;
uint32_t pri_mi2s_clk_count;
uint32_t sec_mi2s_clk_count;
uint32_t quat_tdm_clk_count;
+ struct sdw_stream_runtime *sruntime[AFE_PORT_MAX];
};
static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28};
@@ -42,15 +47,22 @@ static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28};
static int sdm845_slim_snd_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai_link *dai_link = rtd->dai_link;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai;
+ struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+ struct sdw_stream_runtime *sruntime;
u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
int ret = 0, i;
- for (i = 0 ; i < dai_link->num_codecs; i++) {
- ret = snd_soc_dai_get_channel_map(rtd->codec_dais[i],
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ sruntime = snd_soc_dai_get_stream(codec_dai,
+ substream->stream);
+ if (sruntime != ERR_PTR(-ENOTSUPP))
+ pdata->sruntime[cpu_dai->id] = sruntime;
+
+ ret = snd_soc_dai_get_channel_map(codec_dai,
&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
if (ret != 0 && ret != -ENOTSUPP) {
@@ -75,8 +87,9 @@ static int sdm845_slim_snd_hw_params(struct snd_pcm_substream *substream,
static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai;
int ret = 0, j;
int channels, slot_width;
@@ -125,8 +138,7 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream,
}
}
- for (j = 0; j < rtd->num_codecs; j++) {
- struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
+ for_each_rtd_codec_dais(rtd, j, codec_dai) {
if (!strcmp(codec_dai->component->name_prefix, "Left")) {
ret = snd_soc_dai_set_tdm_slot(
@@ -160,9 +172,9 @@ end:
static int sdm845_snd_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
int ret = 0;
switch (cpu_dai->id) {
@@ -210,11 +222,11 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_component *component;
struct snd_soc_card *card = rtd->card;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai_link *link = rtd->dai_link;
struct snd_jack *jack;
- struct snd_soc_dai_link *dai_link = rtd->dai_link;
/*
* Codec SLIMBUS configuration
* RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
@@ -235,7 +247,7 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
SND_JACK_HEADPHONE |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &pdata->jack, NULL, 0);
+ &pdata->jack);
if (rval < 0) {
dev_err(card->dev, "Unable to add Headphone Jack\n");
@@ -266,8 +278,12 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
}
break;
case SLIMBUS_0_RX...SLIMBUS_6_TX:
- for (i = 0 ; i < dai_link->num_codecs; i++) {
- rval = snd_soc_dai_set_channel_map(rtd->codec_dais[i],
+ /* setting up wcd multiple times for slim port is redundant */
+ if (pdata->slim_port_setup || !link->no_pcm)
+ return 0;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ rval = snd_soc_dai_set_channel_map(codec_dai,
ARRAY_SIZE(tx_ch),
tx_ch,
ARRAY_SIZE(rx_ch),
@@ -275,10 +291,20 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
if (rval != 0 && rval != -ENOTSUPP)
return rval;
- snd_soc_dai_set_sysclk(rtd->codec_dais[i], 0,
+ snd_soc_dai_set_sysclk(codec_dai, 0,
WCD934X_DEFAULT_MCLK_RATE,
SNDRV_PCM_STREAM_PLAYBACK);
+
+ rval = snd_soc_component_set_jack(codec_dai->component,
+ &pdata->jack, NULL);
+ if (rval != 0 && rval != -ENOTSUPP) {
+ dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+ return rval;
+ }
}
+
+ pdata->slim_port_setup = true;
+
break;
default:
break;
@@ -290,13 +316,13 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
static int sdm845_snd_startup(struct snd_pcm_substream *substream)
{
- unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS;
- unsigned int codec_dai_fmt = SND_SOC_DAIFMT_CBS_CFS;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int fmt = SND_SOC_DAIFMT_BP_FP;
+ unsigned int codec_dai_fmt = SND_SOC_DAIFMT_BC_FC;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_card *card = rtd->card;
struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card);
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
int j;
int ret;
@@ -330,7 +356,7 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream)
snd_soc_dai_set_sysclk(cpu_dai,
Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT,
MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK);
- snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
+ snd_soc_dai_set_fmt(cpu_dai, fmt);
break;
@@ -345,8 +371,7 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream)
codec_dai_fmt |= SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_DSP_B;
- for (j = 0; j < rtd->num_codecs; j++) {
- codec_dai = rtd->codec_dais[j];
+ for_each_rtd_codec_dais(rtd, j, codec_dai) {
if (!strcmp(codec_dai->component->name_prefix,
"Left")) {
@@ -383,10 +408,10 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream)
static void sdm845_snd_shutdown(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_card *card = rtd->card;
struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card);
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
switch (cpu_dai->id) {
case PRIMARY_MI2S_RX:
@@ -427,8 +452,65 @@ static void sdm845_snd_shutdown(struct snd_pcm_substream *substream)
}
}
+static int sdm845_snd_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct sdm845_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
+ int ret;
+
+ if (!sruntime)
+ return 0;
+
+ if (data->stream_prepared[cpu_dai->id]) {
+ sdw_disable_stream(sruntime);
+ sdw_deprepare_stream(sruntime);
+ data->stream_prepared[cpu_dai->id] = false;
+ }
+
+ 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;
+ }
+ data->stream_prepared[cpu_dai->id] = true;
+
+ return ret;
+}
+
+static int sdm845_snd_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct sdm845_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
+
+ if (sruntime && data->stream_prepared[cpu_dai->id]) {
+ sdw_disable_stream(sruntime);
+ sdw_deprepare_stream(sruntime);
+ data->stream_prepared[cpu_dai->id] = false;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_ops sdm845_be_ops = {
.hw_params = sdm845_snd_hw_params,
+ .hw_free = sdm845_snd_hw_free,
+ .prepare = sdm845_snd_prepare,
.startup = sdm845_snd_startup,
.shutdown = sdm845_snd_shutdown,
};
@@ -478,57 +560,30 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
int ret;
- card = kzalloc(sizeof(*card), GFP_KERNEL);
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
/* Allocate the private data */
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data) {
- ret = -ENOMEM;
- goto data_alloc_fail;
- }
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ card->driver_name = DRIVER_NAME;
card->dapm_widgets = sdm845_snd_widgets;
card->num_dapm_widgets = ARRAY_SIZE(sdm845_snd_widgets);
card->dev = dev;
+ card->owner = THIS_MODULE;
dev_set_drvdata(dev, card);
ret = qcom_snd_parse_of(card);
- if (ret) {
- dev_err(dev, "Error parsing OF data\n");
- goto parse_dt_fail;
- }
+ if (ret)
+ return ret;
data->card = card;
snd_soc_card_set_drvdata(card, data);
sdm845_add_ops(card);
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(dev, "Sound card registration failed\n");
- goto register_card_fail;
- }
- return ret;
-
-register_card_fail:
- kfree(card->dai_link);
-parse_dt_fail:
- kfree(data);
-data_alloc_fail:
- kfree(card);
- return ret;
-}
-
-static int sdm845_snd_platform_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = dev_get_drvdata(&pdev->dev);
- struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card);
-
- snd_soc_unregister_card(card);
- kfree(card->dai_link);
- kfree(data);
- kfree(card);
- return 0;
+ return devm_snd_soc_register_card(dev, card);
}
static const struct of_device_id sdm845_snd_device_id[] = {
@@ -541,7 +596,6 @@ MODULE_DEVICE_TABLE(of, sdm845_snd_device_id);
static struct platform_driver sdm845_snd_driver = {
.probe = sdm845_snd_platform_probe,
- .remove = sdm845_snd_platform_remove,
.driver = {
.name = "msm-snd-sdm845",
.of_match_table = sdm845_snd_device_id,