params_buffer_bytes(hw_params));
 }
 
+static int rsnd_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
+       struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+       struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+       int ret;
+
+       ret = rsnd_dai_call(hw_free, io, substream);
+       if (ret)
+               return ret;
+
+       return snd_pcm_lib_free_pages(substream);
+}
+
 static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream)
 {
        struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
 static const struct snd_pcm_ops rsnd_pcm_ops = {
        .ioctl          = snd_pcm_lib_ioctl,
        .hw_params      = rsnd_hw_params,
-       .hw_free        = snd_pcm_lib_free_pages,
+       .hw_free        = rsnd_hw_free,
        .pointer        = rsnd_pointer,
 };
 
 
        int (*cleanup)(struct rsnd_mod *mod,
                       struct rsnd_dai_stream *io,
                       struct rsnd_priv *priv);
+       int (*hw_free)(struct rsnd_mod *mod,
+                      struct rsnd_dai_stream *io,
+                      struct snd_pcm_substream *substream);
        u32 *(*get_status)(struct rsnd_mod *mod,
                           struct rsnd_dai_stream *io,
                           enum rsnd_mod_type type);
  *
  * B   0: init         1: quit
  * C   0: start        1: stop
+ * D   0: hw_params    1: hw_free
  *
  * H is always called (see __rsnd_mod_call)
  * H   0: probe        1: remove
  * H   0: pcm_new
  * H   0: fallback
- * H   0: hw_params
  * H   0: pointer
  * H   0: prepare
  * H   0: cleanup
 #define __rsnd_mod_shift_quit          4
 #define __rsnd_mod_shift_start         8
 #define __rsnd_mod_shift_stop          8
+#define __rsnd_mod_shift_hw_params     12
+#define __rsnd_mod_shift_hw_free       12
 #define __rsnd_mod_shift_probe         28 /* always called */
 #define __rsnd_mod_shift_remove                28 /* always called */
 #define __rsnd_mod_shift_irq           28 /* always called */
 #define __rsnd_mod_shift_pcm_new       28 /* always called */
 #define __rsnd_mod_shift_fallback      28 /* always called */
-#define __rsnd_mod_shift_hw_params     28 /* always called */
 #define __rsnd_mod_shift_pointer       28 /* always called */
 #define __rsnd_mod_shift_prepare       28 /* always called */
 #define __rsnd_mod_shift_cleanup       28 /* always called */
 #define __rsnd_mod_add_quit            -1
 #define __rsnd_mod_add_start            1
 #define __rsnd_mod_add_stop            -1
+#define __rsnd_mod_add_hw_params       1
+#define __rsnd_mod_add_hw_free         -1
 #define __rsnd_mod_add_irq             0
 #define __rsnd_mod_add_pcm_new         0
 #define __rsnd_mod_add_fallback                0
-#define __rsnd_mod_add_hw_params       0
 #define __rsnd_mod_add_pointer         0
 
 #define __rsnd_mod_call_probe          0
 #define __rsnd_mod_call_fallback       0
 #define __rsnd_mod_call_hw_params      0
 #define __rsnd_mod_call_pointer                0
+#define __rsnd_mod_call_hw_free                1
 
 #define rsnd_mod_to_priv(mod)  ((mod)->priv)
 #define rsnd_mod_power_on(mod) clk_enable((mod)->clk)