Merge remote-tracking branches 'asoc/topic/ts3a227e', 'asoc/topic/ts3a277e' and ...
[firefly-linux-kernel-4.4.55.git] / sound / soc / soc-core.c
index 2c62620abca691af4033552518eb7138ea6485d5..30579ca5bacb985c7dfa9b9ad30f58cee45d6e1a 100644 (file)
@@ -191,6 +191,39 @@ static ssize_t pmdown_time_set(struct device *dev,
 
 static DEVICE_ATTR(pmdown_time, 0644, pmdown_time_show, pmdown_time_set);
 
+static struct attribute *soc_dev_attrs[] = {
+       &dev_attr_codec_reg.attr,
+       &dev_attr_pmdown_time.attr,
+       NULL
+};
+
+static umode_t soc_dev_attr_is_visible(struct kobject *kobj,
+                                      struct attribute *attr, int idx)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
+
+       if (attr == &dev_attr_pmdown_time.attr)
+               return attr->mode; /* always visible */
+       return rtd->codec ? attr->mode : 0; /* enabled only with codec */
+}
+
+static const struct attribute_group soc_dapm_dev_group = {
+       .attrs = soc_dapm_dev_attrs,
+       .is_visible = soc_dev_attr_is_visible,
+};
+
+static const struct attribute_group soc_dev_roup = {
+       .attrs = soc_dev_attrs,
+       .is_visible = soc_dev_attr_is_visible,
+};
+
+static const struct attribute_group *soc_dev_attr_groups[] = {
+       &soc_dapm_dev_group,
+       &soc_dev_roup,
+       NULL
+};
+
 #ifdef CONFIG_DEBUG_FS
 static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
                                   size_t count, loff_t *ppos)
@@ -949,8 +982,6 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
 
        /* unregister the rtd device */
        if (rtd->dev_registered) {
-               device_remove_file(rtd->dev, &dev_attr_pmdown_time);
-               device_remove_file(rtd->dev, &dev_attr_codec_reg);
                device_unregister(rtd->dev);
                rtd->dev_registered = 0;
        }
@@ -1120,6 +1151,7 @@ static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd,
        device_initialize(rtd->dev);
        rtd->dev->parent = rtd->card->dev;
        rtd->dev->release = rtd_release;
+       rtd->dev->groups = soc_dev_attr_groups;
        dev_set_name(rtd->dev, "%s", name);
        dev_set_drvdata(rtd->dev, rtd);
        mutex_init(&rtd->pcm_mutex);
@@ -1136,23 +1168,6 @@ static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd,
                return ret;
        }
        rtd->dev_registered = 1;
-
-       if (rtd->codec) {
-               /* add DAPM sysfs entries for this codec */
-               ret = snd_soc_dapm_sys_add(rtd->dev);
-               if (ret < 0)
-                       dev_err(rtd->dev,
-                               "ASoC: failed to add codec dapm sysfs entries: %d\n",
-                               ret);
-
-               /* add codec sysfs entries */
-               ret = device_create_file(rtd->dev, &dev_attr_codec_reg);
-               if (ret < 0)
-                       dev_err(rtd->dev,
-                               "ASoC: failed to add codec sysfs files: %d\n",
-                               ret);
-       }
-
        return 0;
 }
 
@@ -1308,11 +1323,6 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
        }
 #endif
 
-       ret = device_create_file(rtd->dev, &dev_attr_pmdown_time);
-       if (ret < 0)
-               dev_warn(rtd->dev, "ASoC: failed to add pmdown_time sysfs: %d\n",
-                       ret);
-
        if (cpu_dai->driver->compress_dai) {
                /*create compress_device"*/
                ret = soc_new_compress(rtd, num);
@@ -1427,11 +1437,76 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec)
        return 0;
 }
 
+/**
+ * snd_soc_runtime_set_dai_fmt() - Change DAI link format for a ASoC runtime
+ * @rtd: The runtime for which the DAI link format should be changed
+ * @dai_fmt: The new DAI link format
+ *
+ * This function updates the DAI link format for all DAIs connected to the DAI
+ * link for the specified runtime.
+ *
+ * Note: For setups with a static format set the dai_fmt field in the
+ * corresponding snd_dai_link struct instead of using this function.
+ *
+ * Returns 0 on success, otherwise a negative error code.
+ */
+int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
+       unsigned int dai_fmt)
+{
+       struct snd_soc_dai **codec_dais = rtd->codec_dais;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       unsigned int i;
+       int ret;
+
+       for (i = 0; i < rtd->num_codecs; i++) {
+               struct snd_soc_dai *codec_dai = codec_dais[i];
+
+               ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
+               if (ret != 0 && ret != -ENOTSUPP) {
+                       dev_warn(codec_dai->dev,
+                                "ASoC: Failed to set DAI format: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       /* Flip the polarity for the "CPU" end of a CODEC<->CODEC link */
+       if (cpu_dai->codec) {
+               unsigned int inv_dai_fmt;
+
+               inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK;
+               switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFS:
+                       inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+                       inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+                       break;
+               }
+
+               dai_fmt = inv_dai_fmt;
+       }
+
+       ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt);
+       if (ret != 0 && ret != -ENOTSUPP) {
+               dev_warn(cpu_dai->dev,
+                        "ASoC: Failed to set DAI format: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt);
+
 static int snd_soc_instantiate_card(struct snd_soc_card *card)
 {
        struct snd_soc_codec *codec;
-       struct snd_soc_dai_link *dai_link;
-       int ret, i, order, dai_fmt;
+       int ret, i, order;
 
        mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
 
@@ -1542,60 +1617,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                                        card->num_dapm_routes);
 
        for (i = 0; i < card->num_links; i++) {
-               struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
-               dai_link = &card->dai_link[i];
-               dai_fmt = dai_link->dai_fmt;
-
-               if (dai_fmt) {
-                       struct snd_soc_dai **codec_dais = rtd->codec_dais;
-                       int j;
-
-                       for (j = 0; j < rtd->num_codecs; j++) {
-                               struct snd_soc_dai *codec_dai = codec_dais[j];
-
-                               ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
-                               if (ret != 0 && ret != -ENOTSUPP)
-                                       dev_warn(codec_dai->dev,
-                                                "ASoC: Failed to set DAI format: %d\n",
-                                                ret);
-                       }
-               }
-
-               /* If this is a regular CPU link there will be a platform */
-               if (dai_fmt &&
-                   (dai_link->platform_name || dai_link->platform_of_node)) {
-                       ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
-                                                 dai_fmt);
-                       if (ret != 0 && ret != -ENOTSUPP)
-                               dev_warn(card->rtd[i].cpu_dai->dev,
-                                        "ASoC: Failed to set DAI format: %d\n",
-                                        ret);
-               } else if (dai_fmt) {
-                       /* Flip the polarity for the "CPU" end */
-                       dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
-                       switch (dai_link->dai_fmt &
-                               SND_SOC_DAIFMT_MASTER_MASK) {
-                       case SND_SOC_DAIFMT_CBM_CFM:
-                               dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
-                               break;
-                       case SND_SOC_DAIFMT_CBM_CFS:
-                               dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
-                               break;
-                       case SND_SOC_DAIFMT_CBS_CFM:
-                               dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
-                               break;
-                       case SND_SOC_DAIFMT_CBS_CFS:
-                               dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
-                               break;
-                       }
-
-                       ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
-                                                 dai_fmt);
-                       if (ret != 0 && ret != -ENOTSUPP)
-                               dev_warn(card->rtd[i].cpu_dai->dev,
-                                        "ASoC: Failed to set DAI format: %d\n",
-                                        ret);
-               }
+               if (card->dai_link[i].dai_fmt)
+                       snd_soc_runtime_set_dai_fmt(&card->rtd[i],
+                               card->dai_link[i].dai_fmt);
        }
 
        snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
@@ -1626,9 +1650,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                }
        }
 
-       if (card->fully_routed)
-               snd_soc_dapm_auto_nc_pins(card);
-
        snd_soc_dapm_new_widgets(card);
 
        ret = snd_card_register(card->snd_card);
@@ -2119,15 +2140,27 @@ static int snd_soc_xlate_tdm_slot_mask(unsigned int slots,
 }
 
 /**
- * snd_soc_dai_set_tdm_slot - configure DAI TDM.
- * @dai: DAI
+ * snd_soc_dai_set_tdm_slot() - Configures a DAI for TDM operation
+ * @dai: The DAI to configure
  * @tx_mask: bitmask representing active TX slots.
  * @rx_mask: bitmask representing active RX slots.
  * @slots: Number of slots in use.
  * @slot_width: Width in bits for each slot.
  *
- * Configures a DAI for TDM operation. Both mask and slots are codec and DAI
- * specific.
+ * This function configures the specified DAI for TDM operation. @slot contains
+ * the total number of slots of the TDM stream and @slot_with the width of each
+ * slot in bit clock cycles. @tx_mask and @rx_mask are bitmasks specifying the
+ * active slots of the TDM stream for the specified DAI, i.e. which slots the
+ * DAI should write to or read from. If a bit is set the corresponding slot is
+ * active, if a bit is cleared the corresponding slot is inactive. Bit 0 maps to
+ * the first slot, bit 1 to the second slot and so on. The first active slot
+ * maps to the first channel of the DAI, the second active slot to the second
+ * channel and so on.
+ *
+ * TDM mode can be disabled by passing 0 for @slots. In this case @tx_mask,
+ * @rx_mask and @slot_width will be ignored.
+ *
+ * Returns 0 on success, a negative error code otherwise.
  */
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
        unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
@@ -2386,8 +2419,8 @@ int snd_soc_unregister_card(struct snd_soc_card *card)
                card->instantiated = false;
                snd_soc_dapm_shutdown(card);
                soc_cleanup_card_resources(card);
+               dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name);
        }
-       dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name);
 
        return 0;
 }