#define RT5645_PR_BASE (RT5645_PR_RANGE_BASE + (0 * RT5645_PR_SPACING))
 
+#define RT5645_HWEQ_NUM 57
+
 static const struct regmap_range_cfg rt5645_ranges[] = {
        {
                .name = "PR",
        { 0xff, 0x6308 },
 };
 
+struct rt5645_eq_param_s {
+       unsigned short reg;
+       unsigned short val;
+};
+
 static const char *const rt5645_supply_names[] = {
        "avdd",
        "cpvdd",
        struct snd_soc_jack *btn_jack;
        struct delayed_work jack_detect_work;
        struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)];
+       struct rt5645_eq_param_s *eq_param;
 
        int codec_type;
        int sysclk;
        7, 7, TLV_DB_SCALE_ITEM(228, 0, 0)
 );
 
+static int rt5645_hweq_info(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       uinfo->count = RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s);
+
+       return 0;
+}
+
+static int rt5645_hweq_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+       struct rt5645_eq_param_s *eq_param =
+               (struct rt5645_eq_param_s *)ucontrol->value.bytes.data;
+       int i;
+
+       for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+               eq_param[i].reg = cpu_to_be16(rt5645->eq_param[i].reg);
+               eq_param[i].val = cpu_to_be16(rt5645->eq_param[i].val);
+       }
+
+       return 0;
+}
+
+static bool rt5645_validate_hweq(unsigned short reg)
+{
+       if ((reg >= 0x1a4 && reg <= 0x1cd) | (reg >= 0x1e5 && reg <= 0x1f8) |
+               (reg == RT5645_EQ_CTRL2))
+               return true;
+
+       return false;
+}
+
+static int rt5645_hweq_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+       struct rt5645_eq_param_s *eq_param =
+               (struct rt5645_eq_param_s *)ucontrol->value.bytes.data;
+       int i;
+
+       for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+               eq_param[i].reg = be16_to_cpu(eq_param[i].reg);
+               eq_param[i].val = be16_to_cpu(eq_param[i].val);
+       }
+
+       /* The final setting of the table should be RT5645_EQ_CTRL2 */
+       for (i = RT5645_HWEQ_NUM - 1; i >= 0; i--) {
+               if (eq_param[i].reg == 0)
+                       continue;
+               else if (eq_param[i].reg != RT5645_EQ_CTRL2)
+                       return 0;
+               else
+                       break;
+       }
+
+       for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+               if (!rt5645_validate_hweq(eq_param[i].reg) &&
+                       eq_param[i].reg != 0)
+                       return 0;
+               else if (eq_param[i].reg == 0)
+                       break;
+       }
+
+       memcpy(rt5645->eq_param, eq_param,
+               RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s));
+
+       return 0;
+}
+
+#define RT5645_HWEQ(xname) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = rt5645_hweq_info, \
+       .get = rt5645_hweq_get, \
+       .put = rt5645_hweq_put \
+}
+
 static const struct snd_kcontrol_new rt5645_snd_controls[] = {
        /* Speaker Output Volume */
        SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
        /* I2S2 function select */
        SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT,
                1, 1),
+       RT5645_HWEQ("Speaker HWEQ"),
 };
 
 /**
 
 }
 
+static int rt5645_enable_hweq(struct snd_soc_codec *codec)
+{
+       struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+       int i;
+
+       for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+               if (rt5645_validate_hweq(rt5645->eq_param[i].reg))
+                       regmap_write(rt5645->regmap, rt5645->eq_param[i].reg,
+                                       rt5645->eq_param[i].val);
+               else
+                       break;
+       }
+
+       return 0;
+}
+
 /**
  * rt5645_sel_asrc_clk_src - select ASRC clock source for a set of filters
  * @codec: SoC audio codec device.
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
+               rt5645_enable_hweq(codec);
                snd_soc_update_bits(codec, RT5645_PWR_DIG1,
                        RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
                        RT5645_PWR_CLS_D_L,
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_write(codec, RT5645_EQ_CTRL2, 0);
                snd_soc_update_bits(codec, RT5645_PWR_DIG1,
                        RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
                        RT5645_PWR_CLS_D_L, 0);
                snd_soc_dapm_sync(dapm);
        }
 
+       rt5645->eq_param = devm_kzalloc(codec->dev,
+               RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s), GFP_KERNEL);
+
        return 0;
 }