diff options
Diffstat (limited to 'sound/soc/generic/simple-card-utils.c')
-rw-r--r-- | sound/soc/generic/simple-card-utils.c | 226 |
1 files changed, 149 insertions, 77 deletions
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 81077d16d22f..dd414634b4ac 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -4,6 +4,8 @@ // // Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +#include <dt-bindings/sound/audio-graph.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/gpio/consumer.h> #include <linux/module.h> @@ -13,12 +15,11 @@ #include <sound/pcm_params.h> #include <sound/simple_card_utils.h> -static void simple_fixup_sample_fmt(struct simple_util_data *data, - struct snd_pcm_hw_params *params) +int simple_util_get_sample_fmt(struct simple_util_data *data) { int i; - struct snd_mask *mask = hw_param_mask(params, - SNDRV_PCM_HW_PARAM_FORMAT); + int val = -EINVAL; + struct { char *fmt; u32 val; @@ -33,11 +34,26 @@ static void simple_fixup_sample_fmt(struct simple_util_data *data, for (i = 0; i < ARRAY_SIZE(of_sample_fmt_table); i++) { if (!strcmp(data->convert_sample_format, of_sample_fmt_table[i].fmt)) { - snd_mask_none(mask); - snd_mask_set(mask, of_sample_fmt_table[i].val); + val = of_sample_fmt_table[i].val; break; } } + return val; +} +EXPORT_SYMBOL_GPL(simple_util_get_sample_fmt); + +static void simple_fixup_sample_fmt(struct simple_util_data *data, + struct snd_pcm_hw_params *params) +{ + int val; + struct snd_mask *mask = hw_param_mask(params, + SNDRV_PCM_HW_PARAM_FORMAT); + + val = simple_util_get_sample_fmt(data); + if (val >= 0) { + snd_mask_none(mask); + snd_mask_set(mask, val); + } } void simple_util_parse_convert(struct device_node *np, @@ -46,6 +62,9 @@ void simple_util_parse_convert(struct device_node *np, { char prop[128]; + if (!np) + return; + if (!prefix) prefix = ""; @@ -117,13 +136,12 @@ EXPORT_SYMBOL_GPL(simple_util_parse_daifmt); int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np, struct simple_util_dai *dai) { - u32 *array_values, *p; int n, i, ret; - - if (!of_property_read_bool(np, "dai-tdm-slot-width-map")) - return 0; + u32 *p; n = of_property_count_elems_of_size(np, "dai-tdm-slot-width-map", sizeof(u32)); + if (n <= 0) + return 0; if (n % 3) { dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n"); return -EINVAL; @@ -133,14 +151,15 @@ int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np, if (!dai->tdm_width_map) return -ENOMEM; - array_values = kcalloc(n, sizeof(*array_values), GFP_KERNEL); + u32 *array_values __free(kfree) = 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; + return ret; } p = array_values; @@ -151,11 +170,8 @@ int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np, } dai->n_tdm_widths = i; - ret = 0; -out: - kfree(array_values); - return ret; + return 0; } EXPORT_SYMBOL_GPL(simple_util_parse_tdm_width_map); @@ -279,7 +295,7 @@ int simple_util_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); + struct simple_dai_props *props = runtime_simple_priv_to_props(priv, rtd); struct simple_util_dai *dai; unsigned int fixed_sysclk = 0; int i1, i2, i; @@ -340,7 +356,7 @@ void simple_util_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); + struct simple_dai_props *props = runtime_simple_priv_to_props(priv, rtd); struct simple_util_dai *dai; int i; @@ -348,8 +364,7 @@ void simple_util_shutdown(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = snd_soc_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, - 0, 0, SND_SOC_CLOCK_OUT); + snd_soc_dai_set_sysclk(cpu_dai, 0, 0, dai->clk_direction); simple_clk_disable(dai); } @@ -357,8 +372,7 @@ void simple_util_shutdown(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = snd_soc_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, - 0, 0, SND_SOC_CLOCK_IN); + snd_soc_dai_set_sysclk(codec_dai, 0, 0, dai->clk_direction); simple_clk_disable(dai); } @@ -431,7 +445,7 @@ int simple_util_hw_params(struct snd_pcm_substream *substream, struct simple_util_dai *pdai; struct snd_soc_dai *sdai; struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); + struct simple_dai_props *props = runtime_simple_priv_to_props(priv, rtd); unsigned int mclk, mclk_fs = 0; int i, ret; @@ -466,13 +480,15 @@ int simple_util_hw_params(struct snd_pcm_substream *substream, } for_each_rtd_codec_dais(rtd, i, sdai) { - ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_IN); + pdai = simple_props_to_dai_codec(props, i); + ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); if (ret && ret != -ENOTSUPP) return ret; } for_each_rtd_cpu_dais(rtd, i, sdai) { - ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_OUT); + pdai = simple_props_to_dai_cpu(props, i); + ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); if (ret && ret != -ENOTSUPP) return ret; } @@ -500,7 +516,7 @@ int simple_util_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); + struct simple_dai_props *dai_props = runtime_simple_priv_to_props(priv, rtd); struct simple_util_data *data = &dai_props->adata; 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); @@ -611,7 +627,7 @@ static int simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd, int simple_util_dai_init(struct snd_soc_pcm_runtime *rtd) { struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); + struct simple_dai_props *props = runtime_simple_priv_to_props(priv, rtd); struct simple_util_dai *dai; int i, ret; @@ -696,7 +712,7 @@ int simple_util_parse_routing(struct snd_soc_card *card, snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); - if (!of_property_read_bool(node, prop)) + if (!of_property_present(node, prop)) return 0; return snd_soc_of_parse_audio_routing(card, prop); @@ -714,7 +730,7 @@ int simple_util_parse_widgets(struct snd_soc_card *card, snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets"); - if (of_property_read_bool(node, prop)) + if (of_property_present(node, prop)) return snd_soc_of_parse_audio_simple_widgets(card, prop); /* no widgets is not error */ @@ -752,8 +768,6 @@ int simple_util_init_jack(struct snd_soc_card *card, if (!prefix) prefix = ""; - sjack->gpio.gpio = -ENOENT; - if (is_hp) { snprintf(prop, sizeof(prop), "%shp-det", prefix); pin_name = pin ? pin : "Headphones"; @@ -843,6 +857,10 @@ int simple_util_init_aux_jacks(struct simple_util_priv *priv, char *prefix) } EXPORT_SYMBOL_GPL(simple_util_init_aux_jacks); +static struct simple_util_dai dummy_util_dais = { + .name = "dummy_util_dais", +}; + int simple_util_init_priv(struct simple_util_priv *priv, struct link_info *li) { @@ -914,6 +932,7 @@ int simple_util_init_priv(struct simple_util_priv *priv, dai_link[i].cpus = &snd_soc_dummy_dlc; dai_props[i].num.cpus = dai_link[i].num_cpus = 1; + dai_props[i].cpu_dai = &dummy_util_dais; } if (li->num[i].codecs) { @@ -936,6 +955,7 @@ int simple_util_init_priv(struct simple_util_priv *priv, dai_link[i].codecs = &snd_soc_dummy_dlc; dai_props[i].num.codecs = dai_link[i].num_codecs = 1; + dai_props[i].codec_dai = &dummy_util_dais; } if (li->num[i].platforms) { @@ -984,36 +1004,27 @@ EXPORT_SYMBOL_GPL(graph_util_card_probe); int graph_util_is_ports0(struct device_node *np) { - struct device_node *port, *ports, *ports0, *top; - int ret; + struct device_node *parent __free(device_node) = of_get_parent(np); + struct device_node *port; /* np is "endpoint" or "port" */ - if (of_node_name_eq(np, "endpoint")) { - port = of_get_parent(np); - } else { + if (of_node_name_eq(np, "endpoint")) + port = parent; + 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"); + struct device_node *ports __free(device_node) = of_get_parent(port); + struct device_node *top __free(device_node) = of_get_parent(ports); + struct device_node *ports0 __free(device_node) = 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; + return ports0 == ports; } EXPORT_SYMBOL_GPL(graph_util_is_ports0); static int graph_get_dai_id(struct device_node *ep) { - struct device_node *node; - struct device_node *endpoint; + struct device_node *node __free(device_node) = of_graph_get_port_parent(ep); + struct device_node *port __free(device_node) = of_get_parent(ep); struct of_endpoint info; int i, id; int ret; @@ -1032,16 +1043,16 @@ static int graph_get_dai_id(struct device_node *ep) * only of_graph_parse_endpoint(). * We need to check "reg" property */ - if (of_property_present(ep, "reg")) - return info.id; - node = of_get_parent(ep); - ret = of_property_present(node, "reg"); - of_node_put(node); + /* check port first */ + ret = of_property_present(port, "reg"); if (ret) return info.port; + + /* check endpoint 2nd as backup */ + if (of_property_present(ep, "reg")) + return info.id; } - node = of_graph_get_port_parent(ep); /* * Non HDMI sound case, counting port/endpoint on its DT @@ -1049,14 +1060,14 @@ static int graph_get_dai_id(struct device_node *ep) */ i = 0; id = -1; - for_each_endpoint_of_node(node, endpoint) { - if (endpoint == ep) + for_each_of_graph_port(node, p) { + if (port == p) { id = i; + break; + } i++; } - of_node_put(node); - if (id < 0) return -ENODEV; @@ -1066,7 +1077,6 @@ static int graph_get_dai_id(struct device_node *ep) int graph_util_parse_dai(struct device *dev, struct device_node *ep, struct snd_soc_dai_link_component *dlc, int *is_single_link) { - struct device_node *node; struct of_phandle_args args = {}; struct snd_soc_dai *dai; int ret; @@ -1074,7 +1084,7 @@ int graph_util_parse_dai(struct device *dev, struct device_node *ep, if (!ep) return 0; - node = of_graph_get_port_parent(ep); + struct device_node *node __free(device_node) = of_graph_get_port_parent(ep); /* * Try to find from DAI node @@ -1115,10 +1125,8 @@ int graph_util_parse_dai(struct device *dev, struct device_node *ep, * if he unbinded CPU or Codec. */ ret = snd_soc_get_dlc(&args, dlc); - if (ret < 0) { - of_node_put(node); + if (ret < 0) return ret; - } parse_dai_end: if (is_single_link) @@ -1128,24 +1136,88 @@ parse_dai_end: } EXPORT_SYMBOL_GPL(graph_util_parse_dai); -int graph_util_parse_link_direction(struct device_node *np, +void graph_util_parse_link_direction(struct device_node *np, bool *playback_only, bool *capture_only) { - bool is_playback_only = false; - bool is_capture_only = false; + bool is_playback_only = of_property_read_bool(np, "playback-only"); + bool is_capture_only = of_property_read_bool(np, "capture-only"); - is_playback_only = of_property_read_bool(np, "playback-only"); - is_capture_only = of_property_read_bool(np, "capture-only"); + if (is_playback_only) + *playback_only = is_playback_only; + if (is_capture_only) + *capture_only = is_capture_only; +} +EXPORT_SYMBOL_GPL(graph_util_parse_link_direction); - if (is_playback_only && is_capture_only) - return -EINVAL; +static enum snd_soc_trigger_order +__graph_util_parse_trigger_order(struct simple_util_priv *priv, + struct device_node *np, + const char *prop) +{ + u32 val[SND_SOC_TRIGGER_SIZE]; + int ret; + + ret = of_property_read_u32_array(np, prop, val, SND_SOC_TRIGGER_SIZE); + if (ret == 0) { + struct device *dev = simple_priv_to_dev(priv); + u32 order = (val[0] << 8) + + (val[1] << 4) + + (val[2]); + + switch (order) { + case (SND_SOC_TRIGGER_LINK << 8) + + (SND_SOC_TRIGGER_COMPONENT << 4) + + (SND_SOC_TRIGGER_DAI): + return SND_SOC_TRIGGER_ORDER_DEFAULT; + + case (SND_SOC_TRIGGER_LINK << 8) + + (SND_SOC_TRIGGER_DAI << 4) + + (SND_SOC_TRIGGER_COMPONENT): + return SND_SOC_TRIGGER_ORDER_LDC; + + default: + dev_err(dev, "unsupported trigger order [0x%x]\n", order); + } + } - *playback_only = is_playback_only; - *capture_only = is_capture_only; + /* SND_SOC_TRIGGER_ORDER_MAX means error */ + return SND_SOC_TRIGGER_ORDER_MAX; +} - return 0; +void graph_util_parse_trigger_order(struct simple_util_priv *priv, + struct device_node *np, + enum snd_soc_trigger_order *trigger_start, + enum snd_soc_trigger_order *trigger_stop) +{ + static enum snd_soc_trigger_order order; + + /* + * We can use it like below + * + * #include <dt-bindings/sound/audio-graph.h> + * + * link-trigger-order = <SND_SOC_TRIGGER_LINK + * SND_SOC_TRIGGER_COMPONENT + * SND_SOC_TRIGGER_DAI>; + */ + + order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order"); + if (order < SND_SOC_TRIGGER_ORDER_MAX) { + *trigger_start = order; + *trigger_stop = order; + } + + order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order-start"); + if (order < SND_SOC_TRIGGER_ORDER_MAX) + *trigger_start = order; + + order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order-stop"); + if (order < SND_SOC_TRIGGER_ORDER_MAX) + *trigger_stop = order; + + return; } -EXPORT_SYMBOL_GPL(graph_util_parse_link_direction); +EXPORT_SYMBOL_GPL(graph_util_parse_trigger_order); /* Module information */ MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); |