diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c
index be872eefa61e..ca5f4e43476b 100644
--- a/sound/soc/sunxi/sun8i-codec-analog.c
+++ b/sound/soc/sunxi/sun8i-codec-analog.c
@@ -19,6 +19,8 @@
 #include <sound/tlv.h>
 
 #include "sun8i-adda-pr-regmap.h"
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 
 /* Codec analog control register offsets and bit fields */
 #define SUN8I_ADDA_HP_VOLC		0x00
@@ -113,6 +115,81 @@
 #define SUN8I_ADDA_ADC_AP_EN_ADCLEN		6
 #define SUN8I_ADDA_ADC_AP_EN_ADCG		0
 
+/* Analog control register access bits */
+#define ADDA_PR			0x0		/* PRCM base + 0x1c0 */
+#define ADDA_PR_RESET			BIT(28)
+#define ADDA_PR_WRITE			BIT(24)
+#define ADDA_PR_ADDR_SHIFT		16
+#define ADDA_PR_ADDR_MASK		GENMASK(4, 0)
+#define ADDA_PR_DATA_IN_SHIFT		8
+#define ADDA_PR_DATA_IN_MASK		GENMASK(7, 0)
+#define ADDA_PR_DATA_OUT_SHIFT		0
+#define ADDA_PR_DATA_OUT_MASK		GENMASK(7, 0)
+
+/* regmap access bits */
+static int adda_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+	void __iomem *base = (void __iomem *)context;
+	u32 tmp;
+
+	/* De-assert reset */
+	writel(readl(base) | ADDA_PR_RESET, base);
+
+	/* Clear write bit */
+	writel(readl(base) & ~ADDA_PR_WRITE, base);
+
+	/* Set register address */
+	tmp = readl(base);
+	tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT);
+	tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT;
+	writel(tmp, base);
+
+	/* Read back value */
+	*val = readl(base) & ADDA_PR_DATA_OUT_MASK;
+
+	return 0;
+}
+
+static int adda_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+	void __iomem *base = (void __iomem *)context;
+	u32 tmp;
+
+	/* De-assert reset */
+	writel(readl(base) | ADDA_PR_RESET, base);
+
+	/* Set register address */
+	tmp = readl(base);
+	tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT);
+	tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT;
+	writel(tmp, base);
+
+	/* Set data to write */
+	tmp = readl(base);
+	tmp &= ~(ADDA_PR_DATA_IN_MASK << ADDA_PR_DATA_IN_SHIFT);
+	tmp |= (val & ADDA_PR_DATA_IN_MASK) << ADDA_PR_DATA_IN_SHIFT;
+	writel(tmp, base);
+
+	/* Set write bit to signal a write */
+	writel(readl(base) | ADDA_PR_WRITE, base);
+
+	/* Clear write bit */
+	writel(readl(base) & ~ADDA_PR_WRITE, base);
+
+	return 0;
+}
+
+static const struct regmap_config adda_pr_regmap_cfg = {
+	.name		= "adda-pr",
+	.reg_bits	= 5,
+	.reg_stride	= 1,
+	.val_bits	= 8,
+	.reg_read	= adda_reg_read,
+	.reg_write	= adda_reg_write,
+	.fast_io	= true,
+	.max_register	= 24,
+};
+
 /* mixer controls */
 static const struct snd_kcontrol_new sun8i_codec_mixer_controls[] = {
 	SOC_DAPM_DOUBLE_R("DAC Playback Switch",
@@ -316,7 +393,7 @@ static const struct snd_soc_dapm_route sun8i_codec_mixer_routes[] = {
 /* headphone specific controls, widgets, and routes */
 static const DECLARE_TLV_DB_SCALE(sun8i_codec_hp_vol_scale, -6300, 100, 1);
 static const struct snd_kcontrol_new sun8i_codec_headphone_controls[] = {
-	SOC_SINGLE_TLV("Headphone Playback Volume",
+	SOC_SINGLE_TLV("Master Playback Volume",
 		       SUN8I_ADDA_HP_VOLC,
 		       SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0,
 		       sun8i_codec_hp_vol_scale),
@@ -673,7 +750,7 @@ struct sun8i_codec_analog_quirks {
 
 static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = {
 	.has_headphone	= true,
-	.has_hmic	= true,
+//	.has_hmic	= true,
 	.has_linein	= true,
 	.has_mbias	= true,
 	.has_mic2	= true,
@@ -817,10 +894,148 @@ static const struct of_device_id sun8i_codec_analog_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match);
 
+#define SUNXI_HMIC_ENABLE          (0x4)
+#define SUNXI_HMIC_CTL 	           (0x8)
+#define SUNXI_HMIC_DATA	           (0xc)
+
+/*
+*	SUNXI_HMIC_CTL
+*HMIC Control Register
+*CONFIG_ARCH_SUN8IW5:0x1c8
+*/
+#define HMIC_M					  (28)
+#define HMIC_N					  (24)
+#define HMIC_DIRQ				  (23)
+#define HMIC_TH1_HYS			  (21)
+#define HMIC_EARPHONE_OUT_IRQ_EN  (20)
+#define HMIC_EARPHONE_IN_IRQ_EN	  (19)
+#define HMIC_KEY_UP_IRQ_EN		  (18)
+#define HMIC_KEY_DOWN_IRQ_EN	  (17)
+#define HMIC_DATA_IRQ_EN		  (16)
+#define HMIC_DS_SAMP			  (14)
+#define HMIC_TH2_HYS			  (13)
+#define HMIC_TH2_KEY		      (8)
+#define HMIC_SF_SMOOTH_FIL		  (6)
+#define KEY_UP_IRQ_PEND			  (5)
+#define HMIC_TH1_EARPHONE		  (0)
+
+/*
+*	SUNXI_HMIC_DATA
+*HMIC Data Register
+*
+*CONFIG_ARCH_SUN8IW5:0x1cc
+*/
+#define HMIC_EARPHONE_OUT_IRQ_PEND  (20)
+#define HMIC_EARPHONE_IN_IRQ_PEND   (19)
+#define HMIC_KEY_UP_IRQ_PEND 	    (18)
+#define HMIC_KEY_DOWN_IRQ_PEND 		(17)
+#define HMIC_DATA_IRQ_PEND			(16)
+#define HMIC_ADC_DATA				(0)
+
+#define HP_VOLC					  (0x00)
+#define LOMIXSC					  (0x01)
+#define ROMIXSC					  (0x02)
+#define DAC_PA_SRC				  (0x03)
+#define PAEN_HP_CTRL			  (0x07)
+#define ADDA_APT2				  (0x12)
+#define MIC1G_MICBIAS_CTRL		  (0x0B)
+#define PA_ANTI_POP_REG_CTRL	  (0x0E)
+#define PA_SLOPE_SELECT	  (3)
+#define PA_ANTI_POP_EN		(0)
+
+static void __iomem *sun8i_codec_analog_base;
+static struct gpio_desc * speaker_amplifier_gpio;
+
+static int hmic_wrreg_prcm_bits(unsigned short reg, unsigned int mask, unsigned int value)
+{
+	unsigned int old, new;
+
+	adda_reg_read(sun8i_codec_analog_base, reg, &old);
+	new	=	(old & ~mask) | value;
+	adda_reg_write(sun8i_codec_analog_base, reg,new);
+
+	return 0;
+}
+
+static int hmic_wr_prcm_control(u32 reg, u32 mask, u32 shift, u32 val)
+{
+	u32 reg_val;
+	reg_val = val << shift;
+	mask = mask << shift;
+	hmic_wrreg_prcm_bits(reg, mask, reg_val);
+	return 0;
+}
+
+static int hmic_wrreg_bits(unsigned short reg, unsigned int	mask,	unsigned int value)
+{
+	unsigned int old, new;
+
+	old	=	readl(sun8i_codec_analog_base + reg);
+	new	=	(old & ~mask) | value;
+
+	writel(new, sun8i_codec_analog_base + reg);
+
+	return 0;
+}
+
+static int hmic_wr_control(u32 reg, u32 mask, u32 shift, u32 val)
+{
+	u32 reg_val;
+	reg_val = val << shift;
+	mask = mask << shift;
+	hmic_wrreg_bits(reg, mask, reg_val);
+	return 0;
+}
+
+static irqreturn_t sunxi_codec_analog_irq(int irq, void *dev_id)
+{
+	u32 tmp;
+
+	hmic_wr_control(SUNXI_HMIC_DATA, 0x1, HMIC_KEY_DOWN_IRQ_PEND, 0x1);
+	hmic_wr_control(SUNXI_HMIC_DATA, 0x1, HMIC_EARPHONE_IN_IRQ_PEND, 0x1);
+	hmic_wr_control(SUNXI_HMIC_DATA, 0x1, HMIC_KEY_UP_IRQ_PEND, 0x1);
+	hmic_wr_control(SUNXI_HMIC_DATA, 0x1, HMIC_EARPHONE_OUT_IRQ_PEND, 0x1);
+	hmic_wr_control(SUNXI_HMIC_DATA, 0x1, HMIC_DATA_IRQ_PEND, 0x1);
+
+	tmp = readl(sun8i_codec_analog_base + SUNXI_HMIC_DATA);
+	if(tmp & 0x1f)
+		gpiod_set_value(speaker_amplifier_gpio, 0);
+	else
+		gpiod_set_value(speaker_amplifier_gpio, 1);
+
+	return IRQ_HANDLED;
+}
+
+static void sunxi_hppa_enable(void) {
+    /*fix the resume blaze blaze noise*/
+	hmic_wr_prcm_control(ADDA_APT2, 0x1, PA_SLOPE_SELECT, 0x0);
+	hmic_wr_prcm_control(SUN8I_ADDA_PAEN_HP_CTRL, 0x3, SUN8I_ADDA_PAEN_HP_CTRL_PA_ANTI_POP_CTRL, 0x1);
+	hmic_wr_prcm_control(PA_ANTI_POP_REG_CTRL, 0x7, PA_ANTI_POP_EN, 0x2);
+	usleep_range(100,200);
+	/*enable pa*/
+	hmic_wr_prcm_control(SUN8I_ADDA_PAEN_HP_CTRL, 0x1, SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0x1);
+}
+
+static void sunxi_hbias_enable(void) {
+	/*audio codec hardware bug. the HBIASADCEN bit must be enable in init*/
+	hmic_wr_prcm_control(SUN8I_ADDA_MIC1G_MICBIAS_CTRL, 0x1, SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIAS_MODE, 0x1);
+	hmic_wr_prcm_control(SUN8I_ADDA_MIC1G_MICBIAS_CTRL, 0x1, SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN, 0x1);
+}
+
+static void codec_init_events(void)
+{
+	/*fix the resume blaze blaze noise*/
+	sunxi_hppa_enable();
+	msleep(450);
+	/*audio codec hardware bug. the HBIASADCEN bit must be enable in init*/
+	sunxi_hbias_enable();
+}
+
 static int sun8i_codec_analog_probe(struct platform_device *pdev)
 {
 	struct regmap *regmap;
 	void __iomem *base;
+	int irq, ret;
 
 	base = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(base)) {
@@ -828,6 +1043,38 @@ static int sun8i_codec_analog_probe(struct platform_device *pdev)
 		return PTR_ERR(base);
 	}
 
+	sun8i_codec_analog_base = base;
+	speaker_amplifier_gpio = devm_gpiod_get_optional(&pdev->dev, "speaker-amplifier", GPIOD_OUT_HIGH);
+	if (!IS_ERR_OR_NULL(speaker_amplifier_gpio)) {
+
+		hmic_wr_control(SUNXI_HMIC_CTL, 0xf, HMIC_M, 0x0);						/*0xf should be get from hw_debug 28*/
+		hmic_wr_control(SUNXI_HMIC_CTL, 0xf, HMIC_N, 0x0);						/*0xf should be get from hw_debug 24 0xf*/
+//		hmic_wr_control(SUNXI_HMIC_CTL, 0x1, HMIC_DIRQ, 0x1);					/*23*/
+		hmic_wr_control(SUNXI_HMIC_CTL, 0x1, HMIC_EARPHONE_OUT_IRQ_EN, 0x1); 	/*20*/
+		hmic_wr_control(SUNXI_HMIC_CTL, 0x1, HMIC_EARPHONE_IN_IRQ_EN, 0x1); 	/*19*/
+		hmic_wr_control(SUNXI_HMIC_CTL, 0x1, HMIC_KEY_UP_IRQ_EN, 0x1); 			/*18*/
+		hmic_wr_control(SUNXI_HMIC_CTL, 0x1, HMIC_KEY_DOWN_IRQ_EN, 0x1); 		/*17*/
+		hmic_wr_control(SUNXI_HMIC_CTL, 0x1, HMIC_DATA_IRQ_EN, 0x1); 			/*16*/
+		hmic_wr_control(SUNXI_HMIC_CTL, 0x3, HMIC_DS_SAMP, 0x0); 				/*14 */
+		hmic_wr_control(SUNXI_HMIC_CTL, 0x1f, HMIC_TH2_KEY, 0x0);				/*0xf should be get from hw_debug 8*/
+		hmic_wr_control(SUNXI_HMIC_CTL, 0x1f, HMIC_TH1_EARPHONE, 0x1);			/*0x1 should be get from hw_debug 0*/
+
+		irq = platform_get_irq(pdev, 0);
+		if (irq < 0) {
+			dev_err(&pdev->dev, "Can't retrieve our interrupt\n");
+			return irq;
+		}
+
+		ret = devm_request_irq(&pdev->dev, irq, sunxi_codec_analog_irq, 0, "audio_hmic_irq", NULL);
+		if (ret) {
+			dev_err(&pdev->dev, "can't register interrupt handler irq %d: %d\n",
+				irq, ret);
+			return ret;
+		}
+
+		codec_init_events();
+	}
+
 	regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base);
 	if (IS_ERR(regmap)) {
 		dev_err(&pdev->dev, "Failed to create regmap\n");
diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 518bfb724a5b..b6a90e33f20a 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -257,6 +257,9 @@ static int sun8i_codec_update_sample_rate(struct sun8i_codec *scodec)
 	regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
 			   SUN8I_SYS_SR_CTRL_AIF1_FS_MASK,
 			   hw_rate << SUN8I_SYS_SR_CTRL_AIF1_FS);
+	regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
+			   SUN8I_SYS_SR_CTRL_AIF2_FS_MASK,
+			   hw_rate << SUN8I_SYS_SR_CTRL_AIF2_FS);
 
 	return 0;
 }
@@ -288,6 +291,9 @@ static int sun8i_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 				   SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_MASK,
 				   SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF2);
 	} else {
+		regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(SUN8I_CODEC_AIF1),
+				   BIT(SUN8I_AIF_CLK_CTRL_MSTR_MOD),
+				   value << SUN8I_AIF_CLK_CTRL_MSTR_MOD);
 		regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id),
 				   BIT(SUN8I_AIF_CLK_CTRL_MSTR_MOD),
 				   value << SUN8I_AIF_CLK_CTRL_MSTR_MOD);
@@ -321,6 +327,9 @@ static int sun8i_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 		if (format != 3)
 			return -EINVAL;
 	} else {
+		regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(SUN8I_CODEC_AIF1),
+				   SUN8I_AIF_CLK_CTRL_DATA_FMT_MASK,
+				   format << SUN8I_AIF_CLK_CTRL_DATA_FMT);
 		regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id),
 				   SUN8I_AIF_CLK_CTRL_DATA_FMT_MASK,
 				   format << SUN8I_AIF_CLK_CTRL_DATA_FMT);
@@ -365,6 +374,9 @@ static int sun8i_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 		invert ^= scodec->quirks->lrck_inversion;
 	}
 
+	regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(SUN8I_CODEC_AIF1),
+			   SUN8I_AIF_CLK_CTRL_CLK_INV_MASK,
+			   invert << SUN8I_AIF_CLK_CTRL_CLK_INV);
 	regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id),
 			   SUN8I_AIF_CLK_CTRL_CLK_INV_MASK,
 			   invert << SUN8I_AIF_CLK_CTRL_CLK_INV);
@@ -519,6 +531,9 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
 		return -EINVAL;
 	}
 
+	regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(SUN8I_CODEC_AIF1),
+			   SUN8I_AIF_CLK_CTRL_WORD_SIZ_MASK,
+			   word_size << SUN8I_AIF_CLK_CTRL_WORD_SIZ);
 	regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(dai->id),
 			   SUN8I_AIF_CLK_CTRL_WORD_SIZ_MASK,
 			   word_size << SUN8I_AIF_CLK_CTRL_WORD_SIZ);
@@ -548,6 +563,9 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
 		clk_reg = SUN8I_AIF_CLK_CTRL(dai->id);
 	}
 
+	regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(SUN8I_CODEC_AIF1),
+			   SUN8I_AIF_CLK_CTRL_LRCK_DIV_MASK,
+			   (lrck_div_order - 4) << SUN8I_AIF_CLK_CTRL_LRCK_DIV);
 	regmap_update_bits(scodec->regmap, clk_reg,
 			   SUN8I_AIF_CLK_CTRL_LRCK_DIV_MASK,
 			   (lrck_div_order - 4) << SUN8I_AIF_CLK_CTRL_LRCK_DIV);
@@ -557,6 +575,9 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
 	if (bclk_div < 0)
 		return bclk_div;
 
+	regmap_update_bits(scodec->regmap, SUN8I_AIF_CLK_CTRL(SUN8I_CODEC_AIF1),
+			   SUN8I_AIF_CLK_CTRL_BCLK_DIV_MASK,
+			   bclk_div << SUN8I_AIF_CLK_CTRL_BCLK_DIV);
 	regmap_update_bits(scodec->regmap, clk_reg,
 			   SUN8I_AIF_CLK_CTRL_BCLK_DIV_MASK,
 			   bclk_div << SUN8I_AIF_CLK_CTRL_BCLK_DIV);
@@ -1226,6 +1247,7 @@ static const struct snd_soc_component_driver sun8i_soc_component = {
 };
 
 static const struct regmap_config sun8i_codec_regmap_config = {
+	.name = "sun8i_codec",
 	.reg_bits	= 32,
 	.reg_stride	= 4,
 	.val_bits	= 32,
