[3.13.y.z extended stable] Patch "ALSA: hda - restore BCLK M/N values when resuming HSW/BDW display controller" has been added to staging queue

Kamal Mostafa kamal at canonical.com
Tue Jul 15 21:30:01 UTC 2014


This is a note to let you know that I have just added a patch titled

    ALSA: hda - restore BCLK M/N values when resuming HSW/BDW display controller

to the linux-3.13.y-queue branch of the 3.13.y.z extended stable tree 
which can be found at:

 http://kernel.ubuntu.com/git?p=ubuntu/linux.git;a=shortlog;h=refs/heads/linux-3.13.y-queue

This patch is scheduled to be released in version 3.13.11.5.

If you, or anyone else, feels it should not be added to this tree, please 
reply to this email.

For more information about the 3.13.y.z tree, see
https://wiki.ubuntu.com/Kernel/Dev/ExtendedStable

Thanks.
-Kamal

------

>From a29920f00683bfd488245c8b0afa2d0f1507bbfe Mon Sep 17 00:00:00 2001
From: Mengdong Lin <mengdong.lin at intel.com>
Date: Thu, 26 Jun 2014 18:45:16 +0800
Subject: ALSA: hda - restore BCLK M/N values when resuming HSW/BDW display
 controller

commit a07187c992be945ab561b370cbb49cfd72064c3c upstream.

For Intel Haswell/Broadwell display HD-A controller, the 24MHz HD-A link BCLK
is converted from Core Display Clock (CDCLK): BCLK = CDCLK * M / N
And there are two registers EM4 and EM5 to program M, N value respectively.
The EM4/EM5 values will be lost and when the display power well is disabled.

BIOS programs CDCLK selected by OEM and EM4/EM5, but BIOS has no idea about
display power well on/off at runtime. So the M/N can be wrong if non-default
CDCLK is used when the audio controller resumes, which results in an invalid
BCLK and abnormal audio playback rate. So this patch saves and restores valid
M/N values on controller suspend/resume.

And 'struct hda_intel' is defined to contain standard HD-A 'struct azx' and
Intel specific fields, as Takashi suggested.

Signed-off-by: Mengdong Lin <mengdong.lin at intel.com>
Signed-off-by: Takashi Iwai <tiwai at suse.de>
[ kamal: backport to 3.13-stable: context ]
Signed-off-by: Kamal Mostafa <kamal at canonical.com>
---
 sound/pci/hda/hda_intel.c | 63 +++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 56 insertions(+), 7 deletions(-)

diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 619d9a3..eab13e2 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -663,6 +663,22 @@ static char *driver_short_names[] = {
 	[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
 };

+/* Intel HSW/BDW display HDA controller Extended Mode registers.
+ * EM4 (M value) and EM5 (N Value) are used to convert CDClk (Core Display
+ * Clock) to 24MHz BCLK: BCLK = CDCLK * M / N
+ * The values will be lost when the display power well is disabled.
+ */
+#define ICH6_REG_EM4			0x100c
+#define ICH6_REG_EM5			0x1010
+
+struct hda_intel {
+	struct azx chip;
+
+	/* HSW/BDW display HDA controller to restore BCLK from CDCLK */
+	unsigned int bclk_m;
+	unsigned int bclk_n;
+};
+
 /*
  * macros for easy use
  */
@@ -2906,6 +2922,22 @@ static int param_set_xint(const char *val, const struct kernel_param *kp)
 #define azx_del_card_list(chip) /* NOP */
 #endif /* CONFIG_PM */

+static void haswell_save_bclk(struct azx *chip)
+{
+	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+
+	hda->bclk_m = azx_readw(chip, EM4);
+	hda->bclk_n = azx_readw(chip, EM5);
+}
+
+static void haswell_restore_bclk(struct azx *chip)
+{
+	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+
+	azx_writew(chip, EM4, hda->bclk_m);
+	azx_writew(chip, EM5, hda->bclk_n);
+}
+
 #if defined(CONFIG_PM_SLEEP) || defined(SUPPORT_VGA_SWITCHEROO)
 /*
  * power management
@@ -2932,6 +2964,13 @@ static int azx_suspend(struct device *dev)
 		free_irq(chip->irq, chip);
 		chip->irq = -1;
 	}
+
+	/* Save BCLK M/N values before they become invalid in D3.
+	 * Will test if display power well can be released now.
+	 */
+	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+		haswell_save_bclk(chip);
+
 	if (chip->msi)
 		pci_disable_msi(chip->pci);
 	pci_disable_device(pci);
@@ -2951,8 +2990,10 @@ static int azx_resume(struct device *dev)
 	if (chip->disabled)
 		return 0;

-	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
 		hda_display_power(true);
+		haswell_restore_bclk(chip);
+	}
 	pci_set_power_state(pci, PCI_D0);
 	pci_restore_state(pci);
 	if (pci_enable_device(pci) < 0) {
@@ -2996,8 +3037,10 @@ static int azx_runtime_suspend(struct device *dev)
 	azx_stop_chip(chip);
 	azx_enter_link_reset(chip);
 	azx_clear_irq_pending(chip);
-	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+		haswell_save_bclk(chip);
 		hda_display_power(false);
+	}
 	return 0;
 }

@@ -3015,8 +3058,10 @@ static int azx_runtime_resume(struct device *dev)
 	if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
 		return 0;

-	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
 		hda_display_power(true);
+		haswell_restore_bclk(chip);
+	}

 	/* Read STATESTS before controller reset */
 	status = azx_readw(chip, STATESTS);
@@ -3213,6 +3258,8 @@ static int register_vga_switcheroo(struct azx *chip)
 static int azx_free(struct azx *chip)
 {
 	struct pci_dev *pci = chip->pci;
+	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+
 	int i;

 	if ((chip->driver_caps & AZX_DCAPS_PM_RUNTIME)
@@ -3274,7 +3321,7 @@ static int azx_free(struct azx *chip)
 		hda_display_power(false);
 		hda_i915_exit();
 	}
-	kfree(chip);
+	kfree(hda);

 	return 0;
 }
@@ -3521,6 +3568,7 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
 	static struct snd_device_ops ops = {
 		.dev_free = azx_dev_free,
 	};
+	struct hda_intel *hda;
 	struct azx *chip;
 	int err;

@@ -3530,13 +3578,14 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
 	if (err < 0)
 		return err;

-	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-	if (!chip) {
-		snd_printk(KERN_ERR SFX "%s: Cannot allocate chip\n", pci_name(pci));
+	hda = kzalloc(sizeof(*hda), GFP_KERNEL);
+	if (!hda) {
+		snd_printk(KERN_ERR SFX "%s: Cannot allocate hda\n", pci_name(pci));
 		pci_disable_device(pci);
 		return -ENOMEM;
 	}

+	chip = &hda->chip;
 	spin_lock_init(&chip->reg_lock);
 	mutex_init(&chip->open_mutex);
 	chip->card = card;
--
1.9.1





More information about the kernel-team mailing list