mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-15 06:55:13 +08:00
Machine driver to support LPASS SC7280 sound card registration
Merge series from Srinivasa Rao Mandadapu <quic_srivasam@quicinc.com>: This patch set is to add support for SC7280 sound card registration and to add dt-bindings documentation file.
This commit is contained in:
commit
b0d0e85f14
@ -0,0 +1,180 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/google,sc7280-herobrine.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Google SC7280-Herobrine ASoC sound card driver
|
||||
|
||||
maintainers:
|
||||
- Srinivasa Rao Mandadapu <srivasam@codeaurora.org>
|
||||
- Judy Hsiao <judyhsiao@chromium.org>
|
||||
|
||||
description:
|
||||
This binding describes the SC7280 sound card which uses LPASS for audio.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- google,sc7280-herobrine
|
||||
|
||||
audio-routing:
|
||||
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
|
||||
description:
|
||||
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.
|
||||
|
||||
model:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description: User specified audio sound card name
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^dai-link@[0-9a-f]$":
|
||||
description:
|
||||
Each subnode represents a dai link. Subnodes of each dai links would be
|
||||
cpu/codec dais.
|
||||
|
||||
type: object
|
||||
|
||||
properties:
|
||||
link-name:
|
||||
description: Indicates dai-link name and PCM stream name.
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
maxItems: 1
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: dai link address.
|
||||
|
||||
cpu:
|
||||
description: Holds subnode which indicates cpu dai.
|
||||
type: object
|
||||
properties:
|
||||
sound-dai: true
|
||||
|
||||
required:
|
||||
- sound-dai
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
codec:
|
||||
description: Holds subnode which indicates codec dai.
|
||||
type: object
|
||||
properties:
|
||||
sound-dai: true
|
||||
|
||||
required:
|
||||
- sound-dai
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- link-name
|
||||
- cpu
|
||||
- codec
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- model
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
||||
- |
|
||||
#include <dt-bindings/sound/qcom,lpass.h>
|
||||
sound {
|
||||
compatible = "google,sc7280-herobrine";
|
||||
model = "sc7280-wcd938x-max98360a-4dmic";
|
||||
|
||||
audio-routing =
|
||||
"IN1_HPHL", "HPHL_OUT",
|
||||
"IN2_HPHR", "HPHR_OUT",
|
||||
"AMIC1", "MIC BIAS1",
|
||||
"AMIC2", "MIC BIAS2",
|
||||
"VA DMIC0", "MIC BIAS3",
|
||||
"VA DMIC1", "MIC BIAS3",
|
||||
"VA DMIC2", "MIC BIAS4",
|
||||
"VA DMIC3", "MIC BIAS4",
|
||||
"TX SWR_ADC0", "ADC1_OUTPUT",
|
||||
"TX SWR_ADC1", "ADC2_OUTPUT",
|
||||
"TX SWR_ADC2", "ADC3_OUTPUT",
|
||||
"TX SWR_DMIC0", "DMIC1_OUTPUT",
|
||||
"TX SWR_DMIC1", "DMIC2_OUTPUT",
|
||||
"TX SWR_DMIC2", "DMIC3_OUTPUT",
|
||||
"TX SWR_DMIC3", "DMIC4_OUTPUT";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
dai-link@0 {
|
||||
link-name = "WCD Playback";
|
||||
reg = <LPASS_CDC_DMA_RX0>;
|
||||
cpu {
|
||||
sound-dai = <&lpass_cpu LPASS_CDC_DMA_RX0>;
|
||||
};
|
||||
|
||||
codec {
|
||||
sound-dai = <&wcd938x 0>, <&swr0 0>, <&rxmacro 0>;
|
||||
};
|
||||
};
|
||||
dai-link@1 {
|
||||
link-name = "WCD Capture";
|
||||
reg = <LPASS_CDC_DMA_TX3>;
|
||||
cpu {
|
||||
sound-dai = <&lpass_cpu LPASS_CDC_DMA_TX3>;
|
||||
};
|
||||
|
||||
codec {
|
||||
sound-dai = <&wcd938x 1>, <&swr1 0>, <&txmacro 0>;
|
||||
};
|
||||
};
|
||||
|
||||
dai-link@2 {
|
||||
link-name = "MI2S Playback";
|
||||
reg = <MI2S_SECONDARY>;
|
||||
cpu {
|
||||
sound-dai = <&lpass_cpu MI2S_SECONDARY>;
|
||||
};
|
||||
|
||||
codec {
|
||||
sound-dai = <&max98360a>;
|
||||
};
|
||||
};
|
||||
|
||||
dai-link@3 {
|
||||
link-name = "DMIC Capture";
|
||||
reg = <LPASS_CDC_DMA_VA_TX0>;
|
||||
cpu {
|
||||
sound-dai = <&lpass_cpu LPASS_CDC_DMA_VA_TX0>;
|
||||
};
|
||||
|
||||
codec {
|
||||
sound-dai = <&vamacro 0>;
|
||||
};
|
||||
};
|
||||
|
||||
dai-link@5 {
|
||||
link-name = "DP Playback";
|
||||
reg = <LPASS_DP_RX>;
|
||||
cpu {
|
||||
sound-dai = <&lpass_cpu LPASS_DP_RX>;
|
||||
};
|
||||
|
||||
codec {
|
||||
sound-dai = <&mdss_dp>;
|
||||
};
|
||||
};
|
||||
};
|
@ -176,4 +176,18 @@ config SND_SOC_SC7180
|
||||
SC7180 SoC-based systems.
|
||||
Say Y if you want to use audio device on this SoCs.
|
||||
|
||||
config SND_SOC_SC7280
|
||||
tristate "SoC Machine driver for SC7280 boards"
|
||||
depends on I2C && SOUNDWIRE || COMPILE_TEST
|
||||
select SND_SOC_QCOM_COMMON
|
||||
select SND_SOC_LPASS_SC7280
|
||||
select SND_SOC_MAX98357A
|
||||
select SND_SOC_WCD938X
|
||||
select SND_SOC_LPASS_RX_MACRO
|
||||
select SND_SOC_LPASS_TX_MACRO
|
||||
help
|
||||
Add support for audio on Qualcomm Technologies Inc.
|
||||
SC7280 SoC-based systems.
|
||||
Say Y or M if you want to use audio device on this SoCs.
|
||||
|
||||
endif #SND_SOC_QCOM
|
||||
|
@ -19,6 +19,7 @@ snd-soc-storm-objs := storm.o
|
||||
snd-soc-apq8016-sbc-objs := apq8016_sbc.o
|
||||
snd-soc-apq8096-objs := apq8096.o
|
||||
snd-soc-sc7180-objs := sc7180.o
|
||||
snd-soc-sc7280-objs := sc7280.o
|
||||
snd-soc-sdm845-objs := sdm845.o
|
||||
snd-soc-sm8250-objs := sm8250.o
|
||||
snd-soc-qcom-common-objs := common.o
|
||||
@ -27,6 +28,7 @@ obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o
|
||||
obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o
|
||||
obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-apq8096.o
|
||||
obj-$(CONFIG_SND_SOC_SC7180) += snd-soc-sc7180.o
|
||||
obj-$(CONFIG_SND_SOC_SC7280) += snd-soc-sc7280.o
|
||||
obj-$(CONFIG_SND_SOC_SDM845) += snd-soc-sdm845.o
|
||||
obj-$(CONFIG_SND_SOC_SM8250) += snd-soc-sm8250.o
|
||||
obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "lpass-hdmi.h"
|
||||
|
||||
#define LPASS_AHBIX_CLOCK_FREQUENCY 131072000
|
||||
#define LPASS_MAX_PORTS (LPASS_CDC_DMA_VA_TX8 + 1)
|
||||
#define LPASS_MAX_MI2S_PORTS (8)
|
||||
#define LPASS_MAX_DMA_CHANNELS (8)
|
||||
#define LPASS_MAX_HDMI_DMA_CHANNELS (4)
|
||||
|
284
sound/soc/qcom/sc7280.c
Normal file
284
sound/soc/qcom/sc7280.c
Normal file
@ -0,0 +1,284 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
|
||||
//
|
||||
// ALSA SoC Machine driver for sc7280
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "lpass.h"
|
||||
|
||||
struct sc7280_snd_data {
|
||||
struct snd_soc_card card;
|
||||
struct sdw_stream_runtime *sruntime[LPASS_MAX_PORTS];
|
||||
struct snd_soc_jack hs_jack;
|
||||
struct snd_soc_jack hdmi_jack;
|
||||
bool jack_setup;
|
||||
bool stream_prepared[LPASS_MAX_PORTS];
|
||||
};
|
||||
|
||||
static void sc7280_jack_free(struct snd_jack *jack)
|
||||
{
|
||||
struct snd_soc_component *component = jack->private_data;
|
||||
|
||||
snd_soc_component_set_jack(component, NULL, NULL);
|
||||
}
|
||||
|
||||
static int sc7280_headset_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct sc7280_snd_data *pdata = snd_soc_card_get_drvdata(card);
|
||||
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 snd_soc_component *component = codec_dai->component;
|
||||
struct snd_jack *jack;
|
||||
int rval, i;
|
||||
|
||||
if (!pdata->jack_setup) {
|
||||
rval = snd_soc_card_jack_new(card, "Headset Jack",
|
||||
SND_JACK_HEADSET | SND_JACK_LINEOUT |
|
||||
SND_JACK_MECHANICAL |
|
||||
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
|
||||
SND_JACK_BTN_2 | SND_JACK_BTN_3 |
|
||||
SND_JACK_BTN_4 | SND_JACK_BTN_5,
|
||||
&pdata->hs_jack, NULL, 0);
|
||||
|
||||
if (rval < 0) {
|
||||
dev_err(card->dev, "Unable to add Headset Jack\n");
|
||||
return rval;
|
||||
}
|
||||
|
||||
jack = pdata->hs_jack.jack;
|
||||
|
||||
snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
|
||||
snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
|
||||
snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
|
||||
snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
|
||||
|
||||
jack->private_data = component;
|
||||
jack->private_free = sc7280_jack_free;
|
||||
pdata->jack_setup = true;
|
||||
}
|
||||
switch (cpu_dai->id) {
|
||||
case LPASS_CDC_DMA_RX0:
|
||||
case LPASS_CDC_DMA_TX3:
|
||||
for_each_rtd_codec_dais(rtd, i, codec_dai) {
|
||||
rval = snd_soc_component_set_jack(component, &pdata->hs_jack, NULL);
|
||||
if (rval != 0 && rval != -ENOTSUPP) {
|
||||
dev_err(card->dev, "Failed to set jack: %d\n", rval);
|
||||
return rval;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc7280_hdmi_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
struct sc7280_snd_data *pdata = snd_soc_card_get_drvdata(card);
|
||||
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
struct snd_soc_component *component = codec_dai->component;
|
||||
struct snd_jack *jack;
|
||||
int rval;
|
||||
|
||||
rval = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,
|
||||
&pdata->hdmi_jack, NULL, 0);
|
||||
|
||||
if (rval < 0) {
|
||||
dev_err(card->dev, "Unable to add HDMI Jack\n");
|
||||
return rval;
|
||||
}
|
||||
|
||||
jack = pdata->hdmi_jack.jack;
|
||||
jack->private_data = component;
|
||||
jack->private_free = sc7280_jack_free;
|
||||
|
||||
return snd_soc_component_set_jack(component, &pdata->hdmi_jack, NULL);
|
||||
}
|
||||
|
||||
static int sc7280_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
|
||||
switch (cpu_dai->id) {
|
||||
case LPASS_CDC_DMA_TX3:
|
||||
return sc7280_headset_init(rtd);
|
||||
case LPASS_CDC_DMA_RX0:
|
||||
case LPASS_CDC_DMA_VA_TX0:
|
||||
case MI2S_SECONDARY:
|
||||
return 0;
|
||||
case LPASS_DP_RX:
|
||||
return sc7280_hdmi_init(rtd);
|
||||
default:
|
||||
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, cpu_dai->id);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int sc7280_snd_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
const struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
struct sc7280_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct sdw_stream_runtime *sruntime;
|
||||
int i;
|
||||
|
||||
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
|
||||
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, 48000, 48000);
|
||||
|
||||
switch (cpu_dai->id) {
|
||||
case LPASS_CDC_DMA_TX3:
|
||||
case LPASS_CDC_DMA_RX0:
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc7280_snd_swr_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
const struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
struct sc7280_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
|
||||
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;
|
||||
|
||||
ret = sdw_enable_stream(sruntime);
|
||||
if (ret) {
|
||||
sdw_deprepare_stream(sruntime);
|
||||
return ret;
|
||||
}
|
||||
data->stream_prepared[cpu_dai->id] = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sc7280_snd_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
const struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
|
||||
switch (cpu_dai->id) {
|
||||
case LPASS_CDC_DMA_RX0:
|
||||
case LPASS_CDC_DMA_TX3:
|
||||
return sc7280_snd_swr_prepare(substream);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc7280_snd_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct sc7280_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
|
||||
const struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
|
||||
|
||||
switch (cpu_dai->id) {
|
||||
case LPASS_CDC_DMA_RX0:
|
||||
case LPASS_CDC_DMA_TX3:
|
||||
if (sruntime && data->stream_prepared[cpu_dai->id]) {
|
||||
sdw_disable_stream(sruntime);
|
||||
sdw_deprepare_stream(sruntime);
|
||||
data->stream_prepared[cpu_dai->id] = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops sc7280_ops = {
|
||||
.hw_params = sc7280_snd_hw_params,
|
||||
.hw_free = sc7280_snd_hw_free,
|
||||
.prepare = sc7280_snd_prepare,
|
||||
};
|
||||
|
||||
static int sc7280_snd_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card;
|
||||
struct sc7280_snd_data *data;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct snd_soc_dai_link *link;
|
||||
int ret, i;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
card = &data->card;
|
||||
snd_soc_card_set_drvdata(card, data);
|
||||
|
||||
card->owner = THIS_MODULE;
|
||||
card->driver_name = "SC7280";
|
||||
card->dev = dev;
|
||||
|
||||
ret = qcom_snd_parse_of(card);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for_each_card_prelinks(card, i, link) {
|
||||
link->init = sc7280_init;
|
||||
link->ops = &sc7280_ops;
|
||||
}
|
||||
|
||||
return devm_snd_soc_register_card(dev, card);
|
||||
}
|
||||
|
||||
static const struct of_device_id sc7280_snd_device_id[] = {
|
||||
{ .compatible = "google,sc7280-herobrine" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sc7280_snd_device_id);
|
||||
|
||||
static struct platform_driver sc7280_snd_driver = {
|
||||
.probe = sc7280_snd_platform_probe,
|
||||
.driver = {
|
||||
.name = "msm-snd-sc7280",
|
||||
.of_match_table = sc7280_snd_device_id,
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(sc7280_snd_driver);
|
||||
|
||||
MODULE_DESCRIPTION("sc7280 ASoC Machine Driver");
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Reference in New Issue
Block a user