#include <linux/io.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
+#include <linux/log2.h>
 
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV               13
 #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV               9
 #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV               6
-#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_16            (1 << 6)
 #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ               4
 #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16            (1 << 4)
 #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT               2
        return best_val;
 }
 
+static int sun8i_codec_get_lrck_div(unsigned int channels,
+                                   unsigned int word_size)
+{
+       unsigned int div = word_size * channels;
+
+       if (div < 16 || div > 256)
+               return -EINVAL;
+
+       return ilog2(div) - 4;
+}
+
 static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
                                 struct snd_pcm_hw_params *params,
                                 struct snd_soc_dai *dai)
 {
        struct sun8i_codec *scodec = snd_soc_component_get_drvdata(dai->component);
-       int sample_rate;
+       int sample_rate, lrck_div;
        u8 bclk_div;
 
        /*
                           SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
                           bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
 
+       lrck_div = sun8i_codec_get_lrck_div(params_channels(params),
+                                           params_physical_width(params));
+       if (lrck_div < 0)
+               return lrck_div;
+
        regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
                           SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK,
-                          SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_16);
+                          lrck_div << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);
 
        sample_rate = sun8i_codec_get_hw_rate(params);
        if (sample_rate < 0)