summaryrefslogtreecommitdiff
path: root/sound/soc/qcom/sdm845.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/qcom/sdm845.c')
-rw-r--r--sound/soc/qcom/sdm845.c281
1 files changed, 209 insertions, 72 deletions
diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c
index 1db8ef668223..e18a8e44f2db 100644
--- a/sound/soc/qcom/sdm845.c
+++ b/sound/soc/qcom/sdm845.c
@@ -3,19 +3,22 @@
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*/
+#include <dt-bindings/sound/qcom,q6afe.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#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 "sdw.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
@@ -24,23 +27,77 @@
#define RIGHT_SPK_TDM_TX_MASK 0xC0
#define SPK_TDM_RX_MASK 0x03
#define NUM_TDM_SLOTS 8
+#define SLIM_MAX_TX_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;
};
+static struct snd_soc_jack_pin sdm845_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
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 = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai;
+ u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+ u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+ int ret = 0, i;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+
+ if (ret != 0 && ret != -ENOTSUPP) {
+ pr_err("failed to get codec chan map, err:%d\n", ret);
+ return ret;
+ } else if (ret == -ENOTSUPP) {
+ /* Ignore unsupported */
+ continue;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+ rx_ch_cnt, rx_ch);
+ else
+ ret = snd_soc_dai_set_channel_map(cpu_dai, tx_ch_cnt,
+ tx_ch, 0, NULL);
+ if (ret != 0 && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "failed to set cpu chan map, err:%d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
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 = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai;
int ret = 0, j;
int channels, slot_width;
@@ -89,8 +146,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(
@@ -124,9 +180,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 = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int ret = 0;
switch (cpu_dai->id) {
@@ -151,6 +207,12 @@ static int sdm845_snd_hw_params(struct snd_pcm_substream *substream,
case QUATERNARY_TDM_TX_0:
ret = sdm845_tdm_snd_hw_params(substream, params);
break;
+ case SLIMBUS_0_RX...SLIMBUS_6_TX:
+ ret = sdm845_slim_snd_hw_params(substream, params);
+ break;
+ case QUATERNARY_MI2S_RX:
+ case SECONDARY_MI2S_RX:
+ break;
default:
pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id);
break;
@@ -158,23 +220,45 @@ static int sdm845_snd_hw_params(struct snd_pcm_substream *substream,
return ret;
}
+static void sdm845_jack_free(struct snd_jack *jack)
+{
+ struct snd_soc_component *component = jack->private_data;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
+}
+
static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_component *component;
- struct snd_soc_dai_link *dai_link = rtd->dai_link;
struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card);
- int i, rval;
+ struct snd_soc_dai_link *link = rtd->dai_link;
+ struct snd_jack *jack;
+ /*
+ * Codec SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+ * TX14, TX15, TX16
+ */
+ unsigned int rx_ch[SLIM_MAX_RX_PORTS] = {144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156};
+ unsigned int tx_ch[SLIM_MAX_TX_PORTS] = {128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143};
+ int rval, i;
- if (!pdata->jack_setup) {
- struct snd_jack *jack;
- rval = snd_soc_card_jack_new(card, "Headset Jack",
- SND_JACK_HEADSET |
- SND_JACK_HEADPHONE |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &pdata->jack, NULL, 0);
+ if (!pdata->jack_setup) {
+ rval = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_HEADPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &pdata->jack,
+ sdm845_jack_pins,
+ ARRAY_SIZE(sdm845_jack_pins));
if (rval < 0) {
dev_err(card->dev, "Unable to add Headphone Jack\n");
@@ -190,16 +274,51 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
pdata->jack_setup = true;
}
- for (i = 0 ; i < dai_link->num_codecs; i++) {
- struct snd_soc_dai *dai = rtd->codec_dais[i];
+ switch (cpu_dai->id) {
+ case PRIMARY_MI2S_RX:
+ jack = pdata->jack.jack;
+ component = codec_dai->component;
- component = dai->component;
- rval = snd_soc_component_set_jack(
- component, &pdata->jack, NULL);
+ jack->private_data = component;
+ jack->private_free = sdm845_jack_free;
+ rval = snd_soc_component_set_jack(component,
+ &pdata->jack, NULL);
if (rval != 0 && rval != -ENOTSUPP) {
dev_warn(card->dev, "Failed to set jack: %d\n", rval);
return rval;
}
+ break;
+ case SLIMBUS_0_RX...SLIMBUS_6_TX:
+ /* 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),
+ rx_ch);
+ if (rval != 0 && rval != -ENOTSUPP)
+ return rval;
+
+ 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;
}
return 0;
@@ -208,13 +327,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 = snd_soc_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 = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int j;
int ret;
@@ -234,13 +353,22 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream)
snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt);
break;
+ case SECONDARY_MI2S_RX:
case SECONDARY_MI2S_TX:
+ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
if (++(data->sec_mi2s_clk_count) == 1) {
snd_soc_dai_set_sysclk(cpu_dai,
Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT,
MI2S_BCLK_RATE, SNDRV_PCM_STREAM_CAPTURE);
}
snd_soc_dai_set_fmt(cpu_dai, fmt);
+ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt);
+ break;
+ case QUATERNARY_MI2S_RX:
+ 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, fmt);
break;
case QUATERNARY_TDM_RX_0:
@@ -253,8 +381,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")) {
@@ -279,20 +406,22 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream)
}
}
break;
+ case SLIMBUS_0_RX...SLIMBUS_6_TX:
+ break;
default:
pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id);
break;
}
- return 0;
+ return qcom_snd_sdw_startup(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 = snd_soc_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 = snd_soc_rtd_to_cpu(rtd, 0);
switch (cpu_dai->id) {
case PRIMARY_MI2S_RX:
@@ -304,9 +433,10 @@ static void sdm845_snd_shutdown(struct snd_pcm_substream *substream)
snd_soc_dai_set_sysclk(cpu_dai,
Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
0, SNDRV_PCM_STREAM_PLAYBACK);
- };
+ }
break;
+ case SECONDARY_MI2S_RX:
case SECONDARY_MI2S_TX:
if (--(data->sec_mi2s_clk_count) == 0) {
snd_soc_dai_set_sysclk(cpu_dai,
@@ -323,15 +453,40 @@ static void sdm845_snd_shutdown(struct snd_pcm_substream *substream)
0, SNDRV_PCM_STREAM_PLAYBACK);
}
break;
+ case SLIMBUS_0_RX...SLIMBUS_6_TX:
+ case QUATERNARY_MI2S_RX:
+ break;
default:
pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id);
break;
}
+
+ qcom_snd_sdw_shutdown(substream);
+}
+
+static int sdm845_snd_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sdm845_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]);
+}
+
+static int sdm845_snd_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sdm845_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return qcom_snd_sdw_hw_free(substream, &data->stream_prepared[cpu_dai->id]);
}
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,
};
@@ -360,6 +515,11 @@ static const struct snd_soc_dapm_widget sdm845_snd_widgets[] = {
SND_SOC_DAPM_MIC("Int Mic", NULL),
};
+static const struct snd_kcontrol_new sdm845_snd_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
static void sdm845_add_ops(struct snd_soc_card *card)
{
struct snd_soc_dai_link *link;
@@ -381,68 +541,45 @@ 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->controls = sdm845_snd_controls;
+ card->num_controls = ARRAY_SIZE(sdm845_snd_controls);
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[] = {
{ .compatible = "qcom,sdm845-sndcard" },
+ /* Do not grow the list for compatible devices */
+ { .compatible = "qcom,db845c-sndcard" },
+ { .compatible = "lenovo,yoga-c630-sndcard" },
{},
};
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,
@@ -451,4 +588,4 @@ static struct platform_driver sdm845_snd_driver = {
module_platform_driver(sdm845_snd_driver);
MODULE_DESCRIPTION("sdm845 ASoC Machine Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");