]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - sound/soc/soc-core.c
Merge remote-tracking branch 'asoc/topic/uda134x' into asoc-next
[karo-tx-linux.git] / sound / soc / soc-core.c
index d82ee386eab564d352eccd4209dff767b222e6f3..528f8708221d4cdc9ac3877fc818c7d558b7ad70 100644 (file)
 #include <linux/bitops.h>
 #include <linux/debugfs.h>
 #include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
 #include <linux/ctype.h>
 #include <linux/slab.h>
 #include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <sound/ac97_codec.h>
 #include <sound/core.h>
 #include <sound/jack.h>
@@ -47,8 +50,6 @@
 
 #define NAME_SIZE      32
 
-static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
-
 #ifdef CONFIG_DEBUG_FS
 struct dentry *snd_soc_debugfs_root;
 EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
@@ -69,6 +70,16 @@ static int pmdown_time = 5000;
 module_param(pmdown_time, int, 0);
 MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
 
+struct snd_ac97_reset_cfg {
+       struct pinctrl *pctl;
+       struct pinctrl_state *pstate_reset;
+       struct pinctrl_state *pstate_warm_reset;
+       struct pinctrl_state *pstate_run;
+       int gpio_sdata;
+       int gpio_sync;
+       int gpio_reset;
+};
+
 /* returns the minimum number of bytes needed to represent
  * a particular given value */
 static int min_bytes_needed(unsigned long val)
@@ -530,6 +541,15 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
 }
 #endif
 
+static void codec2codec_close_delayed_work(struct work_struct *work)
+{
+       /* Currently nothing to do for c2c links
+        * Since c2c links are internal nodes in the DAPM graph and
+        * don't interface with the outside world or application layer
+        * we don't have to do any special handling on close.
+        */
+}
+
 #ifdef CONFIG_PM_SLEEP
 /* powers down audio subsystem for suspend */
 int snd_soc_suspend(struct device *dev)
@@ -1428,6 +1448,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
                                return ret;
                        }
                } else {
+                       INIT_DELAYED_WORK(&rtd->delayed_work,
+                                               codec2codec_close_delayed_work);
+
                        /* link the DAI widgets */
                        play_w = codec_dai->playback_widget;
                        capture_w = cpu_dai->capture_widget;
@@ -2080,6 +2103,117 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
 }
 EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
 
+static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
+
+static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+       struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
+
+       udelay(10);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+       msleep(2);
+}
+
+static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
+{
+       struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
+
+       udelay(10);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+       msleep(2);
+}
+
+static int snd_soc_ac97_parse_pinctl(struct device *dev,
+               struct snd_ac97_reset_cfg *cfg)
+{
+       struct pinctrl *p;
+       struct pinctrl_state *state;
+       int gpio;
+       int ret;
+
+       p = devm_pinctrl_get(dev);
+       if (IS_ERR(p)) {
+               dev_err(dev, "Failed to get pinctrl\n");
+               return PTR_RET(p);
+       }
+       cfg->pctl = p;
+
+       state = pinctrl_lookup_state(p, "ac97-reset");
+       if (IS_ERR(state)) {
+               dev_err(dev, "Can't find pinctrl state ac97-reset\n");
+               return PTR_RET(state);
+       }
+       cfg->pstate_reset = state;
+
+       state = pinctrl_lookup_state(p, "ac97-warm-reset");
+       if (IS_ERR(state)) {
+               dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
+               return PTR_RET(state);
+       }
+       cfg->pstate_warm_reset = state;
+
+       state = pinctrl_lookup_state(p, "ac97-running");
+       if (IS_ERR(state)) {
+               dev_err(dev, "Can't find pinctrl state ac97-running\n");
+               return PTR_RET(state);
+       }
+       cfg->pstate_run = state;
+
+       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
+       if (gpio < 0) {
+               dev_err(dev, "Can't find ac97-sync gpio\n");
+               return gpio;
+       }
+       ret = devm_gpio_request(dev, gpio, "AC97 link sync");
+       if (ret) {
+               dev_err(dev, "Failed requesting ac97-sync gpio\n");
+               return ret;
+       }
+       cfg->gpio_sync = gpio;
+
+       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
+       if (gpio < 0) {
+               dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
+               return gpio;
+       }
+       ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
+       if (ret) {
+               dev_err(dev, "Failed requesting ac97-sdata gpio\n");
+               return ret;
+       }
+       cfg->gpio_sdata = gpio;
+
+       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
+       if (gpio < 0) {
+               dev_err(dev, "Can't find ac97-reset gpio\n");
+               return gpio;
+       }
+       ret = devm_gpio_request(dev, gpio, "AC97 link reset");
+       if (ret) {
+               dev_err(dev, "Failed requesting ac97-reset gpio\n");
+               return ret;
+       }
+       cfg->gpio_reset = gpio;
+
+       return 0;
+}
+
 struct snd_ac97_bus_ops *soc_ac97_ops;
 EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
@@ -2097,6 +2231,35 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
 }
 EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
 
+/**
+ * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
+ *
+ * This function sets the reset and warm_reset properties of ops and parses
+ * the device node of pdev to get pinctrl states and gpio numbers to use.
+ */
+int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
+               struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct snd_ac97_reset_cfg cfg;
+       int ret;
+
+       ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_set_ac97_ops(ops);
+       if (ret)
+               return ret;
+
+       ops->warm_reset = snd_soc_ac97_warm_reset;
+       ops->reset = snd_soc_ac97_reset;
+
+       snd_ac97_rst_cfg = cfg;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
+
 /**
  * snd_soc_free_ac97_codec - free AC97 codec device
  * @codec: audio codec
@@ -2299,6 +2462,22 @@ static int snd_soc_add_controls(struct snd_card *card, struct device *dev,
        return 0;
 }
 
+struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
+                                              const char *name)
+{
+       struct snd_card *card = soc_card->snd_card;
+       struct snd_kcontrol *kctl;
+
+       if (unlikely(!name))
+               return NULL;
+
+       list_for_each_entry(kctl, &card->controls, list)
+               if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name)))
+                       return kctl;
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
+
 /**
  * snd_soc_add_codec_controls - add an array of controls to a codec.
  * Convenience function to add a list of controls. Many codecs were
@@ -2540,59 +2719,6 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double);
 
-/**
- * snd_soc_info_enum_ext - external enumerated single mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about an external enumerated
- * single mixer.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
-{
-       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-       uinfo->count = 1;
-       uinfo->value.enumerated.items = e->max;
-
-       if (uinfo->value.enumerated.item > e->max - 1)
-               uinfo->value.enumerated.item = e->max - 1;
-       strcpy(uinfo->value.enumerated.name,
-               e->texts[uinfo->value.enumerated.item]);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext);
-
-/**
- * snd_soc_info_volsw_ext - external single mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a single external mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
-{
-       int max = kcontrol->private_value;
-
-       if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-       else
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-
-       uinfo->count = 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = max;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);
-
 /**
  * snd_soc_info_volsw - single mixer info callback
  * @kcontrol: mixer control