From 4f6c097e1c50f6bb40cdcc3f669c5cd04652d6bd Mon Sep 17 00:00:00 2001
From: handsomeyingyan <handsomeyingyan@github.com>
Date: Sat, 1 May 2021 22:32:13 +0800
Subject: [PATCH 13/17] LS2K: HDA support

---
 arch/mips/boot/dts/loongson/loongson2k.dts |   12 +-
 arch/mips/configs/ls2k_defconfig           |    1 +
 sound/hda/hdac_bus.c                       |    9 +-
 sound/hda/hdac_controller.c                |   56 +-
 sound/hda/hdac_controller.c.rej            |   10 +
 sound/hda/hdac_stream.c                    |  105 +-
 sound/hda/hdac_stream.c.rej                |   15 +
 sound/pci/hda/Kconfig                      |   10 +
 sound/pci/hda/Makefile                     |    2 +
 sound/pci/hda/hda_controller.c             |   56 +-
 sound/pci/hda/hda_controller.h             |    3 +
 sound/pci/hda/hda_intel.c                  |    5 +
 sound/pci/hda/hda_loongson.c               | 1076 ++++++++++++++++++++
 sound/pci/hda/patch_realtek.c              |   28 +-
 14 files changed, 1319 insertions(+), 69 deletions(-)
 create mode 100644 sound/hda/hdac_controller.c.rej
 create mode 100644 sound/hda/hdac_stream.c.rej
 create mode 100644 sound/pci/hda/hda_loongson.c

diff --git a/arch/mips/boot/dts/loongson/loongson2k.dts b/arch/mips/boot/dts/loongson/loongson2k.dts
index ce6f9b4b5..2375f7d2c 100644
--- a/arch/mips/boot/dts/loongson/loongson2k.dts
+++ b/arch/mips/boot/dts/loongson/loongson2k.dts
@@ -24,7 +24,7 @@
 	};
 
 	package0: bus@10000000 {
-		compatible = "simple-bus";
+		compatible = "simple-bus", "ls,nbus";
 		#address-cells = <2>;
 		#size-cells = <2>;
 		ranges = <0 0x10000000 0 0x10000000 0 0x10000000
@@ -143,6 +143,13 @@
 			reg = <0 0x1fe07800 0 0x78>;
 		};
 
+		hda: hda@40690000 {
+			compatible = "loongson,ls-audio";
+			reg = <0 0x40690000 0 0xffff>;
+			interrupt-parent = <&liointc0>;
+			interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
+		};
+
 		liointc0: interrupt-controller@1fe11400 {
 			compatible = "loongson,liointc-2.0";
 			reg = <0 0x1fe11400 0 0x40>,
@@ -264,6 +271,7 @@
 				interrupt-parent = <&liointc0>;
 			};
 
+/*
 			hda@7,0 {
 				compatible = "pci0014,7a07.0",
 						   "pci0014,7a07",
@@ -275,7 +283,7 @@
 				interrupt-parent = <&liointc0>;
 			};
 
-
+*/
 			sata@8,0 {
 				compatible = "pci0014,7a08.0",
 						   "pci0014,7a08",
diff --git a/arch/mips/configs/ls2k_defconfig b/arch/mips/configs/ls2k_defconfig
index 8af6b2056..d797ee3e7 100644
--- a/arch/mips/configs/ls2k_defconfig
+++ b/arch/mips/configs/ls2k_defconfig
@@ -3100,6 +3100,7 @@ CONFIG_SND_HDA_CODEC_CONEXANT=y
 # CONFIG_SND_HDA_CODEC_SI3054 is not set
 CONFIG_SND_HDA_GENERIC=y
 CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0
+CONFIG_SND_HDA_LS=y
 # end of HD-Audio
 
 CONFIG_SND_HDA_CORE=y
diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c
index 9766f6af8..41ec1096b 100644
--- a/sound/hda/hdac_bus.c
+++ b/sound/hda/hdac_bus.c
@@ -11,7 +11,7 @@
 #include <sound/hdaudio.h>
 #include "local.h"
 #include "trace.h"
-
+#include "../pci/hda/hda_controller.h"
 static void snd_hdac_bus_process_unsol_events(struct work_struct *work);
 
 static const struct hdac_bus_ops default_ops = {
@@ -36,7 +36,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
 		bus->ops = ops;
 	else
 		bus->ops = &default_ops;
-	bus->dma_type = SNDRV_DMA_TYPE_DEV;
+	//bus->dma_type = SNDRV_DMA_TYPE_DEV;
 	INIT_LIST_HEAD(&bus->stream_list);
 	INIT_LIST_HEAD(&bus->codec_list);
 	INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events);
@@ -108,6 +108,7 @@ int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
 {
 	unsigned int tmp;
 	int err;
+	struct azx *chip = bus_to_azx(bus);
 
 	if (cmd == ~0)
 		return -EINVAL;
@@ -116,7 +117,9 @@ int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
 		*res = -1;
 	else if (bus->sync_write)
 		res = &tmp;
-	for (;;) {
+	if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND)
+		err = bus->ops->command(bus, cmd);
+	else for (;;) {
 		trace_hda_send_cmd(bus, cmd);
 		err = bus->ops->command(bus, cmd);
 		if (err != -EAGAIN)
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
index b98449fd9..a73164394 100644
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/hdac_controller.c
@@ -9,6 +9,7 @@
 #include <sound/core.h>
 #include <sound/hdaudio.h>
 #include <sound/hda_register.h>
+#include "../pci/hda/hda_controller.h"
 #include "local.h"
 
 /* clear CORB read pointer properly */
@@ -42,6 +43,7 @@ static void azx_clear_corbrp(struct hdac_bus *bus)
  */
 void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
 {
+	struct azx *chip = bus_to_azx(bus);
 	WARN_ON_ONCE(!bus->rb.area);
 
 	spin_lock_irq(&bus->reg_lock);
@@ -58,11 +60,15 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
 
 	/* reset the corb hw read pointer */
 	snd_hdac_chip_writew(bus, CORBRP, AZX_CORBRP_RST);
-	if (!bus->corbrp_self_clear)
+	if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND)
+		snd_hdac_chip_writew(bus, CORBRP, 0);
+	else if (!bus->corbrp_self_clear)
 		azx_clear_corbrp(bus);
 
 	/* enable corb dma */
 	snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN);
+	if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND)
+		snd_hdac_chip_readb(bus, CORBCTL);
 
 	/* RIRB set up */
 	bus->rirb.addr = bus->rb.addr + 2048;
@@ -79,7 +85,12 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
 	/* set N=1, get RIRB response interrupt for new entry */
 	snd_hdac_chip_writew(bus, RINTCNT, 1);
 	/* enable rirb dma and response irq */
-	snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
+	if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND) {
+		snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN);
+		snd_hdac_chip_readb(bus, RIRBCTL);
+	}
+	else
+		snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
 	/* Accept unsolicited responses */
 	snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
 	spin_unlock_irq(&bus->reg_lock);
@@ -244,6 +255,7 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
 	unsigned long timeout;
 	unsigned long loopcounter;
 	wait_queue_entry_t wait;
+	struct azx *chip = bus_to_azx(bus);
 	bool warned = false;
 
 	init_wait_entry(&wait, 0);
@@ -254,8 +266,11 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
 		if (!bus->polling_mode)
 			prepare_to_wait(&bus->rirb_wq, &wait,
 					TASK_UNINTERRUPTIBLE);
-		if (bus->polling_mode)
+		if (bus->polling_mode) {
+			if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND)
+				bus->rirb.cmds[addr] %= AZX_MAX_RIRB_ENTRIES;
 			snd_hdac_bus_update_rirb(bus);
+		}
 		if (!bus->rirb.cmds[addr]) {
 			if (res)
 				*res = bus->rirb.res[addr]; /* the last value */
@@ -407,7 +422,7 @@ void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus)
 {
 	unsigned long timeout;
 
-	snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET);
+	snd_hdac_chip_updateb(bus, GCTL, 0, AZX_GCTL_RESET);
 
 	timeout = jiffies + msecs_to_jiffies(100);
 	while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout))
@@ -444,6 +459,7 @@ int snd_hdac_bus_reset_link(struct hdac_bus *bus, bool full_reset)
 		dev_dbg(bus->dev, "controller not ready!\n");
 		return -EBUSY;
 	}
+	snd_hdac_chip_updatel(bus, GCTL, 0, AZX_GCTL_UNSOL);
 
 	/* detect codecs */
 	if (!bus->codec_mask) {
@@ -459,9 +475,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_reset_link);
 static void azx_int_enable(struct hdac_bus *bus)
 {
 	/* enable controller CIE and GIE */
-	snd_hdac_chip_updatel(bus, INTCTL,
-			      AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN,
-			      AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN);
+	snd_hdac_chip_updatel(bus, INTCTL, 0, AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN);
 }
 
 /* disable interrupts */
@@ -484,16 +498,23 @@ static void azx_int_disable(struct hdac_bus *bus)
 static void azx_int_clear(struct hdac_bus *bus)
 {
 	struct hdac_stream *azx_dev;
+	struct azx *chip = bus_to_azx(bus);
 
 	/* clear stream status */
-	list_for_each_entry(azx_dev, &bus->stream_list, list)
-		snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
-
+	list_for_each_entry(azx_dev, &bus->stream_list, list) {
+		if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND)
+			snd_hdac_stream_updateb(azx_dev, SD_STS, 0, 0);
+		else
+			snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
+	}
 	/* clear STATESTS */
 	snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK);
 
 	/* clear rirb status */
-	snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
+	if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND)
+		snd_hdac_chip_updateb(bus, RIRBSTS, ~RIRB_INT_MASK, 0);
+	else
+		snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
 
 	/* clear int status */
 	snd_hdac_chip_writel(bus, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM);
@@ -508,19 +529,16 @@ bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
 {
 	if (bus->chip_init)
 		return false;
-
 	/* reset controller */
 	snd_hdac_bus_reset_link(bus, full_reset);
 
 	/* clear interrupts */
 	azx_int_clear(bus);
+	azx_int_enable(bus);
 
 	/* initialize the codec command I/O */
 	snd_hdac_bus_init_cmd_io(bus);
 
-	/* enable interrupts after CORB/RIRB buffers are initialized above */
-	azx_int_enable(bus);
-
 	/* program the position buffer */
 	if (bus->use_posbuf && bus->posbuf.addr) {
 		snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr);
@@ -574,11 +592,17 @@ int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
 	struct hdac_stream *azx_dev;
 	u8 sd_status;
 	int handled = 0;
+	struct azx *chip = bus_to_azx(bus);
 
 	list_for_each_entry(azx_dev, &bus->stream_list, list) {
 		if (status & azx_dev->sd_int_sta_mask) {
 			sd_status = snd_hdac_stream_readb(azx_dev, SD_STS);
-			snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
+			if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND) {
+				snd_hdac_stream_writeb(azx_dev, SD_STS, sd_status);
+				snd_hdac_stream_readb(azx_dev, SD_STS);
+			}
+			else
+				snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
 			handled |= 1 << azx_dev->index;
 			if (!azx_dev->substream || !azx_dev->running ||
 			    !(sd_status & SD_INT_COMPLETE))
diff --git a/sound/hda/hdac_controller.c.rej b/sound/hda/hdac_controller.c.rej
new file mode 100644
index 000000000..8fba74f8d
--- /dev/null
+++ b/sound/hda/hdac_controller.c.rej
@@ -0,0 +1,10 @@
+--- sound/hda/hdac_controller.c
++++ sound/hda/hdac_controller.c
+@@ -9,6 +9,7 @@
+ #include <sound/core.h>
+ #include <sound/hdaudio.h>
+ #include <sound/hda_register.h>
++#include "../pci/hda/hda_controller.h"
+ 
+ /* clear CORB read pointer properly */
+ static void azx_clear_corbrp(struct hdac_bus *bus)
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
index abe7a1b16..312565f27 100644
--- a/sound/hda/hdac_stream.c
+++ b/sound/hda/hdac_stream.c
@@ -12,6 +12,7 @@
 #include <sound/hdaudio.h>
 #include <sound/hda_register.h>
 #include "trace.h"
+#include "../pci/hda/hda_controller.h"
 
 /**
  * snd_hdac_get_stream_stripe_ctl - get stripe control value
@@ -84,27 +85,33 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start)
 {
 	struct hdac_bus *bus = azx_dev->bus;
 	int stripe_ctl;
-
+	struct azx *chip = bus_to_azx(bus);
+	unsigned int val;
 	trace_snd_hdac_stream_start(bus, azx_dev);
 
 	azx_dev->start_wallclk = snd_hdac_chip_readl(bus, WALLCLK);
-	if (!fresh_start)
+	if (!fresh_start && !(chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND))
 		azx_dev->start_wallclk -= azx_dev->period_wallclk;
 
 	/* enable SIE */
-	snd_hdac_chip_updatel(bus, INTCTL,
-			      1 << azx_dev->index,
-			      1 << azx_dev->index);
+	snd_hdac_chip_updatel(bus, INTCTL, 0, 1 << azx_dev->index);
 	/* set stripe control */
-	if (azx_dev->stripe) {
+	/*if (azx_dev->stripe) {
 		if (azx_dev->substream)
 			stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream);
 		else
 			stripe_ctl = 0;
 		snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
 					stripe_ctl);
-	}
+	}*/
 	/* set DMA start and interrupt mask */
+	if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND) {
+		snd_hdac_stream_updatel(azx_dev, SD_CTL,
+				0, SD_CTL_DMA_START | SD_INT_MASK);
+		snd_hdac_stream_updatel(azx_dev, SD_CTL, SD_CTL_STREAM_TAG_MASK, azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT);
+		val = snd_hdac_stream_readl(azx_dev, SD_CTL);
+	}
+	else
 	snd_hdac_stream_updateb(azx_dev, SD_CTL,
 				0, SD_CTL_DMA_START | SD_INT_MASK);
 	azx_dev->running = true;
@@ -117,12 +124,30 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_start);
  */
 void snd_hdac_stream_clear(struct hdac_stream *azx_dev)
 {
-	snd_hdac_stream_updateb(azx_dev, SD_CTL,
+/*	snd_hdac_stream_updateb(azx_dev, SD_CTL,
 				SD_CTL_DMA_START | SD_INT_MASK, 0);
-	snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+	snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
 	if (azx_dev->stripe)
 		snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
 	azx_dev->running = false;
+*/
+	struct snd_pcm_substream *substream = azx_dev->substream;
+	int stream = substream->stream;
+	struct azx *chip = bus_to_azx(azx_dev->bus);
+
+	if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND) {
+		snd_hdac_stream_updatel(azx_dev, SD_CTL,
+				SD_CTL_DMA_START | SD_INT_MASK, 0);
+		snd_hdac_stream_updateb(azx_dev, SD_STS, 0, 0); /* to be sure */
+		stream_to_azx_dev(azx_dev)->fix_prvpos =
+			chip->get_position[stream](chip, stream_to_azx_dev(azx_dev));
+	}
+	else {
+		snd_hdac_stream_updateb(azx_dev, SD_CTL,
+				SD_CTL_DMA_START | SD_INT_MASK, 0);
+		snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+	}
+	azx_dev->running = false;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_stream_clear);
 
@@ -152,6 +177,10 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev)
 	int timeout;
 	int dma_run_state;
 
+	struct azx *chip = bus_to_azx(azx_dev->bus);
+	if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND)
+		goto out;
+
 	snd_hdac_stream_clear(azx_dev);
 
 	dma_run_state = snd_hdac_stream_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START;
@@ -182,6 +211,7 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev)
 			break;
 	} while (--timeout);
 
+out:
 	/* reset first position - may not be synced with hw at this time */
 	if (azx_dev->posbuf)
 		*azx_dev->posbuf = 0;
@@ -196,6 +226,7 @@ int snd_hdac_stream_setup(struct hdac_stream *azx_dev)
 {
 	struct hdac_bus *bus = azx_dev->bus;
 	struct snd_pcm_runtime *runtime;
+	struct azx *chip = bus_to_azx(bus);
 	unsigned int val;
 
 	if (azx_dev->substream)
@@ -206,14 +237,24 @@ int snd_hdac_stream_setup(struct hdac_stream *azx_dev)
 	snd_hdac_stream_clear(azx_dev);
 	/* program the stream_tag */
 	val = snd_hdac_stream_readl(azx_dev, SD_CTL);
-	val = (val & ~SD_CTL_STREAM_TAG_MASK) |
-		(azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT);
+
+	if (!(chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND)) {
+		val = (val & ~SD_CTL_STREAM_TAG_MASK) |
+			(azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT);
+	}
 	if (!bus->snoop)
 		val |= SD_CTL_TRAFFIC_PRIO;
 	snd_hdac_stream_writel(azx_dev, SD_CTL, val);
 
 	/* program the length of samples in cyclic buffer */
-	snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize);
+	if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND) {
+		if(azx_dev->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize - 64);
+		else
+			snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize - 16);
+	}
+	else
+		snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize);
 
 	/* program the stream format */
 	/* this value needs to be the same as the one programmed */
@@ -239,7 +280,11 @@ int snd_hdac_stream_setup(struct hdac_stream *azx_dev)
 	/* set the interrupt enable bits in the descriptor control register */
 	snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK);
 
-	azx_dev->fifo_size = snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1;
+	if (azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK)
+		azx_dev->fifo_size =
+			snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1;
+	else
+		azx_dev->fifo_size = 0;
 
 	/* when LPIB delay correction gives a small negative value,
 	 * we ignore it; currently set the threshold statically to
@@ -419,6 +464,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
 	__le32 *bdl;
 	int i, ofs, periods, period_bytes;
 	int pos_adj, pos_align;
+	struct azx *chip = bus_to_azx(bus);
 
 	/* reset BDL address */
 	snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
@@ -433,6 +479,8 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
 	azx_dev->frags = 0;
 
 	pos_adj = bus->bdl_pos_adj;
+	if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND)
+		pos_adj = 0;
 	if (!azx_dev->no_period_wakeup && pos_adj > 0) {
 		pos_align = pos_adj;
 		pos_adj = (pos_adj * runtime->rate + 47999) / 48000;
@@ -638,27 +686,20 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start,
 		nwait = 0;
 		i = 0;
 		list_for_each_entry(s, &bus->stream_list, list) {
-			if (!(streams & (1 << i++)))
-				continue;
-
-			if (start) {
-				/* check FIFO gets ready */
-				if (!(snd_hdac_stream_readb(s, SD_STS) &
-				      SD_STS_FIFO_READY))
-					nwait++;
-			} else {
-				/* check RUN bit is cleared */
-				if (snd_hdac_stream_readb(s, SD_CTL) &
-				    SD_CTL_DMA_START) {
-					nwait++;
-					/*
-					 * Perform stream reset if DMA RUN
-					 * bit not cleared within given timeout
-					 */
-					if (timeout == 1)
-						snd_hdac_stream_reset(s);
+			if (streams & (1 << i)) {
+				if (start) {
+					/* check FIFO gets ready */
+					if (!(snd_hdac_stream_readb(s, SD_STS) &
+					      SD_STS_FIFO_READY))
+						nwait++;
+				} else {
+					/* check RUN bit is cleared */
+					if (snd_hdac_stream_readb(s, SD_CTL) &
+					    SD_CTL_DMA_START)
+						nwait++;
 				}
 			}
+			i++;
 		}
 		if (!nwait)
 			break;
diff --git a/sound/hda/hdac_stream.c.rej b/sound/hda/hdac_stream.c.rej
new file mode 100644
index 000000000..8bc5981dd
--- /dev/null
+++ b/sound/hda/hdac_stream.c.rej
@@ -0,0 +1,15 @@
+--- sound/hda/hdac_stream.c
++++ sound/hda/hdac_stream.c
+@@ -175,9 +200,11 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev)
+ {
+ 	unsigned char val;
+ 	int timeout;
++	struct azx *chip = bus_to_azx(azx_dev->bus);
++	if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND)
++		goto out;
+ 
+ 	snd_hdac_stream_clear(azx_dev);
+-
+ 	snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET);
+ 	udelay(3);
+ 	timeout = 300;
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 90759391c..09c96bc75 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -42,6 +42,16 @@ config SND_HDA_TEGRA
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-hda-tegra.
 
+config SND_HDA_LS
+	tristate "Loongson HD Audio"
+	select SND_HDA
+	help
+	  Say Y here to include support for Loongson 2H/7A "High Definition
+	  Audio" controller.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-hda-loongson.
+
 if SND_HDA
 
 config SND_HDA_HWDEP
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index b57432f00..bc265a501 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 snd-hda-intel-objs := hda_intel.o
 snd-hda-tegra-objs := hda_tegra.o
+snd-hda-loongson-objs := hda_loongson.o
 
 snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
 snd-hda-codec-y += hda_controller.o
@@ -48,3 +49,4 @@ obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o
 # when built in kernel
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
 obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o
+obj-$(CONFIG_SND_HDA_LS) += snd-hda-loongson.o
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index b972d59eb..0de00cb2a 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -294,6 +294,23 @@ unsigned int azx_get_position(struct azx *chip,
 	int stream = substream->stream;
 	int delay = 0;
 
+	if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND) {
+		pos = chip->get_position[stream](chip, azx_dev);
+
+		if (pos >= azx_dev->fix_prvpos) {
+			pos = pos - azx_dev->fix_prvpos;
+			pos %= azx_dev->core.bufsize;
+		} else {
+			if (azx_dev->fix_prvpos > azx_dev->core.bufsize)
+				pos = (0x100000000ULL + pos-azx_dev->fix_prvpos)
+					% azx_dev->core.bufsize;
+			else
+				pos = pos + azx_dev->core.bufsize - azx_dev->fix_prvpos;
+		}
+
+		return pos;
+	}
+
 	if (chip->get_position[stream])
 		pos = chip->get_position[stream](chip, azx_dev);
 	else /* use the position buffer as default */
@@ -777,6 +794,9 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
 {
 	struct azx *chip = bus_to_azx(bus);
 	struct hda_bus *hbus = &chip->bus;
+	unsigned long timeout;
+	unsigned long loopcounter;
+	int do_poll = 0;
 	int err;
 
  again:
@@ -919,6 +939,8 @@ static int azx_send_cmd(struct hdac_bus *bus, unsigned int val)
 
 	if (chip->disabled)
 		return 0;
+	if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND)
+		udelay(500);
 	if (chip->single_cmd)
 		return azx_single_send_cmd(bus, val);
 	else
@@ -1077,8 +1099,9 @@ static void stream_update(struct hdac_bus *bus, struct hdac_stream *s)
 irqreturn_t azx_interrupt(int irq, void *dev_id)
 {
 	struct azx *chip = dev_id;
+	struct hdac_stream *azx_dev;
 	struct hdac_bus *bus = azx_bus(chip);
-	u32 status;
+	u32 i = 0, status = 0;
 	bool active, handled = false;
 	int repeat = 0; /* count for avoiding endless loop */
 
@@ -1094,8 +1117,19 @@ irqreturn_t azx_interrupt(int irq, void *dev_id)
 		goto unlock;
 
 	do {
-		status = azx_readl(chip, INTSTS);
-		if (status == 0 || status == 0xffffffff)
+		if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND) {
+			list_for_each_entry(azx_dev, &bus->stream_list, list) {
+				status |= (snd_hdac_stream_readb(azx_dev, SD_STS) & SD_INT_MASK) ?
+				    (1 << i) : 0;
+				i++;
+			}
+			status |= (status & ~0) ? (1 << 31) : 0;
+		}
+		else
+			status = azx_readl(chip, INTSTS);
+
+		if (status == 0 ||
+			(status == 0xffffffff && !(chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND)))
 			break;
 
 		handled = true;
@@ -1105,21 +1139,17 @@ irqreturn_t azx_interrupt(int irq, void *dev_id)
 
 		status = azx_readb(chip, RIRBSTS);
 		if (status & RIRB_INT_MASK) {
-			/*
-			 * Clearing the interrupt status here ensures that no
-			 * interrupt gets masked after the RIRB wp is read in
-			 * snd_hdac_bus_update_rirb. This avoids a possible
-			 * race condition where codec response in RIRB may
-			 * remain unserviced by IRQ, eventually falling back
-			 * to polling mode in azx_rirb_get_response.
-			 */
-			azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
+		//	azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
 			active = true;
 			if (status & RIRB_INT_RESPONSE) {
 				if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
 					udelay(80);
 				snd_hdac_bus_update_rirb(bus);
 			}
+			if (chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND)
+				azx_writeb(chip, RIRBSTS, status & RIRB_INT_MASK);
+			else
+				azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
 		}
 	} while (active && ++repeat < 10);
 
@@ -1147,7 +1177,7 @@ static int probe_codec(struct azx *chip, int addr)
 
 	mutex_lock(&bus->cmd_mutex);
 	chip->probing = 1;
-	azx_send_cmd(bus, cmd);
+	err = azx_send_cmd(bus, cmd);
 	err = azx_get_response(bus, addr, &res);
 	chip->probing = 0;
 	mutex_unlock(&bus->cmd_mutex);
diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h
index 68f966878..7b273ac01 100644
--- a/sound/pci/hda/hda_controller.h
+++ b/sound/pci/hda/hda_controller.h
@@ -45,6 +45,7 @@
 #define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28)	/* CORBRP clears itself after reset */
 #define AZX_DCAPS_NO_MSI64      (1 << 29)	/* Stick to 32-bit MSIs */
 #define AZX_DCAPS_SEPARATE_STREAM_TAG	(1 << 30) /* capture and playback use separate stream tag */
+#define AZX_DCAPS_LS_HDA_WORKAROUND (1 << 31)	/* Loongson-HDA workaround */
 
 enum {
 	AZX_SNOOP_TYPE_NONE,
@@ -63,6 +64,8 @@ struct azx_dev {
 	 *  when link position is not greater than FIFO size
 	 */
 	unsigned int insufficient:1;
+	/* For Loongson */
+	unsigned int fix_prvpos;
 };
 
 #define azx_stream(dev)		(&(dev)->core)
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 4c8b281c3..8c059f56c 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -273,6 +273,7 @@ enum {
 	AZX_DRIVER_CTHDA,
 	AZX_DRIVER_CMEDIA,
 	AZX_DRIVER_ZHAOXIN,
+	AZX_DRIVER_LOONGSON,
 	AZX_DRIVER_GENERIC,
 	AZX_NUM_DRIVERS, /* keep this as last entry */
 };
@@ -390,6 +391,7 @@ static const char * const driver_short_names[] = {
 	[AZX_DRIVER_CTHDA] = "HDA Creative",
 	[AZX_DRIVER_CMEDIA] = "HDA C-Media",
 	[AZX_DRIVER_ZHAOXIN] = "HDA Zhaoxin",
+	[AZX_DRIVER_LOONGSON] = "HDA Loongson",
 	[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
 };
 
@@ -2778,6 +2780,9 @@ static const struct pci_device_id azx_ids[] = {
 	  .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
 	/* Zhaoxin */
 	{ PCI_DEVICE(0x1d17, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN },
+	/* Loongson HDA */
+	/* { PCI_DEVICE(0x0014, 0x7a07), .driver_data = AZX_DRIVER_LOONGSON |
+	  AZX_DCAPS_LS_HDA_WORKAROUND | AZX_DCAPS_SNOOP_OFF | AZX_DCAPS_NO_MSI }, */
 	{ 0, }
 };
 MODULE_DEVICE_TABLE(pci, azx_ids);
diff --git a/sound/pci/hda/hda_loongson.c b/sound/pci/hda/hda_loongson.c
new file mode 100644
index 000000000..9d945283a
--- /dev/null
+++ b/sound/pci/hda/hda_loongson.c
@@ -0,0 +1,1076 @@
+/*
+ *
+ *  hda_loongson.c - Implementation of primary alsa driver code base
+ *                for Intel HD Audio.
+ *
+ *  Copyright (c) 2004 Intel Corporation. All rights reserved.
+ *
+ *  Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *                     PeiSen Hou <pshou@realtek.com.tw>
+ *
+ *  Copyright (c) 2014 Huacai Chen <chenhc@lemote.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the Free
+ *  Software Foundation; either version 2 of the License, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ *  more details.
+ *
+ *  You should have received a copy of the GNU General Public License along with
+ *  this program; if not, write to the Free Software Foundation, Inc., 59
+ *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *  CONTACTS:
+ *
+ *  Matt Jared		matt.jared@intel.com
+ *  Andy Kopp		andy.kopp@intel.com
+ *  Dan Kogan		dan.d.kogan@intel.com
+ *
+ *  CHANGES:
+ *
+ *  2004.12.01	Major rewrite by tiwai, merged the work of pshou
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/reboot.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+#include <linux/completion.h>
+#include <linux/pci.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <linux/firmware.h>
+#include <sound/hda_codec.h>
+//#include "hda_codec.h"
+#include "hda_controller.h"
+
+#ifdef CONFIG_CPU_LOONGSON3
+#include <boot_param.h>
+#include <loongson-pch.h>
+#endif
+
+/* macros for convenience. */
+#define platform_resource_start(dev,bar)   ((dev)->resource[(bar)].start)
+#define platform_resource_end(dev,bar)     ((dev)->resource[(bar)].end)
+#define platform_resource_flags(dev,bar)   ((dev)->resource[(bar)].flags)
+#define platform_resource_len(dev,bar) \
+        ((platform_resource_start((dev),(bar)) == 0 &&       \
+          platform_resource_end((dev),(bar)) ==              \
+          platform_resource_start((dev),(bar))) ? 0 :        \
+                                                        \
+         (platform_resource_end((dev),(bar)) -               \
+          platform_resource_start((dev),(bar)) + 1))
+
+#define ICH6_NUM_CAPTURE	4
+#define ICH6_NUM_PLAYBACK	4
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static char *model[SNDRV_CARDS];
+static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
+static int probe_only[SNDRV_CARDS];
+static int jackpoll_ms[SNDRV_CARDS] = {1000};
+static bool single_cmd;
+static int enable_msi = -1;
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
+					CONFIG_SND_HDA_INPUT_BEEP_MODE};
+#endif
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Intel HD audio interface.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
+module_param_array(model, charp, NULL, 0444);
+MODULE_PARM_DESC(model, "Use the given board model.");
+module_param_array(bdl_pos_adj, int, NULL, 0644);
+MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
+module_param_array(probe_only, int, NULL, 0444);
+MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization.");
+module_param_array(jackpoll_ms, int, NULL, 0444);
+MODULE_PARM_DESC(jackpoll_ms, "Ms between polling for jack events (default = 0, using unsol events only)");
+module_param(single_cmd, bool, 0444);
+MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
+		 "(for debugging only).");
+module_param(enable_msi, bint, 0444);
+MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+module_param_array(beep_mode, bool, NULL, 0444);
+MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
+			    "(0=off, 1=on) (default=1).");
+#endif
+
+#ifdef CONFIG_PM
+static int param_set_xint(const char *val, const struct kernel_param *kp);
+static struct kernel_param_ops param_ops_xint = {
+	.set = param_set_xint,
+	.get = param_get_int,
+};
+#define param_check_xint param_check_int
+
+static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
+module_param(power_save, xint, 0644);
+MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
+		 "(in second, 0 = disable).");
+
+/* reset the HD-audio controller in power save mode.
+ * this may give more power-saving, but will take longer time to
+ * wake up.
+ */
+static bool power_save_controller = 1;
+module_param(power_save_controller, bool, 0644);
+MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
+#else
+#define power_save	0
+#endif /* CONFIG_PM */
+static int use_pci_probe = 0;
+static int align_buffer_size = -1;
+module_param(align_buffer_size, bint, 0644);
+MODULE_PARM_DESC(align_buffer_size,
+		"Force buffer and period sizes to be multiple of 128 bytes.");
+
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
+			 "{Intel, ICH6M},"
+			 "{Intel, ICH7},"
+			 "{Intel, ICH8},"
+			 "{Intel, ICH9},"
+			 "{Intel, ICH10}}");
+MODULE_DESCRIPTION("Loongson HDA driver");
+
+/* driver types */
+enum {
+	AZX_DRIVER_ICH,
+	AZX_NUM_DRIVERS, /* keep this as last entry */
+};
+
+static char *driver_short_names[] = {
+	[AZX_DRIVER_ICH] = "HD-Audio Loongson",
+};
+
+struct hda_loongson {
+	struct azx chip;
+
+	/* for pending irqs */
+	struct work_struct irq_pending_work;
+
+	/* sync probing */
+	struct completion probe_wait;
+	struct work_struct probe_work;
+
+	/* card list (for power_save trigger) */
+	struct list_head list;
+
+	/* extra flags */
+	unsigned int irq_pending_warned:1;
+	unsigned int probe_continued:1;
+};
+
+static int azx_acquire_irq(struct azx *chip, int do_disconnect);
+
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev);
+
+/*
+ * HDA controller ops.
+ */
+
+static u32 loongson_azx_readl(u32 __iomem *addr)
+{
+	return readl(addr);
+}
+
+static void loongson_azx_writel(u32 val, u32 __iomem *addr)
+{
+	writel(val, addr);
+}
+
+static u16 loongson_azx_readw(u16 __iomem *addr)
+{
+	return readw(addr);
+}
+
+static void loongson_azx_writew(u16 val, u16 __iomem *addr)
+{
+	writew(val, addr);
+}
+
+static u8 loongson_azx_readb(u8 __iomem *addr)
+{
+	return readb(addr);
+}
+
+static void loongson_azx_writeb(u8 val, u8 __iomem *addr)
+{
+	writeb(val, addr);
+}
+/* called from IRQ */
+static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
+{
+	struct hda_loongson *hda = container_of(chip, struct hda_loongson, chip);
+	int ok;
+
+	ok = azx_position_ok(chip, azx_dev);
+	if (ok == 1) {
+		azx_dev->irq_pending = 0;
+		return ok;
+	} else if (ok == 0) {
+		/* bogus IRQ, process it later */
+		azx_dev->irq_pending = 1;
+		schedule_work(&hda->irq_pending_work);
+	}
+	return 0;
+}
+
+/*
+ * Check whether the current DMA position is acceptable for updating
+ * periods.  Returns non-zero if it's OK.
+ *
+ * Many HD-audio controllers appear pretty inaccurate about
+ * the update-IRQ timing.  The IRQ is issued before actually the
+ * data is processed.  So, we need to process it afterwords in a
+ * workqueue.
+ */
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
+{
+	return 1; /* OK, it's fine */
+}
+
+/*
+ * The work for pending PCM period updates.
+ */
+static void azx_irq_pending_work(struct work_struct *work)
+{
+	struct hda_loongson *hda = container_of(work, struct hda_loongson, irq_pending_work);
+	struct azx *chip = &hda->chip;
+	struct hdac_bus *bus = azx_bus(chip);
+	struct hdac_stream *s;
+	int pending, ok;
+
+	if (!hda->irq_pending_warned) {
+		dev_info(chip->card->dev,
+			 "IRQ timing workaround is activated for card #%d. Suggest a bigger bdl_pos_adj.\n",
+			 chip->card->number);
+		hda->irq_pending_warned = 1;
+	}
+
+	for (;;) {
+		pending = 0;
+		spin_lock_irq(&bus->reg_lock);
+		list_for_each_entry(s, &bus->stream_list, list) {
+			struct azx_dev *azx_dev = stream_to_azx_dev(s);
+			if (!azx_dev->irq_pending ||
+			    !s->substream ||
+			    !s->running)
+				continue;
+			ok = azx_position_ok(chip, azx_dev);
+			if (ok > 0) {
+				azx_dev->irq_pending = 0;
+				spin_unlock(&bus->reg_lock);
+				snd_pcm_period_elapsed(s->substream);
+				spin_lock(&bus->reg_lock);
+			} else if (ok < 0) {
+				pending = 0;	/* too early */
+			} else
+				pending++;
+		}
+		spin_unlock_irq(&bus->reg_lock);
+		if (!pending)
+			return;
+		msleep(1);
+	}
+}
+
+/* clear irq_pending flags and assure no on-going workq */
+static void azx_clear_irq_pending(struct azx *chip)
+{
+	struct hdac_bus *bus = azx_bus(chip);
+	struct hdac_stream *s;
+
+	spin_lock_irq(&bus->reg_lock);
+	list_for_each_entry(s, &bus->stream_list, list) {
+		struct azx_dev *azx_dev = stream_to_azx_dev(s);
+		azx_dev->irq_pending = 0;
+	}
+	spin_unlock_irq(&bus->reg_lock);
+}
+
+static int azx_acquire_irq(struct azx *chip, int do_disconnect)
+{
+	struct hdac_bus *bus = azx_bus(chip);
+	int irq;
+	
+	if (use_pci_probe)
+		irq = chip->pci->irq;
+	else
+		irq = platform_get_irq(to_platform_device(chip->card->dev), 0);
+
+	if (request_irq(irq, azx_interrupt, chip->msi ? 0 : IRQF_SHARED,
+			KBUILD_MODNAME, chip)) {
+		dev_err(chip->card->dev,
+			"unable to grab IRQ %d, disabling device\n", irq);
+		if (do_disconnect)
+			snd_card_disconnect(chip->card);
+		return -1;
+	}
+	bus->irq = irq;
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static DEFINE_MUTEX(card_list_lock);
+static LIST_HEAD(card_list);
+
+static void azx_add_card_list(struct azx *chip)
+{
+	struct hda_loongson *hda = container_of(chip, struct hda_loongson, chip);
+	mutex_lock(&card_list_lock);
+	list_add(&hda->list, &card_list);
+	mutex_unlock(&card_list_lock);
+}
+
+static void azx_del_card_list(struct azx *chip)
+{
+	struct hda_loongson *hda = container_of(chip, struct hda_loongson, chip);
+	mutex_lock(&card_list_lock);
+	list_del_init(&hda->list);
+	mutex_unlock(&card_list_lock);
+}
+
+/* trigger power-save check at writing parameter */
+static int param_set_xint(const char *val, const struct kernel_param *kp)
+{
+	struct hda_loongson *hda;
+	struct azx *chip;
+	int prev = power_save;
+	int ret = param_set_int(val, kp);
+
+	if (ret || prev == power_save)
+		return ret;
+
+	mutex_lock(&card_list_lock);
+	list_for_each_entry(hda, &card_list, list) {
+		chip = &hda->chip;
+		if (!hda->probe_continued || chip->disabled)
+			continue;
+		snd_hda_set_power_save(&chip->bus, power_save * 1000);
+	}
+	mutex_unlock(&card_list_lock);
+	return 0;
+}
+#else
+#define azx_add_card_list(chip) /* NOP */
+#define azx_del_card_list(chip) /* NOP */
+#endif /* CONFIG_PM */
+
+#if defined(CONFIG_PM_SLEEP)
+/*
+ * power management
+ */
+static int azx_suspend(struct device *dev)
+{
+	struct snd_card *card = dev_get_drvdata(dev);
+	struct azx *chip = card->private_data;
+	struct hdac_bus *bus;
+
+	if (chip->disabled)
+		return 0;
+
+	bus = azx_bus(chip);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+	azx_clear_irq_pending(chip);
+	azx_stop_chip(chip);
+	azx_enter_link_reset(chip);
+	if (bus->irq >= 0) {
+		free_irq(bus->irq, chip);
+		bus->irq = -1;
+	}
+	return 0;
+}
+
+static int azx_resume(struct device *dev)
+{
+	struct snd_card *card = dev_get_drvdata(dev);
+	struct azx *chip = card->private_data;
+
+	if (chip->disabled)
+		return 0;
+
+	chip->msi = 0;
+	if (azx_acquire_irq(chip, 1) < 0)
+		return -EIO;
+
+	azx_init_chip(chip, true);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM
+static int azx_runtime_suspend(struct device *dev)
+{
+	struct snd_card *card = dev_get_drvdata(dev);
+	struct azx *chip = card->private_data;
+
+	if (chip->disabled)
+		return 0;
+
+	if (!azx_has_pm_runtime(chip))
+		return 0;
+
+	/* enable controller wake up event */
+	azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) |
+		  STATESTS_INT_MASK);
+
+	azx_stop_chip(chip);
+	azx_enter_link_reset(chip);
+	azx_clear_irq_pending(chip);
+	return 0;
+}
+
+static int azx_runtime_resume(struct device *dev)
+{
+	struct snd_card *card = dev_get_drvdata(dev);
+	struct azx *chip = card->private_data;
+	struct hda_codec *codec;
+	int status;
+
+	if (chip->disabled)
+		return 0;
+
+	if (!azx_has_pm_runtime(chip))
+		return 0;
+
+	/* Read STATESTS before controller reset */
+	status = azx_readw(chip, STATESTS);
+
+	azx_init_chip(chip, true);
+
+	if (status) {
+		list_for_each_codec(codec, &chip->bus)
+			if (status & (1 << codec->addr))
+				schedule_delayed_work(&codec->jackpoll_work,
+						      codec->jackpoll_interval);
+	}
+
+	/* disable controller Wake Up event*/
+	azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
+			~STATESTS_INT_MASK);
+
+	return 0;
+}
+
+static int azx_runtime_idle(struct device *dev)
+{
+	struct snd_card *card = dev_get_drvdata(dev);
+	struct azx *chip = card->private_data;
+
+	if (chip->disabled)
+		return 0;
+
+	if (!power_save_controller || !azx_has_pm_runtime(chip) ||
+	    azx_bus(chip)->codec_powered)
+		return -EBUSY;
+
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops azx_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
+	SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle)
+};
+
+#define AZX_PM_OPS	&azx_pm
+#else
+#define AZX_PM_OPS	NULL
+#endif /* CONFIG_PM */
+
+static int azx_probe_continue(struct azx *chip);
+
+/*
+ * destructor
+ */
+static int azx_free(struct azx *chip)
+{
+	struct device *snddev = chip->card->dev;
+	struct hda_loongson *hda = container_of(chip, struct hda_loongson, chip);
+	struct hdac_bus *bus = azx_bus(chip);
+
+	if (azx_has_pm_runtime(chip) && chip->running)
+		pm_runtime_get_noresume(snddev);
+
+	azx_del_card_list(chip);
+
+	complete_all(&hda->probe_wait);
+
+	if (bus->chip_init) {
+		azx_clear_irq_pending(chip);
+		azx_stop_all_streams(chip);
+		azx_stop_chip(chip);
+	}
+
+	if (bus->irq >= 0)
+		free_irq(bus->irq, (void*)chip);
+	if (bus->remap_addr)
+		iounmap(bus->remap_addr);
+
+	azx_free_stream_pages(chip);
+	azx_free_streams(chip);
+	snd_hdac_bus_exit(bus);
+	kfree(hda);
+
+	return 0;
+}
+
+static int azx_dev_disconnect(struct snd_device *device)
+{
+	struct azx *chip = device->device_data;
+
+	chip->bus.shutdown = 1;
+	return 0;
+}
+
+static int azx_dev_free(struct snd_device *device)
+{
+	return azx_free(device->device_data);
+}
+
+/*
+ * constructor
+ */
+
+static const struct hda_controller_ops loongson_hda_ops;
+static void azx_probe_work(struct work_struct *work)
+{
+	struct hda_loongson *hda = container_of(work, struct hda_loongson, probe_work);
+	azx_probe_continue(&hda->chip);
+}
+
+static int azx_create(struct snd_card *card, struct pci_dev *pci, int dev, 
+				unsigned int driver_caps, struct azx **rchip)
+{
+	static struct snd_device_ops ops = {
+		.dev_disconnect = azx_dev_disconnect,
+		.dev_free = azx_dev_free,
+	};
+	struct hda_loongson *hda;
+	struct azx *chip;
+	int err;
+
+	*rchip = NULL;
+
+	hda = kzalloc(sizeof(*hda), GFP_KERNEL);
+	if (!hda) {
+		dev_err(card->dev, "Cannot allocate hda\n");
+		return -ENOMEM;
+	}
+
+	chip = &hda->chip;
+	mutex_init(&chip->open_mutex);
+	chip->card = card;
+	chip->pci = pci;
+	chip->ops = &loongson_hda_ops;
+	chip->driver_caps = driver_caps;
+	chip->driver_type = driver_caps & 0xff;
+	chip->dev_index = dev;
+	if (jackpoll_ms[dev] >= 50 && jackpoll_ms[dev] <= 60000)
+		chip->jackpoll_interval = msecs_to_jiffies(jackpoll_ms[dev]);
+	INIT_LIST_HEAD(&chip->pcm_list);
+	INIT_WORK(&hda->irq_pending_work, azx_irq_pending_work);
+	INIT_LIST_HEAD(&hda->list);
+	init_completion(&hda->probe_wait);
+
+	chip->get_position[0] = chip->get_position[1] = azx_get_pos_lpib;
+
+	chip->snoop = false;
+	chip->single_cmd = single_cmd;
+	azx_bus(chip)->codec_mask = chip->codec_probe_mask = 0x1;
+
+
+	if (bdl_pos_adj[dev] < 0) {
+		switch (chip->driver_type) {
+		case AZX_DRIVER_ICH:
+			bdl_pos_adj[dev] = 1;
+			break;
+		default:
+			bdl_pos_adj[dev] = 32;
+			break;
+		}
+	}
+	chip->bdl_pos_adj = bdl_pos_adj[dev];
+
+	err = azx_bus_init(chip, model[dev]);
+	if (err < 0) {
+		kfree(hda);
+		return err;
+	}
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0) {
+		dev_err(card->dev, "Error creating device [card]!\n");
+		azx_free(chip);
+		return err;
+	}
+	INIT_WORK(&hda->probe_work, azx_probe_work);
+
+	*rchip = chip;
+
+	return 0;
+}
+
+static int azx_first_init(struct azx *chip)
+{
+	int dev = chip->dev_index;
+	struct pci_dev *pci = chip->pci;
+	struct snd_card *card = chip->card;
+	struct hdac_bus *bus = azx_bus(chip);
+	int err;
+	unsigned short gcap = 0;
+
+	if (use_pci_probe) {
+		err = pci_request_regions(chip->pci, "LS HD audio");
+		if (err < 0)
+			return err;
+		chip->region_requested = 1;
+
+		bus->addr = pci_resource_start(chip->pci, 0);
+		bus->remap_addr = pci_ioremap_bar(chip->pci, 0);
+	} else {
+		bus->addr = platform_resource_start(to_platform_device(chip->card->dev), 0);
+		bus->remap_addr = ioremap(bus->addr,
+					   platform_resource_len(to_platform_device(chip->card->dev), 0));
+	}
+	if (bus->remap_addr == NULL) {
+		dev_err(card->dev, "ioremap error\n");
+		return -ENXIO;
+	}
+
+	chip->msi = 0;
+	if (azx_acquire_irq(chip, 0) < 0)
+		return -EBUSY;
+
+	synchronize_irq(bus->irq);
+
+	gcap = azx_readw(chip, GCAP);
+	
+	dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
+
+	/* disable 64bit DMA address on some devices */
+	if (chip->driver_caps & AZX_DCAPS_NO_64BIT) {
+		dev_dbg(card->dev, "Disabling 64bit DMA\n");
+		gcap &= ~AZX_GCAP_64OK;
+	}
+
+	/* disable buffer size rounding to 128-byte multiples if supported */
+	if (align_buffer_size >= 0)
+		chip->align_buffer_size = !!align_buffer_size;
+	else {
+		if (chip->driver_caps & AZX_DCAPS_NO_ALIGN_BUFSIZE)
+			chip->align_buffer_size = 0;
+		else
+			chip->align_buffer_size = 1;
+	}
+
+	/* allow 64bit DMA address if supported by H/W */
+	if ((gcap & AZX_GCAP_64OK) && !dma_set_mask(chip->card->dev, DMA_BIT_MASK(64)))
+		dma_set_coherent_mask(chip->card->dev, DMA_BIT_MASK(64));
+	else {
+		dma_set_mask(chip->card->dev, DMA_BIT_MASK(32));
+		dma_set_coherent_mask(chip->card->dev, DMA_BIT_MASK(32));
+	}
+
+	/* read number of streams from GCAP register instead of using
+	 * hardcoded value
+	 */
+	chip->capture_streams = (gcap >> 8) & 0x0f;
+	chip->playback_streams = (gcap >> 12) & 0x0f;
+	if (!chip->playback_streams && !chip->capture_streams) {
+		/* gcap didn't give any info, switching to old method */
+		chip->playback_streams = ICH6_NUM_PLAYBACK;
+		chip->capture_streams = ICH6_NUM_CAPTURE;
+	}
+	
+	chip->capture_index_offset = 0;
+	chip->playback_index_offset = chip->capture_streams;
+	chip->num_streams = chip->playback_streams + chip->capture_streams;
+	
+
+	/* initialize streams */
+	err = azx_init_streams(chip);
+	if (err < 0)
+		return err;
+
+//	chip->playback_streams = chip->capture_streams = 1; /* Loongson */
+	err = azx_alloc_stream_pages(chip);
+	if (err < 0)
+		return err;
+
+	/* initialize chip */
+	azx_init_chip(chip, (probe_only[dev] & 2) == 0);
+
+	/* codec detection */
+	
+	if (!azx_bus(chip)->codec_mask) {
+		dev_err(card->dev, "no codecs found!\n");
+		return -ENODEV;
+	}
+
+	strcpy(card->driver, "HDA-Loongson");
+	strlcpy(card->shortname, driver_short_names[chip->driver_type],
+		sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s at 0x%lx irq %i",
+		 card->shortname, bus->addr, bus->irq);
+
+	return 0;
+}
+
+
+/* DMA page allocation helpers.  */
+static int dma_alloc_pages(struct hdac_bus *bus, int type, size_t size,
+			   struct snd_dma_buffer *buf)
+{
+	return snd_dma_alloc_pages(type, bus->dev, size, buf);
+}
+
+static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
+{
+	snd_dma_free_pages(buf);
+}
+
+static int substream_alloc_pages(struct azx *chip,
+				 struct snd_pcm_substream *substream,
+				 size_t size)
+{
+	return snd_pcm_lib_malloc_pages(substream, size);
+}
+
+static int substream_free_pages(struct azx *chip,
+				struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+static int disable_msi_reset_irq(struct azx *chip)
+{
+	struct hdac_bus *bus = azx_bus(chip);
+	int err;
+
+	free_irq(bus->irq, chip);
+	bus->irq = -1;
+	chip->card->sync_irq = -1;
+	pci_disable_msi(chip->pci);
+	chip->msi = 0;
+	err = azx_acquire_irq(chip, 1);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
+			     struct vm_area_struct *area)
+{
+	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+	struct azx *chip = apcm->chip;
+	if (chip->uc_buffer)
+		area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+}
+static const struct hda_controller_ops loongson_hda_ops = {
+	.disable_msi_reset_irq = disable_msi_reset_irq,
+	.position_check = azx_position_check,
+};
+
+static int azx_probe(struct platform_device *pdev)
+{
+	static int dev;
+	struct snd_card *card;
+	struct hda_loongson *hda;
+	struct azx *chip;
+	bool probe_now;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+	err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
+			   0, &card);
+	if (err < 0) {
+		dev_err(&pdev->dev, "Error creating card!\n");
+		return err;
+	}
+
+	err = azx_create(card, NULL, dev, AZX_DRIVER_ICH | AZX_DCAPS_LS_HDA_WORKAROUND, &chip);
+	if (err < 0)
+		goto out_free;
+	card->private_data = chip;
+	hda = container_of(chip, struct hda_loongson, chip);
+
+	dev_set_drvdata(&pdev->dev, card);
+
+	probe_now = !chip->disabled;
+
+	if (probe_now)
+		schedule_work(&hda->probe_work);
+		
+	dev++;
+	if (chip->disabled)
+		complete_all(&hda->probe_wait);
+	return 0;
+
+out_free:
+	snd_card_free(card);
+	return err;
+}
+
+/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
+static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {};
+
+static int azx_probe_continue(struct azx *chip)
+{
+	struct hda_loongson *hda = container_of(chip, struct hda_loongson, chip);
+	struct hdac_bus *bus = azx_bus(chip);
+	int dev = chip->dev_index;
+	int err;
+	struct device *snddev = chip->card->dev;
+
+	to_hda_bus(bus)->bus_probing = 1;
+	hda->probe_continued = 1;
+	err = azx_first_init(chip);
+	if (err < 0)
+		goto out_free;
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+	chip->beep_mode = beep_mode[dev];
+#endif
+
+	/* create codec instances */
+	err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]);
+	if (err < 0)
+		goto out_free;
+	if ((probe_only[dev] & 1) == 0) {
+		err = azx_codec_configure(chip);
+		if (err < 0)
+			goto out_free;
+	}
+	err = snd_card_register(chip->card);
+	if (err < 0)
+		goto out_free;
+
+	chip->running = 1;
+	azx_add_card_list(chip);
+#ifdef CONFIG_PM
+	pm_runtime_forbid(snddev);
+	pm_runtime_set_active(snddev);
+#endif
+	snd_hda_set_power_save(&chip->bus, power_save * 1000);
+	if (azx_has_pm_runtime(chip))
+		pm_runtime_put_noidle(snddev);
+
+out_free:
+	complete_all(&hda->probe_wait);
+	to_hda_bus(bus)->bus_probing = 0;
+	return err;
+}
+
+static int azx_remove(struct platform_device *pdev)
+{
+	return snd_card_free(dev_get_drvdata(&pdev->dev));
+}
+
+static void azx_shutdown(struct platform_device *pdev)
+{
+	struct snd_card *card = dev_get_drvdata(&pdev->dev);
+	struct azx *chip;
+
+	if (!card)
+		return;
+	chip = card->private_data;
+	if (chip && chip->running)
+		azx_stop_chip(chip);
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id ls_hda_id_table[] = {
+	{.compatible = "loongson,ls-audio"},
+	{},
+};
+MODULE_DEVICE_TABLE(of, ls_hda_id_table);
+#endif
+
+/* platform_driver definition */
+static struct platform_driver azx_driver = {
+	.probe = azx_probe,
+	.remove = azx_remove,
+	.shutdown = azx_shutdown,
+	.driver = {
+		.name = "ls-audio",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_OF
+		.of_match_table = of_match_ptr(ls_hda_id_table),
+#endif
+		.pm = AZX_PM_OPS,
+	},
+};
+
+#define PCI_DEVICE_ID_HDA 0x7a07
+#define PCI_VENDOR_ID_HDA 0x0014
+/*
+static DEFINE_PCI_DEVICE_TABLE(azx_id_table) = {
+	{PCI_DEVICE(PCI_VENDOR_ID_HDA, PCI_DEVICE_ID_HDA)},
+	{}
+};
+MODULE_DEVICE_TABLE(pci, azx_id_table);
+*/
+static const struct pci_device_id azx_id_table[] = {
+	{PCI_DEVICE(PCI_VENDOR_ID_HDA, PCI_DEVICE_ID_HDA)},
+	{}
+};
+MODULE_DEVICE_TABLE(pci, azx_id_table);
+
+static int azx_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+	static int dev;
+	struct snd_card *card;
+	struct hda_loongson *hda;
+	struct azx *chip;
+	bool schedule_probe;
+	int err;
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	use_pci_probe = 1;
+
+	err = pci_enable_device(pci);
+	if (err < 0)
+		return err;
+
+	err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 0, &card);
+	if (err < 0) {
+		dev_err(&pci->dev, "Error creating card!\n");
+		return err;
+	}
+	err = azx_create(card, pci, dev, AZX_DRIVER_ICH | AZX_DCAPS_LS_HDA_WORKAROUND, &chip);
+	if (err < 0)
+		goto out_free;
+	card->private_data = chip;
+	hda = container_of(chip, struct hda_loongson, chip);
+
+	pci_set_drvdata(pci, card);
+
+	schedule_probe = !chip->disabled;
+
+	if (schedule_probe)
+		schedule_work(&hda->probe_work);
+	dev++;
+	if (chip->disabled)
+		complete_all(&hda->probe_wait);
+	return 0;
+
+out_free:
+	snd_card_free(card);
+	return err;
+}
+
+static void azx_pci_remove(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct azx *chip;
+	struct hda_loongson *hda;
+
+	if (card) {
+		/* cancel the pending probing work */
+		chip = card->private_data;
+		hda = container_of(chip, struct hda_loongson, chip);
+		/* FIXME: below is an ugly workaround.
+		 * Both device_release_driver() and driver_probe_device()
+		 * take *both* the device's and its parent's lock before
+		 * calling the remove() and probe() callbacks.  The codec
+		 * probe takes the locks of both the codec itself and its
+		 * parent, i.e. the PCI controller dev.  Meanwhile, when
+		 * the PCI controller is unbound, it takes its lock, too
+		 * ==> ouch, a deadlock!
+		 * As a workaround, we unlock temporarily here the controller
+		 * device during cancel_work_sync() call.
+		 */
+		device_unlock(&pci->dev);
+		cancel_work_sync(&hda->probe_work);
+		device_lock(&pci->dev);
+
+		snd_card_free(card);
+	}
+
+}
+
+static void azx_pci_shutdown(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct azx *chip;
+
+	if (!card)
+		return;
+	chip = card->private_data;
+	if (chip && chip->running)
+		azx_stop_chip(chip);
+}
+
+/* pci_driver definition */
+static struct pci_driver azx_pci_driver = {
+	.name = "azx_pci_driver",
+	.id_table = azx_id_table,
+	.probe = azx_pci_probe,
+	.remove = azx_pci_remove,
+	.shutdown = azx_pci_shutdown,
+	.driver = {
+		.pm = AZX_PM_OPS,
+	},
+};
+
+static int __init alsa_card_azx_init(void)
+{
+	int err;
+	err = platform_driver_register(&azx_driver);
+//	if (!err) {
+//		err = pci_register_driver(&azx_pci_driver);
+//	}
+
+	printk("hda azx driver register err:%d\n", err); 
+	return err;
+}
+
+static void __exit alsa_card_azx_exit(void)
+{
+	platform_driver_unregister(&azx_driver);
+}
+
+module_init(alsa_card_azx_init)
+module_exit(alsa_card_azx_exit)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index a7544b77d..8b11eb448 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -25,6 +25,7 @@
 #include "hda_auto_parser.h"
 #include "hda_jack.h"
 #include "hda_generic.h"
+#include "hda_controller.h"
 
 /* keep halting ALC5505 DSP, for power saving */
 #define HALT_REALTEK_ALC5505
@@ -296,6 +297,13 @@ static void alc_fixup_micmute_led(struct hda_codec *codec,
 		snd_hda_gen_add_micmute_led_cdev(codec, NULL);
 }
 
+int has_loongson_workaround(struct hda_codec *codec)
+{
+	struct azx *chip = bus_to_azx(&codec->bus->core);
+
+	return chip->driver_caps & AZX_DCAPS_LS_HDA_WORKAROUND;
+}
+
 /*
  * Fix hardware PLL issue
  * On some codecs, the analog PLL gating control must be off while
@@ -629,10 +637,10 @@ static int alc_auto_parse_customize_define(struct hda_codec *codec)
 		goto do_sku;
 	}
 
-	if (!codec->bus->pci)
+	if (!codec->bus->pci && !has_loongson_workaround(codec))
 		return -1;
 	ass = codec->core.subsystem_id & 0xffff;
-	if (ass != codec->bus->pci->subsystem_device && (ass & 1))
+	if (codec->bus->pci && ass != codec->bus->pci->subsystem_device && (ass & 1))
 		goto do_sku;
 
 	nid = 0x1d;
@@ -3845,6 +3853,10 @@ static int alc269_resume(struct hda_codec *codec)
 		msleep(150);
 	}
 
+	if (!spec->no_depop_delay) {
+		msleep(150);
+	}
+
 	codec->patch_ops.init(codec);
 
 	if (spec->codec_variant == ALC269_TYPE_ALC269VB)
@@ -6412,6 +6424,7 @@ enum {
 	ALC269_FIXUP_CZC_B20,
 	ALC269_FIXUP_CZC_TMI,
 	ALC269_FIXUP_CZC_L101,
+	ALC269_FIXUP_LS3A7A,
 	ALC269_FIXUP_LEMOTE_A1802,
 	ALC269_FIXUP_LEMOTE_A190X,
 	ALC256_FIXUP_INTEL_NUC8_RUGGED,
@@ -7901,6 +7914,14 @@ static const struct hda_fixup alc269_fixups[] = {
 		.chained = true,
 		.chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
 	},
+	[ALC269_FIXUP_LS3A7A] = {
+		.type = HDA_FIXUP_PINS,
+		.v.pins = (const struct hda_pintbl[]) {
+			{ 0x1b, 0x02214c40 }, /* Front Mic */
+			{ 0x15, 0x01014030 }, /* Rear Mic */
+			{ }
+		},
+	},
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -8271,6 +8292,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
 	SND_PCI_QUIRK(0x1b35, 0x1235, "CZC B20", ALC269_FIXUP_CZC_B20),
 	SND_PCI_QUIRK(0x1b35, 0x1236, "CZC TMI", ALC269_FIXUP_CZC_TMI),
 	SND_PCI_QUIRK(0x1b35, 0x1237, "CZC L101", ALC269_FIXUP_CZC_L101),
+	SND_PCI_QUIRK(0x10ec, 0x0269, "ls3a7a", ALC269_FIXUP_LS3A7A),
 	SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
 	SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
 	SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -9583,8 +9605,8 @@ enum {
 	ALC662_FIXUP_LED_GPIO1,
 	ALC662_FIXUP_IDEAPAD,
 	ALC272_FIXUP_MARIO,
-	ALC662_FIXUP_CZC_ET26,
 	ALC662_FIXUP_CZC_P10T,
+	ALC662_FIXUP_CZC_ET26,
 	ALC662_FIXUP_SKU_IGNORE,
 	ALC662_FIXUP_HP_RP5800,
 	ALC662_FIXUP_ASUS_MODE1,
-- 
2.17.1

