Commit 190357e1 authored by Mark Brown's avatar Mark Brown

ASoC: qcom: apq8016_sbc: Allow routing audio through QDSP6

Merge series from Stephan Gerhold <stephan@gerhold.net>:

This series makes it possible to route audio through the combined
audio/modem DSP on MSM8916/APQ8016 devices instead of bypassing it using
the LPASS drivers. This is necessary to support certain functionality such
as voice call audio. See PATCH 4/5 for details.

Also, qcom,apq8016-sbc.txt is converted to DT schema by adding it to the
existing qcom,sm8250.yaml. The bindings are similar enough that it is easier
to share a single schema instead of duplicating everything into multiple ones.
parents 03c2192a a78a42fb
* Qualcomm Technologies APQ8016 SBC ASoC machine driver
This node models the Qualcomm Technologies APQ8016 SBC ASoC machine driver
Required properties:
- compatible : "qcom,apq8016-sbc-sndcard"
- pinctrl-N : One property must exist for each entry in
pinctrl-names. See ../pinctrl/pinctrl-bindings.txt
for details of the property values.
- pinctrl-names : Must contain a "default" entry.
- reg : Must contain an address for each entry in reg-names.
- reg-names : A list which must include the following entries:
* "mic-iomux"
* "spkr-iomux"
- qcom,model : Name of the sound card.
- qcom,audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the
connection's sink, the second being the connection's
source. Valid names could be power supplies, MicBias
of msm8x16_wcd codec and the jacks on the board:
Power supplies:
* MIC BIAS External1
* MIC BIAS External2
* MIC BIAS Internal1
* MIC BIAS Internal2
Board connectors:
* Headset Mic
* Secondary Mic
* DMIC
* Ext Spk
Optional properties:
- aux-devs : A list of phandles for auxiliary devices (e.g. analog
amplifiers) that do not appear directly within the DAI
links. Should be connected to another audio component
using "qcom,audio-routing".
Dai-link subnode properties and subnodes:
Required dai-link subnodes:
- cpu : CPU sub-node
- codec : CODEC sub-node
Required CPU/CODEC subnodes properties:
-link-name : Name of the dai link.
-sound-dai : phandle/s and port of CPU/CODEC
Example:
sound: sound {
compatible = "qcom,apq8016-sbc-sndcard";
reg = <0x07702000 0x4>, <0x07702004 0x4>;
reg-names = "mic-iomux", "spkr-iomux";
qcom,model = "DB410c";
qcom,audio-routing =
"MIC BIAS External1", "Handset Mic",
"MIC BIAS Internal2", "Headset Mic",
"MIC BIAS External1", "Secondary Mic",
"AMIC1", "MIC BIAS External1",
"AMIC2", "MIC BIAS Internal2",
"AMIC3", "MIC BIAS External1",
"DMIC1", "MIC BIAS Internal1",
"MIC BIAS Internal1", "Digital Mic1",
"DMIC2", "MIC BIAS Internal1",
"MIC BIAS Internal1", "Digital Mic2";
/* I2S - Internal codec */
internal-dai-link@0 {
cpu { /* PRIMARY */
sound-dai = <&lpass MI2S_PRIMARY>;
};
codec {
sound-dai = <&lpass_codec 0>, <&wcd_codec 0>;
};
};
/* External Primary or External Secondary -ADV7533 HDMI */
external-dai-link@0 {
link-name = "ADV7533";
cpu { /* QUAT */
sound-dai = <&lpass MI2S_QUATERNARY>;
};
codec {
sound-dai = <&adv_bridge 0>;
};
};
};
...@@ -4,18 +4,20 @@ ...@@ -4,18 +4,20 @@
$id: http://devicetree.org/schemas/sound/qcom,sm8250.yaml# $id: http://devicetree.org/schemas/sound/qcom,sm8250.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Technologies Inc. SM8250 ASoC sound card driver title: Qualcomm Technologies Inc. ASoC sound card drivers
maintainers: maintainers:
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org> - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
description: description:
This bindings describes SC8250 SoC based sound cards This bindings describes Qualcomm SoC based sound cards
which uses LPASS internal codec for audio. which uses LPASS internal codec for audio.
properties: properties:
compatible: compatible:
enum: enum:
- qcom,apq8016-sbc-sndcard
- qcom,msm8916-qdsp6-sndcard
- qcom,sm8250-sndcard - qcom,sm8250-sndcard
- qcom,qrb5165-rb5-sndcard - qcom,qrb5165-rb5-sndcard
...@@ -27,10 +29,20 @@ properties: ...@@ -27,10 +29,20 @@ properties:
being the connection's source. Valid names could be power supplies, being the connection's source. Valid names could be power supplies,
MicBias of codec and the jacks on the board. MicBias of codec and the jacks on the board.
aux-devs:
$ref: /schemas/types.yaml#/definitions/phandle-array
description: |
List of phandles pointing to auxiliary devices, such
as amplifiers, to be added to the sound card.
model: model:
$ref: /schemas/types.yaml#/definitions/string $ref: /schemas/types.yaml#/definitions/string
description: User visible long sound card name description: User visible long sound card name
# Only valid for some compatibles (see allOf if below)
reg: true
reg-names: true
patternProperties: patternProperties:
".*-dai-link$": ".*-dai-link$":
description: description:
...@@ -73,6 +85,34 @@ required: ...@@ -73,6 +85,34 @@ required:
- compatible - compatible
- model - model
allOf:
- if:
properties:
compatible:
contains:
enum:
- qcom,apq8016-sbc-sndcard
- qcom,msm8916-qdsp6-sndcard
then:
properties:
reg:
items:
- description: Microphone I/O mux register address
- description: Speaker I/O mux register address
reg-names:
items:
- const: mic-iomux
- const: spkr-iomux
required:
- compatible
- model
- reg
- reg-names
else:
properties:
reg: false
reg-names: false
additionalProperties: false additionalProperties: false
examples: examples:
...@@ -86,10 +126,7 @@ examples: ...@@ -86,10 +126,7 @@ examples:
audio-routing = "SpkrLeft IN", "WSA_SPK1 OUT", audio-routing = "SpkrLeft IN", "WSA_SPK1 OUT",
"SpkrRight IN", "WSA_SPK2 OUT", "SpkrRight IN", "WSA_SPK2 OUT",
"VA DMIC0", "vdd-micb", "VA DMIC0", "vdd-micb",
"VA DMIC1", "vdd-micb", "VA DMIC1", "vdd-micb";
"MM_DL1", "MultiMedia1 Playback",
"MM_DL2", "MultiMedia2 Playback",
"MultiMedia3 Capture", "MM_UL3";
mm1-dai-link { mm1-dai-link {
link-name = "MultiMedia0"; link-name = "MultiMedia0";
...@@ -157,3 +194,90 @@ examples: ...@@ -157,3 +194,90 @@ examples:
}; };
}; };
}; };
- |
#include <dt-bindings/sound/qcom,lpass.h>
sound@7702000 {
compatible = "qcom,apq8016-sbc-sndcard";
reg = <0x07702000 0x4>, <0x07702004 0x4>;
reg-names = "mic-iomux", "spkr-iomux";
model = "DB410c";
audio-routing =
"AMIC2", "MIC BIAS Internal2",
"AMIC3", "MIC BIAS External1";
pinctrl-0 = <&cdc_pdm_lines_act &ext_sec_tlmm_lines_act &ext_mclk_tlmm_lines_act>;
pinctrl-1 = <&cdc_pdm_lines_sus &ext_sec_tlmm_lines_sus &ext_mclk_tlmm_lines_sus>;
pinctrl-names = "default", "sleep";
quaternary-dai-link {
link-name = "ADV7533";
cpu {
sound-dai = <&lpass MI2S_QUATERNARY>;
};
codec {
sound-dai = <&adv_bridge 0>;
};
};
primary-dai-link {
link-name = "WCD";
cpu {
sound-dai = <&lpass MI2S_PRIMARY>;
};
codec {
sound-dai = <&lpass_codec 0>, <&wcd_codec 0>;
};
};
tertiary-dai-link {
link-name = "WCD-Capture";
cpu {
sound-dai = <&lpass MI2S_TERTIARY>;
};
codec {
sound-dai = <&lpass_codec 1>, <&wcd_codec 1>;
};
};
};
- |
#include <dt-bindings/sound/qcom,q6afe.h>
#include <dt-bindings/sound/qcom,q6asm.h>
sound@7702000 {
compatible = "qcom,msm8916-qdsp6-sndcard";
reg = <0x07702000 0x4>, <0x07702004 0x4>;
reg-names = "mic-iomux", "spkr-iomux";
model = "msm8916";
audio-routing =
"AMIC1", "MIC BIAS Internal1",
"AMIC2", "MIC BIAS Internal2",
"AMIC3", "MIC BIAS Internal3";
aux-devs = <&speaker_amp>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&cdc_pdm_lines_act>;
pinctrl-1 = <&cdc_pdm_lines_sus>;
mm1-dai-link {
link-name = "MultiMedia1";
cpu {
sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>;
};
};
primary-dai-link {
link-name = "Primary MI2S";
cpu {
sound-dai = <&q6afedai PRIMARY_MI2S_RX>;
};
platform {
sound-dai = <&q6routing>;
};
codec {
sound-dai = <&lpass_codec 0>, <&wcd_codec 0>;
};
};
};
...@@ -17,6 +17,9 @@ ...@@ -17,6 +17,9 @@
#include <uapi/linux/input-event-codes.h> #include <uapi/linux/input-event-codes.h>
#include <dt-bindings/sound/apq8016-lpass.h> #include <dt-bindings/sound/apq8016-lpass.h>
#include "common.h" #include "common.h"
#include "qdsp6/q6afe.h"
#define MI2S_COUNT (MI2S_QUATERNARY + 1)
struct apq8016_sbc_data { struct apq8016_sbc_data {
struct snd_soc_card card; struct snd_soc_card card;
...@@ -24,6 +27,7 @@ struct apq8016_sbc_data { ...@@ -24,6 +27,7 @@ struct apq8016_sbc_data {
void __iomem *spkr_iomux; void __iomem *spkr_iomux;
struct snd_soc_jack jack; struct snd_soc_jack jack;
bool jack_setup; bool jack_setup;
int mi2s_clk_count[MI2S_COUNT];
}; };
#define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21) #define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21)
...@@ -38,10 +42,10 @@ struct apq8016_sbc_data { ...@@ -38,10 +42,10 @@ struct apq8016_sbc_data {
#define SPKR_CTL_TLMM_WS_EN_SEL_MASK GENMASK(19, 18) #define SPKR_CTL_TLMM_WS_EN_SEL_MASK GENMASK(19, 18)
#define SPKR_CTL_TLMM_WS_EN_SEL_SEC BIT(18) #define SPKR_CTL_TLMM_WS_EN_SEL_SEC BIT(18)
#define DEFAULT_MCLK_RATE 9600000 #define DEFAULT_MCLK_RATE 9600000
#define MI2S_BCLK_RATE 1536000
static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) static int apq8016_dai_init(struct snd_soc_pcm_runtime *rtd, int mi2s)
{ {
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
struct snd_soc_dai *codec_dai; struct snd_soc_dai *codec_dai;
struct snd_soc_component *component; struct snd_soc_component *component;
struct snd_soc_card *card = rtd->card; struct snd_soc_card *card = rtd->card;
...@@ -49,7 +53,7 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) ...@@ -49,7 +53,7 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
int i, rval; int i, rval;
u32 value; u32 value;
switch (cpu_dai->id) { switch (mi2s) {
case MI2S_PRIMARY: case MI2S_PRIMARY:
writel(readl(pdata->spkr_iomux) | SPKR_CTL_PRI_WS_SLAVE_SEL_11, writel(readl(pdata->spkr_iomux) | SPKR_CTL_PRI_WS_SLAVE_SEL_11,
pdata->spkr_iomux); pdata->spkr_iomux);
...@@ -128,6 +132,13 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) ...@@ -128,6 +132,13 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
return 0; return 0;
} }
static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
return apq8016_dai_init(rtd, cpu_dai->id);
}
static void apq8016_sbc_add_ops(struct snd_soc_card *card) static void apq8016_sbc_add_ops(struct snd_soc_card *card)
{ {
struct snd_soc_dai_link *link; struct snd_soc_dai_link *link;
...@@ -137,6 +148,113 @@ static void apq8016_sbc_add_ops(struct snd_soc_card *card) ...@@ -137,6 +148,113 @@ static void apq8016_sbc_add_ops(struct snd_soc_card *card)
link->init = apq8016_sbc_dai_init; link->init = apq8016_sbc_dai_init;
} }
static int qdsp6_dai_get_lpass_id(struct snd_soc_dai *cpu_dai)
{
switch (cpu_dai->id) {
case PRIMARY_MI2S_RX:
case PRIMARY_MI2S_TX:
return MI2S_PRIMARY;
case SECONDARY_MI2S_RX:
case SECONDARY_MI2S_TX:
return MI2S_SECONDARY;
case TERTIARY_MI2S_RX:
case TERTIARY_MI2S_TX:
return MI2S_TERTIARY;
case QUATERNARY_MI2S_RX:
case QUATERNARY_MI2S_TX:
return MI2S_QUATERNARY;
default:
return -EINVAL;
}
}
static int msm8916_qdsp6_dai_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
return apq8016_dai_init(rtd, qdsp6_dai_get_lpass_id(cpu_dai));
}
static int msm8916_qdsp6_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *card = rtd->card;
struct apq8016_sbc_data *data = snd_soc_card_get_drvdata(card);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
int mi2s, ret;
mi2s = qdsp6_dai_get_lpass_id(cpu_dai);
if (mi2s < 0)
return mi2s;
if (++data->mi2s_clk_count[mi2s] > 1)
return 0;
ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_BIT_CLK, MI2S_BCLK_RATE, 0);
if (ret)
dev_err(card->dev, "Failed to enable LPAIF bit clk: %d\n", ret);
return ret;
}
static void msm8916_qdsp6_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *card = rtd->card;
struct apq8016_sbc_data *data = snd_soc_card_get_drvdata(card);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
int mi2s, ret;
mi2s = qdsp6_dai_get_lpass_id(cpu_dai);
if (mi2s < 0)
return;
if (--data->mi2s_clk_count[mi2s] > 0)
return;
ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_BIT_CLK, 0, 0);
if (ret)
dev_err(card->dev, "Failed to disable LPAIF bit clk: %d\n", ret);
}
static const struct snd_soc_ops msm8916_qdsp6_be_ops = {
.startup = msm8916_qdsp6_startup,
.shutdown = msm8916_qdsp6_shutdown,
};
static int msm8916_qdsp6_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
return 0;
}
static void msm8916_qdsp6_add_ops(struct snd_soc_card *card)
{
struct snd_soc_dai_link *link;
int i;
/* Make it obvious to userspace that QDSP6 is used */
card->components = "qdsp6";
for_each_card_prelinks(card, i, link) {
if (link->no_pcm) {
link->init = msm8916_qdsp6_dai_init;
link->ops = &msm8916_qdsp6_be_ops;
link->be_hw_params_fixup = msm8916_qdsp6_be_hw_params_fixup;
}
}
}
static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Handset Mic", NULL), SND_SOC_DAPM_MIC("Handset Mic", NULL),
...@@ -148,11 +266,16 @@ static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { ...@@ -148,11 +266,16 @@ static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = {
static int apq8016_sbc_platform_probe(struct platform_device *pdev) static int apq8016_sbc_platform_probe(struct platform_device *pdev)
{ {
void (*add_ops)(struct snd_soc_card *card);
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct snd_soc_card *card; struct snd_soc_card *card;
struct apq8016_sbc_data *data; struct apq8016_sbc_data *data;
int ret; int ret;
add_ops = device_get_match_data(&pdev->dev);
if (!add_ops)
return -EINVAL;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
...@@ -177,12 +300,13 @@ static int apq8016_sbc_platform_probe(struct platform_device *pdev) ...@@ -177,12 +300,13 @@ static int apq8016_sbc_platform_probe(struct platform_device *pdev)
snd_soc_card_set_drvdata(card, data); snd_soc_card_set_drvdata(card, data);
apq8016_sbc_add_ops(card); add_ops(card);
return devm_snd_soc_register_card(&pdev->dev, card); return devm_snd_soc_register_card(&pdev->dev, card);
} }
static const struct of_device_id apq8016_sbc_device_id[] __maybe_unused = { static const struct of_device_id apq8016_sbc_device_id[] __maybe_unused = {
{ .compatible = "qcom,apq8016-sbc-sndcard" }, { .compatible = "qcom,apq8016-sbc-sndcard", .data = apq8016_sbc_add_ops },
{ .compatible = "qcom,msm8916-qdsp6-sndcard", .data = msm8916_qdsp6_add_ops },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, apq8016_sbc_device_id); MODULE_DEVICE_TABLE(of, apq8016_sbc_device_id);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment