Merge branch 'topic/asoc' into for-linus
[safe/jmp/linux-2.6] / sound / soc / soc-core.c
index 7b4179e..998569d 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/bitops.h>
 #include <linux/debugfs.h>
 #include <linux/platform_device.h>
+#include <linux/slab.h>
 #include <sound/ac97_codec.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -404,6 +405,12 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                        codec_dai->playback.formats & cpu_dai->playback.formats;
                runtime->hw.rates =
                        codec_dai->playback.rates & cpu_dai->playback.rates;
+               if (codec_dai->playback.rates
+                          & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
+                       runtime->hw.rates |= cpu_dai->playback.rates;
+               if (cpu_dai->playback.rates
+                          & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
+                       runtime->hw.rates |= codec_dai->playback.rates;
        } else {
                runtime->hw.rate_min =
                        max(codec_dai->capture.rate_min,
@@ -421,30 +428,36 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                        codec_dai->capture.formats & cpu_dai->capture.formats;
                runtime->hw.rates =
                        codec_dai->capture.rates & cpu_dai->capture.rates;
+               if (codec_dai->capture.rates
+                          & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
+                       runtime->hw.rates |= cpu_dai->capture.rates;
+               if (cpu_dai->capture.rates
+                          & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
+                       runtime->hw.rates |= codec_dai->capture.rates;
        }
 
        snd_pcm_limit_hw_rates(runtime);
        if (!runtime->hw.rates) {
                printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
                        codec_dai->name, cpu_dai->name);
-               goto machine_err;
+               goto config_err;
        }
        if (!runtime->hw.formats) {
                printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
                        codec_dai->name, cpu_dai->name);
-               goto machine_err;
+               goto config_err;
        }
        if (!runtime->hw.channels_min || !runtime->hw.channels_max) {
                printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
                        codec_dai->name, cpu_dai->name);
-               goto machine_err;
+               goto config_err;
        }
 
        /* Symmetry only applies if we've already got an active stream. */
        if (cpu_dai->active || codec_dai->active) {
                ret = soc_pcm_apply_symmetry(substream);
                if (ret != 0)
-                       goto machine_err;
+                       goto config_err;
        }
 
        pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
@@ -467,10 +480,14 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        mutex_unlock(&pcm_mutex);
        return 0;
 
-machine_err:
+config_err:
        if (machine->ops && machine->ops->shutdown)
                machine->ops->shutdown(substream);
 
+machine_err:
+       if (codec_dai->ops->shutdown)
+               codec_dai->ops->shutdown(substream, codec_dai);
+
 codec_dai_err:
        if (platform->pcm_ops->close)
                platform->pcm_ops->close(substream);
@@ -800,6 +817,41 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        return 0;
 }
 
+/*
+ * soc level wrapper for pointer callback
+ * If cpu_dai, codec_dai, platform driver has the delay callback, than
+ * the runtime->delay will be updated accordingly.
+ */
+static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_card *card = socdev->card;
+       struct snd_soc_platform *platform = card->platform;
+       struct snd_soc_dai_link *machine = rtd->dai;
+       struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+       struct snd_soc_dai *codec_dai = machine->codec_dai;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       snd_pcm_uframes_t offset = 0;
+       snd_pcm_sframes_t delay = 0;
+
+       if (platform->pcm_ops->pointer)
+               offset = platform->pcm_ops->pointer(substream);
+
+       if (cpu_dai->ops->delay)
+               delay += cpu_dai->ops->delay(substream, cpu_dai);
+
+       if (codec_dai->ops->delay)
+               delay += codec_dai->ops->delay(substream, codec_dai);
+
+       if (platform->delay)
+               delay += platform->delay(substream, codec_dai);
+
+       runtime->delay = delay;
+
+       return offset;
+}
+
 /* ASoC PCM operations */
 static struct snd_pcm_ops soc_pcm_ops = {
        .open           = soc_pcm_open,
@@ -808,6 +860,7 @@ static struct snd_pcm_ops soc_pcm_ops = {
        .hw_free        = soc_pcm_hw_free,
        .prepare        = soc_pcm_prepare,
        .trigger        = soc_pcm_trigger,
+       .pointer        = soc_pcm_pointer,
 };
 
 #ifdef CONFIG_PM
@@ -841,19 +894,31 @@ static int soc_suspend(struct device *dev)
        /* mute any active DAC's */
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai *dai = card->dai_link[i].codec_dai;
+
+               if (card->dai_link[i].ignore_suspend)
+                       continue;
+
                if (dai->ops->digital_mute && dai->playback.active)
                        dai->ops->digital_mute(dai, 1);
        }
 
        /* suspend all pcms */
-       for (i = 0; i < card->num_links; i++)
+       for (i = 0; i < card->num_links; i++) {
+               if (card->dai_link[i].ignore_suspend)
+                       continue;
+
                snd_pcm_suspend_all(card->dai_link[i].pcm);
+       }
 
        if (card->suspend_pre)
                card->suspend_pre(pdev, PMSG_SUSPEND);
 
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai  *cpu_dai = card->dai_link[i].cpu_dai;
+
+               if (card->dai_link[i].ignore_suspend)
+                       continue;
+
                if (cpu_dai->suspend && !cpu_dai->ac97_control)
                        cpu_dai->suspend(cpu_dai);
                if (platform->suspend)
@@ -866,6 +931,10 @@ static int soc_suspend(struct device *dev)
 
        for (i = 0; i < codec->num_dai; i++) {
                char *stream = codec->dai[i].playback.stream_name;
+
+               if (card->dai_link[i].ignore_suspend)
+                       continue;
+
                if (stream != NULL)
                        snd_soc_dapm_stream_event(codec, stream,
                                SND_SOC_DAPM_STREAM_SUSPEND);
@@ -875,11 +944,26 @@ static int soc_suspend(struct device *dev)
                                SND_SOC_DAPM_STREAM_SUSPEND);
        }
 
-       if (codec_dev->suspend)
-               codec_dev->suspend(pdev, PMSG_SUSPEND);
+       /* If there are paths active then the CODEC will be held with
+        * bias _ON and should not be suspended. */
+       if (codec_dev->suspend) {
+               switch (codec->bias_level) {
+               case SND_SOC_BIAS_STANDBY:
+               case SND_SOC_BIAS_OFF:
+                       codec_dev->suspend(pdev, PMSG_SUSPEND);
+                       break;
+               default:
+                       dev_dbg(socdev->dev, "CODEC is on over suspend\n");
+                       break;
+               }
+       }
 
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
+
+               if (card->dai_link[i].ignore_suspend)
+                       continue;
+
                if (cpu_dai->suspend && cpu_dai->ac97_control)
                        cpu_dai->suspend(cpu_dai);
        }
@@ -911,20 +995,44 @@ static void soc_resume_deferred(struct work_struct *work)
 
        dev_dbg(socdev->dev, "starting resume work\n");
 
+       /* Bring us up into D2 so that DAPM starts enabling things */
+       snd_power_change_state(codec->card, SNDRV_CTL_POWER_D2);
+
        if (card->resume_pre)
                card->resume_pre(pdev);
 
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
+
+               if (card->dai_link[i].ignore_suspend)
+                       continue;
+
                if (cpu_dai->resume && cpu_dai->ac97_control)
                        cpu_dai->resume(cpu_dai);
        }
 
-       if (codec_dev->resume)
-               codec_dev->resume(pdev);
+       /* If the CODEC was idle over suspend then it will have been
+        * left with bias OFF or STANDBY and suspended so we must now
+        * resume.  Otherwise the suspend was suppressed.
+        */
+       if (codec_dev->resume) {
+               switch (codec->bias_level) {
+               case SND_SOC_BIAS_STANDBY:
+               case SND_SOC_BIAS_OFF:
+                       codec_dev->resume(pdev);
+                       break;
+               default:
+                       dev_dbg(socdev->dev, "CODEC was on over suspend\n");
+                       break;
+               }
+       }
 
        for (i = 0; i < codec->num_dai; i++) {
                char *stream = codec->dai[i].playback.stream_name;
+
+               if (card->dai_link[i].ignore_suspend)
+                       continue;
+
                if (stream != NULL)
                        snd_soc_dapm_stream_event(codec, stream,
                                SND_SOC_DAPM_STREAM_RESUME);
@@ -937,12 +1045,20 @@ static void soc_resume_deferred(struct work_struct *work)
        /* unmute any active DACs */
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai *dai = card->dai_link[i].codec_dai;
+
+               if (card->dai_link[i].ignore_suspend)
+                       continue;
+
                if (dai->ops->digital_mute && dai->playback.active)
                        dai->ops->digital_mute(dai, 0);
        }
 
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
+
+               if (card->dai_link[i].ignore_suspend)
+                       continue;
+
                if (cpu_dai->resume && !cpu_dai->ac97_control)
                        cpu_dai->resume(cpu_dai);
                if (platform->resume)
@@ -966,6 +1082,12 @@ static int soc_resume(struct device *dev)
        struct snd_soc_card *card = socdev->card;
        struct snd_soc_dai *cpu_dai = card->dai_link[0].cpu_dai;
 
+       /* If the initialization of this soc device failed, there is no codec
+        * associated with it. Just bail out in this case.
+        */
+       if (!card->codec)
+               return 0;
+
        /* AC97 devices might have other drivers hanging off them so
         * need to resume immediately.  Other drivers don't have that
         * problem and may take a substantial amount of time to resume
@@ -1225,26 +1347,25 @@ static int soc_remove(struct platform_device *pdev)
        struct snd_soc_platform *platform = card->platform;
        struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
 
-       if (!card->instantiated)
-               return 0;
+       if (card->instantiated) {
+               run_delayed_work(&card->delayed_work);
 
-       run_delayed_work(&card->delayed_work);
+               if (platform->remove)
+                       platform->remove(pdev);
 
-       if (platform->remove)
-               platform->remove(pdev);
+               if (codec_dev->remove)
+                       codec_dev->remove(pdev);
 
-       if (codec_dev->remove)
-               codec_dev->remove(pdev);
+               for (i = 0; i < card->num_links; i++) {
+                       struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
+                       if (cpu_dai->remove)
+                               cpu_dai->remove(pdev, cpu_dai);
+               }
 
-       for (i = 0; i < card->num_links; i++) {
-               struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
-               if (cpu_dai->remove)
-                       cpu_dai->remove(pdev, cpu_dai);
+               if (card->remove)
+                       card->remove(pdev);
        }
 
-       if (card->remove)
-               card->remove(pdev);
-
        snd_soc_unregister_card(card);
 
        return 0;
@@ -1328,7 +1449,6 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
        dai_link->pcm = pcm;
        pcm->private_data = rtd;
        soc_pcm_ops.mmap = platform->pcm_ops->mmap;
-       soc_pcm_ops.pointer = platform->pcm_ops->pointer;
        soc_pcm_ops.ioctl = platform->pcm_ops->ioctl;
        soc_pcm_ops.copy = platform->pcm_ops->copy;
        soc_pcm_ops.silence = platform->pcm_ops->silence;
@@ -1541,7 +1661,8 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
                        mutex_unlock(&codec->mutex);
                        return ret;
                }
-               if (card->dai_link[i].codec_dai->ac97_control) {
+               /* Check for codec->ac97 to handle the ac97.c fun */
+               if (card->dai_link[i].codec_dai->ac97_control && codec->ac97) {
                        snd_ac97_dev_add_pdata(codec->ac97,
                                card->dai_link[i].cpu_dai->ac97_pdata);
                }
@@ -1897,18 +2018,22 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
 {
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
-       int max = mc->max;
+       int platform_max;
        unsigned int shift = mc->shift;
        unsigned int rshift = mc->rshift;
 
-       if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
+       if (!mc->platform_max)
+               mc->platform_max = mc->max;
+       platform_max = mc->platform_max;
+
+       if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
                uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
        else
                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 
        uinfo->count = shift == rshift ? 1 : 2;
        uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = max;
+       uinfo->value.integer.max = platform_max;
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
@@ -2006,16 +2131,20 @@ int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,
 {
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
-       int max = mc->max;
+       int platform_max;
 
-       if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
+       if (!mc->platform_max)
+               mc->platform_max = mc->max;
+       platform_max = mc->platform_max;
+
+       if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
                uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
        else
                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 
        uinfo->count = 2;
        uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = max;
+       uinfo->value.integer.max = platform_max;
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r);
@@ -2116,13 +2245,17 @@ int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
 {
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
-       int max = mc->max;
+       int platform_max;
        int min = mc->min;
 
+       if (!mc->platform_max)
+               mc->platform_max = mc->max;
+       platform_max = mc->platform_max;
+
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
        uinfo->count = 2;
        uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = max-min;
+       uinfo->value.integer.max = platform_max - min;
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8);
@@ -2181,6 +2314,45 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
 
 /**
+ * snd_soc_limit_volume - Set new limit to an existing volume control.
+ *
+ * @codec: where to look for the control
+ * @name: Name of the control
+ * @max: new maximum limit
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_limit_volume(struct snd_soc_codec *codec,
+       const char *name, int max)
+{
+       struct snd_card *card = codec->card;
+       struct snd_kcontrol *kctl;
+       struct soc_mixer_control *mc;
+       int found = 0;
+       int ret = -EINVAL;
+
+       /* Sanity check for name and max */
+       if (unlikely(!name || max <= 0))
+               return -EINVAL;
+
+       list_for_each_entry(kctl, &card->controls, list) {
+               if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (found) {
+               mc = (struct soc_mixer_control *)kctl->private_value;
+               if (max <= mc->max) {
+                       mc->platform_max = max;
+                       ret = 0;
+               }
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
+
+/**
  * snd_soc_dai_set_sysclk - configure DAI system or master clock.
  * @dai: DAI
  * @clk_id: DAI specific clock ID