[alsa-devel] [PATCH] ALSA: hda - set intel audio clock to a properly value

libin.yang at intel.com libin.yang at intel.com
Tue Mar 7 07:20:22 CET 2017


From: Libin Yang <libin.yang at intel.com>

On some Intel platforms, the audio clock may not be set correctly
with initial setting. This will cause the audio playback/capture
rates wrong.

This patch checks the audio clock setting and will set it to a
properly value if it is not set correct.

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=188411

Signed-off-by: Libin Yang <libin.yang at intel.com>
---
 include/sound/hda_register.h        | 12 +++--
 sound/hda/ext/hdac_ext_controller.c |  6 +--
 sound/pci/hda/hda_intel.c           | 91 +++++++++++++++++++++++++++++++++++++
 3 files changed, 103 insertions(+), 6 deletions(-)

diff --git a/include/sound/hda_register.h b/include/sound/hda_register.h
index 0013063..7ea16cb 100644
--- a/include/sound/hda_register.h
+++ b/include/sound/hda_register.h
@@ -227,6 +227,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 #define AZX_REG_PPLCLLPU		0xC
 
 /* registers for Multiple Links Capability Structure */
+/* Multiple Links Capability */
+#define AZX_REG_ML_CAP_BASE		0xc00
 #define AZX_ML_CAP_ID			0x2
 #define AZX_REG_ML_MLCH			0x00
 #define AZX_REG_ML_MLCD			0x04
@@ -243,9 +245,13 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 #define AZX_REG_ML_LOUTPAY		0x20
 #define AZX_REG_ML_LINPAY		0x30
 
-#define AZX_MLCTL_SPA			(1<<16)
-#define AZX_MLCTL_CPA			23
-
+#define AZX_REG_ML_LCAPx(x)	(AZX_REG_ML_CAP_BASE + (0x40 + 0x40 * x))
+#define AZX_REG_ML_LCTLx(x)	(AZX_REG_ML_CAP_BASE + (0x44 + 0x40 * x))
+#define ML_LCTL_SCF_MASK			0xF
+#define AZX_MLCTL_SPA				(0x1 << 16)
+#define AZX_MLCTL_CPA				(0x1 << 23)
+#define AZX_MLCTL_SPA_SHIFT			16
+#define AZX_MLCTL_CPA_SHIFT			23
 
 /* registers for DMA Resume Capability Structure */
 #define AZX_DRSM_CAP_ID			0x5
diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c
index 2614691..84f3b81 100644
--- a/sound/hda/ext/hdac_ext_controller.c
+++ b/sound/hda/ext/hdac_ext_controller.c
@@ -171,7 +171,7 @@ static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable)
 {
 	int timeout;
 	u32 val;
-	int mask = (1 << AZX_MLCTL_CPA);
+	int mask = (1 << AZX_MLCTL_CPA_SHIFT);
 
 	udelay(3);
 	timeout = 150;
@@ -179,10 +179,10 @@ static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable)
 	do {
 		val = readl(link->ml_addr + AZX_REG_ML_LCTL);
 		if (enable) {
-			if (((val & mask) >> AZX_MLCTL_CPA))
+			if (((val & mask) >> AZX_MLCTL_CPA_SHIFT))
 				return 0;
 		} else {
-			if (!((val & mask) >> AZX_MLCTL_CPA))
+			if (!((val & mask) >> AZX_MLCTL_CPA_SHIFT))
 				return 0;
 		}
 		udelay(3);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index c8256a8..017f64f 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -539,6 +539,94 @@ static void bxt_reduce_dma_latency(struct azx *chip)
 	azx_writel(chip, SKL_EM4L, val);
 }
 
+/*
+ * ML_LCAP bits:
+ *  bit 0: 6 MHz Supported
+ *  bit 1: 12 MHz Supported
+ *  bit 2: 24 MHz Supported
+ *  bit 3: 48 MHz Supported
+ *  bit 4: 96 MHz Supported
+ *  bit 5: 192 MHz Supported
+ */
+static int intel_get_lctl_scf(struct azx *chip)
+{
+	u32 val;
+
+	val = azx_readl(chip, ML_LCAPx(0));
+
+	if (val & (1 << 2))
+		return 2;
+	else if (val & (1 << 3))
+		return 3;
+	else if (val & (1 << 1))
+		return 1;
+	else if (val & (1 << 4))
+		return 4;
+	else if (val & (1 << 5))
+		return 5;
+
+	dev_warn(chip->card->dev, "set audio clock frequency to 6MHz");
+	return 0;
+}
+
+static void intel_init_lctl(struct azx *chip)
+{
+	u32 val;
+	int timeout;
+
+	/* 0. check lctl register value is correct or not */
+	/* the codecs are sharing the first link setting by default */
+	val = azx_readl(chip, ML_LCTLx(0));
+	/* if SCF is already set, let's use it */
+	if ((val & ML_LCTL_SCF_MASK) != 0)
+		return;
+
+	/*
+	 * Before operatiing on SPA, CPA must match SPA.
+	 * Any deviation may result in undefined behavior.
+	 */
+	if (((val & AZX_MLCTL_SPA) >> AZX_MLCTL_SPA_SHIFT) ^
+		((val & AZX_MLCTL_CPA) >> AZX_MLCTL_CPA_SHIFT))
+		return;
+
+	/* 1. turn link down: set SPA to 0 and wait CPA to 0 */
+	val = azx_readl(chip, ML_LCTLx(0));
+	val &= ~AZX_MLCTL_SPA;
+	azx_writel(chip, ML_LCTLx(0), val);
+	/* wait for CPA */
+	timeout = 50;
+	while (timeout) {
+		if ((azx_readl(chip, ML_LCTLx(0)) & AZX_MLCTL_CPA) == 0)
+			break;
+		timeout--;
+		udelay(10);
+	}
+	if (!timeout)
+		goto SET_SPA;
+	/* need add 100ns delay for hardware ready */
+	udelay(100);
+
+	/* 2. update SCF to select a properly audio clock*/
+	val &= ~ML_LCTL_SCF_MASK;
+	val |= intel_get_lctl_scf(chip);
+	azx_writel(chip, ML_LCTLx(0), val);
+
+SET_SPA:
+	/* 4. turn link up: set SPA to 1 and wait CPA to 1 */
+	val = azx_readl(chip, ML_LCTLx(0));
+	val |= AZX_MLCTL_SPA;
+	azx_writel(chip, ML_LCTLx(0), val);
+	timeout = 50;
+	while (timeout) {
+		if ((azx_readl(chip, ML_LCTLx(0)) & AZX_MLCTL_CPA) != 0)
+			break;
+		timeout--;
+		udelay(10);
+	}
+	/* need add 100ns delay for hardware ready */
+	udelay(100);
+}
+
 static void hda_intel_init_chip(struct azx *chip, bool full_reset)
 {
 	struct hdac_bus *bus = azx_bus(chip);
@@ -564,6 +652,9 @@ static void hda_intel_init_chip(struct azx *chip, bool full_reset)
 	/* reduce dma latency to avoid noise */
 	if (IS_BXT(pci))
 		bxt_reduce_dma_latency(chip);
+
+	if (IS_SKL_PLUS(pci))
+		intel_init_lctl(chip);
 }
 
 /* calculate runtime delay from LPIB */
-- 
2.7.4



More information about the Alsa-devel mailing list