2018-07-02 14:30:44 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//
|
|
|
|
// simple-card-utils.c
|
|
|
|
//
|
|
|
|
// Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
|
|
|
|
2016-08-08 13:59:56 +08:00
|
|
|
#include <linux/clk.h>
|
2018-06-11 16:32:12 +08:00
|
|
|
#include <linux/gpio.h>
|
|
|
|
#include <linux/gpio/consumer.h>
|
2016-08-03 09:24:05 +08:00
|
|
|
#include <linux/module.h>
|
2016-05-31 17:00:14 +08:00
|
|
|
#include <linux/of.h>
|
2018-06-11 16:32:12 +08:00
|
|
|
#include <linux/of_gpio.h>
|
2017-04-20 09:35:18 +08:00
|
|
|
#include <linux/of_graph.h>
|
2018-06-11 16:32:12 +08:00
|
|
|
#include <sound/jack.h>
|
2022-03-01 01:27:54 +08:00
|
|
|
#include <sound/pcm_params.h>
|
2016-05-31 17:00:14 +08:00
|
|
|
#include <sound/simple_card_utils.h>
|
|
|
|
|
2019-03-20 12:56:50 +08:00
|
|
|
void asoc_simple_convert_fixup(struct asoc_simple_data *data,
|
|
|
|
struct snd_pcm_hw_params *params)
|
2017-06-15 08:24:09 +08:00
|
|
|
{
|
|
|
|
struct snd_interval *rate = hw_param_interval(params,
|
|
|
|
SNDRV_PCM_HW_PARAM_RATE);
|
|
|
|
struct snd_interval *channels = hw_param_interval(params,
|
|
|
|
SNDRV_PCM_HW_PARAM_CHANNELS);
|
|
|
|
|
|
|
|
if (data->convert_rate)
|
|
|
|
rate->min =
|
|
|
|
rate->max = data->convert_rate;
|
|
|
|
|
|
|
|
if (data->convert_channels)
|
|
|
|
channels->min =
|
|
|
|
channels->max = data->convert_channels;
|
|
|
|
}
|
2019-03-20 12:56:50 +08:00
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_convert_fixup);
|
2017-06-15 08:24:09 +08:00
|
|
|
|
2021-04-12 07:53:00 +08:00
|
|
|
void asoc_simple_parse_convert(struct device_node *np,
|
2019-03-20 12:56:50 +08:00
|
|
|
char *prefix,
|
|
|
|
struct asoc_simple_data *data)
|
2017-06-15 08:24:09 +08:00
|
|
|
{
|
|
|
|
char prop[128];
|
|
|
|
|
|
|
|
if (!prefix)
|
|
|
|
prefix = "";
|
|
|
|
|
|
|
|
/* sampling rate convert */
|
|
|
|
snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate");
|
|
|
|
of_property_read_u32(np, prop, &data->convert_rate);
|
|
|
|
|
|
|
|
/* channels transfer */
|
|
|
|
snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
|
|
|
|
of_property_read_u32(np, prop, &data->convert_channels);
|
|
|
|
}
|
2019-03-20 12:56:50 +08:00
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_parse_convert);
|
2017-06-15 08:24:09 +08:00
|
|
|
|
2019-03-20 12:56:50 +08:00
|
|
|
int asoc_simple_parse_daifmt(struct device *dev,
|
|
|
|
struct device_node *node,
|
|
|
|
struct device_node *codec,
|
|
|
|
char *prefix,
|
|
|
|
unsigned int *retfmt)
|
2016-05-31 17:00:14 +08:00
|
|
|
{
|
|
|
|
struct device_node *bitclkmaster = NULL;
|
|
|
|
struct device_node *framemaster = NULL;
|
|
|
|
unsigned int daifmt;
|
|
|
|
|
2021-06-14 08:58:22 +08:00
|
|
|
daifmt = snd_soc_daifmt_parse_format(node, prefix);
|
2016-05-31 17:00:14 +08:00
|
|
|
|
2021-06-14 08:58:22 +08:00
|
|
|
snd_soc_daifmt_parse_clock_provider_as_phandle(node, prefix, &bitclkmaster, &framemaster);
|
2017-05-25 09:51:31 +08:00
|
|
|
if (!bitclkmaster && !framemaster) {
|
2016-05-31 17:00:14 +08:00
|
|
|
/*
|
|
|
|
* No dai-link level and master setting was not found from
|
|
|
|
* sound node level, revert back to legacy DT parsing and
|
|
|
|
* take the settings from codec node.
|
|
|
|
*/
|
|
|
|
dev_dbg(dev, "Revert to legacy daifmt parsing\n");
|
|
|
|
|
2021-06-14 08:58:22 +08:00
|
|
|
daifmt |= snd_soc_daifmt_parse_clock_provider_as_flag(codec, NULL);
|
2016-05-31 17:00:14 +08:00
|
|
|
} else {
|
2021-06-14 08:58:22 +08:00
|
|
|
daifmt |= snd_soc_daifmt_clock_provider_from_bitmap(
|
|
|
|
((codec == bitclkmaster) << 4) | (codec == framemaster));
|
2016-05-31 17:00:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
of_node_put(bitclkmaster);
|
|
|
|
of_node_put(framemaster);
|
|
|
|
|
|
|
|
*retfmt = daifmt;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-03-20 12:56:50 +08:00
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_parse_daifmt);
|
2016-07-12 07:57:14 +08:00
|
|
|
|
2022-03-01 01:27:54 +08:00
|
|
|
int asoc_simple_parse_tdm_width_map(struct device *dev, struct device_node *np,
|
|
|
|
struct asoc_simple_dai *dai)
|
|
|
|
{
|
|
|
|
u32 *array_values, *p;
|
|
|
|
int n, i, ret;
|
|
|
|
|
|
|
|
if (!of_property_read_bool(np, "dai-tdm-slot-width-map"))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
n = of_property_count_elems_of_size(np, "dai-tdm-slot-width-map", sizeof(u32));
|
|
|
|
if (n % 3) {
|
|
|
|
dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dai->tdm_width_map = devm_kcalloc(dev, n, sizeof(*dai->tdm_width_map), GFP_KERNEL);
|
|
|
|
if (!dai->tdm_width_map)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
array_values = kcalloc(n, sizeof(*array_values), GFP_KERNEL);
|
|
|
|
if (!array_values)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = array_values;
|
|
|
|
for (i = 0; i < n / 3; ++i) {
|
|
|
|
dai->tdm_width_map[i].sample_bits = *p++;
|
|
|
|
dai->tdm_width_map[i].slot_width = *p++;
|
|
|
|
dai->tdm_width_map[i].slot_count = *p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
dai->n_tdm_widths = i;
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
kfree(array_values);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_parse_tdm_width_map);
|
|
|
|
|
2019-03-20 12:56:50 +08:00
|
|
|
int asoc_simple_set_dailink_name(struct device *dev,
|
|
|
|
struct snd_soc_dai_link *dai_link,
|
|
|
|
const char *fmt, ...)
|
2016-07-12 07:57:14 +08:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
char *name = NULL;
|
|
|
|
int ret = -ENOMEM;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
if (name) {
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
dai_link->name = name;
|
|
|
|
dai_link->stream_name = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2019-03-20 12:56:50 +08:00
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_set_dailink_name);
|
2016-07-12 07:59:16 +08:00
|
|
|
|
2019-03-20 12:56:50 +08:00
|
|
|
int asoc_simple_parse_card_name(struct snd_soc_card *card,
|
|
|
|
char *prefix)
|
2016-07-12 07:59:16 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2017-04-20 09:34:49 +08:00
|
|
|
if (!prefix)
|
|
|
|
prefix = "";
|
2016-07-12 07:59:16 +08:00
|
|
|
|
|
|
|
/* Parse the card name from DT */
|
2017-04-20 09:34:49 +08:00
|
|
|
ret = snd_soc_of_parse_card_name(card, "label");
|
2017-08-29 23:51:22 +08:00
|
|
|
if (ret < 0 || !card->name) {
|
2017-04-20 09:34:49 +08:00
|
|
|
char prop[128];
|
|
|
|
|
|
|
|
snprintf(prop, sizeof(prop), "%sname", prefix);
|
|
|
|
ret = snd_soc_of_parse_card_name(card, prop);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
2016-07-12 07:59:16 +08:00
|
|
|
|
|
|
|
if (!card->name && card->dai_link)
|
|
|
|
card->name = card->dai_link->name;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-03-20 12:56:50 +08:00
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_parse_card_name);
|
2016-08-03 09:24:05 +08:00
|
|
|
|
2019-03-20 12:56:50 +08:00
|
|
|
static int asoc_simple_clk_enable(struct asoc_simple_dai *dai)
|
2017-06-09 08:43:18 +08:00
|
|
|
{
|
2018-11-21 10:09:16 +08:00
|
|
|
if (dai)
|
|
|
|
return clk_prepare_enable(dai->clk);
|
|
|
|
|
|
|
|
return 0;
|
2017-06-09 08:43:18 +08:00
|
|
|
}
|
|
|
|
|
2019-03-20 12:56:50 +08:00
|
|
|
static void asoc_simple_clk_disable(struct asoc_simple_dai *dai)
|
2017-06-09 08:43:18 +08:00
|
|
|
{
|
2018-11-21 10:09:16 +08:00
|
|
|
if (dai)
|
|
|
|
clk_disable_unprepare(dai->clk);
|
2017-06-09 08:43:18 +08:00
|
|
|
}
|
|
|
|
|
2019-03-20 12:56:50 +08:00
|
|
|
int asoc_simple_parse_clk(struct device *dev,
|
|
|
|
struct device_node *node,
|
|
|
|
struct asoc_simple_dai *simple_dai,
|
|
|
|
struct snd_soc_dai_link_component *dlc)
|
2016-08-08 13:59:56 +08:00
|
|
|
{
|
|
|
|
struct clk *clk;
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse dai->sysclk come from "clocks = <&xxx>"
|
|
|
|
* (if system has common clock)
|
|
|
|
* or "system-clock-frequency = <xxx>"
|
|
|
|
* or device's module clock.
|
|
|
|
*/
|
2017-01-23 15:29:42 +08:00
|
|
|
clk = devm_get_clk_from_child(dev, node, NULL);
|
2022-01-21 03:58:32 +08:00
|
|
|
simple_dai->clk_fixed = of_property_read_bool(
|
|
|
|
node, "system-clock-fixed");
|
2021-02-10 14:43:39 +08:00
|
|
|
if (!IS_ERR(clk)) {
|
|
|
|
simple_dai->sysclk = clk_get_rate(clk);
|
2021-03-16 01:31:31 +08:00
|
|
|
|
|
|
|
simple_dai->clk = clk;
|
|
|
|
} else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
|
2016-08-08 13:59:56 +08:00
|
|
|
simple_dai->sysclk = val;
|
2022-01-21 03:58:32 +08:00
|
|
|
simple_dai->clk_fixed = true;
|
2021-03-16 01:31:31 +08:00
|
|
|
} else {
|
|
|
|
clk = devm_get_clk_from_child(dev, dlc->of_node, NULL);
|
|
|
|
if (!IS_ERR(clk))
|
|
|
|
simple_dai->sysclk = clk_get_rate(clk);
|
2016-08-08 13:59:56 +08:00
|
|
|
}
|
|
|
|
|
2017-08-17 19:42:36 +08:00
|
|
|
if (of_property_read_bool(node, "system-clock-direction-out"))
|
|
|
|
simple_dai->clk_direction = SND_SOC_CLOCK_OUT;
|
|
|
|
|
2016-08-08 13:59:56 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2019-03-20 12:56:50 +08:00
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_parse_clk);
|
2016-08-08 13:59:56 +08:00
|
|
|
|
2022-01-21 03:58:32 +08:00
|
|
|
static int asoc_simple_check_fixed_sysclk(struct device *dev,
|
|
|
|
struct asoc_simple_dai *dai,
|
|
|
|
unsigned int *fixed_sysclk)
|
|
|
|
{
|
|
|
|
if (dai->clk_fixed) {
|
|
|
|
if (*fixed_sysclk && *fixed_sysclk != dai->sysclk) {
|
|
|
|
dev_err(dev, "inconsistent fixed sysclk rates (%u vs %u)\n",
|
|
|
|
*fixed_sysclk, dai->sysclk);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
*fixed_sysclk = dai->sysclk;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-20 12:55:14 +08:00
|
|
|
int asoc_simple_startup(struct snd_pcm_substream *substream)
|
|
|
|
{
|
2020-07-20 09:19:32 +08:00
|
|
|
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
2019-03-20 12:55:14 +08:00
|
|
|
struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
2021-04-12 07:52:09 +08:00
|
|
|
struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
|
|
|
|
struct asoc_simple_dai *dai;
|
2022-01-21 03:58:32 +08:00
|
|
|
unsigned int fixed_sysclk = 0;
|
2021-04-12 07:52:09 +08:00
|
|
|
int i1, i2, i;
|
2019-03-20 12:55:14 +08:00
|
|
|
int ret;
|
|
|
|
|
2021-04-12 07:52:09 +08:00
|
|
|
for_each_prop_dai_cpu(props, i1, dai) {
|
|
|
|
ret = asoc_simple_clk_enable(dai);
|
|
|
|
if (ret)
|
|
|
|
goto cpu_err;
|
2022-01-21 03:58:32 +08:00
|
|
|
ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);
|
|
|
|
if (ret)
|
|
|
|
goto cpu_err;
|
2021-04-12 07:52:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for_each_prop_dai_codec(props, i2, dai) {
|
|
|
|
ret = asoc_simple_clk_enable(dai);
|
|
|
|
if (ret)
|
|
|
|
goto codec_err;
|
2022-01-21 03:58:32 +08:00
|
|
|
ret = asoc_simple_check_fixed_sysclk(rtd->dev, dai, &fixed_sysclk);
|
|
|
|
if (ret)
|
|
|
|
goto codec_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fixed_sysclk && props->mclk_fs) {
|
|
|
|
unsigned int fixed_rate = fixed_sysclk / props->mclk_fs;
|
|
|
|
|
|
|
|
if (fixed_sysclk % props->mclk_fs) {
|
|
|
|
dev_err(rtd->dev, "fixed sysclk %u not divisible by mclk_fs %u\n",
|
|
|
|
fixed_sysclk, props->mclk_fs);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE,
|
|
|
|
fixed_rate, fixed_rate);
|
|
|
|
if (ret)
|
|
|
|
goto codec_err;
|
2021-04-12 07:52:09 +08:00
|
|
|
}
|
2019-03-20 12:55:14 +08:00
|
|
|
|
2021-04-12 07:52:09 +08:00
|
|
|
return 0;
|
2019-03-20 12:55:14 +08:00
|
|
|
|
2021-04-12 07:52:09 +08:00
|
|
|
codec_err:
|
|
|
|
for_each_prop_dai_codec(props, i, dai) {
|
|
|
|
if (i >= i2)
|
|
|
|
break;
|
|
|
|
asoc_simple_clk_disable(dai);
|
|
|
|
}
|
|
|
|
cpu_err:
|
|
|
|
for_each_prop_dai_cpu(props, i, dai) {
|
|
|
|
if (i >= i1)
|
|
|
|
break;
|
|
|
|
asoc_simple_clk_disable(dai);
|
|
|
|
}
|
2019-03-20 12:55:14 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_startup);
|
|
|
|
|
2019-03-20 12:55:27 +08:00
|
|
|
void asoc_simple_shutdown(struct snd_pcm_substream *substream)
|
|
|
|
{
|
2020-07-20 09:19:32 +08:00
|
|
|
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
2019-03-20 12:55:27 +08:00
|
|
|
struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
2021-04-12 07:52:09 +08:00
|
|
|
struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
|
|
|
|
struct asoc_simple_dai *dai;
|
|
|
|
int i;
|
2019-03-20 12:55:27 +08:00
|
|
|
|
2022-01-21 03:58:32 +08:00
|
|
|
for_each_prop_dai_cpu(props, i, dai) {
|
2022-03-10 19:16:39 +08:00
|
|
|
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, i);
|
|
|
|
|
|
|
|
if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(cpu_dai))
|
|
|
|
snd_soc_dai_set_sysclk(cpu_dai,
|
2022-01-21 03:58:32 +08:00
|
|
|
0, 0, SND_SOC_CLOCK_IN);
|
SoC: simple-card-utils: set 0Hz to sysclk when shutdown
This patch set 0Hz to sysclk when shutdown the card.
Some codecs set rate constraints that derives from sysclk. This
mechanism works correctly if machine drivers give fixed frequency.
But simple-audio and audio-graph card set variable clock rate if
'mclk-fs' property exists. In this case, rate constraints will go
bad scenario. For example a codec accepts three limited rates
(mclk / 256, mclk / 384, mclk / 512).
Bad scenario as follows (mclk-fs = 256):
- Initialize sysclk by correct value (Ex. 12.288MHz)
- Codec set constraints of PCM rate by sysclk
48kHz (1/256), 32kHz (1/384), 24kHz (1/512)
- Play 48kHz sound, it's acceptable
- Sysclk is not changed
- Play 32kHz sound, it's acceptable
- Set sysclk to 8.192MHz (= fs * mclk-fs = 32k * 256)
- Codec set constraints of PCM rate by sysclk
32kHz (1/256), 21.33kHz (1/384), 16kHz (1/512)
- Play 48kHz again, but it's NOT acceptable because constraints
do not allow 48kHz
So codecs treat 0Hz sysclk as signal of applying no constraints to
avoid this problem.
Signed-off-by: Katsuhiro Suzuki <katsuhiro@katsuster.net>
Link: https://lore.kernel.org/r/20190907174501.19833-1-katsuhiro@katsuster.net
Signed-off-by: Mark Brown <broonie@kernel.org>
2019-09-08 01:45:01 +08:00
|
|
|
|
2021-04-12 07:52:09 +08:00
|
|
|
asoc_simple_clk_disable(dai);
|
2022-01-21 03:58:32 +08:00
|
|
|
}
|
|
|
|
for_each_prop_dai_codec(props, i, dai) {
|
2022-03-10 19:16:39 +08:00
|
|
|
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, i);
|
|
|
|
|
|
|
|
if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(codec_dai))
|
|
|
|
snd_soc_dai_set_sysclk(codec_dai,
|
2022-01-21 03:58:32 +08:00
|
|
|
0, 0, SND_SOC_CLOCK_IN);
|
|
|
|
|
2021-04-12 07:52:09 +08:00
|
|
|
asoc_simple_clk_disable(dai);
|
2022-01-21 03:58:32 +08:00
|
|
|
}
|
2019-03-20 12:55:27 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_shutdown);
|
|
|
|
|
2022-01-21 03:58:32 +08:00
|
|
|
static int asoc_simple_set_clk_rate(struct device *dev,
|
|
|
|
struct asoc_simple_dai *simple_dai,
|
2019-03-20 12:55:39 +08:00
|
|
|
unsigned long rate)
|
|
|
|
{
|
|
|
|
if (!simple_dai)
|
|
|
|
return 0;
|
|
|
|
|
2022-01-21 03:58:32 +08:00
|
|
|
if (simple_dai->clk_fixed && rate != simple_dai->sysclk) {
|
|
|
|
dev_err(dev, "dai %s invalid clock rate %lu\n", simple_dai->name, rate);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-03-20 12:55:39 +08:00
|
|
|
if (!simple_dai->clk)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (clk_get_rate(simple_dai->clk) == rate)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return clk_set_rate(simple_dai->clk, rate);
|
|
|
|
}
|
|
|
|
|
2022-03-01 01:27:54 +08:00
|
|
|
static int asoc_simple_set_tdm(struct snd_soc_dai *dai,
|
|
|
|
struct asoc_simple_dai *simple_dai,
|
|
|
|
struct snd_pcm_hw_params *params)
|
|
|
|
{
|
|
|
|
int sample_bits = params_width(params);
|
2022-04-04 19:32:52 +08:00
|
|
|
int slot_width, slot_count;
|
2022-03-01 01:27:54 +08:00
|
|
|
int i, ret;
|
|
|
|
|
|
|
|
if (!simple_dai || !simple_dai->tdm_width_map)
|
|
|
|
return 0;
|
|
|
|
|
2022-04-04 19:32:52 +08:00
|
|
|
slot_width = simple_dai->slot_width;
|
|
|
|
slot_count = simple_dai->slots;
|
|
|
|
|
2022-03-01 01:27:54 +08:00
|
|
|
if (slot_width == 0)
|
|
|
|
slot_width = sample_bits;
|
|
|
|
|
|
|
|
for (i = 0; i < simple_dai->n_tdm_widths; ++i) {
|
|
|
|
if (simple_dai->tdm_width_map[i].sample_bits == sample_bits) {
|
|
|
|
slot_width = simple_dai->tdm_width_map[i].slot_width;
|
|
|
|
slot_count = simple_dai->tdm_width_map[i].slot_count;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = snd_soc_dai_set_tdm_slot(dai,
|
|
|
|
simple_dai->tx_slot_mask,
|
|
|
|
simple_dai->rx_slot_mask,
|
|
|
|
slot_count,
|
|
|
|
slot_width);
|
|
|
|
if (ret && ret != -ENOTSUPP) {
|
|
|
|
dev_err(dai->dev, "simple-card: set_tdm_slot error: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-20 12:55:39 +08:00
|
|
|
int asoc_simple_hw_params(struct snd_pcm_substream *substream,
|
|
|
|
struct snd_pcm_hw_params *params)
|
|
|
|
{
|
2020-07-20 09:19:32 +08:00
|
|
|
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
2021-04-12 07:52:09 +08:00
|
|
|
struct asoc_simple_dai *pdai;
|
|
|
|
struct snd_soc_dai *sdai;
|
2019-03-20 12:55:39 +08:00
|
|
|
struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
2021-04-12 07:52:09 +08:00
|
|
|
struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
|
2019-03-20 12:55:39 +08:00
|
|
|
unsigned int mclk, mclk_fs = 0;
|
2021-04-12 07:52:09 +08:00
|
|
|
int i, ret;
|
2019-03-20 12:55:39 +08:00
|
|
|
|
2021-04-12 07:52:09 +08:00
|
|
|
if (props->mclk_fs)
|
|
|
|
mclk_fs = props->mclk_fs;
|
2019-03-20 12:55:39 +08:00
|
|
|
|
|
|
|
if (mclk_fs) {
|
2022-01-21 03:58:30 +08:00
|
|
|
struct snd_soc_component *component;
|
2019-03-20 12:55:39 +08:00
|
|
|
mclk = params_rate(params) * mclk_fs;
|
|
|
|
|
2021-04-12 07:52:09 +08:00
|
|
|
for_each_prop_dai_codec(props, i, pdai) {
|
2022-01-21 03:58:32 +08:00
|
|
|
ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk);
|
2021-04-12 07:52:09 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
2022-01-21 03:58:30 +08:00
|
|
|
|
2021-04-12 07:52:09 +08:00
|
|
|
for_each_prop_dai_cpu(props, i, pdai) {
|
2022-01-21 03:58:32 +08:00
|
|
|
ret = asoc_simple_set_clk_rate(rtd->dev, pdai, mclk);
|
2021-04-12 07:52:09 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
2022-01-21 03:58:30 +08:00
|
|
|
|
|
|
|
/* Ensure sysclk is set on all components in case any
|
|
|
|
* (such as platform components) are missed by calls to
|
|
|
|
* snd_soc_dai_set_sysclk.
|
|
|
|
*/
|
|
|
|
for_each_rtd_components(rtd, i, component) {
|
|
|
|
ret = snd_soc_component_set_sysclk(component, 0, 0,
|
|
|
|
mclk, SND_SOC_CLOCK_IN);
|
|
|
|
if (ret && ret != -ENOTSUPP)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-04-12 07:52:09 +08:00
|
|
|
for_each_rtd_codec_dais(rtd, i, sdai) {
|
|
|
|
ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_IN);
|
|
|
|
if (ret && ret != -ENOTSUPP)
|
|
|
|
return ret;
|
|
|
|
}
|
2022-01-21 03:58:30 +08:00
|
|
|
|
2021-04-12 07:52:09 +08:00
|
|
|
for_each_rtd_cpu_dais(rtd, i, sdai) {
|
|
|
|
ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_OUT);
|
|
|
|
if (ret && ret != -ENOTSUPP)
|
|
|
|
return ret;
|
|
|
|
}
|
2019-03-20 12:55:39 +08:00
|
|
|
}
|
2022-03-01 01:27:54 +08:00
|
|
|
|
|
|
|
for_each_prop_dai_codec(props, i, pdai) {
|
|
|
|
sdai = asoc_rtd_to_codec(rtd, i);
|
|
|
|
ret = asoc_simple_set_tdm(sdai, pdai, params);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
for_each_prop_dai_cpu(props, i, pdai) {
|
|
|
|
sdai = asoc_rtd_to_cpu(rtd, i);
|
|
|
|
ret = asoc_simple_set_tdm(sdai, pdai, params);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-03-20 12:55:39 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_hw_params);
|
|
|
|
|
2019-03-20 12:56:06 +08:00
|
|
|
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);
|
|
|
|
|
2019-03-20 12:56:50 +08:00
|
|
|
asoc_simple_convert_fixup(&dai_props->adata, params);
|
2019-03-20 12:56:06 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_be_hw_params_fixup);
|
|
|
|
|
2019-03-20 12:56:50 +08:00
|
|
|
static int asoc_simple_init_dai(struct snd_soc_dai *dai,
|
2019-03-20 12:55:52 +08:00
|
|
|
struct asoc_simple_dai *simple_dai)
|
2016-08-09 13:48:30 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2018-11-21 10:09:16 +08:00
|
|
|
if (!simple_dai)
|
|
|
|
return 0;
|
|
|
|
|
2016-08-09 13:48:30 +08:00
|
|
|
if (simple_dai->sysclk) {
|
2017-08-17 19:42:36 +08:00
|
|
|
ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,
|
|
|
|
simple_dai->clk_direction);
|
2016-08-09 13:48:30 +08:00
|
|
|
if (ret && ret != -ENOTSUPP) {
|
|
|
|
dev_err(dai->dev, "simple-card: set_sysclk error\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (simple_dai->slots) {
|
|
|
|
ret = snd_soc_dai_set_tdm_slot(dai,
|
|
|
|
simple_dai->tx_slot_mask,
|
|
|
|
simple_dai->rx_slot_mask,
|
|
|
|
simple_dai->slots,
|
|
|
|
simple_dai->slot_width);
|
|
|
|
if (ret && ret != -ENOTSUPP) {
|
|
|
|
dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-03-20 12:55:52 +08:00
|
|
|
|
2020-03-05 13:11:43 +08:00
|
|
|
static int asoc_simple_init_dai_link_params(struct snd_soc_pcm_runtime *rtd,
|
|
|
|
struct simple_dai_props *dai_props)
|
|
|
|
{
|
|
|
|
struct snd_soc_dai_link *dai_link = rtd->dai_link;
|
|
|
|
struct snd_soc_component *component;
|
|
|
|
struct snd_soc_pcm_stream *params;
|
|
|
|
struct snd_pcm_hardware hw;
|
|
|
|
int i, ret, stream;
|
|
|
|
|
2021-10-18 10:05:24 +08:00
|
|
|
/* Only Codecs */
|
2020-03-05 13:11:43 +08:00
|
|
|
for_each_rtd_components(rtd, i, component) {
|
2021-10-18 10:05:24 +08:00
|
|
|
if (!snd_soc_component_is_codec(component))
|
2020-03-05 13:11:43 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assumes the capabilities are the same for all supported streams */
|
2020-03-09 12:02:37 +08:00
|
|
|
for_each_pcm_streams(stream) {
|
2020-03-05 13:11:43 +08:00
|
|
|
ret = snd_soc_runtime_calc_hw(rtd, &hw, stream);
|
|
|
|
if (ret == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(rtd->dev, "simple-card: no valid dai_link params\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
params = devm_kzalloc(rtd->dev, sizeof(*params), GFP_KERNEL);
|
|
|
|
if (!params)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
params->formats = hw.formats;
|
|
|
|
params->rates = hw.rates;
|
|
|
|
params->rate_min = hw.rate_min;
|
|
|
|
params->rate_max = hw.rate_max;
|
|
|
|
params->channels_min = hw.channels_min;
|
|
|
|
params->channels_max = hw.channels_max;
|
|
|
|
|
|
|
|
dai_link->params = params;
|
|
|
|
dai_link->num_params = 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-20 12:55:52 +08:00
|
|
|
int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|
|
|
{
|
|
|
|
struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
2021-04-12 07:52:09 +08:00
|
|
|
struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num);
|
|
|
|
struct asoc_simple_dai *dai;
|
|
|
|
int i, ret;
|
2019-03-20 12:55:52 +08:00
|
|
|
|
2021-04-12 07:52:09 +08:00
|
|
|
for_each_prop_dai_codec(props, i, dai) {
|
|
|
|
ret = asoc_simple_init_dai(asoc_rtd_to_codec(rtd, i), dai);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
for_each_prop_dai_cpu(props, i, dai) {
|
|
|
|
ret = asoc_simple_init_dai(asoc_rtd_to_cpu(rtd, i), dai);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
2019-03-20 12:55:52 +08:00
|
|
|
|
2021-04-12 07:52:09 +08:00
|
|
|
ret = asoc_simple_init_dai_link_params(rtd, props);
|
2020-03-05 13:11:43 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2019-03-20 12:55:52 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_dai_init);
|
2016-08-09 13:48:30 +08:00
|
|
|
|
2021-04-12 07:52:45 +08:00
|
|
|
void asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component *platforms,
|
|
|
|
struct snd_soc_dai_link_component *cpus)
|
2016-08-09 13:49:41 +08:00
|
|
|
{
|
2019-06-28 09:49:44 +08:00
|
|
|
/* Assumes platform == cpu */
|
2021-04-12 07:52:45 +08:00
|
|
|
if (!platforms->of_node)
|
|
|
|
platforms->of_node = cpus->of_node;
|
2016-08-09 13:49:41 +08:00
|
|
|
}
|
2019-03-20 12:56:50 +08:00
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform);
|
2016-08-09 13:49:41 +08:00
|
|
|
|
2021-04-12 07:52:45 +08:00
|
|
|
void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link_component *cpus,
|
2019-03-20 12:56:50 +08:00
|
|
|
int is_single_links)
|
2016-08-10 10:20:19 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* In soc_bind_dai_link() will check cpu name after
|
|
|
|
* of_node matching if dai_link has cpu_dai_name.
|
|
|
|
* but, it will never match if name was created by
|
|
|
|
* fmt_single_name() remove cpu_dai_name if cpu_args
|
|
|
|
* was 0. See:
|
|
|
|
* fmt_single_name()
|
|
|
|
* fmt_multiple_name()
|
|
|
|
*/
|
|
|
|
if (is_single_links)
|
2021-04-12 07:52:45 +08:00
|
|
|
cpus->dai_name = NULL;
|
2016-08-10 10:20:19 +08:00
|
|
|
}
|
2019-03-20 12:56:50 +08:00
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_cpu);
|
2016-08-10 10:20:19 +08:00
|
|
|
|
2019-03-20 12:56:50 +08:00
|
|
|
int asoc_simple_clean_reference(struct snd_soc_card *card)
|
2016-08-10 10:21:25 +08:00
|
|
|
{
|
|
|
|
struct snd_soc_dai_link *dai_link;
|
2021-04-12 07:52:18 +08:00
|
|
|
struct snd_soc_dai_link_component *cpu;
|
|
|
|
struct snd_soc_dai_link_component *codec;
|
|
|
|
int i, j;
|
2016-08-10 10:21:25 +08:00
|
|
|
|
2018-09-18 09:28:49 +08:00
|
|
|
for_each_card_prelinks(card, i, dai_link) {
|
2021-04-12 07:52:18 +08:00
|
|
|
for_each_link_cpus(dai_link, j, cpu)
|
|
|
|
of_node_put(cpu->of_node);
|
|
|
|
for_each_link_codecs(dai_link, j, codec)
|
|
|
|
of_node_put(codec->of_node);
|
2016-08-10 10:21:25 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2019-03-20 12:56:50 +08:00
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_clean_reference);
|
2016-08-10 10:21:25 +08:00
|
|
|
|
2019-03-20 12:56:50 +08:00
|
|
|
int asoc_simple_parse_routing(struct snd_soc_card *card,
|
|
|
|
char *prefix)
|
2017-06-15 08:25:02 +08:00
|
|
|
{
|
|
|
|
struct device_node *node = card->dev->of_node;
|
|
|
|
char prop[128];
|
|
|
|
|
|
|
|
if (!prefix)
|
|
|
|
prefix = "";
|
|
|
|
|
|
|
|
snprintf(prop, sizeof(prop), "%s%s", prefix, "routing");
|
|
|
|
|
2018-11-21 10:11:13 +08:00
|
|
|
if (!of_property_read_bool(node, prop))
|
|
|
|
return 0;
|
2017-06-15 08:25:02 +08:00
|
|
|
|
|
|
|
return snd_soc_of_parse_audio_routing(card, prop);
|
|
|
|
}
|
2019-03-20 12:56:50 +08:00
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_parse_routing);
|
2017-06-15 08:25:02 +08:00
|
|
|
|
2019-03-20 12:56:50 +08:00
|
|
|
int asoc_simple_parse_widgets(struct snd_soc_card *card,
|
|
|
|
char *prefix)
|
2017-06-16 09:38:50 +08:00
|
|
|
{
|
|
|
|
struct device_node *node = card->dev->of_node;
|
|
|
|
char prop[128];
|
|
|
|
|
|
|
|
if (!prefix)
|
|
|
|
prefix = "";
|
|
|
|
|
|
|
|
snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets");
|
|
|
|
|
|
|
|
if (of_property_read_bool(node, prop))
|
|
|
|
return snd_soc_of_parse_audio_simple_widgets(card, prop);
|
|
|
|
|
|
|
|
/* no widgets is not error */
|
|
|
|
return 0;
|
|
|
|
}
|
2019-03-20 12:56:50 +08:00
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_parse_widgets);
|
2017-06-16 09:38:50 +08:00
|
|
|
|
2019-04-26 10:25:49 +08:00
|
|
|
int asoc_simple_parse_pin_switches(struct snd_soc_card *card,
|
|
|
|
char *prefix)
|
|
|
|
{
|
|
|
|
char prop[128];
|
|
|
|
|
|
|
|
if (!prefix)
|
|
|
|
prefix = "";
|
|
|
|
|
|
|
|
snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches");
|
|
|
|
|
2021-12-14 22:20:46 +08:00
|
|
|
return snd_soc_of_parse_pin_switches(card, prop);
|
2019-04-26 10:25:49 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_parse_pin_switches);
|
|
|
|
|
2019-03-20 12:56:50 +08:00
|
|
|
int asoc_simple_init_jack(struct snd_soc_card *card,
|
|
|
|
struct asoc_simple_jack *sjack,
|
2020-07-15 22:09:37 +08:00
|
|
|
int is_hp, char *prefix,
|
|
|
|
char *pin)
|
2018-06-11 16:32:12 +08:00
|
|
|
{
|
|
|
|
struct device *dev = card->dev;
|
|
|
|
enum of_gpio_flags flags;
|
|
|
|
char prop[128];
|
|
|
|
char *pin_name;
|
|
|
|
char *gpio_name;
|
|
|
|
int mask;
|
|
|
|
int det;
|
|
|
|
|
|
|
|
if (!prefix)
|
|
|
|
prefix = "";
|
|
|
|
|
|
|
|
sjack->gpio.gpio = -ENOENT;
|
|
|
|
|
|
|
|
if (is_hp) {
|
|
|
|
snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix);
|
2020-07-15 22:09:37 +08:00
|
|
|
pin_name = pin ? pin : "Headphones";
|
2018-06-11 16:32:12 +08:00
|
|
|
gpio_name = "Headphone detection";
|
|
|
|
mask = SND_JACK_HEADPHONE;
|
|
|
|
} else {
|
|
|
|
snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix);
|
2020-07-15 22:09:37 +08:00
|
|
|
pin_name = pin ? pin : "Mic Jack";
|
2018-06-11 16:32:12 +08:00
|
|
|
gpio_name = "Mic detection";
|
|
|
|
mask = SND_JACK_MICROPHONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags);
|
|
|
|
if (det == -EPROBE_DEFER)
|
|
|
|
return -EPROBE_DEFER;
|
|
|
|
|
|
|
|
if (gpio_is_valid(det)) {
|
|
|
|
sjack->pin.pin = pin_name;
|
|
|
|
sjack->pin.mask = mask;
|
|
|
|
|
|
|
|
sjack->gpio.name = gpio_name;
|
|
|
|
sjack->gpio.report = mask;
|
|
|
|
sjack->gpio.gpio = det;
|
|
|
|
sjack->gpio.invert = !!(flags & OF_GPIO_ACTIVE_LOW);
|
|
|
|
sjack->gpio.debounce_time = 150;
|
|
|
|
|
|
|
|
snd_soc_card_jack_new(card, pin_name, mask,
|
|
|
|
&sjack->jack,
|
|
|
|
&sjack->pin, 1);
|
|
|
|
|
|
|
|
snd_soc_jack_add_gpios(&sjack->jack, 1,
|
|
|
|
&sjack->gpio);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-03-20 12:56:50 +08:00
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_init_jack);
|
2018-06-11 16:32:12 +08:00
|
|
|
|
2019-03-20 12:56:50 +08:00
|
|
|
int asoc_simple_init_priv(struct asoc_simple_priv *priv,
|
|
|
|
struct link_info *li)
|
2019-03-20 12:56:26 +08:00
|
|
|
{
|
|
|
|
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;
|
2021-03-26 11:26:06 +08:00
|
|
|
struct snd_soc_dai_link_component *dlcs;
|
2019-03-20 12:57:02 +08:00
|
|
|
struct snd_soc_codec_conf *cconf = NULL;
|
2021-10-12 12:54:13 +08:00
|
|
|
struct snd_soc_pcm_stream *c2c_conf = NULL;
|
|
|
|
int i, dai_num = 0, dlc_num = 0, cnf_num = 0, c2c_num = 0;
|
2019-03-20 12:56:26 +08:00
|
|
|
|
|
|
|
dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);
|
|
|
|
dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL);
|
2021-04-01 12:15:23 +08:00
|
|
|
if (!dai_props || !dai_link)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* dais (= CPU+Codec)
|
|
|
|
* dlcs (= CPU+Codec+Platform)
|
|
|
|
*/
|
|
|
|
for (i = 0; i < li->link; i++) {
|
|
|
|
int cc = li->num[i].cpus + li->num[i].codecs;
|
|
|
|
|
|
|
|
dai_num += cc;
|
|
|
|
dlc_num += cc + li->num[i].platforms;
|
2021-04-12 07:52:04 +08:00
|
|
|
|
|
|
|
if (!li->num[i].cpus)
|
|
|
|
cnf_num += li->num[i].codecs;
|
2021-10-12 12:54:13 +08:00
|
|
|
|
|
|
|
c2c_num += li->num[i].c2c;
|
2021-04-01 12:15:23 +08:00
|
|
|
}
|
|
|
|
|
2021-08-05 13:07:06 +08:00
|
|
|
dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL);
|
|
|
|
dlcs = devm_kcalloc(dev, dlc_num, sizeof(*dlcs), GFP_KERNEL);
|
2021-04-01 12:15:23 +08:00
|
|
|
if (!dais || !dlcs)
|
2019-03-20 12:56:26 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2021-04-12 07:52:04 +08:00
|
|
|
if (cnf_num) {
|
|
|
|
cconf = devm_kcalloc(dev, cnf_num, sizeof(*cconf), GFP_KERNEL);
|
2019-03-20 12:57:02 +08:00
|
|
|
if (!cconf)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2021-10-12 12:54:13 +08:00
|
|
|
if (c2c_num) {
|
|
|
|
c2c_conf = devm_kcalloc(dev, c2c_num, sizeof(*c2c_conf), GFP_KERNEL);
|
|
|
|
if (!c2c_conf)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2021-04-12 07:52:04 +08:00
|
|
|
dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
|
|
|
|
li->link, dai_num, cnf_num);
|
|
|
|
|
2021-04-01 12:15:33 +08:00
|
|
|
/* dummy CPU/Codec */
|
|
|
|
priv->dummy.of_node = NULL;
|
|
|
|
priv->dummy.dai_name = "snd-soc-dummy-dai";
|
|
|
|
priv->dummy.name = "snd-soc-dummy";
|
|
|
|
|
2019-03-20 12:56:26 +08:00
|
|
|
priv->dai_props = dai_props;
|
|
|
|
priv->dai_link = dai_link;
|
|
|
|
priv->dais = dais;
|
2021-03-26 11:26:06 +08:00
|
|
|
priv->dlcs = dlcs;
|
2019-03-20 12:56:26 +08:00
|
|
|
priv->codec_conf = cconf;
|
2021-10-12 12:54:13 +08:00
|
|
|
priv->c2c_conf = c2c_conf;
|
2019-03-20 12:56:26 +08:00
|
|
|
|
|
|
|
card->dai_link = priv->dai_link;
|
|
|
|
card->num_links = li->link;
|
|
|
|
card->codec_conf = cconf;
|
2021-04-12 07:52:04 +08:00
|
|
|
card->num_configs = cnf_num;
|
2019-03-20 12:56:26 +08:00
|
|
|
|
2021-04-01 12:15:23 +08:00
|
|
|
for (i = 0; i < li->link; i++) {
|
|
|
|
if (li->num[i].cpus) {
|
|
|
|
/* Normal CPU */
|
|
|
|
dai_props[i].cpus =
|
|
|
|
dai_link[i].cpus = dlcs;
|
|
|
|
dai_props[i].num.cpus =
|
|
|
|
dai_link[i].num_cpus = li->num[i].cpus;
|
2021-04-12 07:51:59 +08:00
|
|
|
dai_props[i].cpu_dai = dais;
|
2021-04-01 12:15:23 +08:00
|
|
|
|
|
|
|
dlcs += li->num[i].cpus;
|
2021-04-12 07:51:59 +08:00
|
|
|
dais += li->num[i].cpus;
|
2021-10-12 12:54:13 +08:00
|
|
|
|
|
|
|
if (li->num[i].c2c) {
|
|
|
|
/* Codec2Codec */
|
|
|
|
dai_props[i].c2c_conf = c2c_conf;
|
|
|
|
c2c_conf += li->num[i].c2c;
|
|
|
|
}
|
2021-04-01 12:15:33 +08:00
|
|
|
} else {
|
|
|
|
/* DPCM Be's CPU = dummy */
|
|
|
|
dai_props[i].cpus =
|
|
|
|
dai_link[i].cpus = &priv->dummy;
|
|
|
|
dai_props[i].num.cpus =
|
|
|
|
dai_link[i].num_cpus = 1;
|
2021-04-01 12:15:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (li->num[i].codecs) {
|
|
|
|
/* Normal Codec */
|
|
|
|
dai_props[i].codecs =
|
|
|
|
dai_link[i].codecs = dlcs;
|
|
|
|
dai_props[i].num.codecs =
|
|
|
|
dai_link[i].num_codecs = li->num[i].codecs;
|
2021-04-12 07:51:59 +08:00
|
|
|
dai_props[i].codec_dai = dais;
|
2021-04-01 12:15:23 +08:00
|
|
|
|
|
|
|
dlcs += li->num[i].codecs;
|
2021-04-12 07:51:59 +08:00
|
|
|
dais += li->num[i].codecs;
|
|
|
|
|
|
|
|
if (!li->num[i].cpus) {
|
|
|
|
/* DPCM Be's Codec */
|
|
|
|
dai_props[i].codec_conf = cconf;
|
|
|
|
cconf += li->num[i].codecs;
|
|
|
|
}
|
2021-04-01 12:15:33 +08:00
|
|
|
} else {
|
|
|
|
/* DPCM Fe's Codec = dummy */
|
|
|
|
dai_props[i].codecs =
|
|
|
|
dai_link[i].codecs = &priv->dummy;
|
|
|
|
dai_props[i].num.codecs =
|
|
|
|
dai_link[i].num_codecs = 1;
|
2021-04-01 12:15:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (li->num[i].platforms) {
|
|
|
|
/* Have Platform */
|
|
|
|
dai_props[i].platforms =
|
|
|
|
dai_link[i].platforms = dlcs;
|
|
|
|
dai_props[i].num.platforms =
|
|
|
|
dai_link[i].num_platforms = li->num[i].platforms;
|
|
|
|
|
|
|
|
dlcs += li->num[i].platforms;
|
2021-04-01 12:15:33 +08:00
|
|
|
} else {
|
|
|
|
/* Doesn't have Platform */
|
|
|
|
dai_props[i].platforms =
|
|
|
|
dai_link[i].platforms = NULL;
|
|
|
|
dai_props[i].num.platforms =
|
|
|
|
dai_link[i].num_platforms = 0;
|
2021-04-01 12:15:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-20 12:56:26 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_init_priv);
|
|
|
|
|
2021-04-19 10:02:25 +08:00
|
|
|
int asoc_simple_remove(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
|
|
|
|
|
|
|
return asoc_simple_clean_reference(card);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(asoc_simple_remove);
|
|
|
|
|
2021-04-19 10:02:19 +08:00
|
|
|
int asoc_graph_card_probe(struct snd_soc_card *card)
|
|
|
|
{
|
|
|
|
struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = asoc_simple_init_hp(card, &priv->hp_jack, NULL);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = asoc_simple_init_mic(card, &priv->mic_jack, NULL);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(asoc_graph_card_probe);
|
|
|
|
|
2021-10-12 12:54:01 +08:00
|
|
|
int asoc_graph_is_ports0(struct device_node *np)
|
|
|
|
{
|
|
|
|
struct device_node *port, *ports, *ports0, *top;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* np is "endpoint" or "port" */
|
|
|
|
if (of_node_name_eq(np, "endpoint")) {
|
|
|
|
port = of_get_parent(np);
|
|
|
|
} else {
|
|
|
|
port = np;
|
|
|
|
of_node_get(port);
|
|
|
|
}
|
|
|
|
|
|
|
|
ports = of_get_parent(port);
|
|
|
|
top = of_get_parent(ports);
|
|
|
|
ports0 = of_get_child_by_name(top, "ports");
|
|
|
|
|
|
|
|
ret = ports0 == ports;
|
|
|
|
|
|
|
|
of_node_put(port);
|
|
|
|
of_node_put(ports);
|
|
|
|
of_node_put(ports0);
|
|
|
|
of_node_put(top);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(asoc_graph_is_ports0);
|
|
|
|
|
2016-08-03 09:24:05 +08:00
|
|
|
/* Module information */
|
|
|
|
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
|
|
|
|
MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
|
|
|
|
MODULE_LICENSE("GPL v2");
|