mutex_unlock(&hcp->lock);
 }
 
+static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai,
+                                       unsigned int sample_width,
+                                       unsigned int sample_rate,
+                                       unsigned int channels,
+                                       struct hdmi_codec_params *hp)
+{
+       struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+       int idx;
+
+       /* Select a channel allocation that matches with ELD and pcm channels */
+       idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels);
+       if (idx < 0) {
+               dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
+                       idx);
+               hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+               return idx;
+       }
+
+       memset(hp, 0, sizeof(*hp));
+
+       hdmi_audio_infoframe_init(&hp->cea);
+       hp->cea.channels = channels;
+       hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
+       hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
+       hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
+       hp->cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
+
+       hp->sample_width = sample_width;
+       hp->sample_rate = sample_rate;
+       hp->channels = channels;
+
+       hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
+
+       return 0;
+}
+
 static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params,
                                struct snd_soc_dai *dai)
                        .dig_subframe = { 0 },
                }
        };
-       int ret, idx;
+       int ret;
+
+       if (!hcp->hcd.ops->hw_params)
+               return 0;
 
        dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
                params_width(params), params_rate(params),
                params_channels(params));
 
+       ret = hdmi_codec_fill_codec_params(dai,
+                                          params_width(params),
+                                          params_rate(params),
+                                          params_channels(params),
+                                          &hp);
+       if (ret < 0)
+               return ret;
+
        memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
        ret = snd_pcm_fill_iec958_consumer_hw_params(params, hp.iec.status,
                                                     sizeof(hp.iec.status));
                return ret;
        }
 
-       hdmi_audio_infoframe_init(&hp.cea);
-       hp.cea.channels = params_channels(params);
-       hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
-       hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
-       hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
-
-       /* Select a channel allocation that matches with ELD and pcm channels */
-       idx = hdmi_codec_get_ch_alloc_table_idx(hcp, hp.cea.channels);
-       if (idx < 0) {
-               dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
-                       idx);
-               hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
-               return idx;
-       }
-       hp.cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
-       hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
-
-       hp.sample_width = params_width(params);
-       hp.sample_rate = params_rate(params);
-       hp.channels = params_channels(params);
-
        cf->bit_fmt = params_format(params);
        return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
                                       cf, &hp);
 }
 
+static int hdmi_codec_prepare(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
+{
+       struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+       struct hdmi_codec_daifmt *cf = dai->playback_dma_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned int channels = runtime->channels;
+       unsigned int width = snd_pcm_format_width(runtime->format);
+       unsigned int rate = runtime->rate;
+       struct hdmi_codec_params hp;
+       int ret;
+
+       if (!hcp->hcd.ops->prepare)
+               return 0;
+
+       dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
+               width, rate, channels);
+
+       ret = hdmi_codec_fill_codec_params(dai, width, rate, channels, &hp);
+       if (ret < 0)
+               return ret;
+
+       memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
+       ret = snd_pcm_fill_iec958_consumer(runtime, hp.iec.status,
+                                          sizeof(hp.iec.status));
+       if (ret < 0) {
+               dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
+                       ret);
+               return ret;
+       }
+
+       cf->bit_fmt = runtime->format;
+       return hcp->hcd.ops->prepare(dai->dev->parent, hcp->hcd.data,
+                                    cf, &hp);
+}
+
 static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai,
                                  unsigned int fmt)
 {
        .startup        = hdmi_codec_startup,
        .shutdown       = hdmi_codec_shutdown,
        .hw_params      = hdmi_codec_hw_params,
+       .prepare        = hdmi_codec_prepare,
        .set_fmt        = hdmi_codec_i2s_set_fmt,
        .mute_stream    = hdmi_codec_mute,
 };
        }
 
        dai_count = hcd->i2s + hcd->spdif;
-       if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params ||
+       if (dai_count < 1 || !hcd->ops ||
+           (!hcd->ops->hw_params && !hcd->ops->prepare) ||
            !hcd->ops->audio_shutdown) {
                dev_err(dev, "%s: Invalid parameters\n", __func__);
                return -EINVAL;