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.
This commit is contained in:
Коммит
190357e1e0
|
@ -1,96 +0,0 @@
|
|||
* 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 @@
|
|||
$id: http://devicetree.org/schemas/sound/qcom,sm8250.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:
|
||||
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
||||
|
||||
description:
|
||||
This bindings describes SC8250 SoC based sound cards
|
||||
This bindings describes Qualcomm SoC based sound cards
|
||||
which uses LPASS internal codec for audio.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,apq8016-sbc-sndcard
|
||||
- qcom,msm8916-qdsp6-sndcard
|
||||
- qcom,sm8250-sndcard
|
||||
- qcom,qrb5165-rb5-sndcard
|
||||
|
||||
|
@ -27,10 +29,20 @@ properties:
|
|||
being the connection's source. Valid names could be power supplies,
|
||||
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:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description: User visible long sound card name
|
||||
|
||||
# Only valid for some compatibles (see allOf if below)
|
||||
reg: true
|
||||
reg-names: true
|
||||
|
||||
patternProperties:
|
||||
".*-dai-link$":
|
||||
description:
|
||||
|
@ -73,6 +85,34 @@ required:
|
|||
- compatible
|
||||
- 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
|
||||
|
||||
examples:
|
||||
|
@ -86,10 +126,7 @@ examples:
|
|||
audio-routing = "SpkrLeft IN", "WSA_SPK1 OUT",
|
||||
"SpkrRight IN", "WSA_SPK2 OUT",
|
||||
"VA DMIC0", "vdd-micb",
|
||||
"VA DMIC1", "vdd-micb",
|
||||
"MM_DL1", "MultiMedia1 Playback",
|
||||
"MM_DL2", "MultiMedia2 Playback",
|
||||
"MultiMedia3 Capture", "MM_UL3";
|
||||
"VA DMIC1", "vdd-micb";
|
||||
|
||||
mm1-dai-link {
|
||||
link-name = "MultiMedia0";
|
||||
|
@ -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 @@
|
|||
#include <uapi/linux/input-event-codes.h>
|
||||
#include <dt-bindings/sound/apq8016-lpass.h>
|
||||
#include "common.h"
|
||||
#include "qdsp6/q6afe.h"
|
||||
|
||||
#define MI2S_COUNT (MI2S_QUATERNARY + 1)
|
||||
|
||||
struct apq8016_sbc_data {
|
||||
struct snd_soc_card card;
|
||||
|
@ -24,6 +27,7 @@ struct apq8016_sbc_data {
|
|||
void __iomem *spkr_iomux;
|
||||
struct snd_soc_jack jack;
|
||||
bool jack_setup;
|
||||
int mi2s_clk_count[MI2S_COUNT];
|
||||
};
|
||||
|
||||
#define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21)
|
||||
|
@ -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_SEC BIT(18)
|
||||
#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_component *component;
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
|
@ -49,7 +53,7 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|||
int i, rval;
|
||||
u32 value;
|
||||
|
||||
switch (cpu_dai->id) {
|
||||
switch (mi2s) {
|
||||
case MI2S_PRIMARY:
|
||||
writel(readl(pdata->spkr_iomux) | SPKR_CTL_PRI_WS_SLAVE_SEL_11,
|
||||
pdata->spkr_iomux);
|
||||
|
@ -128,6 +132,13 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|||
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)
|
||||
{
|
||||
struct snd_soc_dai_link *link;
|
||||
|
@ -137,6 +148,113 @@ static void apq8016_sbc_add_ops(struct snd_soc_card *card)
|
|||
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[] = {
|
||||
|
||||
SND_SOC_DAPM_MIC("Handset Mic", NULL),
|
||||
|
@ -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)
|
||||
{
|
||||
void (*add_ops)(struct snd_soc_card *card);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct snd_soc_card *card;
|
||||
struct apq8016_sbc_data *data;
|
||||
int ret;
|
||||
|
||||
add_ops = device_get_match_data(&pdev->dev);
|
||||
if (!add_ops)
|
||||
return -EINVAL;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
@ -177,12 +300,13 @@ static int apq8016_sbc_platform_probe(struct platform_device *pdev)
|
|||
|
||||
snd_soc_card_set_drvdata(card, data);
|
||||
|
||||
apq8016_sbc_add_ops(card);
|
||||
add_ops(card);
|
||||
return devm_snd_soc_register_card(&pdev->dev, card);
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче