summaryrefslogtreecommitdiff
path: root/sound/soc/generic/simple-card-utils.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2019-05-06 16:14:09 +0200
committerTakashi Iwai <tiwai@suse.de>2019-05-06 16:14:34 +0200
commitd81645510ce2a140816c4cb37c45b78d810ca63f (patch)
treeedd9464900904d22a23da362bb152669480c5d26 /sound/soc/generic/simple-card-utils.c
parent2854cd34fbab5f28a356d3667c26b7856a7b73e2 (diff)
parent378d590c494551a68a824b939c711bb9a280e9ef (diff)
Merge tag 'asoc-v5.2' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v5.2 This is a pretty huge set of changes, it's been a pretty active release all round but the big thing with this release is the Sound Open Firmware changes from Intel, providing another DSP framework for use with the DSPs in their SoCs. This one works with the firmware of the same name which is free software (unlike the previous DSP firmwares and framework) and there has been some interest in adoption by other systems already so hopefully we will see adoption by other vendors in the future. Other highlights include: - Support for MCLK/sample rate ratio setting in the generic cards. - Support for pin switches in the generic cards. - A big set of improvements to the TLV320AIC32x4 drivers from Annaliese McDermond. - New drivers for Freescale audio mixers, several Intel machines, several Mediatek machines, Meson G12A, Sound Open Firmware and Spreadtrum compressed audio and DMA devices.
Diffstat (limited to 'sound/soc/generic/simple-card-utils.c')
-rw-r--r--sound/soc/generic/simple-card-utils.c440
1 files changed, 256 insertions, 184 deletions
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 5c1424f03620..f4c6375d11c7 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -14,8 +14,8 @@
#include <sound/jack.h>
#include <sound/simple_card_utils.h>
-void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data,
- struct snd_pcm_hw_params *params)
+void asoc_simple_convert_fixup(struct asoc_simple_data *data,
+ struct snd_pcm_hw_params *params)
{
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
@@ -30,12 +30,12 @@ void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data,
channels->min =
channels->max = data->convert_channels;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_convert_fixup);
+EXPORT_SYMBOL_GPL(asoc_simple_convert_fixup);
-void asoc_simple_card_parse_convert(struct device *dev,
- struct device_node *np,
- char *prefix,
- struct asoc_simple_card_data *data)
+void asoc_simple_parse_convert(struct device *dev,
+ struct device_node *np,
+ char *prefix,
+ struct asoc_simple_data *data)
{
char prop[128];
@@ -49,17 +49,14 @@ void asoc_simple_card_parse_convert(struct device *dev,
/* channels transfer */
snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
of_property_read_u32(np, prop, &data->convert_channels);
-
- dev_dbg(dev, "convert_rate %d\n", data->convert_rate);
- dev_dbg(dev, "convert_channels %d\n", data->convert_channels);
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_parse_convert);
+EXPORT_SYMBOL_GPL(asoc_simple_parse_convert);
-int asoc_simple_card_parse_daifmt(struct device *dev,
- struct device_node *node,
- struct device_node *codec,
- char *prefix,
- unsigned int *retfmt)
+int asoc_simple_parse_daifmt(struct device *dev,
+ struct device_node *node,
+ struct device_node *codec,
+ char *prefix,
+ unsigned int *retfmt)
{
struct device_node *bitclkmaster = NULL;
struct device_node *framemaster = NULL;
@@ -93,15 +90,13 @@ int asoc_simple_card_parse_daifmt(struct device *dev,
*retfmt = daifmt;
- dev_dbg(dev, "format : %04x\n", daifmt);
-
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt);
+EXPORT_SYMBOL_GPL(asoc_simple_parse_daifmt);
-int asoc_simple_card_set_dailink_name(struct device *dev,
- struct snd_soc_dai_link *dai_link,
- const char *fmt, ...)
+int asoc_simple_set_dailink_name(struct device *dev,
+ struct snd_soc_dai_link *dai_link,
+ const char *fmt, ...)
{
va_list ap;
char *name = NULL;
@@ -116,16 +111,14 @@ int asoc_simple_card_set_dailink_name(struct device *dev,
dai_link->name = name;
dai_link->stream_name = name;
-
- dev_dbg(dev, "name : %s\n", name);
}
return ret;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name);
+EXPORT_SYMBOL_GPL(asoc_simple_set_dailink_name);
-int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
- char *prefix)
+int asoc_simple_parse_card_name(struct snd_soc_card *card,
+ char *prefix)
{
int ret;
@@ -146,34 +139,30 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
if (!card->name && card->dai_link)
card->name = card->dai_link->name;
- dev_dbg(card->dev, "Card Name: %s\n", card->name ? card->name : "");
-
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
+EXPORT_SYMBOL_GPL(asoc_simple_parse_card_name);
-int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai)
+static int asoc_simple_clk_enable(struct asoc_simple_dai *dai)
{
if (dai)
return clk_prepare_enable(dai->clk);
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_clk_enable);
-void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai)
+static void asoc_simple_clk_disable(struct asoc_simple_dai *dai)
{
if (dai)
clk_disable_unprepare(dai->clk);
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_clk_disable);
-
-int asoc_simple_card_parse_clk(struct device *dev,
- struct device_node *node,
- struct device_node *dai_of_node,
- struct asoc_simple_dai *simple_dai,
- const char *dai_name,
- struct snd_soc_dai_link_component *dlc)
+
+int asoc_simple_parse_clk(struct device *dev,
+ struct device_node *node,
+ struct device_node *dai_of_node,
+ struct asoc_simple_dai *simple_dai,
+ const char *dai_name,
+ struct snd_soc_dai_link_component *dlc)
{
struct clk *clk;
u32 val;
@@ -184,10 +173,8 @@ int asoc_simple_card_parse_clk(struct device *dev,
* see
* soc-core.c :: snd_soc_init_multicodec()
*/
- if (dlc) {
+ if (dlc)
dai_of_node = dlc->of_node;
- dai_name = dlc->dai_name;
- }
/*
* Parse dai->sysclk come from "clocks = <&xxx>"
@@ -211,158 +198,113 @@ int asoc_simple_card_parse_clk(struct device *dev,
if (of_property_read_bool(node, "system-clock-direction-out"))
simple_dai->clk_direction = SND_SOC_CLOCK_OUT;
- dev_dbg(dev, "%s : sysclk = %d, direction %d\n", dai_name,
- simple_dai->sysclk, simple_dai->clk_direction);
-
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk);
-
-int asoc_simple_card_parse_dai(struct device_node *node,
- struct snd_soc_dai_link_component *dlc,
- struct device_node **dai_of_node,
- const char **dai_name,
- const char *list_name,
- const char *cells_name,
- int *is_single_link)
+EXPORT_SYMBOL_GPL(asoc_simple_parse_clk);
+
+int asoc_simple_startup(struct snd_pcm_substream *substream)
{
- struct of_phandle_args args;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
int ret;
- if (!node)
- return 0;
-
- /*
- * Use snd_soc_dai_link_component instead of legacy style.
- * It is only for codec, but cpu will be supported in the future.
- * see
- * soc-core.c :: snd_soc_init_multicodec()
- */
- if (dlc) {
- dai_name = &dlc->dai_name;
- dai_of_node = &dlc->of_node;
- }
-
- /*
- * Get node via "sound-dai = <&phandle port>"
- * it will be used as xxx_of_node on soc_bind_dai_link()
- */
- ret = of_parse_phandle_with_args(node, list_name, cells_name, 0, &args);
+ ret = asoc_simple_clk_enable(dai_props->cpu_dai);
if (ret)
return ret;
- /* Get dai->name */
- if (dai_name) {
- ret = snd_soc_of_get_dai_name(node, dai_name);
- if (ret < 0)
- return ret;
- }
-
- *dai_of_node = args.np;
-
- if (is_single_link)
- *is_single_link = !args.args_count;
+ ret = asoc_simple_clk_enable(dai_props->codec_dai);
+ if (ret)
+ asoc_simple_clk_disable(dai_props->cpu_dai);
- return 0;
+ return ret;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai);
+EXPORT_SYMBOL_GPL(asoc_simple_startup);
-static int asoc_simple_card_get_dai_id(struct device_node *ep)
+void asoc_simple_shutdown(struct snd_pcm_substream *substream)
{
- struct device_node *node;
- struct device_node *endpoint;
- struct of_endpoint info;
- int i, id;
- int ret;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *dai_props =
+ simple_priv_to_props(priv, rtd->num);
- /* use driver specified DAI ID if exist */
- ret = snd_soc_get_dai_id(ep);
- if (ret != -ENOTSUPP)
- return ret;
+ asoc_simple_clk_disable(dai_props->cpu_dai);
- /* use endpoint/port reg if exist */
- ret = of_graph_parse_endpoint(ep, &info);
- if (ret == 0) {
- /*
- * Because it will count port/endpoint if it doesn't have "reg".
- * But, we can't judge whether it has "no reg", or "reg = <0>"
- * only of_graph_parse_endpoint().
- * We need to check "reg" property
- */
- if (of_get_property(ep, "reg", NULL))
- return info.id;
-
- node = of_get_parent(ep);
- of_node_put(node);
- if (of_get_property(node, "reg", NULL))
- return info.port;
- }
- node = of_graph_get_port_parent(ep);
+ asoc_simple_clk_disable(dai_props->codec_dai);
+}
+EXPORT_SYMBOL_GPL(asoc_simple_shutdown);
- /*
- * Non HDMI sound case, counting port/endpoint on its DT
- * is enough. Let's count it.
- */
- i = 0;
- id = -1;
- for_each_endpoint_of_node(node, endpoint) {
- if (endpoint == ep)
- id = i;
- i++;
- }
+static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
+ unsigned long rate)
+{
+ if (!simple_dai)
+ return 0;
- of_node_put(node);
+ if (!simple_dai->clk)
+ return 0;
- if (id < 0)
- return -ENODEV;
+ if (clk_get_rate(simple_dai->clk) == rate)
+ return 0;
- return id;
+ return clk_set_rate(simple_dai->clk, rate);
}
-int asoc_simple_card_parse_graph_dai(struct device_node *ep,
- struct snd_soc_dai_link_component *dlc,
- struct device_node **dai_of_node,
- const char **dai_name)
+int asoc_simple_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
- struct device_node *node;
- struct of_phandle_args args;
- int ret;
-
- /*
- * Use snd_soc_dai_link_component instead of legacy style.
- * It is only for codec, but cpu will be supported in the future.
- * see
- * soc-core.c :: snd_soc_init_multicodec()
- */
- if (dlc) {
- dai_name = &dlc->dai_name;
- dai_of_node = &dlc->of_node;
- }
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *dai_props =
+ simple_priv_to_props(priv, rtd->num);
+ unsigned int mclk, mclk_fs = 0;
+ int ret = 0;
+
+ if (dai_props->mclk_fs)
+ mclk_fs = dai_props->mclk_fs;
+
+ if (mclk_fs) {
+ mclk = params_rate(params) * mclk_fs;
+
+ ret = asoc_simple_set_clk_rate(dai_props->codec_dai, mclk);
+ if (ret < 0)
+ return ret;
- if (!ep)
- return 0;
- if (!dai_name)
- return 0;
+ ret = asoc_simple_set_clk_rate(dai_props->cpu_dai, mclk);
+ if (ret < 0)
+ return ret;
- node = of_graph_get_port_parent(ep);
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (ret && ret != -ENOTSUPP)
+ goto err;
- /* Get dai->name */
- args.np = node;
- args.args[0] = asoc_simple_card_get_dai_id(ep);
- args.args_count = (of_graph_get_endpoint_count(node) > 1);
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
+ SND_SOC_CLOCK_OUT);
+ if (ret && ret != -ENOTSUPP)
+ goto err;
+ }
+ return 0;
+err:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_hw_params);
- ret = snd_soc_get_dai_name(&args, dai_name);
- if (ret < 0)
- return ret;
+int asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
- *dai_of_node = node;
+ asoc_simple_convert_fixup(&dai_props->adata, params);
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_parse_graph_dai);
+EXPORT_SYMBOL_GPL(asoc_simple_be_hw_params_fixup);
-int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
- struct asoc_simple_dai *simple_dai)
+static int asoc_simple_init_dai(struct snd_soc_dai *dai,
+ struct asoc_simple_dai *simple_dai)
{
int ret;
@@ -392,18 +334,37 @@ int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_init_dai);
-void asoc_simple_card_canonicalize_platform(struct snd_soc_dai_link *dai_link)
+int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
+ int ret;
+
+ ret = asoc_simple_init_dai(rtd->codec_dai,
+ dai_props->codec_dai);
+ if (ret < 0)
+ return ret;
+
+ ret = asoc_simple_init_dai(rtd->cpu_dai,
+ dai_props->cpu_dai);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_dai_init);
+
+void asoc_simple_canonicalize_platform(struct snd_soc_dai_link *dai_link)
{
/* Assumes platform == cpu */
if (!dai_link->platforms->of_node)
dai_link->platforms->of_node = dai_link->cpu_of_node;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_platform);
+EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform);
-void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
- int is_single_links)
+void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
+ int is_single_links)
{
/*
* In soc_bind_dai_link() will check cpu name after
@@ -417,9 +378,9 @@ void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
if (is_single_links)
dai_link->cpu_dai_name = NULL;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_cpu);
+EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_cpu);
-int asoc_simple_card_clean_reference(struct snd_soc_card *card)
+int asoc_simple_clean_reference(struct snd_soc_card *card)
{
struct snd_soc_dai_link *dai_link;
int i;
@@ -430,10 +391,10 @@ int asoc_simple_card_clean_reference(struct snd_soc_card *card)
}
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference);
+EXPORT_SYMBOL_GPL(asoc_simple_clean_reference);
-int asoc_simple_card_of_parse_routing(struct snd_soc_card *card,
- char *prefix)
+int asoc_simple_parse_routing(struct snd_soc_card *card,
+ char *prefix)
{
struct device_node *node = card->dev->of_node;
char prop[128];
@@ -448,10 +409,10 @@ int asoc_simple_card_of_parse_routing(struct snd_soc_card *card,
return snd_soc_of_parse_audio_routing(card, prop);
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_routing);
+EXPORT_SYMBOL_GPL(asoc_simple_parse_routing);
-int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card,
- char *prefix)
+int asoc_simple_parse_widgets(struct snd_soc_card *card,
+ char *prefix)
{
struct device_node *node = card->dev->of_node;
char prop[128];
@@ -467,11 +428,68 @@ int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card,
/* no widgets is not error */
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_widgets);
+EXPORT_SYMBOL_GPL(asoc_simple_parse_widgets);
+
+int asoc_simple_parse_pin_switches(struct snd_soc_card *card,
+ char *prefix)
+{
+ const unsigned int nb_controls_max = 16;
+ const char **strings, *control_name;
+ struct snd_kcontrol_new *controls;
+ struct device *dev = card->dev;
+ unsigned int i, nb_controls;
+ char prop[128];
+ int ret;
+
+ if (!prefix)
+ prefix = "";
+
+ snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches");
+
+ if (!of_property_read_bool(dev->of_node, prop))
+ return 0;
+
+ strings = devm_kcalloc(dev, nb_controls_max,
+ sizeof(*strings), GFP_KERNEL);
+ if (!strings)
+ return -ENOMEM;
+
+ ret = of_property_read_string_array(dev->of_node, prop,
+ strings, nb_controls_max);
+ if (ret < 0)
+ return ret;
+
+ nb_controls = (unsigned int)ret;
+
+ controls = devm_kcalloc(dev, nb_controls,
+ sizeof(*controls), GFP_KERNEL);
+ if (!controls)
+ return -ENOMEM;
+
+ for (i = 0; i < nb_controls; i++) {
+ control_name = devm_kasprintf(dev, GFP_KERNEL,
+ "%s Switch", strings[i]);
+ if (!control_name)
+ return -ENOMEM;
+
+ controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ controls[i].name = control_name;
+ controls[i].info = snd_soc_dapm_info_pin_switch;
+ controls[i].get = snd_soc_dapm_get_pin_switch;
+ controls[i].put = snd_soc_dapm_put_pin_switch;
+ controls[i].private_value = (unsigned long)strings[i];
+ }
+
+ card->controls = controls;
+ card->num_controls = nb_controls;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_parse_pin_switches);
-int asoc_simple_card_init_jack(struct snd_soc_card *card,
- struct asoc_simple_jack *sjack,
- int is_hp, char *prefix)
+int asoc_simple_init_jack(struct snd_soc_card *card,
+ struct asoc_simple_jack *sjack,
+ int is_hp, char *prefix)
{
struct device *dev = card->dev;
enum of_gpio_flags flags;
@@ -522,7 +540,61 @@ int asoc_simple_card_init_jack(struct snd_soc_card *card,
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_init_jack);
+EXPORT_SYMBOL_GPL(asoc_simple_init_jack);
+
+int asoc_simple_init_priv(struct asoc_simple_priv *priv,
+ struct link_info *li)
+{
+ struct snd_soc_card *card = simple_priv_to_card(priv);
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_dai_link *dai_link;
+ struct simple_dai_props *dai_props;
+ struct asoc_simple_dai *dais;
+ struct snd_soc_codec_conf *cconf = NULL;
+ int i;
+
+ dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);
+ dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL);
+ dais = devm_kcalloc(dev, li->dais, sizeof(*dais), GFP_KERNEL);
+ if (!dai_props || !dai_link || !dais)
+ return -ENOMEM;
+
+ if (li->conf) {
+ cconf = devm_kcalloc(dev, li->conf, sizeof(*cconf), GFP_KERNEL);
+ if (!cconf)
+ return -ENOMEM;
+ }
+
+ /*
+ * Use snd_soc_dai_link_component instead of legacy style
+ * It is codec only. but cpu/platform will be supported in the future.
+ * see
+ * soc-core.c :: snd_soc_init_multicodec()
+ *
+ * "platform" might be removed
+ * see
+ * simple-card-utils.c :: asoc_simple_canonicalize_platform()
+ */
+ for (i = 0; i < li->link; i++) {
+ dai_link[i].codecs = &dai_props[i].codecs;
+ dai_link[i].num_codecs = 1;
+ dai_link[i].platforms = &dai_props[i].platforms;
+ dai_link[i].num_platforms = 1;
+ }
+
+ priv->dai_props = dai_props;
+ priv->dai_link = dai_link;
+ priv->dais = dais;
+ priv->codec_conf = cconf;
+
+ card->dai_link = priv->dai_link;
+ card->num_links = li->link;
+ card->codec_conf = cconf;
+ card->num_configs = li->conf;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_init_priv);
/* Module information */
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");