mirror of
https://github.com/Fishwaldo/build.git
synced 2025-07-23 13:29:33 +00:00
* Attach Meson64 to mainline with a bunch of patches. Tested, but need further work. * Enable DVFS on N2 which sometimes works, sometime doesn't, cleanup * Enable beta targets for Meson64 kernel family * Bump with version
1036 lines
27 KiB
Diff
1036 lines
27 KiB
Diff
From aad1cb3b4cd485c983fdeabed0954115e8409e7c Mon Sep 17 00:00:00 2001
|
|
From: Jerome Brunet <jbrunet@baylibre.com>
|
|
Date: Tue, 22 Oct 2019 18:09:18 +0200
|
|
Subject: [PATCH 54/94] WIP: ASoC: meson: extract utils from axg sound card
|
|
|
|
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
|
|
---
|
|
sound/soc/meson/Kconfig | 4 +
|
|
sound/soc/meson/Makefile | 2 +
|
|
sound/soc/meson/axg-card.c | 403 +++----------------------------------
|
|
sound/soc/meson/meson-card-utils.c | 386 +++++++++++++++++++++++++++++++++++
|
|
sound/soc/meson/meson-card.h | 53 +++++
|
|
5 files changed, 472 insertions(+), 376 deletions(-)
|
|
create mode 100644 sound/soc/meson/meson-card-utils.c
|
|
create mode 100644 sound/soc/meson/meson-card.h
|
|
|
|
diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
|
|
index 8028d6b..2beea1f 100644
|
|
--- a/sound/soc/meson/Kconfig
|
|
+++ b/sound/soc/meson/Kconfig
|
|
@@ -91,6 +91,7 @@ config SND_MESON_AXG_TDMOUT
|
|
config SND_MESON_AXG_SOUND_CARD
|
|
tristate "Amlogic AXG Sound Card Support"
|
|
select SND_MESON_AXG_TDM_INTERFACE
|
|
+ select SND_MESON_CARD_UTILS
|
|
imply SND_MESON_AXG_FRDDR
|
|
imply SND_MESON_AXG_TODDR
|
|
imply SND_MESON_AXG_TDMIN
|
|
@@ -126,6 +127,9 @@ config SND_MESON_AXG_PDM
|
|
Select Y or M to add support for PDM input embedded
|
|
in the Amlogic AXG SoC family
|
|
|
|
+config SND_MESON_CARD_UTILS
|
|
+ tristate
|
|
+
|
|
config SND_MESON_CODEC_GLUE
|
|
tristate
|
|
|
|
diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
|
|
index 5309af8..cce27b2 100644
|
|
--- a/sound/soc/meson/Makefile
|
|
+++ b/sound/soc/meson/Makefile
|
|
@@ -17,6 +17,7 @@ snd-soc-meson-axg-sound-card-objs := axg-card.o
|
|
snd-soc-meson-axg-spdifin-objs := axg-spdifin.o
|
|
snd-soc-meson-axg-spdifout-objs := axg-spdifout.o
|
|
snd-soc-meson-axg-pdm-objs := axg-pdm.o
|
|
+snd-soc-meson-card-utils-objs := meson-card-utils.o
|
|
snd-soc-meson-codec-glue-objs := meson-codec-glue.o
|
|
snd-soc-meson-g12a-toacodec-objs := g12a-toacodec.o
|
|
snd-soc-meson-g12a-tohdmitx-objs := g12a-tohdmitx.o
|
|
@@ -39,6 +40,7 @@ obj-$(CONFIG_SND_MESON_AXG_SOUND_CARD) += snd-soc-meson-axg-sound-card.o
|
|
obj-$(CONFIG_SND_MESON_AXG_SPDIFIN) += snd-soc-meson-axg-spdifin.o
|
|
obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o
|
|
obj-$(CONFIG_SND_MESON_AXG_PDM) += snd-soc-meson-axg-pdm.o
|
|
+obj-$(CONFIG_SND_MESON_CARD_UTILS) += snd-soc-meson-card-utils.o
|
|
obj-$(CONFIG_SND_MESON_CODEC_GLUE) += snd-soc-meson-codec-glue.o
|
|
obj-$(CONFIG_SND_MESON_G12A_TOACODEC) += snd-soc-meson-g12a-toacodec.o
|
|
obj-$(CONFIG_SND_MESON_G12A_TOHDMITX) += snd-soc-meson-g12a-tohdmitx.o
|
|
diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c
|
|
index 3e4f045..d9f3c08 100644
|
|
--- a/sound/soc/meson/axg-card.c
|
|
+++ b/sound/soc/meson/axg-card.c
|
|
@@ -9,11 +9,7 @@
|
|
#include <sound/soc-dai.h>
|
|
|
|
#include "axg-tdm.h"
|
|
-
|
|
-struct axg_card {
|
|
- struct snd_soc_card card;
|
|
- void **link_data;
|
|
-};
|
|
+#include "meson-card.h"
|
|
|
|
struct axg_dai_link_tdm_mask {
|
|
u32 tx;
|
|
@@ -41,161 +37,15 @@ static const struct snd_soc_pcm_stream codec_params = {
|
|
.channels_max = 8,
|
|
};
|
|
|
|
-#define PREFIX "amlogic,"
|
|
-
|
|
-static int axg_card_reallocate_links(struct axg_card *priv,
|
|
- unsigned int num_links)
|
|
-{
|
|
- struct snd_soc_dai_link *links;
|
|
- void **ldata;
|
|
-
|
|
- links = krealloc(priv->card.dai_link,
|
|
- num_links * sizeof(*priv->card.dai_link),
|
|
- GFP_KERNEL | __GFP_ZERO);
|
|
- ldata = krealloc(priv->link_data,
|
|
- num_links * sizeof(*priv->link_data),
|
|
- GFP_KERNEL | __GFP_ZERO);
|
|
-
|
|
- if (!links || !ldata) {
|
|
- dev_err(priv->card.dev, "failed to allocate links\n");
|
|
- return -ENOMEM;
|
|
- }
|
|
-
|
|
- priv->card.dai_link = links;
|
|
- priv->link_data = ldata;
|
|
- priv->card.num_links = num_links;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int axg_card_parse_dai(struct snd_soc_card *card,
|
|
- struct device_node *node,
|
|
- struct device_node **dai_of_node,
|
|
- const char **dai_name)
|
|
-{
|
|
- struct of_phandle_args args;
|
|
- int ret;
|
|
-
|
|
- if (!dai_name || !dai_of_node || !node)
|
|
- return -EINVAL;
|
|
-
|
|
- ret = of_parse_phandle_with_args(node, "sound-dai",
|
|
- "#sound-dai-cells", 0, &args);
|
|
- if (ret) {
|
|
- if (ret != -EPROBE_DEFER)
|
|
- dev_err(card->dev, "can't parse dai %d\n", ret);
|
|
- return ret;
|
|
- }
|
|
- *dai_of_node = args.np;
|
|
-
|
|
- return snd_soc_get_dai_name(&args, dai_name);
|
|
-}
|
|
-
|
|
-static int axg_card_set_link_name(struct snd_soc_card *card,
|
|
- struct snd_soc_dai_link *link,
|
|
- struct device_node *node,
|
|
- const char *prefix)
|
|
-{
|
|
- char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s",
|
|
- prefix, node->full_name);
|
|
- if (!name)
|
|
- return -ENOMEM;
|
|
-
|
|
- link->name = name;
|
|
- link->stream_name = name;
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static void axg_card_clean_references(struct axg_card *priv)
|
|
-{
|
|
- struct snd_soc_card *card = &priv->card;
|
|
- struct snd_soc_dai_link *link;
|
|
- struct snd_soc_dai_link_component *codec;
|
|
- struct snd_soc_aux_dev *aux;
|
|
- int i, j;
|
|
-
|
|
- if (card->dai_link) {
|
|
- for_each_card_prelinks(card, i, link) {
|
|
- if (link->cpus)
|
|
- of_node_put(link->cpus->of_node);
|
|
- for_each_link_codecs(link, j, codec)
|
|
- of_node_put(codec->of_node);
|
|
- }
|
|
- }
|
|
-
|
|
- if (card->aux_dev) {
|
|
- for_each_card_pre_auxs(card, i, aux)
|
|
- of_node_put(aux->dlc.of_node);
|
|
- }
|
|
-
|
|
- kfree(card->dai_link);
|
|
- kfree(priv->link_data);
|
|
-}
|
|
-
|
|
-static int axg_card_add_aux_devices(struct snd_soc_card *card)
|
|
-{
|
|
- struct device_node *node = card->dev->of_node;
|
|
- struct snd_soc_aux_dev *aux;
|
|
- int num, i;
|
|
-
|
|
- num = of_count_phandle_with_args(node, "audio-aux-devs", NULL);
|
|
- if (num == -ENOENT) {
|
|
- /*
|
|
- * It is ok to have no auxiliary devices but for this card it
|
|
- * is a strange situtation. Let's warn the about it.
|
|
- */
|
|
- dev_warn(card->dev, "card has no auxiliary devices\n");
|
|
- return 0;
|
|
- } else if (num < 0) {
|
|
- dev_err(card->dev, "error getting auxiliary devices: %d\n",
|
|
- num);
|
|
- return num;
|
|
- }
|
|
-
|
|
- aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL);
|
|
- if (!aux)
|
|
- return -ENOMEM;
|
|
- card->aux_dev = aux;
|
|
- card->num_aux_devs = num;
|
|
-
|
|
- for_each_card_pre_auxs(card, i, aux) {
|
|
- aux->dlc.of_node =
|
|
- of_parse_phandle(node, "audio-aux-devs", i);
|
|
- if (!aux->dlc.of_node)
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
static int axg_card_tdm_be_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *params)
|
|
{
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
- struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card);
|
|
+ struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
|
|
struct axg_dai_link_tdm_data *be =
|
|
(struct axg_dai_link_tdm_data *)priv->link_data[rtd->num];
|
|
- struct snd_soc_dai *codec_dai;
|
|
- unsigned int mclk;
|
|
- int ret, i;
|
|
-
|
|
- if (be->mclk_fs) {
|
|
- mclk = params_rate(params) * be->mclk_fs;
|
|
-
|
|
- for_each_rtd_codec_dai(rtd, i, codec_dai) {
|
|
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
|
|
- SND_SOC_CLOCK_IN);
|
|
- if (ret && ret != -ENOTSUPP)
|
|
- return ret;
|
|
- }
|
|
-
|
|
- ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk,
|
|
- SND_SOC_CLOCK_OUT);
|
|
- if (ret && ret != -ENOTSUPP)
|
|
- return ret;
|
|
- }
|
|
|
|
- return 0;
|
|
+ return meson_card_i2s_set_sysclk(substream, params, be->mclk_fs);
|
|
}
|
|
|
|
static const struct snd_soc_ops axg_card_tdm_be_ops = {
|
|
@@ -204,7 +54,7 @@ static const struct snd_soc_ops axg_card_tdm_be_ops = {
|
|
|
|
static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
- struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card);
|
|
+ struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
|
|
struct axg_dai_link_tdm_data *be =
|
|
(struct axg_dai_link_tdm_data *)priv->link_data[rtd->num];
|
|
struct snd_soc_dai *codec_dai;
|
|
@@ -234,7 +84,7 @@ static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|
|
|
static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
- struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card);
|
|
+ struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
|
|
struct axg_dai_link_tdm_data *be =
|
|
(struct axg_dai_link_tdm_data *)priv->link_data[rtd->num];
|
|
int ret;
|
|
@@ -253,14 +103,14 @@ static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd)
|
|
static int axg_card_add_tdm_loopback(struct snd_soc_card *card,
|
|
int *index)
|
|
{
|
|
- struct axg_card *priv = snd_soc_card_get_drvdata(card);
|
|
+ struct meson_card *priv = snd_soc_card_get_drvdata(card);
|
|
struct snd_soc_dai_link *pad = &card->dai_link[*index];
|
|
struct snd_soc_dai_link *lb;
|
|
struct snd_soc_dai_link_component *dlc;
|
|
int ret;
|
|
|
|
/* extend links */
|
|
- ret = axg_card_reallocate_links(priv, card->num_links + 1);
|
|
+ ret = meson_card_reallocate_links(card, card->num_links + 1);
|
|
if (ret)
|
|
return ret;
|
|
|
|
@@ -304,32 +154,6 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card,
|
|
return 0;
|
|
}
|
|
|
|
-static unsigned int axg_card_parse_daifmt(struct device_node *node,
|
|
- struct device_node *cpu_node)
|
|
-{
|
|
- struct device_node *bitclkmaster = NULL;
|
|
- struct device_node *framemaster = NULL;
|
|
- unsigned int daifmt;
|
|
-
|
|
- daifmt = snd_soc_of_parse_daifmt(node, PREFIX,
|
|
- &bitclkmaster, &framemaster);
|
|
- daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
|
|
-
|
|
- /* If no master is provided, default to cpu master */
|
|
- if (!bitclkmaster || bitclkmaster == cpu_node) {
|
|
- daifmt |= (!framemaster || framemaster == cpu_node) ?
|
|
- SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM;
|
|
- } else {
|
|
- daifmt |= (!framemaster || framemaster == cpu_node) ?
|
|
- SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM;
|
|
- }
|
|
-
|
|
- of_node_put(bitclkmaster);
|
|
- of_node_put(framemaster);
|
|
-
|
|
- return daifmt;
|
|
-}
|
|
-
|
|
static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card,
|
|
struct snd_soc_dai_link *link,
|
|
struct device_node *node,
|
|
@@ -424,7 +248,7 @@ static int axg_card_parse_tdm(struct snd_soc_card *card,
|
|
struct device_node *node,
|
|
int *index)
|
|
{
|
|
- struct axg_card *priv = snd_soc_card_get_drvdata(card);
|
|
+ struct meson_card *priv = snd_soc_card_get_drvdata(card);
|
|
struct snd_soc_dai_link *link = &card->dai_link[*index];
|
|
struct axg_dai_link_tdm_data *be;
|
|
int ret;
|
|
@@ -438,7 +262,7 @@ static int axg_card_parse_tdm(struct snd_soc_card *card,
|
|
/* Setup tdm link */
|
|
link->ops = &axg_card_tdm_be_ops;
|
|
link->init = axg_card_tdm_dai_init;
|
|
- link->dai_fmt = axg_card_parse_daifmt(node, link->cpus->of_node);
|
|
+ link->dai_fmt = meson_card_parse_daifmt(node, link->cpus->of_node);
|
|
|
|
of_property_read_u32(node, "mclk-fs", &be->mclk_fs);
|
|
|
|
@@ -462,98 +286,25 @@ static int axg_card_parse_tdm(struct snd_soc_card *card,
|
|
return 0;
|
|
}
|
|
|
|
-static int axg_card_set_be_link(struct snd_soc_card *card,
|
|
- struct snd_soc_dai_link *link,
|
|
- struct device_node *node)
|
|
-{
|
|
- struct snd_soc_dai_link_component *codec;
|
|
- struct device_node *np;
|
|
- int ret, num_codecs;
|
|
-
|
|
- link->no_pcm = 1;
|
|
- link->dpcm_playback = 1;
|
|
- link->dpcm_capture = 1;
|
|
-
|
|
- num_codecs = of_get_child_count(node);
|
|
- if (!num_codecs) {
|
|
- dev_err(card->dev, "be link %s has no codec\n",
|
|
- node->full_name);
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL);
|
|
- if (!codec)
|
|
- return -ENOMEM;
|
|
-
|
|
- link->codecs = codec;
|
|
- link->num_codecs = num_codecs;
|
|
-
|
|
- for_each_child_of_node(node, np) {
|
|
- ret = axg_card_parse_dai(card, np, &codec->of_node,
|
|
- &codec->dai_name);
|
|
- if (ret) {
|
|
- of_node_put(np);
|
|
- return ret;
|
|
- }
|
|
-
|
|
- codec++;
|
|
- }
|
|
-
|
|
- ret = axg_card_set_link_name(card, link, node, "be");
|
|
- if (ret)
|
|
- dev_err(card->dev, "error setting %pOFn link name\n", np);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int axg_card_set_fe_link(struct snd_soc_card *card,
|
|
- struct snd_soc_dai_link *link,
|
|
- struct device_node *node,
|
|
- bool is_playback)
|
|
-{
|
|
- struct snd_soc_dai_link_component *codec;
|
|
-
|
|
- codec = devm_kzalloc(card->dev, sizeof(*codec), GFP_KERNEL);
|
|
- if (!codec)
|
|
- return -ENOMEM;
|
|
-
|
|
- link->codecs = codec;
|
|
- link->num_codecs = 1;
|
|
-
|
|
- link->dynamic = 1;
|
|
- link->dpcm_merged_format = 1;
|
|
- link->dpcm_merged_chan = 1;
|
|
- link->dpcm_merged_rate = 1;
|
|
- link->codecs->dai_name = "snd-soc-dummy-dai";
|
|
- link->codecs->name = "snd-soc-dummy";
|
|
-
|
|
- if (is_playback)
|
|
- link->dpcm_playback = 1;
|
|
- else
|
|
- link->dpcm_capture = 1;
|
|
-
|
|
- return axg_card_set_link_name(card, link, node, "fe");
|
|
-}
|
|
-
|
|
static int axg_card_cpu_is_capture_fe(struct device_node *np)
|
|
{
|
|
- return of_device_is_compatible(np, PREFIX "axg-toddr");
|
|
+ return of_device_is_compatible(np, DT_PREFIX "axg-toddr");
|
|
}
|
|
|
|
static int axg_card_cpu_is_playback_fe(struct device_node *np)
|
|
{
|
|
- return of_device_is_compatible(np, PREFIX "axg-frddr");
|
|
+ return of_device_is_compatible(np, DT_PREFIX "axg-frddr");
|
|
}
|
|
|
|
static int axg_card_cpu_is_tdm_iface(struct device_node *np)
|
|
{
|
|
- return of_device_is_compatible(np, PREFIX "axg-tdm-iface");
|
|
+ return of_device_is_compatible(np, DT_PREFIX "axg-tdm-iface");
|
|
}
|
|
|
|
static int axg_card_cpu_is_codec(struct device_node *np)
|
|
{
|
|
- return of_device_is_compatible(np, PREFIX "g12a-tohdmitx") ||
|
|
- of_device_is_compatible(np, PREFIX "g12a-toacodec");
|
|
+ return of_device_is_compatible(np, DT_PREFIX "g12a-tohdmitx") ||
|
|
+ of_device_is_compatible(np, DT_PREFIX "g12a-toacodec");
|
|
}
|
|
|
|
static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
|
|
@@ -570,17 +321,17 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
|
|
dai_link->cpus = cpu;
|
|
dai_link->num_cpus = 1;
|
|
|
|
- ret = axg_card_parse_dai(card, np, &dai_link->cpus->of_node,
|
|
+ ret = meson_card_parse_dai(card, np, &dai_link->cpus->of_node,
|
|
&dai_link->cpus->dai_name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (axg_card_cpu_is_playback_fe(dai_link->cpus->of_node))
|
|
- ret = axg_card_set_fe_link(card, dai_link, np, true);
|
|
+ ret = meson_card_set_fe_link(card, dai_link, np, true);
|
|
else if (axg_card_cpu_is_capture_fe(dai_link->cpus->of_node))
|
|
- ret = axg_card_set_fe_link(card, dai_link, np, false);
|
|
+ ret = meson_card_set_fe_link(card, dai_link, np, false);
|
|
else
|
|
- ret = axg_card_set_be_link(card, dai_link, np);
|
|
+ ret = meson_card_set_be_link(card, dai_link, np);
|
|
|
|
if (ret)
|
|
return ret;
|
|
@@ -593,121 +344,21 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
|
|
return ret;
|
|
}
|
|
|
|
-static int axg_card_add_links(struct snd_soc_card *card)
|
|
-{
|
|
- struct axg_card *priv = snd_soc_card_get_drvdata(card);
|
|
- struct device_node *node = card->dev->of_node;
|
|
- struct device_node *np;
|
|
- int num, i, ret;
|
|
-
|
|
- num = of_get_child_count(node);
|
|
- if (!num) {
|
|
- dev_err(card->dev, "card has no links\n");
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- ret = axg_card_reallocate_links(priv, num);
|
|
- if (ret)
|
|
- return ret;
|
|
-
|
|
- i = 0;
|
|
- for_each_child_of_node(node, np) {
|
|
- ret = axg_card_add_link(card, np, &i);
|
|
- if (ret) {
|
|
- of_node_put(np);
|
|
- return ret;
|
|
- }
|
|
-
|
|
- i++;
|
|
- }
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int axg_card_parse_of_optional(struct snd_soc_card *card,
|
|
- const char *propname,
|
|
- int (*func)(struct snd_soc_card *c,
|
|
- const char *p))
|
|
-{
|
|
- /* If property is not provided, don't fail ... */
|
|
- if (!of_property_read_bool(card->dev->of_node, propname))
|
|
- return 0;
|
|
-
|
|
- /* ... but do fail if it is provided and the parsing fails */
|
|
- return func(card, propname);
|
|
-}
|
|
+static const struct meson_card_match_data axg_card_match_data = {
|
|
+ .add_link = axg_card_add_link,
|
|
+};
|
|
|
|
static const struct of_device_id axg_card_of_match[] = {
|
|
- { .compatible = "amlogic,axg-sound-card", },
|
|
- {}
|
|
+ {
|
|
+ .compatible = "amlogic,axg-sound-card",
|
|
+ .data = &axg_card_match_data,
|
|
+ }, {}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, axg_card_of_match);
|
|
|
|
-static int axg_card_probe(struct platform_device *pdev)
|
|
-{
|
|
- struct device *dev = &pdev->dev;
|
|
- struct axg_card *priv;
|
|
- int ret;
|
|
-
|
|
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
|
- if (!priv)
|
|
- return -ENOMEM;
|
|
-
|
|
- platform_set_drvdata(pdev, priv);
|
|
- snd_soc_card_set_drvdata(&priv->card, priv);
|
|
-
|
|
- priv->card.owner = THIS_MODULE;
|
|
- priv->card.dev = dev;
|
|
-
|
|
- ret = snd_soc_of_parse_card_name(&priv->card, "model");
|
|
- if (ret < 0)
|
|
- return ret;
|
|
-
|
|
- ret = axg_card_parse_of_optional(&priv->card, "audio-routing",
|
|
- snd_soc_of_parse_audio_routing);
|
|
- if (ret) {
|
|
- dev_err(dev, "error while parsing routing\n");
|
|
- return ret;
|
|
- }
|
|
-
|
|
- ret = axg_card_parse_of_optional(&priv->card, "audio-widgets",
|
|
- snd_soc_of_parse_audio_simple_widgets);
|
|
- if (ret) {
|
|
- dev_err(dev, "error while parsing widgets\n");
|
|
- return ret;
|
|
- }
|
|
-
|
|
- ret = axg_card_add_links(&priv->card);
|
|
- if (ret)
|
|
- goto out_err;
|
|
-
|
|
- ret = axg_card_add_aux_devices(&priv->card);
|
|
- if (ret)
|
|
- goto out_err;
|
|
-
|
|
- ret = devm_snd_soc_register_card(dev, &priv->card);
|
|
- if (ret)
|
|
- goto out_err;
|
|
-
|
|
- return 0;
|
|
-
|
|
-out_err:
|
|
- axg_card_clean_references(priv);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int axg_card_remove(struct platform_device *pdev)
|
|
-{
|
|
- struct axg_card *priv = platform_get_drvdata(pdev);
|
|
-
|
|
- axg_card_clean_references(priv);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
static struct platform_driver axg_card_pdrv = {
|
|
- .probe = axg_card_probe,
|
|
- .remove = axg_card_remove,
|
|
+ .probe = meson_card_probe,
|
|
+ .remove = meson_card_remove,
|
|
.driver = {
|
|
.name = "axg-sound-card",
|
|
.of_match_table = axg_card_of_match,
|
|
diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c
|
|
new file mode 100644
|
|
index 0000000..88250b6
|
|
--- /dev/null
|
|
+++ b/sound/soc/meson/meson-card-utils.c
|
|
@@ -0,0 +1,386 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+//
|
|
+// Copyright (c) 2019 BayLibre, SAS.
|
|
+// Author: Jerome Brunet <jbrunet@baylibre.com>
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <sound/soc.h>
|
|
+
|
|
+#include "meson-card.h"
|
|
+
|
|
+int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream,
|
|
+ struct snd_pcm_hw_params *params,
|
|
+ unsigned int mclk_fs)
|
|
+{
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
+ struct snd_soc_dai *codec_dai;
|
|
+ unsigned int mclk;
|
|
+ int ret, i;
|
|
+
|
|
+ if (!mclk_fs)
|
|
+ return 0;
|
|
+
|
|
+ mclk = params_rate(params) * mclk_fs;
|
|
+
|
|
+ for_each_rtd_codec_dai(rtd, i, codec_dai) {
|
|
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
|
|
+ SND_SOC_CLOCK_IN);
|
|
+ if (ret && ret != -ENOTSUPP)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk,
|
|
+ SND_SOC_CLOCK_OUT);
|
|
+ if (ret && ret != -ENOTSUPP)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(meson_card_i2s_set_sysclk);
|
|
+
|
|
+int meson_card_reallocate_links(struct snd_soc_card *card,
|
|
+ unsigned int num_links)
|
|
+{
|
|
+ struct meson_card *priv = snd_soc_card_get_drvdata(card);
|
|
+ struct snd_soc_dai_link *links;
|
|
+ void **ldata;
|
|
+
|
|
+ links = krealloc(priv->card.dai_link,
|
|
+ num_links * sizeof(*priv->card.dai_link),
|
|
+ GFP_KERNEL | __GFP_ZERO);
|
|
+ ldata = krealloc(priv->link_data,
|
|
+ num_links * sizeof(*priv->link_data),
|
|
+ GFP_KERNEL | __GFP_ZERO);
|
|
+
|
|
+ if (!links || !ldata) {
|
|
+ dev_err(priv->card.dev, "failed to allocate links\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ priv->card.dai_link = links;
|
|
+ priv->link_data = ldata;
|
|
+ priv->card.num_links = num_links;
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(meson_card_reallocate_links);
|
|
+
|
|
+int meson_card_parse_dai(struct snd_soc_card *card,
|
|
+ struct device_node *node,
|
|
+ struct device_node **dai_of_node,
|
|
+ const char **dai_name)
|
|
+{
|
|
+ struct of_phandle_args args;
|
|
+ int ret;
|
|
+
|
|
+ if (!dai_name || !dai_of_node || !node)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = of_parse_phandle_with_args(node, "sound-dai",
|
|
+ "#sound-dai-cells", 0, &args);
|
|
+ if (ret) {
|
|
+ if (ret != -EPROBE_DEFER)
|
|
+ dev_err(card->dev, "can't parse dai %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ *dai_of_node = args.np;
|
|
+
|
|
+ return snd_soc_get_dai_name(&args, dai_name);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(meson_card_parse_dai);
|
|
+
|
|
+static int meson_card_set_link_name(struct snd_soc_card *card,
|
|
+ struct snd_soc_dai_link *link,
|
|
+ struct device_node *node,
|
|
+ const char *prefix)
|
|
+{
|
|
+ char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s",
|
|
+ prefix, node->full_name);
|
|
+ if (!name)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ link->name = name;
|
|
+ link->stream_name = name;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+unsigned int meson_card_parse_daifmt(struct device_node *node,
|
|
+ struct device_node *cpu_node)
|
|
+{
|
|
+ struct device_node *bitclkmaster = NULL;
|
|
+ struct device_node *framemaster = NULL;
|
|
+ unsigned int daifmt;
|
|
+
|
|
+ daifmt = snd_soc_of_parse_daifmt(node, DT_PREFIX,
|
|
+ &bitclkmaster, &framemaster);
|
|
+ daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
|
|
+
|
|
+ /* If no master is provided, default to cpu master */
|
|
+ if (!bitclkmaster || bitclkmaster == cpu_node) {
|
|
+ daifmt |= (!framemaster || framemaster == cpu_node) ?
|
|
+ SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM;
|
|
+ } else {
|
|
+ daifmt |= (!framemaster || framemaster == cpu_node) ?
|
|
+ SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM;
|
|
+ }
|
|
+
|
|
+ of_node_put(bitclkmaster);
|
|
+ of_node_put(framemaster);
|
|
+
|
|
+ return daifmt;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(meson_card_parse_daifmt);
|
|
+
|
|
+int meson_card_set_be_link(struct snd_soc_card *card,
|
|
+ struct snd_soc_dai_link *link,
|
|
+ struct device_node *node)
|
|
+{
|
|
+ struct snd_soc_dai_link_component *codec;
|
|
+ struct device_node *np;
|
|
+ int ret, num_codecs;
|
|
+
|
|
+ link->no_pcm = 1;
|
|
+ link->dpcm_playback = 1;
|
|
+ link->dpcm_capture = 1;
|
|
+
|
|
+ num_codecs = of_get_child_count(node);
|
|
+ if (!num_codecs) {
|
|
+ dev_err(card->dev, "be link %s has no codec\n",
|
|
+ node->full_name);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL);
|
|
+ if (!codec)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ link->codecs = codec;
|
|
+ link->num_codecs = num_codecs;
|
|
+
|
|
+ for_each_child_of_node(node, np) {
|
|
+ ret = meson_card_parse_dai(card, np, &codec->of_node,
|
|
+ &codec->dai_name);
|
|
+ if (ret) {
|
|
+ of_node_put(np);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ codec++;
|
|
+ }
|
|
+
|
|
+ ret = meson_card_set_link_name(card, link, node, "be");
|
|
+ if (ret)
|
|
+ dev_err(card->dev, "error setting %pOFn link name\n", np);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(meson_card_set_be_link);
|
|
+
|
|
+int meson_card_set_fe_link(struct snd_soc_card *card,
|
|
+ struct snd_soc_dai_link *link,
|
|
+ struct device_node *node,
|
|
+ bool is_playback)
|
|
+{
|
|
+ struct snd_soc_dai_link_component *codec;
|
|
+
|
|
+ codec = devm_kzalloc(card->dev, sizeof(*codec), GFP_KERNEL);
|
|
+ if (!codec)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ link->codecs = codec;
|
|
+ link->num_codecs = 1;
|
|
+
|
|
+ link->dynamic = 1;
|
|
+ link->dpcm_merged_format = 1;
|
|
+ link->dpcm_merged_chan = 1;
|
|
+ link->dpcm_merged_rate = 1;
|
|
+ link->codecs->dai_name = "snd-soc-dummy-dai";
|
|
+ link->codecs->name = "snd-soc-dummy";
|
|
+
|
|
+ if (is_playback)
|
|
+ link->dpcm_playback = 1;
|
|
+ else
|
|
+ link->dpcm_capture = 1;
|
|
+
|
|
+ return meson_card_set_link_name(card, link, node, "fe");
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(meson_card_set_fe_link);
|
|
+
|
|
+static int meson_card_add_links(struct snd_soc_card *card)
|
|
+{
|
|
+ struct meson_card *priv = snd_soc_card_get_drvdata(card);
|
|
+ struct device_node *node = card->dev->of_node;
|
|
+ struct device_node *np;
|
|
+ int num, i, ret;
|
|
+
|
|
+ num = of_get_child_count(node);
|
|
+ if (!num) {
|
|
+ dev_err(card->dev, "card has no links\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = meson_card_reallocate_links(card, num);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ i = 0;
|
|
+ for_each_child_of_node(node, np) {
|
|
+ ret = priv->match_data->add_link(card, np, &i);
|
|
+ if (ret) {
|
|
+ of_node_put(np);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ i++;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int meson_card_parse_of_optional(struct snd_soc_card *card,
|
|
+ const char *propname,
|
|
+ int (*func)(struct snd_soc_card *c,
|
|
+ const char *p))
|
|
+{
|
|
+ /* If property is not provided, don't fail ... */
|
|
+ if (!of_property_read_bool(card->dev->of_node, propname))
|
|
+ return 0;
|
|
+
|
|
+ /* ... but do fail if it is provided and the parsing fails */
|
|
+ return func(card, propname);
|
|
+}
|
|
+
|
|
+static int meson_card_add_aux_devices(struct snd_soc_card *card)
|
|
+{
|
|
+ struct device_node *node = card->dev->of_node;
|
|
+ struct snd_soc_aux_dev *aux;
|
|
+ int num, i;
|
|
+
|
|
+ num = of_count_phandle_with_args(node, "audio-aux-devs", NULL);
|
|
+ if (num == -ENOENT) {
|
|
+ return 0;
|
|
+ } else if (num < 0) {
|
|
+ dev_err(card->dev, "error getting auxiliary devices: %d\n",
|
|
+ num);
|
|
+ return num;
|
|
+ }
|
|
+
|
|
+ aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL);
|
|
+ if (!aux)
|
|
+ return -ENOMEM;
|
|
+ card->aux_dev = aux;
|
|
+ card->num_aux_devs = num;
|
|
+
|
|
+ for_each_card_pre_auxs(card, i, aux) {
|
|
+ aux->dlc.of_node =
|
|
+ of_parse_phandle(node, "audio-aux-devs", i);
|
|
+ if (!aux->dlc.of_node)
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void meson_card_clean_references(struct meson_card *priv)
|
|
+{
|
|
+ struct snd_soc_card *card = &priv->card;
|
|
+ struct snd_soc_dai_link *link;
|
|
+ struct snd_soc_dai_link_component *codec;
|
|
+ struct snd_soc_aux_dev *aux;
|
|
+ int i, j;
|
|
+
|
|
+ if (card->dai_link) {
|
|
+ for_each_card_prelinks(card, i, link) {
|
|
+ if (link->cpus)
|
|
+ of_node_put(link->cpus->of_node);
|
|
+ for_each_link_codecs(link, j, codec)
|
|
+ of_node_put(codec->of_node);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (card->aux_dev) {
|
|
+ for_each_card_pre_auxs(card, i, aux)
|
|
+ of_node_put(aux->dlc.of_node);
|
|
+ }
|
|
+
|
|
+ kfree(card->dai_link);
|
|
+ kfree(priv->link_data);
|
|
+}
|
|
+
|
|
+int meson_card_probe(struct platform_device *pdev)
|
|
+{
|
|
+ const struct meson_card_match_data *data;
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct meson_card *priv;
|
|
+ int ret;
|
|
+
|
|
+ data = of_device_get_match_data(dev);
|
|
+ if (!data) {
|
|
+ dev_err(dev, "failed to match device\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
|
+ if (!priv)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ platform_set_drvdata(pdev, priv);
|
|
+ snd_soc_card_set_drvdata(&priv->card, priv);
|
|
+
|
|
+ priv->card.owner = THIS_MODULE;
|
|
+ priv->card.dev = dev;
|
|
+ priv->match_data = data;
|
|
+
|
|
+ ret = snd_soc_of_parse_card_name(&priv->card, "model");
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ ret = meson_card_parse_of_optional(&priv->card, "audio-routing",
|
|
+ snd_soc_of_parse_audio_routing);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "error while parsing routing\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = meson_card_parse_of_optional(&priv->card, "audio-widgets",
|
|
+ snd_soc_of_parse_audio_simple_widgets);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "error while parsing widgets\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = meson_card_add_links(&priv->card);
|
|
+ if (ret)
|
|
+ goto out_err;
|
|
+
|
|
+ ret = meson_card_add_aux_devices(&priv->card);
|
|
+ if (ret)
|
|
+ goto out_err;
|
|
+
|
|
+ ret = devm_snd_soc_register_card(dev, &priv->card);
|
|
+ if (ret)
|
|
+ goto out_err;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_err:
|
|
+ meson_card_clean_references(priv);
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(meson_card_probe);
|
|
+
|
|
+int meson_card_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct meson_card *priv = platform_get_drvdata(pdev);
|
|
+
|
|
+ meson_card_clean_references(priv);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(meson_card_remove);
|
|
+
|
|
+MODULE_DESCRIPTION("Amlogic Sound Card Utils");
|
|
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
|
|
+MODULE_LICENSE("GPL v2");
|
|
diff --git a/sound/soc/meson/meson-card.h b/sound/soc/meson/meson-card.h
|
|
new file mode 100644
|
|
index 0000000..15920c7
|
|
--- /dev/null
|
|
+++ b/sound/soc/meson/meson-card.h
|
|
@@ -0,0 +1,53 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 */
|
|
+/*
|
|
+ * Copyright (c) 2019 BayLibre, SAS.
|
|
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
|
|
+ */
|
|
+
|
|
+#ifndef _MESON_SND_CARD_H
|
|
+#define _MESON_SND_CARD_H
|
|
+
|
|
+struct device_node;
|
|
+struct platform_device;
|
|
+
|
|
+struct snd_soc_card;
|
|
+struct snd_pcm_substream;
|
|
+struct snd_pcm_hw_params;
|
|
+
|
|
+#define DT_PREFIX "amlogic,"
|
|
+
|
|
+struct meson_card_match_data {
|
|
+ int (*add_link)(struct snd_soc_card *, struct device_node *, int *);
|
|
+};
|
|
+
|
|
+struct meson_card {
|
|
+ const struct meson_card_match_data *match_data;
|
|
+ struct snd_soc_card card;
|
|
+ void **link_data;
|
|
+};
|
|
+
|
|
+unsigned int meson_card_parse_daifmt(struct device_node *node,
|
|
+ struct device_node *cpu_node);
|
|
+
|
|
+int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream,
|
|
+ struct snd_pcm_hw_params *params,
|
|
+ unsigned int mclk_fs);
|
|
+
|
|
+int meson_card_reallocate_links(struct snd_soc_card *card,
|
|
+ unsigned int num_links);
|
|
+int meson_card_parse_dai(struct snd_soc_card *card,
|
|
+ struct device_node *node,
|
|
+ struct device_node **dai_of_node,
|
|
+ const char **dai_name);
|
|
+int meson_card_set_be_link(struct snd_soc_card *card,
|
|
+ struct snd_soc_dai_link *link,
|
|
+ struct device_node *node);
|
|
+int meson_card_set_fe_link(struct snd_soc_card *card,
|
|
+ struct snd_soc_dai_link *link,
|
|
+ struct device_node *node,
|
|
+ bool is_playback);
|
|
+
|
|
+int meson_card_probe(struct platform_device *pdev);
|
|
+int meson_card_remove(struct platform_device *pdev);
|
|
+
|
|
+#endif /* _MESON_SND_CARD_H */
|
|
--
|
|
2.7.1
|
|
|