#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/of.h>
++++#include <linux/dmi.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
return 0;
}
++ ++static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais,
++ ++ struct snd_soc_pcm_runtime *rtd)
++ ++{
++ ++ int i, ret = 0;
++ ++
++ ++ for (i = 0; i < num_dais; ++i) {
++ ++ struct snd_soc_dai_driver *drv = dais[i]->driver;
++ ++
++ ++ if (!rtd->dai_link->no_pcm && drv->pcm_new)
++ ++ ret = drv->pcm_new(rtd, dais[i]);
++ ++ if (ret < 0) {
++ ++ dev_err(dais[i]->dev,
++ ++ "ASoC: Failed to bind %s with pcm device\n",
++ ++ dais[i]->name);
++ ++ return ret;
++ ++ }
++ ++ }
++ ++
++ ++ return 0;
++ ++}
++ ++
static int soc_link_dai_widgets(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_link,
struct snd_soc_pcm_runtime *rtd)
dai_link->stream_name, ret);
return ret;
}
++ ++ ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd);
++ ++ if (ret < 0)
++ ++ return ret;
++ ++ ret = soc_link_dai_pcm_new(rtd->codec_dais,
++ ++ rtd->num_codecs, rtd);
++ ++ if (ret < 0)
++ ++ return ret;
} else {
INIT_DELAYED_WORK(&rtd->delayed_work,
codec2codec_close_delayed_work);
component->init = aux_dev->init;
component->auxiliary = 1;
++++ list_add(&component->card_aux_list, &card->aux_comp_list);
return 0;
static int soc_probe_aux_devices(struct snd_soc_card *card)
{
---- struct snd_soc_component *comp;
++++ struct snd_soc_component *comp, *tmp;
int order;
int ret;
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
---- list_for_each_entry(comp, &card->component_dev_list, card_list) {
---- if (!comp->auxiliary)
---- continue;
----
++++ list_for_each_entry_safe(comp, tmp, &card->aux_comp_list,
++++ card_aux_list) {
if (comp->driver->probe_order == order) {
ret = soc_probe_component(card, comp);
if (ret < 0) {
comp->name, ret);
return ret;
}
++++ list_del(&comp->card_aux_list);
}
}
}
}
EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt);
++++
++++/* Trim special characters, and replace '-' with '_' since '-' is used to
++++ * separate different DMI fields in the card long name. Only number and
++++ * alphabet characters and a few separator characters are kept.
++++ */
++++static void cleanup_dmi_name(char *name)
++++{
++++ int i, j = 0;
++++
++++ for (i = 0; name[i]; i++) {
++++ if (isalnum(name[i]) || (name[i] == '.')
++++ || (name[i] == '_'))
++++ name[j++] = name[i];
++++ else if (name[i] == '-')
++++ name[j++] = '_';
++++ }
++++
++++ name[j] = '\0';
++++}
++++
++++/**
++++ * snd_soc_set_dmi_name() - Register DMI names to card
++++ * @card: The card to register DMI names
++++ * @flavour: The flavour "differentiator" for the card amongst its peers.
++++ *
++++ * An Intel machine driver may be used by many different devices but are
++++ * difficult for userspace to differentiate, since machine drivers ususally
++++ * use their own name as the card short name and leave the card long name
++++ * blank. To differentiate such devices and fix bugs due to lack of
++++ * device-specific configurations, this function allows DMI info to be used
++++ * as the sound card long name, in the format of
++++ * "vendor-product-version-board"
++++ * (Character '-' is used to separate different DMI fields here).
++++ * This will help the user space to load the device-specific Use Case Manager
++++ * (UCM) configurations for the card.
++++ *
++++ * Possible card long names may be:
++++ * DellInc.-XPS139343-01-0310JH
++++ * ASUSTeKCOMPUTERINC.-T100TA-1.0-T100TA
++++ * Circuitco-MinnowboardMaxD0PLATFORM-D0-MinnowBoardMAX
++++ *
++++ * This function also supports flavoring the card longname to provide
++++ * the extra differentiation, like "vendor-product-version-board-flavor".
++++ *
++++ * We only keep number and alphabet characters and a few separator characters
++++ * in the card long name since UCM in the user space uses the card long names
++++ * as card configuration directory names and AudoConf cannot support special
++++ * charactors like SPACE.
++++ *
++++ * Returns 0 on success, otherwise a negative error code.
++++ */
++++int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour)
++++{
++++ const char *vendor, *product, *product_version, *board;
++++ size_t longname_buf_size = sizeof(card->snd_card->longname);
++++ size_t len;
++++
++++ if (card->long_name)
++++ return 0; /* long name already set by driver or from DMI */
++++
++++ /* make up dmi long name as: vendor.product.version.board */
++++ vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
++++ if (!vendor) {
++++ dev_warn(card->dev, "ASoC: no DMI vendor name!\n");
++++ return 0;
++++ }
++++
++++ snprintf(card->dmi_longname, sizeof(card->snd_card->longname),
++++ "%s", vendor);
++++ cleanup_dmi_name(card->dmi_longname);
++++
++++ product = dmi_get_system_info(DMI_PRODUCT_NAME);
++++ if (product) {
++++ len = strlen(card->dmi_longname);
++++ snprintf(card->dmi_longname + len,
++++ longname_buf_size - len,
++++ "-%s", product);
++++
++++ len++; /* skip the separator "-" */
++++ if (len < longname_buf_size)
++++ cleanup_dmi_name(card->dmi_longname + len);
++++
++++ /* some vendors like Lenovo may only put a self-explanatory
++++ * name in the product version field
++++ */
++++ product_version = dmi_get_system_info(DMI_PRODUCT_VERSION);
++++ if (product_version) {
++++ len = strlen(card->dmi_longname);
++++ snprintf(card->dmi_longname + len,
++++ longname_buf_size - len,
++++ "-%s", product_version);
++++
++++ len++;
++++ if (len < longname_buf_size)
++++ cleanup_dmi_name(card->dmi_longname + len);
++++ }
++++ }
++++
++++ board = dmi_get_system_info(DMI_BOARD_NAME);
++++ if (board) {
++++ len = strlen(card->dmi_longname);
++++ snprintf(card->dmi_longname + len,
++++ longname_buf_size - len,
++++ "-%s", board);
++++
++++ len++;
++++ if (len < longname_buf_size)
++++ cleanup_dmi_name(card->dmi_longname + len);
++++ } else if (!product) {
++++ /* fall back to using legacy name */
++++ dev_warn(card->dev, "ASoC: no DMI board/product name!\n");
++++ return 0;
++++ }
++++
++++ /* Add flavour to dmi long name */
++++ if (flavour) {
++++ len = strlen(card->dmi_longname);
++++ snprintf(card->dmi_longname + len,
++++ longname_buf_size - len,
++++ "-%s", flavour);
++++
++++ len++;
++++ if (len < longname_buf_size)
++++ cleanup_dmi_name(card->dmi_longname + len);
++++ }
++++
++++ /* set the card long name */
++++ card->long_name = card->dmi_longname;
++++
++++ return 0;
++++}
++++EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name);
++++
static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
struct snd_soc_codec *codec;
component->remove = component->driver->remove;
component->suspend = component->driver->suspend;
component->resume = component->driver->resume;
++++ component->pcm_new = component->driver->pcm_new;
++++ component->pcm_free= component->driver->pcm_free;
dapm = &component->dapm;
dapm->dev = dev;
platform->driver->remove(platform);
}
++++static int snd_soc_platform_drv_pcm_new(struct snd_soc_pcm_runtime *rtd)
++++{
++++ struct snd_soc_platform *platform = rtd->platform;
++++
++++ return platform->driver->pcm_new(rtd);
++++}
++++
++++static void snd_soc_platform_drv_pcm_free(struct snd_pcm *pcm)
++++{
++++ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
++++ struct snd_soc_platform *platform = rtd->platform;
++++
++++ platform->driver->pcm_free(pcm);
++++}
++++
/**
* snd_soc_add_platform - Add a platform to the ASoC core
* @dev: The parent device for the platform
platform->component.probe = snd_soc_platform_drv_probe;
if (platform_drv->remove)
platform->component.remove = snd_soc_platform_drv_remove;
++++ if (platform_drv->pcm_new)
++++ platform->component.pcm_new = snd_soc_platform_drv_pcm_new;
++++ if (platform_drv->pcm_free)
++++ platform->component.pcm_free = snd_soc_platform_drv_pcm_free;
#ifdef CONFIG_DEBUG_FS
platform->component.debugfs_prefix = "platform";