[PATCH v6 0/2] Add documentation and machine driver for SC7180 sound card
Note: - The machine driver patch is made by the collaboration of Cheng-Yi Chiang cychiang@chromium.org Rohit kumar rohitkr@codeaurora.org Ajit Pandey ajitp@codeaurora.org But Ajit has left codeaurora.
Changes from v1 to v2: - Ducumentation: Addressed all suggestions from Doug. - Machine driver: - Fix comment style for license. - Sort includes. - Remove sc7180_snd_hw_params. - Remove sc7180_dai_init and use aux device instead for headset jack registration. - Statically define format for Primary MI2S. - Atomic is not a concern because there is mutex in card to make sure startup and shutdown happen sequentially. - Fix missing return -EINVAL in startup. - Use static sound card. - Use devm_kzalloc to avoid kfree.
Changes from v2 to v3: - Ducumentation: Addressed suggestions from Srini. - Machine driver: - Reuse qcom_snd_parse_of to parse properties. - Remove playback-only and capture-only. - Misc fixes to address comments.
Changes from v3 to v4: - Ducumentation: Addressed suggestions from Rob. - Remove definition of dai. - Use 'sound-dai: true' for sound-dai schema. - Add reg property to pass 'make dt_binding_check' check although reg is not used in the driver. - Machine driver: - Add Reviewed-by: Tzung-Bi Shih tzungbi@google.com
Changes from v4 to v5: - Documentation: Addressed suggestions from Rob. - Add definition for "#address-cells" and "#size-cells". - Add additionalProperties: false - Add required properties.
Changes from v5 to v6: - Documentation: Addressed suggestions from Rob. - Drop contains in compatible strings. - Only allow dai-link@[0-9] - Remove reg ref since it has a type definition already. Ajit Pandey (1): ASoC: qcom: sc7180: Add machine driver for sound card registration
Cheng-Yi Chiang (1): ASoC: qcom: dt-bindings: Add sc7180 machine bindings
.../bindings/sound/qcom,sc7180.yaml | 124 +++++++++ sound/soc/qcom/Kconfig | 12 + sound/soc/qcom/Makefile | 2 + sound/soc/qcom/sc7180.c | 244 ++++++++++++++++++ 4 files changed, 382 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/qcom,sc7180.yaml create mode 100644 sound/soc/qcom/sc7180.c
Add devicetree bindings documentation file for sc7180 sound card.
Signed-off-by: Cheng-Yi Chiang cychiang@chromium.org --- .../bindings/sound/qcom,sc7180.yaml | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/qcom,sc7180.yaml
diff --git a/Documentation/devicetree/bindings/sound/qcom,sc7180.yaml b/Documentation/devicetree/bindings/sound/qcom,sc7180.yaml new file mode 100644 index 000000000000..a85375974a4c --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,sc7180.yaml @@ -0,0 +1,124 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/qcom,sc7180.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies Inc. SC7180 ASoC sound card driver + +maintainers: + - Rohit kumar rohitkr@codeaurora.org + - Cheng-Yi Chiang cychiang@chromium.org + +description: + This binding describes the SC7180 sound card which uses LPASS for audio. + +properties: + compatible: + const: qcom,sc7180-sndcard + + 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 + + aux-dev: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle of the codec for headset detection + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^dai-link(@[0-9])?$": + 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: + description: dai link address. + + cpu: + description: Holds subnode which indicates cpu dai. + type: object + properties: + sound-dai: true + + codec: + description: Holds subnode which indicates codec dai. + type: object + properties: + sound-dai: true + + required: + - link-name + - cpu + - codec + + additionalProperties: false + +required: + - compatible + - model + - "#address-cells" + - "#size-cells" + +additionalProperties: false + +examples: + + - | + sound { + compatible = "qcom,sc7180-sndcard"; + model = "sc7180-snd-card"; + + audio-routing = + "Headphone Jack", "HPOL", + "Headphone Jack", "HPOR"; + + aux-dev = <&alc5682>; + + #address-cells = <1>; + #size-cells = <0>; + + dai-link@0 { + link-name = "MultiMedia0"; + reg = <0>; + cpu { + sound-dai = <&lpass_cpu 0>; + }; + + codec { + sound-dai = <&alc5682 0>; + }; + }; + + dai-link@1 { + link-name = "MultiMedia1"; + reg = <1>; + cpu { + sound-dai = <&lpass_cpu 1>; + }; + + codec { + sound-dai = <&max98357a>; + }; + }; + };
On Wed, 26 Aug 2020 19:04:53 +0800, Cheng-Yi Chiang wrote:
Add devicetree bindings documentation file for sc7180 sound card.
Signed-off-by: Cheng-Yi Chiang cychiang@chromium.org
.../bindings/sound/qcom,sc7180.yaml | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/qcom,sc7180.yaml
Reviewed-by: Rob Herring robh@kernel.org
From: Ajit Pandey ajitp@codeaurora.org
Add new driver to register sound card on sc7180 trogdor board and do the required configuration for lpass cpu dai and external codecs connected over MI2S interfaces.
Signed-off-by: Ajit Pandey ajitp@codeaurora.org Signed-off-by: Cheng-Yi Chiang cychiang@chromium.org Reviewed-by: Tzung-Bi Shih tzungbi@google.com --- sound/soc/qcom/Kconfig | 12 ++ sound/soc/qcom/Makefile | 2 + sound/soc/qcom/sc7180.c | 244 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 sound/soc/qcom/sc7180.c
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index a607ace8b089..63678b746299 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -116,4 +116,16 @@ config SND_SOC_SDM845 SDM845 SoC-based systems. Say Y if you want to use audio device on this SoCs.
+config SND_SOC_SC7180 + tristate "SoC Machine driver for SC7180 boards" + depends on SND_SOC_QCOM + select SND_SOC_QCOM_COMMON + select SND_SOC_LPASS_SC7180 + select SND_SOC_MAX98357A + select SND_SOC_RT5682 + help + To add support for audio on Qualcomm Technologies Inc. + SC7180 SoC-based systems. + Say Y if you want to use audio device on this SoCs. + endif #SND_SOC_QCOM diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 7972c9479ab0..0cdcbf367ef1 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -17,12 +17,14 @@ snd-soc-storm-objs := storm.o snd-soc-apq8016-sbc-objs := apq8016_sbc.o snd-soc-apq8096-objs := apq8096.o snd-soc-sdm845-objs := sdm845.o +snd-soc-sc7180-objs := sc7180.o snd-soc-qcom-common-objs := common.o
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_SDM845) += snd-soc-sdm845.o +obj-$(CONFIG_SND_SOC_SC7180) += snd-soc-sc7180.o obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o
#DSP lib diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c new file mode 100644 index 000000000000..7849376f63ba --- /dev/null +++ b/sound/soc/qcom/sc7180.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright (c) 2020, The Linux Foundation. All rights reserved. +// +// sc7180.c -- ALSA SoC Machine driver for SC7180 + +#include <dt-bindings/sound/sc7180-lpass.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/pcm_params.h> +#include <sound/soc.h> +#include <uapi/linux/input-event-codes.h> + +#include "../codecs/rt5682.h" +#include "common.h" +#include "lpass.h" + +#define DEFAULT_SAMPLE_RATE_48K 48000 +#define DEFAULT_MCLK_RATE 19200000 +#define RT5682_PLL1_FREQ (48000 * 512) + +struct sc7180_snd_data { + struct snd_soc_jack jack; + u32 pri_mi2s_clk_count; +}; + +static void sc7180_jack_free(struct snd_jack *jack) +{ + struct snd_soc_component *component = jack->private_data; + + snd_soc_component_set_jack(component, NULL, NULL); +} + +static int sc7180_headset_init(struct snd_soc_component *component) +{ + struct snd_soc_card *card = component->card; + struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card); + struct snd_jack *jack; + int rval; + + 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 (rval < 0) { + dev_err(card->dev, "Unable to add Headset Jack\n"); + return rval; + } + + jack = pdata->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 = sc7180_jack_free; + + rval = snd_soc_component_set_jack(component, + &pdata->jack, NULL); + if (rval != 0 && rval != -EOPNOTSUPP) { + dev_warn(card->dev, "Failed to set jack: %d\n", rval); + return rval; + } + + return 0; +} + +static struct snd_soc_aux_dev sc7180_headset_dev = { + .dlc = COMP_EMPTY(), + .init = sc7180_headset_init, +}; + +static int sc7180_snd_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card); + 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; + + switch (cpu_dai->id) { + case MI2S_PRIMARY: + if (++data->pri_mi2s_clk_count == 1) { + snd_soc_dai_set_sysclk(cpu_dai, + LPASS_MCLK0, + DEFAULT_MCLK_RATE, + SNDRV_PCM_STREAM_PLAYBACK); + } + + snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_CBS_CFS | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_I2S); + + /* Configure PLL1 for codec */ + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5682_PLL1_S_MCLK, + DEFAULT_MCLK_RATE, RT5682_PLL1_FREQ); + if (ret) { + dev_err(rtd->dev, "can't set codec pll: %d\n", ret); + return ret; + } + + /* Configure sysclk for codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, + RT5682_PLL1_FREQ, + SND_SOC_CLOCK_IN); + if (ret) + dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", + ret); + + break; + case MI2S_SECONDARY: + break; + default: + dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, + cpu_dai->id); + return -EINVAL; + } + return 0; +} + +static void sc7180_snd_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + + switch (cpu_dai->id) { + case MI2S_PRIMARY: + if (--data->pri_mi2s_clk_count == 0) { + snd_soc_dai_set_sysclk(cpu_dai, + LPASS_MCLK0, + 0, + SNDRV_PCM_STREAM_PLAYBACK); + } + break; + case MI2S_SECONDARY: + break; + default: + dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__, + cpu_dai->id); + break; + } +} + +static const struct snd_soc_ops sc7180_ops = { + .startup = sc7180_snd_startup, + .shutdown = sc7180_snd_shutdown, +}; + +static const struct snd_soc_dapm_widget sc7180_snd_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static struct snd_soc_card sc7180_card = { + .owner = THIS_MODULE, + .aux_dev = &sc7180_headset_dev, + .num_aux_devs = 1, + .dapm_widgets = sc7180_snd_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets), +}; + +static int sc7180_parse_aux_of(struct device *dev) +{ + sc7180_headset_dev.dlc.of_node = of_parse_phandle( + dev->of_node, "aux-dev", 0); + + if (!sc7180_headset_dev.dlc.of_node) + return -EINVAL; + return 0; +} + +static void sc7180_add_ops(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *link; + int i; + + for_each_card_prelinks(card, i, link) + link->ops = &sc7180_ops; +} + +static int sc7180_snd_platform_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &sc7180_card; + struct sc7180_snd_data *data; + struct device *dev = &pdev->dev; + int ret; + + /* Allocate the private data */ + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + card->dev = dev; + + ret = qcom_snd_parse_of(card); + if (ret) { + dev_err(dev, "Error parsing OF data\n"); + return ret; + } + + snd_soc_card_set_drvdata(card, data); + + sc7180_add_ops(card); + + ret = sc7180_parse_aux_of(dev); + if (ret) { + dev_err(dev, "Failed to parse OF for jack device\n"); + return ret; + } + + return devm_snd_soc_register_card(dev, card); +} + +static const struct of_device_id sc7180_snd_device_id[] = { + { .compatible = "qcom,sc7180-sndcard" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sc7180_snd_device_id); + +static struct platform_driver sc7180_snd_driver = { + .probe = sc7180_snd_platform_probe, + .driver = { + .name = "msm-snd-sc7180", + .of_match_table = sc7180_snd_device_id, + }, +}; +module_platform_driver(sc7180_snd_driver); + +MODULE_DESCRIPTION("sc7180 ASoC Machine Driver"); +MODULE_LICENSE("GPL v2");
On Wed, Aug 26, 2020 at 7:05 PM Cheng-Yi Chiang cychiang@chromium.org wrote:
From: Ajit Pandey ajitp@codeaurora.org
Add new driver to register sound card on sc7180 trogdor board and do the required configuration for lpass cpu dai and external codecs connected over MI2S interfaces.
Signed-off-by: Ajit Pandey ajitp@codeaurora.org Signed-off-by: Cheng-Yi Chiang cychiang@chromium.org Reviewed-by: Tzung-Bi Shih tzungbi@google.com
sound/soc/qcom/Kconfig | 12 ++ sound/soc/qcom/Makefile | 2 + sound/soc/qcom/sc7180.c | 244 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 sound/soc/qcom/sc7180.c
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index a607ace8b089..63678b746299 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -116,4 +116,16 @@ config SND_SOC_SDM845 SDM845 SoC-based systems. Say Y if you want to use audio device on this SoCs.
+config SND_SOC_SC7180
tristate "SoC Machine driver for SC7180 boards"
depends on SND_SOC_QCOM
select SND_SOC_QCOM_COMMON
select SND_SOC_LPASS_SC7180
select SND_SOC_MAX98357A
select SND_SOC_RT5682
help
To add support for audio on Qualcomm Technologies Inc.
SC7180 SoC-based systems.
Say Y if you want to use audio device on this SoCs.
endif #SND_SOC_QCOM diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 7972c9479ab0..0cdcbf367ef1 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -17,12 +17,14 @@ snd-soc-storm-objs := storm.o snd-soc-apq8016-sbc-objs := apq8016_sbc.o snd-soc-apq8096-objs := apq8096.o snd-soc-sdm845-objs := sdm845.o +snd-soc-sc7180-objs := sc7180.o snd-soc-qcom-common-objs := common.o
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_SDM845) += snd-soc-sdm845.o +obj-$(CONFIG_SND_SOC_SC7180) += snd-soc-sc7180.o obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o
#DSP lib diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c new file mode 100644 index 000000000000..7849376f63ba --- /dev/null +++ b/sound/soc/qcom/sc7180.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright (c) 2020, The Linux Foundation. All rights reserved. +// +// sc7180.c -- ALSA SoC Machine driver for SC7180
+#include <dt-bindings/sound/sc7180-lpass.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/pcm_params.h> +#include <sound/soc.h> +#include <uapi/linux/input-event-codes.h>
+#include "../codecs/rt5682.h" +#include "common.h" +#include "lpass.h"
+#define DEFAULT_SAMPLE_RATE_48K 48000 +#define DEFAULT_MCLK_RATE 19200000 +#define RT5682_PLL1_FREQ (48000 * 512)
+struct sc7180_snd_data {
struct snd_soc_jack jack;
u32 pri_mi2s_clk_count;
+};
+static void sc7180_jack_free(struct snd_jack *jack) +{
struct snd_soc_component *component = jack->private_data;
snd_soc_component_set_jack(component, NULL, NULL);
+}
+static int sc7180_headset_init(struct snd_soc_component *component) +{
struct snd_soc_card *card = component->card;
struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card);
struct snd_jack *jack;
int rval;
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 (rval < 0) {
dev_err(card->dev, "Unable to add Headset Jack\n");
return rval;
}
jack = pdata->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 = sc7180_jack_free;
rval = snd_soc_component_set_jack(component,
&pdata->jack, NULL);
if (rval != 0 && rval != -EOPNOTSUPP) {
dev_warn(card->dev, "Failed to set jack: %d\n", rval);
return rval;
}
return 0;
+}
+static struct snd_soc_aux_dev sc7180_headset_dev = {
.dlc = COMP_EMPTY(),
.init = sc7180_headset_init,
+};
+static int sc7180_snd_startup(struct snd_pcm_substream *substream) +{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *card = rtd->card;
struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
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;
switch (cpu_dai->id) {
case MI2S_PRIMARY:
if (++data->pri_mi2s_clk_count == 1) {
snd_soc_dai_set_sysclk(cpu_dai,
LPASS_MCLK0,
DEFAULT_MCLK_RATE,
SNDRV_PCM_STREAM_PLAYBACK);
}
snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_CBS_CFS |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_I2S);
/* Configure PLL1 for codec */
ret = snd_soc_dai_set_pll(codec_dai, 0, RT5682_PLL1_S_MCLK,
DEFAULT_MCLK_RATE, RT5682_PLL1_FREQ);
if (ret) {
dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
return ret;
}
/* Configure sysclk for codec */
ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1,
RT5682_PLL1_FREQ,
SND_SOC_CLOCK_IN);
if (ret)
dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n",
ret);
break;
case MI2S_SECONDARY:
break;
default:
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
cpu_dai->id);
return -EINVAL;
}
return 0;
+}
+static void sc7180_snd_shutdown(struct snd_pcm_substream *substream) +{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *card = rtd->card;
struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
switch (cpu_dai->id) {
case MI2S_PRIMARY:
if (--data->pri_mi2s_clk_count == 0) {
snd_soc_dai_set_sysclk(cpu_dai,
LPASS_MCLK0,
0,
SNDRV_PCM_STREAM_PLAYBACK);
}
break;
case MI2S_SECONDARY:
break;
default:
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
cpu_dai->id);
break;
}
+}
+static const struct snd_soc_ops sc7180_ops = {
.startup = sc7180_snd_startup,
.shutdown = sc7180_snd_shutdown,
+};
+static const struct snd_soc_dapm_widget sc7180_snd_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+static struct snd_soc_card sc7180_card = {
.owner = THIS_MODULE,
.aux_dev = &sc7180_headset_dev,
.num_aux_devs = 1,
.dapm_widgets = sc7180_snd_widgets,
.num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets),
+};
+static int sc7180_parse_aux_of(struct device *dev) +{
sc7180_headset_dev.dlc.of_node = of_parse_phandle(
dev->of_node, "aux-dev", 0);
if (!sc7180_headset_dev.dlc.of_node)
return -EINVAL;
return 0;
+}
+static void sc7180_add_ops(struct snd_soc_card *card) +{
struct snd_soc_dai_link *link;
int i;
for_each_card_prelinks(card, i, link)
link->ops = &sc7180_ops;
+}
+static int sc7180_snd_platform_probe(struct platform_device *pdev) +{
struct snd_soc_card *card = &sc7180_card;
struct sc7180_snd_data *data;
struct device *dev = &pdev->dev;
int ret;
/* Allocate the private data */
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
card->dev = dev;
ret = qcom_snd_parse_of(card);
if (ret) {
dev_err(dev, "Error parsing OF data\n");
return ret;
}
snd_soc_card_set_drvdata(card, data);
sc7180_add_ops(card);
ret = sc7180_parse_aux_of(dev);
if (ret) {
dev_err(dev, "Failed to parse OF for jack device\n");
return ret;
}
return devm_snd_soc_register_card(dev, card);
+}
+static const struct of_device_id sc7180_snd_device_id[] = {
{ .compatible = "qcom,sc7180-sndcard" },
{},
+}; +MODULE_DEVICE_TABLE(of, sc7180_snd_device_id);
+static struct platform_driver sc7180_snd_driver = {
.probe = sc7180_snd_platform_probe,
.driver = {
.name = "msm-snd-sc7180",
.of_match_table = sc7180_snd_device_id,
},
+}; +module_platform_driver(sc7180_snd_driver);
+MODULE_DESCRIPTION("sc7180 ASoC Machine Driver");
+MODULE_LICENSE("GPL v2");
2.28.0.297.g1956fa8f8d-goog
Hi Mark, Since the LPASS driver has been merged, this driver is ready to go. I noticed that there is a relevant patch by Stephan recently changing how aud-dev is read from dts.
https://patchwork.kernel.org/patch/11737783/
If you plan to merge that one first, I can make changes based on that so the aux-dev can get its init function.
And FYI, there will be a follow-up patch on this machine driver for users to select between two different sound cards. The new sound card will pick up a GPIO from dts to toggle front mic / rear mic.
Please let me know if you have any concerns. Thanks!
On Wed, Aug 26, 2020 at 07:04:54PM +0800, Cheng-Yi Chiang wrote:
From: Ajit Pandey ajitp@codeaurora.org
Add new driver to register sound card on sc7180 trogdor board and do the required configuration for lpass cpu dai and external codecs connected over MI2S interfaces.
Signed-off-by: Ajit Pandey ajitp@codeaurora.org Signed-off-by: Cheng-Yi Chiang cychiang@chromium.org Reviewed-by: Tzung-Bi Shih tzungbi@google.com
sound/soc/qcom/Kconfig | 12 ++ sound/soc/qcom/Makefile | 2 + sound/soc/qcom/sc7180.c | 244 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 sound/soc/qcom/sc7180.c
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index a607ace8b089..63678b746299 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -116,4 +116,16 @@ config SND_SOC_SDM845 SDM845 SoC-based systems. Say Y if you want to use audio device on this SoCs.
+config SND_SOC_SC7180
- tristate "SoC Machine driver for SC7180 boards"
- depends on SND_SOC_QCOM
- select SND_SOC_QCOM_COMMON
- select SND_SOC_LPASS_SC7180
- select SND_SOC_MAX98357A
- select SND_SOC_RT5682
- help
To add support for audio on Qualcomm Technologies Inc.
SC7180 SoC-based systems.
Say Y if you want to use audio device on this SoCs.
endif #SND_SOC_QCOM diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 7972c9479ab0..0cdcbf367ef1 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -17,12 +17,14 @@ snd-soc-storm-objs := storm.o snd-soc-apq8016-sbc-objs := apq8016_sbc.o snd-soc-apq8096-objs := apq8096.o snd-soc-sdm845-objs := sdm845.o +snd-soc-sc7180-objs := sc7180.o snd-soc-qcom-common-objs := common.o
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_SDM845) += snd-soc-sdm845.o +obj-$(CONFIG_SND_SOC_SC7180) += snd-soc-sc7180.o obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o
#DSP lib diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c new file mode 100644 index 000000000000..7849376f63ba --- /dev/null +++ b/sound/soc/qcom/sc7180.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright (c) 2020, The Linux Foundation. All rights reserved. +// +// sc7180.c -- ALSA SoC Machine driver for SC7180
+#include <dt-bindings/sound/sc7180-lpass.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/pcm_params.h> +#include <sound/soc.h> +#include <uapi/linux/input-event-codes.h>
+#include "../codecs/rt5682.h" +#include "common.h" +#include "lpass.h"
+#define DEFAULT_SAMPLE_RATE_48K 48000 +#define DEFAULT_MCLK_RATE 19200000 +#define RT5682_PLL1_FREQ (48000 * 512)
+struct sc7180_snd_data {
- struct snd_soc_jack jack;
- u32 pri_mi2s_clk_count;
+};
+static void sc7180_jack_free(struct snd_jack *jack) +{
- struct snd_soc_component *component = jack->private_data;
- snd_soc_component_set_jack(component, NULL, NULL);
+}
+static int sc7180_headset_init(struct snd_soc_component *component) +{
- struct snd_soc_card *card = component->card;
- struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card);
- struct snd_jack *jack;
- int rval;
- 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 (rval < 0) {
dev_err(card->dev, "Unable to add Headset Jack\n");
return rval;
- }
- jack = pdata->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 = sc7180_jack_free;
- rval = snd_soc_component_set_jack(component,
&pdata->jack, NULL);
- if (rval != 0 && rval != -EOPNOTSUPP) {
dev_warn(card->dev, "Failed to set jack: %d\n", rval);
return rval;
- }
- return 0;
+}
+static struct snd_soc_aux_dev sc7180_headset_dev = {
- .dlc = COMP_EMPTY(),
- .init = sc7180_headset_init,
+};
[...]
+static struct snd_soc_card sc7180_card = {
- .owner = THIS_MODULE,
- .aux_dev = &sc7180_headset_dev,
- .num_aux_devs = 1,
- .dapm_widgets = sc7180_snd_widgets,
- .num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets),
+};
+static int sc7180_parse_aux_of(struct device *dev) +{
- sc7180_headset_dev.dlc.of_node = of_parse_phandle(
dev->of_node, "aux-dev", 0);
- if (!sc7180_headset_dev.dlc.of_node)
return -EINVAL;
- return 0;
+}
Thanks for noting that this conflicts with my patch set that adds the "aux-devs" property for the device tree [1], I didn't see this before.
The use of aux-dev in this patch looks a bit weird to me...
As I understand, the "auxiliary devices" of a sound card are intended to be used for components that should be probed even though they don't appear within one of the DAI links. Examples for that are especially analog amplifiers and other components that do not have digital audio input/output.
On the other hand, in this patch it seems to be just a way to mark the DAI component that will provide the headphone jack detection. In your example, the component that provides the headphone jack then appears both as DAI component and as auxiliary device:
aux-dev = <&alc5682>;
dai-link@0 { link-name = "MultiMedia0"; reg = <0>; cpu { sound-dai = <&lpass_cpu 0>; }; codec { sound-dai = <&alc5682 0>; }; };
Adding &alc5682 to snd_soc_card->aux_dev is kind of pointless in this case because it will already be probed as part of the DAI link.
The only thing you gain is that you have the init() callback which gives you the component that provides the headphone jack. But if someone wants to add an actual auxiliary device later (e.g. an analog amplifier), they would run into trouble...
I wonder if it would be better to just have some sort of phandle, e.g.
audio-jack = <&alc5682>;
but instead of creating an auxiliary device for this you would e.g. iterate over the list of components to find the one the phandle refers to.
Or maybe someone else can comment if using an auxiliary device for this does really make sense?
Thanks, Stephan
[1]: https://lore.kernel.org/alsa-devel/20200826095141.94017-1-stephan@gerhold.ne...
On Wed, Aug 26, 2020 at 10:48 PM Stephan Gerhold stephan@gerhold.net wrote:
On Wed, Aug 26, 2020 at 07:04:54PM +0800, Cheng-Yi Chiang wrote:
From: Ajit Pandey ajitp@codeaurora.org
Add new driver to register sound card on sc7180 trogdor board and do the required configuration for lpass cpu dai and external codecs connected over MI2S interfaces.
Signed-off-by: Ajit Pandey ajitp@codeaurora.org Signed-off-by: Cheng-Yi Chiang cychiang@chromium.org Reviewed-by: Tzung-Bi Shih tzungbi@google.com
sound/soc/qcom/Kconfig | 12 ++ sound/soc/qcom/Makefile | 2 + sound/soc/qcom/sc7180.c | 244 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 sound/soc/qcom/sc7180.c
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index a607ace8b089..63678b746299 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -116,4 +116,16 @@ config SND_SOC_SDM845 SDM845 SoC-based systems. Say Y if you want to use audio device on this SoCs.
+config SND_SOC_SC7180
tristate "SoC Machine driver for SC7180 boards"
depends on SND_SOC_QCOM
select SND_SOC_QCOM_COMMON
select SND_SOC_LPASS_SC7180
select SND_SOC_MAX98357A
select SND_SOC_RT5682
help
To add support for audio on Qualcomm Technologies Inc.
SC7180 SoC-based systems.
Say Y if you want to use audio device on this SoCs.
endif #SND_SOC_QCOM diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 7972c9479ab0..0cdcbf367ef1 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -17,12 +17,14 @@ snd-soc-storm-objs := storm.o snd-soc-apq8016-sbc-objs := apq8016_sbc.o snd-soc-apq8096-objs := apq8096.o snd-soc-sdm845-objs := sdm845.o +snd-soc-sc7180-objs := sc7180.o snd-soc-qcom-common-objs := common.o
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_SDM845) += snd-soc-sdm845.o +obj-$(CONFIG_SND_SOC_SC7180) += snd-soc-sc7180.o obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o
#DSP lib diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c new file mode 100644 index 000000000000..7849376f63ba --- /dev/null +++ b/sound/soc/qcom/sc7180.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright (c) 2020, The Linux Foundation. All rights reserved. +// +// sc7180.c -- ALSA SoC Machine driver for SC7180
+#include <dt-bindings/sound/sc7180-lpass.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/pcm_params.h> +#include <sound/soc.h> +#include <uapi/linux/input-event-codes.h>
+#include "../codecs/rt5682.h" +#include "common.h" +#include "lpass.h"
+#define DEFAULT_SAMPLE_RATE_48K 48000 +#define DEFAULT_MCLK_RATE 19200000 +#define RT5682_PLL1_FREQ (48000 * 512)
+struct sc7180_snd_data {
struct snd_soc_jack jack;
u32 pri_mi2s_clk_count;
+};
+static void sc7180_jack_free(struct snd_jack *jack) +{
struct snd_soc_component *component = jack->private_data;
snd_soc_component_set_jack(component, NULL, NULL);
+}
+static int sc7180_headset_init(struct snd_soc_component *component) +{
struct snd_soc_card *card = component->card;
struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card);
struct snd_jack *jack;
int rval;
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 (rval < 0) {
dev_err(card->dev, "Unable to add Headset Jack\n");
return rval;
}
jack = pdata->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 = sc7180_jack_free;
rval = snd_soc_component_set_jack(component,
&pdata->jack, NULL);
if (rval != 0 && rval != -EOPNOTSUPP) {
dev_warn(card->dev, "Failed to set jack: %d\n", rval);
return rval;
}
return 0;
+}
+static struct snd_soc_aux_dev sc7180_headset_dev = {
.dlc = COMP_EMPTY(),
.init = sc7180_headset_init,
+};
[...]
+static struct snd_soc_card sc7180_card = {
.owner = THIS_MODULE,
.aux_dev = &sc7180_headset_dev,
.num_aux_devs = 1,
.dapm_widgets = sc7180_snd_widgets,
.num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets),
+};
+static int sc7180_parse_aux_of(struct device *dev) +{
sc7180_headset_dev.dlc.of_node = of_parse_phandle(
dev->of_node, "aux-dev", 0);
if (!sc7180_headset_dev.dlc.of_node)
return -EINVAL;
return 0;
+}
Thanks for noting that this conflicts with my patch set that adds the "aux-devs" property for the device tree [1], I didn't see this before.
The use of aux-dev in this patch looks a bit weird to me...
As I understand, the "auxiliary devices" of a sound card are intended to be used for components that should be probed even though they don't appear within one of the DAI links. Examples for that are especially analog amplifiers and other components that do not have digital audio input/output.
On the other hand, in this patch it seems to be just a way to mark the DAI component that will provide the headphone jack detection. In your example, the component that provides the headphone jack then appears both as DAI component and as auxiliary device:
aux-dev = <&alc5682>; dai-link@0 { link-name = "MultiMedia0"; reg = <0>; cpu { sound-dai = <&lpass_cpu 0>; }; codec { sound-dai = <&alc5682 0>; }; };
Adding &alc5682 to snd_soc_card->aux_dev is kind of pointless in this case because it will already be probed as part of the DAI link.
The only thing you gain is that you have the init() callback which gives you the component that provides the headphone jack. But if someone wants to add an actual auxiliary device later (e.g. an analog amplifier), they would run into trouble...
I wonder if it would be better to just have some sort of phandle, e.g.
audio-jack = <&alc5682>;
but instead of creating an auxiliary device for this you would e.g. iterate over the list of components to find the one the phandle refers to.
Hi Stephan, I can try this approach. Thanks for the suggestion. I think this would also be better than the previous approach that the machine driver set init for jack on certain DAI.
Or maybe someone else can comment if using an auxiliary device for this does really make sense?
Thanks, Stephan
Hi,
On Wed, Aug 26, 2020 at 4:05 AM Cheng-Yi Chiang cychiang@chromium.org wrote:
+config SND_SOC_SC7180
tristate "SoC Machine driver for SC7180 boards"
depends on SND_SOC_QCOM
select SND_SOC_QCOM_COMMON
select SND_SOC_LPASS_SC7180
select SND_SOC_MAX98357A
select SND_SOC_RT5682
I haven't done any significant testing / review of your patch (I'm mostly sound-clueless), but I believe that the above needs to be "select SND_SOC_RT5682_I2C" atop the current top of the sound tree. When I fix that I can confirm that I see the rt5682 probe on sc7180-trogdor with Rob Clark's dts patch.
-Doug
+config SND_SOC_SC7180
tristate "SoC Machine driver for SC7180 boards"
depends on SND_SOC_QCOM
this depends is probably not necessary, the code is already in an if case.
select SND_SOC_QCOM_COMMON
select SND_SOC_LPASS_SC7180
select SND_SOC_MAX98357A
select SND_SOC_RT5682
I haven't done any significant testing / review of your patch (I'm mostly sound-clueless), but I believe that the above needs to be "select SND_SOC_RT5682_I2C" atop the current top of the sound tree. When I fix that I can confirm that I see the rt5682 probe on sc7180-trogdor with Rob Clark's dts patch.
Ack, no one should select SND_SOC_RT5682 directly in machine drivers. now that the code is split between I2C and SoundWire parts.
There should probably be a depends on I2C as well?
participants (5)
-
Cheng-Yi Chiang
-
Doug Anderson
-
Pierre-Louis Bossart
-
Rob Herring
-
Stephan Gerhold