From: Mengdong Lin <mengdong....@intel.com>

3.12-stable review patch.  If anyone has any objections, please let me know.

===============

commit e4d9e513dedb5ac4e166c1053314fa935ddecc8c upstream.

For HSW/BDW display HD-A controller, hda_set_bclk() is defined to set BCLK
by programming the M/N values as per the core display clock (CDCLK) queried from
i915 display driver.

And the audio driver will also set BCLK in azx_first_init() since the display
driver can turn off the shared power in boot phase if only eDP is connected
and M/N values will be lost and must be reprogrammed.

Signed-off-by: Mengdong Lin <mengdong....@intel.com>
Cc: <sta...@vger.kernel.org>
Signed-off-by: Takashi Iwai <ti...@suse.de>
Signed-off-by: Jiri Slaby <jsl...@suse.cz>
---
 sound/pci/hda/hda_i915.c  | 16 +++++++++++++
 sound/pci/hda/hda_i915.h  |  2 ++
 sound/pci/hda/hda_intel.c | 60 +++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 76 insertions(+), 2 deletions(-)

diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c
index 3ea8b980460e..9e136beaa591 100644
--- a/sound/pci/hda/hda_i915.c
+++ b/sound/pci/hda/hda_i915.c
@@ -24,6 +24,7 @@
 
 static int (*get_power)(void);
 static int (*put_power)(void);
+static int (*get_cdclk)(void);
 
 int hda_display_power(bool enable)
 {
@@ -38,6 +39,13 @@ int hda_display_power(bool enable)
                return put_power();
 }
 
+int haswell_get_cdclk(void)
+{
+       if (!get_cdclk)
+               return -EINVAL;
+       return get_cdclk();
+}
+
 int hda_i915_init(void)
 {
        int err = 0;
@@ -55,6 +63,10 @@ int hda_i915_init(void)
                return -ENODEV;
        }
 
+       get_cdclk = symbol_request(i915_get_cdclk_freq);
+       if (!get_cdclk) /* may have abnormal BCLK and audio playback rate */
+               snd_printd("hda-i915: get_cdclk symbol get fail\n");
+
        snd_printd("HDA driver get symbol successfully from i915 module\n");
 
        return err;
@@ -70,6 +82,10 @@ int hda_i915_exit(void)
                symbol_put(i915_release_power_well);
                put_power = NULL;
        }
+       if (get_cdclk) {
+               symbol_put(i915_get_cdclk_freq);
+               get_cdclk = NULL;
+       }
 
        return 0;
 }
diff --git a/sound/pci/hda/hda_i915.h b/sound/pci/hda/hda_i915.h
index bfd835f8f1aa..26869fafe11a 100644
--- a/sound/pci/hda/hda_i915.h
+++ b/sound/pci/hda/hda_i915.h
@@ -18,10 +18,12 @@
 
 #ifdef CONFIG_SND_HDA_I915
 int hda_display_power(bool enable);
+int haswell_get_cdclk(void);
 int hda_i915_init(void);
 int hda_i915_exit(void);
 #else
 static inline int hda_display_power(bool enable) { return 0; }
+static inline int haswell_get_cdclk(void) { return -EINVAL; }
 static inline int hda_i915_init(void)
 {
        return -ENODEV;
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 3eb2976824a8..d9a6ef843306 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -748,6 +748,54 @@ static inline void mark_runtime_wc(struct azx *chip, 
struct azx_dev *azx_dev,
 }
 #endif
 
+#ifdef CONFIG_SND_HDA_I915
+/* 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
+
+static void haswell_set_bclk(struct azx *chip)
+{
+       int cdclk_freq;
+       unsigned int bclk_m, bclk_n;
+
+       cdclk_freq = haswell_get_cdclk();
+       if (cdclk_freq < 0)
+               return;
+
+       switch (cdclk_freq) {
+       case 337500:
+               bclk_m = 16;
+               bclk_n = 225;
+               break;
+
+       case 450000:
+       default: /* default CDCLK 450MHz */
+               bclk_m = 4;
+               bclk_n = 75;
+               break;
+
+       case 540000:
+               bclk_m = 4;
+               bclk_n = 90;
+               break;
+
+       case 675000:
+               bclk_m = 8;
+               bclk_n = 225;
+               break;
+       }
+
+       azx_writew(chip, EM4, bclk_m);
+       azx_writew(chip, EM5, bclk_n);
+}
+#else
+static inline void haswell_set_bclk(struct azx *chip) {}
+#endif
+
 static int azx_acquire_irq(struct azx *chip, int do_disconnect);
 static int azx_send_cmd(struct hda_bus *bus, unsigned int val);
 /*
@@ -2951,8 +2999,10 @@ static int azx_resume(struct device *dev)
        if (chip->disabled || chip->init_failed)
                return 0;
 
-       if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+       if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
                hda_display_power(true);
+               haswell_set_bclk(chip);
+       }
        pci_set_power_state(pci, PCI_D0);
        pci_restore_state(pci);
        if (pci_enable_device(pci) < 0) {
@@ -3015,8 +3065,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_set_bclk(chip);
+       }
 
        /* Read STATESTS before controller reset */
        status = azx_readw(chip, STATESTS);
@@ -3744,6 +3796,10 @@ static int azx_first_init(struct azx *chip)
 
        /* initialize chip */
        azx_init_pci(chip);
+
+       if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+               haswell_set_bclk(chip);
+
        azx_init_chip(chip, (probe_only[dev] & 2) == 0);
 
        /* codec detection */
-- 
2.1.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to