|
@@ -34,6 +34,7 @@
|
|
|
#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>
|
|
@@ -1888,6 +1889,139 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
|
|
|
}
|
|
|
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;
|