#define US_TO_SAMPLES(rate, us) \
        (rate / (1000000 / us))
 
+static void dac33_calculate_times(struct snd_pcm_substream *substream);
+static int dac33_prepare_chip(struct snd_pcm_substream *substream);
 
 static struct snd_soc_codec *tlv320dac33_codec;
 
 static int dac33_hard_power(struct snd_soc_codec *codec, int power)
 {
        struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
-       int ret;
+       int ret = 0;
 
        mutex_lock(&dac33->mutex);
+
+       /* Safety check */
+       if (unlikely(power == dac33->chip_power)) {
+               dev_warn(codec->dev, "Trying to set the same power state: %s\n",
+                       power ? "ON" : "OFF");
+               goto exit;
+       }
+
        if (power) {
                ret = regulator_bulk_enable(ARRAY_SIZE(dac33->supplies),
                                          dac33->supplies);
                        gpio_set_value(dac33->power_gpio, 1);
 
                dac33->chip_power = 1;
-
-               dac33_init_chip(codec);
-
-               dac33_soft_power(codec, 1);
        } else {
                dac33_soft_power(codec, 0);
                if (dac33->power_gpio >= 0)
        return ret;
 }
 
+static int playback_event(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(w->codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               if (likely(dac33->substream)) {
+                       dac33_calculate_times(dac33->substream);
+                       dac33_prepare_chip(dac33->substream);
+               }
+               break;
+       }
+       return 0;
+}
+
 static int dac33_get_nsample(struct snd_kcontrol *kcontrol,
                         struct snd_ctl_elem_value *ucontrol)
 {
                         DAC33_OUT_AMP_PWR_CTRL, 6, 3, 3, 0),
        SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Right Amp Power",
                         DAC33_OUT_AMP_PWR_CTRL, 4, 3, 3, 0),
+
+       SND_SOC_DAPM_PRE("Prepare Playback", playback_event),
 };
 
 static const struct snd_soc_dapm_route audio_map[] = {
                break;
        case SND_SOC_BIAS_STANDBY:
                if (codec->bias_level == SND_SOC_BIAS_OFF) {
+                       /* Coming from OFF, switch on the codec */
                        ret = dac33_hard_power(codec, 1);
                        if (ret != 0)
                                return ret;
-               }
 
-               dac33_soft_power(codec, 0);
+                       dac33_init_chip(codec);
+               }
                break;
        case SND_SOC_BIAS_OFF:
                ret = dac33_hard_power(codec, 0);
                if (ret != 0)
                        return ret;
-
                break;
        }
        codec->bias_level = level;
        }
 
        mutex_lock(&dac33->mutex);
+
+       if (!dac33->chip_power) {
+               /*
+                * Chip is not powered yet.
+                * Do the init in the dac33_set_bias_level later.
+                */
+               mutex_unlock(&dac33->mutex);
+               return 0;
+       }
+
        dac33_soft_power(codec, 0);
        dac33_soft_power(codec, 1);
 
 
 }
 
-static int dac33_pcm_prepare(struct snd_pcm_substream *substream,
-                            struct snd_soc_dai *dai)
-{
-       dac33_calculate_times(substream);
-       dac33_prepare_chip(substream);
-
-       return 0;
-}
-
 static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
                             struct snd_soc_dai *dai)
 {
 
        dac33_add_widgets(codec);
 
-       /* power on device */
-       dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        return 0;
 
 pcm_err:
        struct snd_soc_codec *codec = socdev->card->codec;
 
        dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+               dac33_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
        dac33_set_bias_level(codec, codec->suspend_bias_level);
 
        return 0;
        .startup        = dac33_startup,
        .shutdown       = dac33_shutdown,
        .hw_params      = dac33_hw_params,
-       .prepare        = dac33_pcm_prepare,
        .trigger        = dac33_pcm_trigger,
        .delay          = dac33_dai_delay,
        .set_sysclk     = dac33_set_dai_sysclk,
        codec->hw_write = (hw_write_t) i2c_master_send;
        codec->bias_level = SND_SOC_BIAS_OFF;
        codec->set_bias_level = dac33_set_bias_level;
+       codec->idle_bias_off = 1;
        codec->dai = &dac33_dai;
        codec->num_dai = 1;
        codec->reg_cache_size = ARRAY_SIZE(dac33_reg);