// SPDX-License-Identifier: GPL-2.0+ // // soc-core.c -- ALSA SoC Audio Layer // // Copyright 2005 Wolfson Microelectronics PLC. // Copyright 2005 Openedhand Ltd. // Copyright (C) 2010 Slimlogic Ltd. // Copyright (C) 2010 Texas Instruments Inc. // // Author: Liam Girdwood <lrg@slimlogic.co.uk> // with code, comments and ideas from :- // Richard Purdie <richard@openedhand.com> // // TODO: // o Add hw rules to enforce rates, etc. // o More testing with other codecs/machines. // o Add more codecs and platforms to ensure good API coverage. // o Support TDM on PCM and I2S
/* are we waiting on this codec DAI stream */ if (rtd->pop_wait == 1) {
rtd->pop_wait = 0;
snd_soc_dapm_stream_event(rtd, playback,
SND_SOC_DAPM_STREAM_STOP);
}
/* Recheck all endpoints too, their state is affected by suspend */
snd_soc_dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm);
/* suspend all COMPONENTs */
for_each_card_rtds(card, rtd) {
if (rtd->dai_link->ignore_suspend) continue;
for_each_rtd_components(rtd, i, component) { struct snd_soc_dapm_context *dapm =
snd_soc_component_get_dapm(component);
/* *ignoreifcomponentwasalreadysuspended
*/ if (snd_soc_component_is_suspended(component)) continue;
/* *IftherearepathsactivethentheCOMPONENTwillbe *heldwithbias_ONandshouldnotbesuspended.
*/ switch (snd_soc_dapm_get_bias_level(dapm)) { case SND_SOC_BIAS_STANDBY: /* *IftheCOMPONENTiscapableofidle *biasoffthenbeinginSTANDBY *meansit'sdoingsomething, *otherwisefallthrough.
*/ if (dapm->idle_bias_off) {
dev_dbg(component->dev, "ASoC: idle_bias_off CODEC on over suspend\n"); break;
}
fallthrough;
case SND_SOC_BIAS_OFF:
snd_soc_component_suspend(component); if (component->regmap)
regcache_mark_dirty(component->regmap); /* deactivate pins to sleep state */
pinctrl_pm_select_sleep_state(component->dev); break; default:
dev_dbg(component->dev, "ASoC: COMPONENT is on over suspend\n"); break;
}
}
}
/* unmute any active DACs */
soc_playback_digital_mute(card, 0);
snd_soc_card_resume_post(card);
dev_dbg(card->dev, "ASoC: resume work completed\n");
/* Recheck all endpoints too, their state is affected by suspend */
snd_soc_dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm);
/* userspace can access us now we are back as we were before */
snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
}
/* powers up audio subsystem after a suspend */ int snd_soc_resume(struct device *dev)
{ struct snd_soc_card *card = dev_get_drvdata(dev); struct snd_soc_component *component;
/* If the card is not initialized yet there is nothing to do */ if (!snd_soc_card_is_instantiated(card)) return0;
/* activate pins from sleep state */
for_each_card_components(card, component) if (snd_soc_component_active(component))
pinctrl_pm_select_default_state(component->dev);
dev_dbg(dev, "ASoC: Scheduling resume work\n"); if (!schedule_work(&card->deferred_resume_work))
dev_err(dev, "ASoC: resume work item may be lost\n");
/* Find CPU DAI from registered DAIs */
for_each_component(component) if (snd_soc_is_matching_component(dlc, component))
for_each_component_dais(component, dai) if (snd_soc_is_matching_dai(dlc, dai)) return dai;
/* it should have ch_maps if connection was N:M */ if (dai_link->num_cpus > 1 && dai_link->num_codecs > 1 &&
dai_link->num_cpus != dai_link->num_codecs && !dai_link->ch_maps) {
dev_err(card->dev, "need to have ch_maps when N:M connection (%s)",
dai_link->name); return -EINVAL;
}
/* do nothing if it has own maps */ if (dai_link->ch_maps) goto sanity_check;
int snd_soc_add_pcm_runtimes(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link, int num_dai_link)
{ for (int i = 0; i < num_dai_link; i++) { int ret;
ret = snd_soc_compensate_channel_connection_map(card, dai_link + i); if (ret < 0) return ret;
ret = snd_soc_add_pcm_runtime(card, dai_link + i); if (ret < 0) return ret;
}
/* Flip the polarity for the "CPU" end of link */ /* Will effect only for 4. SND_SOC_DAIFMT_CLOCK_PROVIDER */
dai_fmt = snd_soc_daifmt_clock_provider_flipped(dai_fmt);
for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
ext_fmt = rtd->dai_link->cpus[i].ext_fmt;
ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt | ext_fmt); if (ret != 0 && ret != -ENOTSUPP) return ret;
}
if (snd_soc_component_is_dummy(component)) return0;
if (component->card) { if (component->card != card) {
dev_err(component->dev, "Trying to bind component \"%s\" to card \"%s\" but is already bound to card \"%s\"\n",
component->name, card->name, component->card->name); return -ENODEV;
} return0;
}
ret = snd_soc_component_module_get_when_probe(component); if (ret < 0) return ret;
ret = snd_soc_dapm_new_controls(dapm,
component->driver->dapm_widgets,
component->driver->num_dapm_widgets);
if (ret != 0) {
dev_err(component->dev, "Failed to create new controls %d\n", ret); goto err_probe;
}
for_each_component_dais(component, dai) {
ret = snd_soc_dapm_new_dai_widgets(dapm, dai); if (ret != 0) {
dev_err(component->dev, "Failed to create DAI widgets %d\n", ret); goto err_probe;
}
}
ret = snd_soc_component_probe(component); if (ret < 0) goto err_probe;
WARN(dapm->idle_bias_off &&
dapm->bias_level != SND_SOC_BIAS_OFF, "codec %s can not start from non-off bias with idle_bias_off==1\n",
component->name);
probed = 1;
/* *machinespecificinit *see *snd_soc_component_set_aux()
*/
ret = snd_soc_component_init(component); if (ret < 0) goto err_probe;
ret = snd_soc_add_component_controls(component,
component->driver->controls,
component->driver->num_controls); if (ret < 0) goto err_probe;
ret = snd_soc_dapm_add_routes(dapm,
component->driver->dapm_routes,
component->driver->num_dapm_routes); if (ret < 0) goto err_probe;
/* see for_each_card_components */
list_add(&component->card_list, &card->component_dev_list);
err_probe: if (ret < 0)
soc_remove_component(component, probed);
return ret;
}
staticvoid soc_remove_link_dais(struct snd_soc_card *card)
{ struct snd_soc_pcm_runtime *rtd; int order;
for_each_comp_order(order) {
for_each_card_rtds(card, rtd) { /* remove all rtd connected DAIs in good order */
snd_soc_pcm_dai_remove(rtd, order);
}
}
}
for_each_comp_order(order) {
for_each_card_rtds(card, rtd) { /* probe all rtd connected DAIs in good order */
ret = snd_soc_pcm_dai_probe(rtd, order); if (ret) return ret;
}
}
return0;
}
staticvoid soc_remove_link_components(struct snd_soc_card *card)
{ struct snd_soc_component *component; struct snd_soc_pcm_runtime *rtd; int i, order;
for_each_comp_order(order) {
for_each_card_rtds(card, rtd) {
for_each_rtd_components(rtd, i, component) { if (component->driver->remove_order != order) continue;
soc_remove_component(component, 1);
}
}
}
}
staticint soc_probe_link_components(struct snd_soc_card *card)
{ struct snd_soc_component *component; struct snd_soc_pcm_runtime *rtd; int i, ret, order;
for_each_comp_order(order) {
for_each_card_rtds(card, rtd) {
for_each_rtd_components(rtd, i, component) { if (component->driver->probe_order != order) continue;
ret = soc_probe_component(card, component); if (ret < 0) return ret;
}
}
}
for_each_card_pre_auxs(card, i, aux) { /* codecs, usually analog devices */
component = soc_find_component(&aux->dlc); if (!component) return -EPROBE_DEFER;
/* for snd_soc_component_init() */
snd_soc_component_set_aux(component, aux); /* see for_each_card_auxs */
list_add(&component->card_aux_list, &card->aux_comp_list);
} return0;
}
staticint soc_probe_aux_devices(struct snd_soc_card *card)
{ struct snd_soc_component *component; int order; int ret;
for_each_comp_order(order) {
for_each_card_auxs(card, component) { if (component->driver->probe_order != order) continue;
ret = soc_probe_component(card, component); if (ret < 0) return ret;
}
}
if (card->long_name) return0; /* long name already set by driver or from DMI */
if (!dmi_available) return0;
/* make up dmi long name as: vendor-product-version-board */
vendor = dmi_get_system_info(DMI_BOARD_VENDOR); if (!vendor || !is_dmi_valid(vendor)) {
dev_warn(card->dev, "ASoC: no DMI vendor name!\n"); return0;
}
board = dmi_get_system_info(DMI_BOARD_NAME); if (board && is_dmi_valid(board)) { if (!product || strcasecmp(board, product))
append_dmi_string(card, board);
} elseif (!product) { /* fall back to using legacy name */
dev_warn(card->dev, "ASoC: no DMI board/product name!\n"); return0;
}
/* set the card long name */
card->long_name = card->dmi_longname;
/* does this component override BEs ? */ if (!component->driver->ignore_machine) continue;
/* for this machine ? */ if (!strcmp(component->driver->ignore_machine,
card->dev->driver->name)) goto match; if (strcmp(component->driver->ignore_machine,
dev_name(card->dev))) continue;
match: /* machine matches, so override the rtd data */
for_each_card_prelinks(card, i, dai_link) {
/* ignore this FE */ if (dai_link->dynamic) {
dai_link->ignore = true; continue;
}
dev_dbg(card->dev, "info: override BE DAI link %s\n",
card->dai_link[i].name);
#define soc_setup_card_name(card, name, name1, name2) \
__soc_setup_card_name(card, name, sizeof(name), name1, name2) staticvoid __soc_setup_card_name(struct snd_soc_card *card, char *name, int len, constchar *name1, constchar *name2)
{
* = java.lang.StringIndexOutOfBoundsException: Range [33, 32) out of bounds for length 41 int i;
snprintf(name, len, "%s", src);
if (name != card->snd_card->driver) return;
/* *Namenormalization(driverfield) * *Thedrivernameissomewhatspecial,asit'susedasakeyfor *searchesintheuser-space. * *ex) *"abcd??efg"->"abcd__efg"
*/ for (i = 0; i < len; i++) { switch (name[i]) { case'_': case'-': case'\0': break; default: if (!isalnum(name[i]))
name[i] = '_'; break;
}
}
/* *Thedriverfieldshouldcontainavalidstringfromtheuserview. *Thewrappingusuallydoesnotworksowellhere.Setasmallerstring *inthespecificASoCdriver.
*/ if (strlen(src) > len - 1)
dev_err(card->dev, "ASoC: driver name too long '%s' -> '%s'\n", src, name);
}
if (card->snd_card)
snd_card_disconnect_sync(card->snd_card);
snd_soc_dapm_shutdown(card);
/* release machine specific resources */
for_each_card_rtds(card, rtd) if (rtd->initialized)
snd_soc_link_exit(rtd); /* remove and free each DAI */
soc_remove_link_dais(card);
soc_remove_link_components(card);
/* check whether any platform is ignore machine FE and using topology */
soc_check_tplg_fes(card);
/* bind aux_devs too */
ret = soc_bind_aux_dev(card); if (ret < 0) goto probe_end;
/* add predefined DAI links to the list */
card->num_rtd = 0;
ret = snd_soc_add_pcm_runtimes(card, card->dai_link, card->num_links); if (ret < 0) goto probe_end;
/* card bind complete so register a sound card */
ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
card->owner, 0, &card->snd_card); if (ret < 0) {
dev_err(card->dev, "ASoC: can't create sound card for card %s: %d\n",
card->name, ret); goto probe_end;
}
soc_init_card_debugfs(card);
soc_resume_init(card);
ret = snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
card->num_dapm_widgets); if (ret < 0) goto probe_end;
ret = snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets,
card->num_of_dapm_widgets); if (ret < 0) goto probe_end;
/* initialise the sound card only once */
ret = snd_soc_card_probe(card); if (ret < 0) goto probe_end;
/* probe all components used by DAI links on this card */
ret = soc_probe_link_components(card); if (ret < 0) { if (ret != -EPROBE_DEFER) {
dev_err(card->dev, "ASoC: failed to instantiate card %d\n", ret);
} goto probe_end;
}
/* probe auxiliary components */
ret = soc_probe_aux_devices(card); if (ret < 0) {
dev_err(card->dev, "ASoC: failed to probe aux component %d\n", ret); goto probe_end;
}
/* probe all DAI links on this card */
ret = soc_probe_link_dais(card); if (ret < 0) {
dev_err(card->dev, "ASoC: failed to instantiate card %d\n", ret); goto probe_end;
}
for_each_card_rtds(card, rtd) {
ret = soc_init_pcm_runtime(card, rtd); if (ret < 0) goto probe_end;
}
if (card->components) { /* the current implementation of snd_component_add() accepts */ /* multiple components in the string separated by space, */ /* but the string collision (identical string) check might */ /* not work correctly */
ret = snd_component_add(card->snd_card, card->components); if (ret < 0) {
dev_err(card->dev, "ASoC: %s snd_component_add() failed: %d\n",
card->name, ret); goto probe_end;
}
}
ret = snd_soc_card_late_probe(card); if (ret < 0) goto probe_end;
/* deactivate pins to sleep state */
for_each_card_components(card, component) if (!snd_soc_component_active(component))
pinctrl_pm_select_sleep_state(component->dev);
probe_end: if (ret < 0)
soc_cleanup_card_resources(card);
snd_soc_card_mutex_unlock(card);
if (card->devres_dev) {
ret = devm_snd_soc_bind_card(card->devres_dev, card); if (ret == -EPROBE_DEFER) {
list_add(&card->list, &unbind_card_list);
ret = 0;
}
} else {
ret = snd_soc_bind_card(card);
}
name = devm_kstrdup(dev, devname, GFP_KERNEL); if (!name) return NULL;
/* are we a "%s.%d" name (platform and SPI components) */
found = strstr(name, dev->driver->name); if (found) { /* get ID */ if (sscanf(&found[strlen(dev->driver->name)], ".%d", &__id) == 1) {
/* discard ID from name if ID == -1 */ if (__id == -1)
found[strlen(dev->driver->name)] = '\0';
}
/* I2C component devices are named "bus-addr" */
} elseif (sscanf(name, "%x-%x", &id1, &id2) == 2) {
/* create unique ID number from I2C addr and bus */
__id = ((id1 & 0xffff) << 16) + id2;
devm_kfree(dev, name);
/* sanitize component name for DAI link creation */
name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", dev->driver->name, devname);
} else {
__id = 0;
}
if (id)
*id = __id;
return name;
}
/* *SimplifyDAIlinknamingforsingledeviceswithmultipleDAIsbyremoving *any".-1"andusingtheDAIname(insteadofdevicename).
*/ staticinlinechar *fmt_multiple_name(struct device *dev, struct snd_soc_dai_driver *dai_drv)
{ if (dai_drv->name == NULL) {
dev_err(dev, "ASoC: error - multiple DAI %s registered with no name\n",
dev_name(dev)); return NULL;
}
/**
* snd_soc_register_dais - Register a DAI with the ASoC core
*
* @component: The component the DAIs are registered for
* @dai_drv: DAI driver to use for the DAIs
* @count: Number of DAIs
*/
static int snd_soc_register_dais(struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv,
size_t count)
{
struct snd_soc_dai *dai;
unsigned int i;
int ret;
for (i = 0; i < count; i++) {
dai = snd_soc_register_dai(component, dai_drv + i, count == 1 &&
component->driver->legacy_dai_naming);
if (dai == NULL) {
ret = -ENOMEM;
goto err;
}
}
/*
* Fix up the DAI formats for endianness: codecs don't actually see
* the endianness of the data but we're using the CPU format
* definitions which do need to include endianness so we ensure that
* codec DAIs always have both big and little endian variants set.
*/
static void convert_endianness_formats(struct snd_soc_pcm_stream *stream)
{
int i;
for (i = 0; i < ARRAY_SIZE(endianness_format_map); i++)
if (stream->formats & endianness_format_map[i])
stream->formats |= endianness_format_map[i];
}
int snd_soc_add_component(struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv,
int num_dai)
{
struct snd_soc_card *card, *c;
int ret;
int i;
mutex_lock(&client_mutex);
if (component->driver->endianness) {
for (i = 0; i < num_dai; i++) {
convert_endianness_formats(&dai_drv[i].playback);
convert_endianness_formats(&dai_drv[i].capture);
}
}
ret = snd_soc_register_dais(component, dai_drv, num_dai);
if (ret < 0) {
dev_err(component->dev, "ASoC: Failed to register DAIs: %d\n",
ret);
goto err_cleanup;
}
if (!component->driver->write && !component->driver->read) {
if (!component->regmap)
component->regmap = dev_get_regmap(component->dev,
NULL);
if (component->regmap)
snd_soc_component_setup_regmap(component);
}
/* see for_each_component */
list_add(&component->list, &component_list);
/**
* snd_soc_unregister_component_by_driver - Unregister component using a given driver
* from the ASoC core
*
* @dev: The device to unregister
* @component_driver: The component driver to unregister
*/
void snd_soc_unregister_component_by_driver(struct device *dev,
const struct snd_soc_component_driver *component_driver)
{
const char *driver_name = NULL;
if (component_driver)
driver_name = component_driver->name;
mutex_lock(&client_mutex);
while (1) {
struct snd_soc_component *component = snd_soc_lookup_component_nolocked(dev, driver_name);
/* Retrieve a card's name from device tree */
int snd_soc_of_parse_card_name(struct snd_soc_card *card,
const char *propname)
{
struct device_node *np;
int ret;
if (!card->dev) {
pr_err("card->dev is not set before calling %s\n", __func__);
return -EINVAL;
}
np = card->dev->of_node;
ret = of_property_read_string_index(np, propname, 0, &card->name);
/*
* EINVAL means the property does not exist. This is fine providing
* card->name was previously set, which is checked later in
* snd_soc_register_card.
*/
if (ret < 0 && ret != -EINVAL) {
dev_err(card->dev,
"ASoC: Property '%s' could not be read: %d\n",
propname, ret);
return ret;
}
int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
const char *propname)
{
struct device_node *np = card->dev->of_node;
struct snd_soc_dapm_widget *widgets;
const char *template, *wname;
int i, j, num_widgets;
num_widgets = of_property_count_strings(np, propname);
if (num_widgets < 0) {
dev_err(card->dev,
"ASoC: Property '%s' does not exist\n", propname);
return -EINVAL;
}
if (!num_widgets) {
dev_err(card->dev, "ASoC: Property '%s's length is zero\n",
propname);
return -EINVAL;
}
if (num_widgets & 1) {
dev_err(card->dev,
"ASoC: Property '%s' length is not even\n", propname);
return -EINVAL;
}
num_widgets /= 2;
widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets),
GFP_KERNEL);
if (!widgets) {
dev_err(card->dev,
"ASoC: Could not allocate memory for widgets\n");
return -ENOMEM;
}
for (i = 0; i < num_widgets; i++) {
int ret = of_property_read_string_index(np, propname, 2 * i, &template);
if (ret) {
dev_err(card->dev,
"ASoC: Property '%s' index %d read error:%d\n",
propname, 2 * i, ret);
return -EINVAL;
}
for (j = 0; j < ARRAY_SIZE(simple_widgets); j++) {
if (!strncmp(template, simple_widgets[j].name,
strlen(simple_widgets[j].name))) {
widgets[i] = simple_widgets[j];
break;
}
}
if (j >= ARRAY_SIZE(simple_widgets)) {
dev_err(card->dev,
"ASoC: DAPM widget '%s' is not supported\n",
template);
return -EINVAL;
}
int snd_soc_of_parse_tdm_slot(struct device_node *np,
unsigned int *tx_mask,
unsigned int *rx_mask,
unsigned int *slots,
unsigned int *slot_width)
{
u32 val;
int ret;
if (tx_mask)
snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", tx_mask);
if (rx_mask)
snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask);
ret = of_property_read_u32(np, "dai-tdm-slot-num", &val);
if (ret && ret != -EINVAL)
return ret;
if (!ret && slots)
*slots = val;
ret = of_property_read_u32(np, "dai-tdm-slot-width", &val);
if (ret && ret != -EINVAL)
return ret;
if (!ret && slot_width)
*slot_width = val;
int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
const char *propname)
{
struct device_node *np = card->dev->of_node;
int num_routes;
struct snd_soc_dapm_route *routes;
int i;
num_routes = of_property_count_strings(np, propname);
if (num_routes < 0 || num_routes & 1) {
dev_err(card->dev,
"ASoC: Property '%s' does not exist or its length is not even\n",
propname);
return -EINVAL;
}
num_routes /= 2;
routes = devm_kcalloc(card->dev, num_routes, sizeof(*routes),
GFP_KERNEL);
if (!routes) {
dev_err(card->dev,
"ASoC: Could not allocate DAPM route table\n");
return -ENOMEM;
}
for (i = 0; i < num_routes; i++) {
int ret = of_property_read_string_index(np, propname, 2 * i, &routes[i].sink);
if (ret) {
dev_err(card->dev,
"ASoC: Property '%s' index %d could not be read: %d\n",
propname, 2 * i, ret);
return -EINVAL;
}
ret = of_property_read_string_index(np, propname,
(2 * i) + 1, &routes[i].source);
if (ret) {
dev_err(card->dev,
"ASoC: Property '%s' index %d could not be read: %d\n",
propname, (2 * i) + 1, ret);
return -EINVAL;
}
}
unsigned int snd_soc_daifmt_clock_provider_from_bitmap(unsigned int bit_frame)
{
/*
* bit_frame is return value from
* snd_soc_daifmt_parse_clock_provider_raw()
*/
/* Codec base */
switch (bit_frame) {
case 0x11:
return SND_SOC_DAIFMT_CBP_CFP;
case 0x10:
return SND_SOC_DAIFMT_CBP_CFC;
case 0x01:
return SND_SOC_DAIFMT_CBC_CFP;
default:
return SND_SOC_DAIFMT_CBC_CFC;
}
switch ((bit << 4) + frame) {
case 0x11:
format |= SND_SOC_DAIFMT_IB_IF;
break;
case 0x10:
format |= SND_SOC_DAIFMT_IB_NF;
break;
case 0x01:
format |= SND_SOC_DAIFMT_NB_IF;
break;
default:
/* SND_SOC_DAIFMT_NB_NF is default */
break;
}
int snd_soc_get_dai_id(struct device_node *ep)
{
struct snd_soc_component *component;
struct snd_soc_dai_link_component dlc = {
.of_node = of_graph_get_port_parent(ep),
};
int ret;
/*
* For example HDMI case, HDMI has video/sound port,
* but ALSA SoC needs sound port number only.
* Thus counting HDMI DT port/endpoint doesn't work.
* Then, it should have .of_xlate_dai_id
*/
ret = -ENOTSUPP;
mutex_lock(&client_mutex);
component = soc_find_component(&dlc);
if (component)
ret = snd_soc_component_of_xlate_dai_id(component, ep);
mutex_unlock(&client_mutex);
if (component_of_node != args->np || !pos->num_dai)
continue;
ret = snd_soc_component_of_xlate_dai_name(pos, args, &dlc->dai_name);
if (ret == -ENOTSUPP) {
struct snd_soc_dai *dai;
int id = -1;
switch (args->args_count) {
case 0:
id = 0; /* same as dai_drv[0] */
break;
case 1:
id = args->args[0];
break;
default:
/* not supported */
break;
}
if (id < 0 || id >= pos->num_dai) {
ret = -EINVAL;
continue;
}
ret = 0;
/* find target DAI */
for_each_component_dais(pos, dai) {
if (id == 0)
break;
id--;
}
dlc->dai_name = snd_soc_dai_name_get(dai);
} else if (ret) {
/*
* if another error than ENOTSUPP is returned go on and
* check if another component is provided with the same
* node. This may happen if a device provides several
* components
*/
continue;
}
int snd_soc_of_get_dai_name(struct device_node *of_node,
const char **dai_name, int index)
{
struct snd_soc_dai_link_component dlc;
int ret = snd_soc_of_get_dlc(of_node, NULL, &dlc, index);
static int __snd_soc_of_get_dai_link_component_alloc(
struct device *dev, struct device_node *of_node,
struct snd_soc_dai_link_component **ret_component,
int *ret_num)
{
struct snd_soc_dai_link_component *component;
int num;
/* Count the number of CPUs/CODECs */
num = of_count_phandle_with_args(of_node, "sound-dai", "#sound-dai-cells");
if (num <= 0) {
if (num == -ENOENT)
dev_err(dev, "No 'sound-dai' property\n");
else
dev_err(dev, "Bad phandle in 'sound-dai'\n");
return num;
}
component = devm_kcalloc(dev, num, sizeof(*component), GFP_KERNEL);
if (!component)
return -ENOMEM;
*ret_component = component;
*ret_num = num;
return 0;
}
/*
* snd_soc_of_put_dai_link_codecs - Dereference device nodes in the codecs array
* @dai_link: DAI link
*
* Dereference device nodes acquired by snd_soc_of_get_dai_link_codecs().
*/
void snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link)
{
struct snd_soc_dai_link_component *component;
int index;
/*
* snd_soc_of_get_dai_link_codecs - Parse a list of CODECs in the devicetree
* @dev: Card device
* @of_node: Device node
* @dai_link: DAI link
*
* Builds an array of CODEC DAI components from the DAI link property
* 'sound-dai'.
* The array is set in the DAI link and the number of DAIs is set accordingly.
* The device nodes in the array (of_node) must be dereferenced by calling
* snd_soc_of_put_dai_link_codecs() on @dai_link.
*
* Returns 0 for success
*/
int snd_soc_of_get_dai_link_codecs(struct device *dev,
struct device_node *of_node,
struct snd_soc_dai_link *dai_link)
{
struct snd_soc_dai_link_component *component;
int index, ret;
ret = __snd_soc_of_get_dai_link_component_alloc(dev, of_node,
&dai_link->codecs, &dai_link->num_codecs);
if (ret < 0)
return ret;
/* Parse the list */
for_each_link_codecs(dai_link, index, component) {
ret = snd_soc_of_get_dlc(of_node, NULL, component, index);
if (ret)
goto err;
}
return 0;
err:
snd_soc_of_put_dai_link_codecs(dai_link);
dai_link->codecs = NULL;
dai_link->num_codecs = 0;
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs);
/*
* snd_soc_of_put_dai_link_cpus - Dereference device nodes in the codecs array
* @dai_link: DAI link
*
* Dereference device nodes acquired by snd_soc_of_get_dai_link_cpus().
*/
void snd_soc_of_put_dai_link_cpus(struct snd_soc_dai_link *dai_link)
{
struct snd_soc_dai_link_component *component;
int index;
/*
* snd_soc_of_get_dai_link_cpus - Parse a list of CPU DAIs in the devicetree
* @dev: Card device
* @of_node: Device node
* @dai_link: DAI link
*
* Is analogous to snd_soc_of_get_dai_link_codecs but parses a list of CPU DAIs
* instead.
*
* Returns 0 for success
*/
int snd_soc_of_get_dai_link_cpus(struct device *dev,
struct device_node *of_node,
struct snd_soc_dai_link *dai_link)
{
struct snd_soc_dai_link_component *component;
int index, ret;
/* Count the number of CPUs */
ret = __snd_soc_of_get_dai_link_component_alloc(dev, of_node,
&dai_link->cpus, &dai_link->num_cpus);
if (ret < 0)
return ret;
/* Parse the list */
for_each_link_cpus(dai_link, index, component) {
ret = snd_soc_of_get_dlc(of_node, NULL, component, index);
if (ret)
goto err;
}
return 0;
err:
snd_soc_of_put_dai_link_cpus(dai_link);
dai_link->cpus = NULL;
dai_link->num_cpus = 0;
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_cpus);
static int __init snd_soc_init(void)
{
int ret;
snd_soc_debugfs_init();
ret = snd_soc_util_init();
if (ret)
goto err_util_init;
ret = platform_driver_register(&soc_driver);
if (ret)
goto err_register;
return 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.