[alsa-devel] [PATCH 0/2 V2] Power-well API implementation for Haswell
Hi all,
This is V2 and some fixes after first round review:
- use reference count to track power-well usage - remove external module, compiled into snd-hda-intel instead - manage symbols and module loading properly - remove IS_HSW macro, use flag instead - remove audio callback for gfx driver to avoid dependency - split whole patch into two pieces for easy review - more typo fixes
Wang Xingchao (2): drm/915: Add private api for power well usage ALSA: hda - Add power-welll support for haswell HDA
drivers/gpu/drm/i915/intel_pm.c | 76 +++++++++++++++++++++++++++++--- include/drm/i915_powerwell.h | 36 +++++++++++++++ sound/pci/hda/Kconfig | 8 ++++ sound/pci/hda/Makefile | 3 ++ sound/pci/hda/hda_i915.c | 92 +++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_i915.h | 35 +++++++++++++++ sound/pci/hda/hda_intel.c | 33 ++++++++++++-- 7 files changed, 273 insertions(+), 10 deletions(-) create mode 100644 include/drm/i915_powerwell.h create mode 100644 sound/pci/hda/hda_i915.c create mode 100644 sound/pci/hda/hda_i915.h
Haswell Display audio depends on power well in graphic side, it should request power well before use it and release power well after use. I915 will not shutdown power well if it detects audio is using. This patch protects display audio crash for Intel Haswell mobile C3 stepping board.
Signed-off-by: Wang Xingchao xingchao.wang@linux.intel.com --- drivers/gpu/drm/i915/intel_pm.c | 76 +++++++++++++++++++++++++++++++++++---- include/drm/i915_powerwell.h | 36 +++++++++++++++++++ 2 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 include/drm/i915_powerwell.h
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 0f4b46e..cf7e352 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4344,18 +4344,12 @@ bool intel_using_power_well(struct drm_device *dev) return true; }
-void intel_set_power_well(struct drm_device *dev, bool enable) +static void enable_power_well(struct drm_device *dev, bool enable) { struct drm_i915_private *dev_priv = dev->dev_private; bool is_enabled, enable_requested; uint32_t tmp;
- if (!HAS_POWER_WELL(dev)) - return; - - if (!i915_disable_power_well && !enable) - return; - tmp = I915_READ(HSW_PWR_WELL_DRIVER); is_enabled = tmp & HSW_PWR_WELL_STATE; enable_requested = tmp & HSW_PWR_WELL_ENABLE; @@ -4378,6 +4372,74 @@ void intel_set_power_well(struct drm_device *dev, bool enable) } }
+/* Global drm_device for display audio drvier usage */ +static struct drm_device *power_well_device; +/* Lock protecting power well setting */ +static DEFINE_SPINLOCK(powerwell_lock); +static bool i915_power_well_using; +static int hsw_power_count; + +void i915_request_power_well(void) +{ + if (!power_well_device) + return; + + if (!IS_HASWELL(power_well_device)) + return; + + spin_lock_irq(&powerwell_lock); + if (!hsw_power_count++) { + enable_power_well(power_well_device, true); + } + spin_unlock_irq(&powerwell_lock); +} +EXPORT_SYMBOL_GPL(i915_request_power_well); + +void i915_release_power_well(void) +{ + if (!power_well_device) + return; + + if (!IS_HASWELL(power_well_device)) + return; + + spin_lock_irq(&powerwell_lock); + WARN_ON(!hsw_power_count); + if (!--hsw_power_count + && !i915_power_well_using) + enable_power_well(power_well_device, false); + spin_unlock_irq(&powerwell_lock); +} +EXPORT_SYMBOL_GPL(i915_release_power_well); + +/* TODO: Call this when i915 module unload */ +void i915_remove_power_well(void) +{ + i915_power_well_using = false; + power_well_device = NULL; +} + +void intel_set_power_well(struct drm_device *dev, bool enable) +{ + if (!HAS_POWER_WELL(dev)) + return; + + power_well_device = dev; + spin_lock_irq(&powerwell_lock); + i915_power_well_using = enable; + if (!enable && hsw_power_count) { + DRM_DEBUG_KMS("Display audio power well busy using now\n"); + goto out; + } + + if (!i915_disable_power_well && !enable) + goto out; + + enable_power_well(dev, enable); +out: + spin_unlock_irq(&powerwell_lock); +} + /* * Starting with Haswell, we have a "Power Down Well" that can be turned off * when not needed anymore. We have 4 registers that can request the power well diff --git a/include/drm/i915_powerwell.h b/include/drm/i915_powerwell.h new file mode 100644 index 0000000..cfdc884 --- /dev/null +++ b/include/drm/i915_powerwell.h @@ -0,0 +1,36 @@ +/************************************************************************** + * + * Copyright 2013 Intel Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ + +#ifndef _I915_POWERWELL_H_ +#define _I915_POWERWELL_H_ + +/* For use by hda_i915 driver */ +extern void i915_request_power_well(void); +extern void i915_release_power_well(void); + +#endif /* _I915_POWERWELL_H_ */
At Tue, 14 May 2013 19:44:18 +0800, Wang Xingchao wrote:
Haswell Display audio depends on power well in graphic side, it should request power well before use it and release power well after use. I915 will not shutdown power well if it detects audio is using. This patch protects display audio crash for Intel Haswell mobile C3 stepping board.
Signed-off-by: Wang Xingchao xingchao.wang@linux.intel.com
drivers/gpu/drm/i915/intel_pm.c | 76 +++++++++++++++++++++++++++++++++++---- include/drm/i915_powerwell.h | 36 +++++++++++++++++++ 2 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 include/drm/i915_powerwell.h
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 0f4b46e..cf7e352 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4344,18 +4344,12 @@ bool intel_using_power_well(struct drm_device *dev) return true; }
-void intel_set_power_well(struct drm_device *dev, bool enable) +static void enable_power_well(struct drm_device *dev, bool enable) { struct drm_i915_private *dev_priv = dev->dev_private; bool is_enabled, enable_requested; uint32_t tmp;
- if (!HAS_POWER_WELL(dev))
return;
- if (!i915_disable_power_well && !enable)
return;
- tmp = I915_READ(HSW_PWR_WELL_DRIVER); is_enabled = tmp & HSW_PWR_WELL_STATE; enable_requested = tmp & HSW_PWR_WELL_ENABLE;
@@ -4378,6 +4372,74 @@ void intel_set_power_well(struct drm_device *dev, bool enable) } }
+/* Global drm_device for display audio drvier usage */ +static struct drm_device *power_well_device; +/* Lock protecting power well setting */ +static DEFINE_SPINLOCK(powerwell_lock); +static bool i915_power_well_using; +static int hsw_power_count;
+void i915_request_power_well(void) +{
- if (!power_well_device)
return;
- if (!IS_HASWELL(power_well_device))
return;
- spin_lock_irq(&powerwell_lock);
- if (!hsw_power_count++) {
enable_power_well(power_well_device, true);
- }
Should be if (!hsw_power_count++ && !i915_power_well_using) enable_power_well(power_well_device, true);
Takashi
- spin_unlock_irq(&powerwell_lock);
+} +EXPORT_SYMBOL_GPL(i915_request_power_well);
+void i915_release_power_well(void) +{
- if (!power_well_device)
return;
- if (!IS_HASWELL(power_well_device))
return;
- spin_lock_irq(&powerwell_lock);
- WARN_ON(!hsw_power_count);
- if (!--hsw_power_count
&& !i915_power_well_using)
enable_power_well(power_well_device, false);
- spin_unlock_irq(&powerwell_lock);
+} +EXPORT_SYMBOL_GPL(i915_release_power_well);
+/* TODO: Call this when i915 module unload */ +void i915_remove_power_well(void) +{
- i915_power_well_using = false;
- power_well_device = NULL;
+}
+void intel_set_power_well(struct drm_device *dev, bool enable) +{
- if (!HAS_POWER_WELL(dev))
return;
- power_well_device = dev;
- spin_lock_irq(&powerwell_lock);
- i915_power_well_using = enable;
- if (!enable && hsw_power_count) {
DRM_DEBUG_KMS("Display audio power well busy using now\n");
goto out;
- }
- if (!i915_disable_power_well && !enable)
goto out;
- enable_power_well(dev, enable);
+out:
- spin_unlock_irq(&powerwell_lock);
+}
/*
- Starting with Haswell, we have a "Power Down Well" that can be turned off
- when not needed anymore. We have 4 registers that can request the power well
diff --git a/include/drm/i915_powerwell.h b/include/drm/i915_powerwell.h new file mode 100644 index 0000000..cfdc884 --- /dev/null +++ b/include/drm/i915_powerwell.h @@ -0,0 +1,36 @@ +/**************************************************************************
- Copyright 2013 Intel Inc.
- All Rights Reserved.
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sub license, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
- The above copyright notice and this permission notice (including the
- next paragraph) shall be included in all copies or substantial portions
- of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
- THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
- USE OR OTHER DEALINGS IN THE SOFTWARE.
- **************************************************************************/
+#ifndef _I915_POWERWELL_H_ +#define _I915_POWERWELL_H_
+/* For use by hda_i915 driver */ +extern void i915_request_power_well(void); +extern void i915_release_power_well(void);
+#endif /* _I915_POWERWELL_H_ */
1.7.9.5
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Hi Takashi,
-----Original Message----- From: Takashi Iwai [mailto:tiwai@suse.de] Sent: Tuesday, May 14, 2013 8:32 PM To: Wang Xingchao Cc: daniel@ffwll.ch; Girdwood, Liam R; alsa-devel@alsa-project.org; Zanoni, Paulo R; Li, Jocelyn; Lin, Mengdong; intel-gfx@lists.freedesktop.org; Wang, Xingchao; Barnes, Jesse; david.henningsson@canonical.com Subject: Re: [alsa-devel] [PATCH 1/2] drm/915: Add private api for power well usage
At Tue, 14 May 2013 19:44:18 +0800, Wang Xingchao wrote:
Haswell Display audio depends on power well in graphic side, it should request power well before use it and release power well after use. I915 will not shutdown power well if it detects audio is using. This patch protects display audio crash for Intel Haswell mobile C3 stepping board.
Signed-off-by: Wang Xingchao xingchao.wang@linux.intel.com
drivers/gpu/drm/i915/intel_pm.c | 76
+++++++++++++++++++++++++++++++++++----
include/drm/i915_powerwell.h | 36 +++++++++++++++++++ 2 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 include/drm/i915_powerwell.h
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 0f4b46e..cf7e352 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4344,18 +4344,12 @@ bool intel_using_power_well(struct drm_device
*dev)
return true;
}
-void intel_set_power_well(struct drm_device *dev, bool enable) +static void enable_power_well(struct drm_device *dev, bool enable) { struct drm_i915_private *dev_priv = dev->dev_private; bool is_enabled, enable_requested; uint32_t tmp;
- if (!HAS_POWER_WELL(dev))
return;
- if (!i915_disable_power_well && !enable)
return;
- tmp = I915_READ(HSW_PWR_WELL_DRIVER); is_enabled = tmp & HSW_PWR_WELL_STATE; enable_requested = tmp & HSW_PWR_WELL_ENABLE; @@ -4378,6
+4372,74 @@
void intel_set_power_well(struct drm_device *dev, bool enable) } }
+/* Global drm_device for display audio drvier usage */ static struct +drm_device *power_well_device; +/* Lock protecting power well setting */ static +DEFINE_SPINLOCK(powerwell_lock); static bool i915_power_well_using; +static int hsw_power_count;
+void i915_request_power_well(void) +{
- if (!power_well_device)
return;
- if (!IS_HASWELL(power_well_device))
return;
- spin_lock_irq(&powerwell_lock);
- if (!hsw_power_count++) {
enable_power_well(power_well_device, true);
- }
Should be if (!hsw_power_count++ && !i915_power_well_using) enable_power_well(power_well_device, true);
Fixed.
Thanks --xingchao
For Intel Haswell chip, HDA controller and codec have power well dependency from GPU side. This patch added support to request/release power well in audio driver. Power save feature should be enabled to get runtime power saving.
Signed-off-by: Wang Xingchao xingchao.wang@linux.intel.com --- sound/pci/hda/Kconfig | 8 ++++ sound/pci/hda/Makefile | 3 ++ sound/pci/hda/hda_i915.c | 92 +++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_i915.h | 35 +++++++++++++++++ sound/pci/hda/hda_intel.c | 33 ++++++++++++++-- 5 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 sound/pci/hda/hda_i915.c create mode 100644 sound/pci/hda/hda_i915.h
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 80a7d44..d9e71e4 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -152,6 +152,14 @@ config SND_HDA_CODEC_HDMI snd-hda-codec-hdmi. This module is automatically loaded at probing.
+config SND_HDA_I915 + bool "Build Display HD-audio controller/codec power well support for i915 cards" + depends on DRM_I915 + default y + help + Say Y here to include full HDMI and DisplayPort HD-audio controller/codec + support for Intel Haswell graphics cards based on the i915 driver. + config SND_HDA_CODEC_CIRRUS bool "Build Cirrus Logic codec support" default y diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 24a2514..4b0a4bc 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -6,6 +6,9 @@ snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
+# for haswell power well +snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o + # for trace-points CFLAGS_hda_codec.o := -I$(src) CFLAGS_hda_intel.o := -I$(src) diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c new file mode 100644 index 0000000..96ca9e7 --- /dev/null +++ b/sound/pci/hda/hda_i915.c @@ -0,0 +1,92 @@ +/* + * hda_i915.c - routines for Haswell HDA controller power well support + * + * 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. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <sound/core.h> +#include <drm/i915_powerwell.h> +#include "hda_i915.h" + +static const char *module_name = "i915"; +static struct module *i915; +static void (*get_power)(void); +static void (*put_power)(void); + +void hda_display_power(bool enable) +{ + if (!get_power || !put_power) + return; + + snd_printk(KERN_DEBUG "HDA display power %s \n", + enable ? "Enable" : "Disable"); + if (enable) + get_power(); + else + put_power(); +} + +/* in case i915 not loaded, try load i915 first */ +static int load_i915(void) +{ + int err; + mutex_lock(&module_mutex); + i915 = find_module(module_name); + mutex_unlock(&module_mutex); + + if (!i915) { + err = request_module(module_name); + if (err < 0) + return -ENODEV; + } + + return 0; +} + +int hda_i915_init(void) +{ + get_power = symbol_get(i915_request_power_well); + if (!get_power) { + snd_printk(KERN_WARNING "hda-i915: get symbol fail, try again!\n"); + + if (load_i915() < 0) + return -ENODEV; + + get_power = symbol_get(i915_request_power_well); + + if (!get_power) + return -ENODEV; + } + + put_power = symbol_get(i915_release_power_well); + if (!put_power) + return -ENODEV; + + snd_printk(KERN_INFO "HDA driver get symbol successfully from i915 module\n"); + + return 0; +} + +int hda_i915_exit(void) +{ + if (get_power) + symbol_put(i915_request_power_well); + if (put_power) + symbol_put(i915_release_power_well); + + return 0; +} diff --git a/sound/pci/hda/hda_i915.h b/sound/pci/hda/hda_i915.h new file mode 100644 index 0000000..cad4971 --- /dev/null +++ b/sound/pci/hda/hda_i915.h @@ -0,0 +1,35 @@ +/* + * 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. + */ +#ifndef __SOUND_HDA_I915_H +#define __SOUND_HDA_I915_H + +#ifdef CONFIG_SND_HDA_I915 +void hda_display_power(bool enable); +int hda_i915_init(void); +int hda_i915_exit(void); +#else +static inline void hda_display_power(bool enable) {} +static inline int hda_i915_init(void) +{ + return 0; +} +static inline int hda_i915_exit(void) +{ + return 0; +} +#endif + +#endif diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 418bfc0..6854a7e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -62,6 +62,7 @@ #include <linux/vga_switcheroo.h> #include <linux/firmware.h> #include "hda_codec.h" +#include "hda_i915.h"
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; @@ -594,6 +595,7 @@ enum { #define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ #define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ #define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ +#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 power well support */
/* quirks for Intel PCH */ #define AZX_DCAPS_INTEL_PCH_NOPM \ @@ -2869,6 +2871,8 @@ static int azx_suspend(struct device *dev) pci_disable_device(pci); pci_save_state(pci); pci_set_power_state(pci, PCI_D3hot); + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) + hda_display_power(false); return 0; }
@@ -2881,6 +2885,8 @@ static int azx_resume(struct device *dev) if (chip->disabled) return 0;
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) + hda_display_power(true); pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); if (pci_enable_device(pci) < 0) { @@ -2913,6 +2919,8 @@ static int azx_runtime_suspend(struct device *dev)
azx_stop_chip(chip); azx_clear_irq_pending(chip); + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) + hda_display_power(false); return 0; }
@@ -2921,6 +2929,8 @@ static int azx_runtime_resume(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data;
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) + hda_display_power(true); azx_init_pci(chip); azx_init_chip(chip, 1); return 0; @@ -3700,6 +3710,15 @@ static int azx_probe(struct pci_dev *pci, chip->disabled = true; }
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { + err = hda_i915_init(); + if (err < 0) { + snd_printk(KERN_ERR "Error request power-well from i915\n"); + goto out_free; + } + hda_display_power(true); + } + probe_now = !chip->disabled; if (probe_now) { err = azx_first_init(chip); @@ -3799,6 +3818,7 @@ out_free: static void azx_remove(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci); + struct azx *chip = card->private_data;
if (pci_dev_run_wake(pci)) pm_runtime_get_noresume(&pci->dev); @@ -3806,6 +3826,10 @@ static void azx_remove(struct pci_dev *pci) if (card) snd_card_free(card); pci_set_drvdata(pci, NULL); + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { + hda_display_power(false); + hda_i915_exit(); + } }
/* PCI IDs */ @@ -3835,11 +3859,14 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Haswell */ { PCI_DEVICE(0x8086, 0x0a0c), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | + AZX_DCAPS_I915_POWERWELL}, { PCI_DEVICE(0x8086, 0x0c0c), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | + AZX_DCAPS_I915_POWERWELL}, { PCI_DEVICE(0x8086, 0x0d0c), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | + AZX_DCAPS_I915_POWERWELL}, /* 5 Series/3400 */ { PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
At Tue, 14 May 2013 19:44:19 +0800, Wang Xingchao wrote:
For Intel Haswell chip, HDA controller and codec have power well dependency from GPU side. This patch added support to request/release power well in audio driver. Power save feature should be enabled to get runtime power saving.
Signed-off-by: Wang Xingchao xingchao.wang@linux.intel.com
sound/pci/hda/Kconfig | 8 ++++ sound/pci/hda/Makefile | 3 ++ sound/pci/hda/hda_i915.c | 92 +++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_i915.h | 35 +++++++++++++++++ sound/pci/hda/hda_intel.c | 33 ++++++++++++++-- 5 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 sound/pci/hda/hda_i915.c create mode 100644 sound/pci/hda/hda_i915.h
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 80a7d44..d9e71e4 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -152,6 +152,14 @@ config SND_HDA_CODEC_HDMI snd-hda-codec-hdmi. This module is automatically loaded at probing.
+config SND_HDA_I915
- bool "Build Display HD-audio controller/codec power well support for i915 cards"
- depends on DRM_I915
- default y
- help
Say Y here to include full HDMI and DisplayPort HD-audio controller/codec
support for Intel Haswell graphics cards based on the i915 driver.
Do we need to make this selectable? If you have i915 driver, this can be always set.
config SND_HDA_CODEC_CIRRUS bool "Build Cirrus Logic codec support" default y diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 24a2514..4b0a4bc 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -6,6 +6,9 @@ snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
+# for haswell power well +snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o
# for trace-points CFLAGS_hda_codec.o := -I$(src) CFLAGS_hda_intel.o := -I$(src) diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c new file mode 100644 index 0000000..96ca9e7 --- /dev/null +++ b/sound/pci/hda/hda_i915.c @@ -0,0 +1,92 @@ +/*
- hda_i915.c - routines for Haswell HDA controller power well support
- 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.
- */
+#include <linux/init.h> +#include <linux/module.h> +#include <sound/core.h> +#include <drm/i915_powerwell.h> +#include "hda_i915.h"
+static const char *module_name = "i915"; +static struct module *i915; +static void (*get_power)(void); +static void (*put_power)(void);
+void hda_display_power(bool enable) +{
- if (!get_power || !put_power)
return;
- snd_printk(KERN_DEBUG "HDA display power %s \n",
enable ? "Enable" : "Disable");
Use snd_printdd() for such a frequent debug message. We don't want to see this message at each time you open/close the audio stream.
- if (enable)
get_power();
- else
put_power();
+}
+/* in case i915 not loaded, try load i915 first */ +static int load_i915(void) +{
- int err;
- mutex_lock(&module_mutex);
- i915 = find_module(module_name);
- mutex_unlock(&module_mutex);
- if (!i915) {
err = request_module(module_name);
if (err < 0)
return -ENODEV;
Why not passing the returned error code?
Also, you need to get i915 module again after the successful request_module(). request_module() doesn't give the module reference.
Also, we can skip the whole load_i915() if i915 driver is built-in, i.e.
#ifdef CONFIG_DRM_i915_MODULE static int load_i915(void) { .... } #else #define load_i915(void) (-ENODEV) #endif
- }
- return 0;
+}
+int hda_i915_init(void) +{
- get_power = symbol_get(i915_request_power_well);
- if (!get_power) {
snd_printk(KERN_WARNING "hda-i915: get symbol fail, try again!\n");
Doesn't make sense to give a warning at this point. The fact that no symbol is taken at this point is no real error. The error should be reported when the module load failed or the symbol_get() failed twice.
if (load_i915() < 0)
return -ENODEV;
get_power = symbol_get(i915_request_power_well);
if (!get_power)
return -ENODEV;
The i915 module should be unreferenced in the error path.
- }
- put_power = symbol_get(i915_release_power_well);
- if (!put_power)
return -ENODEV;
Ditto.
- snd_printk(KERN_INFO "HDA driver get symbol successfully from i915 module\n");
Use snd_printd() as a debug message, if any.
- return 0;
+}
+int hda_i915_exit(void) +{
- if (get_power)
symbol_put(i915_request_power_well);
- if (put_power)
symbol_put(i915_release_power_well);
i915 module should be unreferenced at exit, too.
- return 0;
+} diff --git a/sound/pci/hda/hda_i915.h b/sound/pci/hda/hda_i915.h new file mode 100644 index 0000000..cad4971 --- /dev/null +++ b/sound/pci/hda/hda_i915.h @@ -0,0 +1,35 @@ +/*
- 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.
- */
+#ifndef __SOUND_HDA_I915_H +#define __SOUND_HDA_I915_H
+#ifdef CONFIG_SND_HDA_I915 +void hda_display_power(bool enable); +int hda_i915_init(void); +int hda_i915_exit(void); +#else +static inline void hda_display_power(bool enable) {} +static inline int hda_i915_init(void) +{
- return 0;
+} +static inline int hda_i915_exit(void) +{
- return 0;
+} +#endif
+#endif diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 418bfc0..6854a7e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -62,6 +62,7 @@ #include <linux/vga_switcheroo.h> #include <linux/firmware.h> #include "hda_codec.h" +#include "hda_i915.h"
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; @@ -594,6 +595,7 @@ enum { #define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ #define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ #define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ +#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 power well support */
/* quirks for Intel PCH */ #define AZX_DCAPS_INTEL_PCH_NOPM \ @@ -2869,6 +2871,8 @@ static int azx_suspend(struct device *dev) pci_disable_device(pci); pci_save_state(pci); pci_set_power_state(pci, PCI_D3hot);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
return 0;hda_display_power(false);
}
@@ -2881,6 +2885,8 @@ static int azx_resume(struct device *dev) if (chip->disabled) return 0;
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); if (pci_enable_device(pci) < 0) {hda_display_power(true);
@@ -2913,6 +2919,8 @@ static int azx_runtime_suspend(struct device *dev)
azx_stop_chip(chip); azx_clear_irq_pending(chip);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
return 0;hda_display_power(false);
}
@@ -2921,6 +2929,8 @@ static int azx_runtime_resume(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data;
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
azx_init_pci(chip); azx_init_chip(chip, 1); return 0;hda_display_power(true);
@@ -3700,6 +3710,15 @@ static int azx_probe(struct pci_dev *pci, chip->disabled = true; }
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
err = hda_i915_init();
if (err < 0) {
snd_printk(KERN_ERR "Error request power-well from i915\n");
goto out_free;
}
hda_display_power(true);
- }
- probe_now = !chip->disabled; if (probe_now) { err = azx_first_init(chip);
Missing hda_display_power(false) and hda_i915_exit() in the error path.
@@ -3799,6 +3818,7 @@ out_free: static void azx_remove(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci);
struct azx *chip = card->private_data;
if (pci_dev_run_wake(pci)) pm_runtime_get_noresume(&pci->dev);
@@ -3806,6 +3826,10 @@ static void azx_remove(struct pci_dev *pci) if (card) snd_card_free(card); pci_set_drvdata(pci, NULL);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
hda_display_power(false);
hda_i915_exit();
- }
}
/* PCI IDs */ @@ -3835,11 +3859,14 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Haswell */ { PCI_DEVICE(0x8086, 0x0a0c),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
{ PCI_DEVICE(0x8086, 0x0c0c),AZX_DCAPS_I915_POWERWELL},
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
{ PCI_DEVICE(0x8086, 0x0d0c),AZX_DCAPS_I915_POWERWELL},
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
AZX_DCAPS_I915_POWERWELL},
Try to follow the indentation / spacing of the original code.
thanks,
Takashi
Hi Takashi,
Thanks your quick feedback, please see my comments below.
-----Original Message----- From: Takashi Iwai [mailto:tiwai@suse.de] Sent: Tuesday, May 14, 2013 8:15 PM To: Wang Xingchao Cc: daniel@ffwll.ch; Girdwood, Liam R; david.henningsson@canonical.com; Lin, Mengdong; Li, Jocelyn; alsa-devel@alsa-project.org; intel-gfx@lists.freedesktop.org; Barnes, Jesse; Zanoni, Paulo R; Wang, Xingchao Subject: Re: [PATCH 2/2] ALSA: hda - Add power-welll support for haswell HDA
At Tue, 14 May 2013 19:44:19 +0800, Wang Xingchao wrote:
For Intel Haswell chip, HDA controller and codec have power well dependency from GPU side. This patch added support to request/release power well in audio driver. Power save feature should be enabled to get runtime power saving.
Signed-off-by: Wang Xingchao xingchao.wang@linux.intel.com
sound/pci/hda/Kconfig | 8 ++++ sound/pci/hda/Makefile | 3 ++ sound/pci/hda/hda_i915.c | 92
+++++++++++++++++++++++++++++++++++++++++++++
sound/pci/hda/hda_i915.h | 35 +++++++++++++++++ sound/pci/hda/hda_intel.c | 33 ++++++++++++++-- 5 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 sound/pci/hda/hda_i915.c create mode 100644 sound/pci/hda/hda_i915.h
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 80a7d44..d9e71e4 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -152,6 +152,14 @@ config SND_HDA_CODEC_HDMI snd-hda-codec-hdmi. This module is automatically loaded at probing.
+config SND_HDA_I915
- bool "Build Display HD-audio controller/codec power well support for i915
cards"
- depends on DRM_I915
- default y
- help
Say Y here to include full HDMI and DisplayPort HD-audio
controller/codec
support for Intel Haswell graphics cards based on the i915 driver.
Do we need to make this selectable? If you have i915 driver, this can be always set.
Well, for Non-Haswell chips with SND_HDA_I915 compiled in, it do nothing as it has no " AZX_DCAPS_I915_POWERWELL" set, so no power-well API called. For Haswell chips, SND_HDA_I915 must be enabled otherwise display audio would fail. Anyway I added a note message in the choice to let user know this option is a *must* one for Haswell chips.
config SND_HDA_CODEC_CIRRUS bool "Build Cirrus Logic codec support" default y diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 24a2514..4b0a4bc 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -6,6 +6,9 @@ snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
+# for haswell power well +snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o
# for trace-points CFLAGS_hda_codec.o := -I$(src) CFLAGS_hda_intel.o := -I$(src) diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c new file mode 100644 index 0000000..96ca9e7 --- /dev/null +++ b/sound/pci/hda/hda_i915.c @@ -0,0 +1,92 @@ +/*
- hda_i915.c - routines for Haswell HDA controller power well
+support
- 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.
- */
+#include <linux/init.h> +#include <linux/module.h> +#include <sound/core.h> +#include <drm/i915_powerwell.h> +#include "hda_i915.h"
+static const char *module_name = "i915"; static struct module *i915; +static void (*get_power)(void); static void (*put_power)(void);
+void hda_display_power(bool enable) +{
- if (!get_power || !put_power)
return;
- snd_printk(KERN_DEBUG "HDA display power %s \n",
enable ? "Enable" : "Disable");
Use snd_printdd() for such a frequent debug message. We don't want to see this message at each time you open/close the audio stream.
Fixed.
- if (enable)
get_power();
- else
put_power();
+}
+/* in case i915 not loaded, try load i915 first */ static int +load_i915(void) {
- int err;
- mutex_lock(&module_mutex);
- i915 = find_module(module_name);
- mutex_unlock(&module_mutex);
- if (!i915) {
err = request_module(module_name);
if (err < 0)
return -ENODEV;
Why not passing the returned error code?
Fixed.
Also, you need to get i915 module again after the successful request_module(). request_module() doesn't give the module reference.
Symbol_get/put will call try_module_get() internally so it think it's not necessary get module reference again. So in above case, if request_module() finished, we can call symbol_get/put safely, Do you think so?
Also, we can skip the whole load_i915() if i915 driver is built-in, i.e.
#ifdef CONFIG_DRM_i915_MODULE static int load_i915(void) { .... } #else #define load_i915(void) (-ENODEV) #endif
Is it really necessary here? I assume if the module is live, symbol_get() would be successful. If the symbol_get fail, the module doesnot exist. Is there any other case? i.e. module is live but symbol_get fail?
- }
- return 0;
+}
+int hda_i915_init(void) +{
- get_power = symbol_get(i915_request_power_well);
- if (!get_power) {
snd_printk(KERN_WARNING "hda-i915: get symbol fail, try
again!\n");
Doesn't make sense to give a warning at this point. The fact that no symbol is taken at this point is no real error. The error should be reported when the module load failed or the symbol_get() failed twice.
Yes, fixed in next version.
if (load_i915() < 0)
return -ENODEV;
get_power = symbol_get(i915_request_power_well);
if (!get_power)
return -ENODEV;
The i915 module should be unreferenced in the error path.
- }
- put_power = symbol_get(i915_release_power_well);
- if (!put_power)
return -ENODEV;
Ditto.
Yeah, please correct me if my understanding was wrong: Symbol_get should be safe here as it would request module reference. Symbol_put is needed in exit() case, but no module_put() needed.
If we donot use symbol finding way, we should use try_module_get() and module_put() here.
- snd_printk(KERN_INFO "HDA driver get symbol successfully from i915
+module\n");
Use snd_printd() as a debug message, if any.
Fixed.
- return 0;
+}
+int hda_i915_exit(void) +{
- if (get_power)
symbol_put(i915_request_power_well);
- if (put_power)
symbol_put(i915_release_power_well);
i915 module should be unreferenced at exit, too.
- return 0;
+} diff --git a/sound/pci/hda/hda_i915.h b/sound/pci/hda/hda_i915.h new file mode 100644 index 0000000..cad4971 --- /dev/null +++ b/sound/pci/hda/hda_i915.h @@ -0,0 +1,35 @@ +/*
- 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.
- */
+#ifndef __SOUND_HDA_I915_H +#define __SOUND_HDA_I915_H
+#ifdef CONFIG_SND_HDA_I915 +void hda_display_power(bool enable); +int hda_i915_init(void); +int hda_i915_exit(void); +#else +static inline void hda_display_power(bool enable) {} static inline +int hda_i915_init(void) {
- return 0;
+} +static inline int hda_i915_exit(void) {
- return 0;
+} +#endif
+#endif diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 418bfc0..6854a7e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -62,6 +62,7 @@ #include <linux/vga_switcheroo.h> #include <linux/firmware.h> #include "hda_codec.h" +#include "hda_i915.h"
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; @@ -594,6 +595,7 @@ enum { #define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k
boundary */
#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as
delay */
#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ +#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 power well
support */
/* quirks for Intel PCH */ #define AZX_DCAPS_INTEL_PCH_NOPM \ @@ -2869,6 +2871,8 @@ static int azx_suspend(struct device *dev) pci_disable_device(pci); pci_save_state(pci); pci_set_power_state(pci, PCI_D3hot);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
return 0;hda_display_power(false);
}
@@ -2881,6 +2885,8 @@ static int azx_resume(struct device *dev) if (chip->disabled) return 0;
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); if (pci_enable_device(pci) < 0) {hda_display_power(true);
@@ -2913,6 +2919,8 @@ static int azx_runtime_suspend(struct device *dev)
azx_stop_chip(chip); azx_clear_irq_pending(chip);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
return 0;hda_display_power(false);
}
@@ -2921,6 +2929,8 @@ static int azx_runtime_resume(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data;
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
azx_init_pci(chip); azx_init_chip(chip, 1); return 0;hda_display_power(true);
@@ -3700,6 +3710,15 @@ static int azx_probe(struct pci_dev *pci, chip->disabled = true; }
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
err = hda_i915_init();
if (err < 0) {
snd_printk(KERN_ERR "Error request power-well from i915\n");
goto out_free;
}
hda_display_power(true);
- }
- probe_now = !chip->disabled; if (probe_now) { err = azx_first_init(chip);
Missing hda_display_power(false) and hda_i915_exit() in the error path.
Okay, fixed.
@@ -3799,6 +3818,7 @@ out_free: static void azx_remove(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci);
struct azx *chip = card->private_data;
if (pci_dev_run_wake(pci)) pm_runtime_get_noresume(&pci->dev);
@@ -3806,6 +3826,10 @@ static void azx_remove(struct pci_dev *pci) if (card) snd_card_free(card); pci_set_drvdata(pci, NULL);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
hda_display_power(false);
hda_i915_exit();
- }
}
/* PCI IDs */ @@ -3835,11 +3859,14 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Haswell */ { PCI_DEVICE(0x8086, 0x0a0c),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
{ PCI_DEVICE(0x8086, 0x0c0c),AZX_DCAPS_I915_POWERWELL},
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
{ PCI_DEVICE(0x8086, 0x0d0c),AZX_DCAPS_I915_POWERWELL},
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
AZX_DCAPS_I915_POWERWELL},
Try to follow the indentation / spacing of the original code.
Fixed too.
Thanks --xingchao
At Thu, 16 May 2013 03:51:16 +0000, Wang, Xingchao wrote:
Hi Takashi,
Thanks your quick feedback, please see my comments below.
-----Original Message----- From: Takashi Iwai [mailto:tiwai@suse.de] Sent: Tuesday, May 14, 2013 8:15 PM To: Wang Xingchao Cc: daniel@ffwll.ch; Girdwood, Liam R; david.henningsson@canonical.com; Lin, Mengdong; Li, Jocelyn; alsa-devel@alsa-project.org; intel-gfx@lists.freedesktop.org; Barnes, Jesse; Zanoni, Paulo R; Wang, Xingchao Subject: Re: [PATCH 2/2] ALSA: hda - Add power-welll support for haswell HDA
At Tue, 14 May 2013 19:44:19 +0800, Wang Xingchao wrote:
For Intel Haswell chip, HDA controller and codec have power well dependency from GPU side. This patch added support to request/release power well in audio driver. Power save feature should be enabled to get runtime power saving.
Signed-off-by: Wang Xingchao xingchao.wang@linux.intel.com
sound/pci/hda/Kconfig | 8 ++++ sound/pci/hda/Makefile | 3 ++ sound/pci/hda/hda_i915.c | 92
+++++++++++++++++++++++++++++++++++++++++++++
sound/pci/hda/hda_i915.h | 35 +++++++++++++++++ sound/pci/hda/hda_intel.c | 33 ++++++++++++++-- 5 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 sound/pci/hda/hda_i915.c create mode 100644 sound/pci/hda/hda_i915.h
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 80a7d44..d9e71e4 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -152,6 +152,14 @@ config SND_HDA_CODEC_HDMI snd-hda-codec-hdmi. This module is automatically loaded at probing.
+config SND_HDA_I915
- bool "Build Display HD-audio controller/codec power well support for i915
cards"
- depends on DRM_I915
- default y
- help
Say Y here to include full HDMI and DisplayPort HD-audio
controller/codec
support for Intel Haswell graphics cards based on the i915 driver.
Do we need to make this selectable? If you have i915 driver, this can be always set.
Well, for Non-Haswell chips with SND_HDA_I915 compiled in, it do nothing as it has no " AZX_DCAPS_I915_POWERWELL" set, so no power-well API called. For Haswell chips, SND_HDA_I915 must be enabled otherwise display audio would fail. Anyway I added a note message in the choice to let user know this option is a *must* one for Haswell chips.
Yes, but the problem is if the driver without CONFIG_SND_HDA_I915 is used on Haswell. Then the driver is loaded as if everything is OK even though you know it's broken.
If you make this selectable, you should give an error from hda_i915_init() in the case CONFIG_SND_HDA_i915=n, so that the driver load shall fail.
config SND_HDA_CODEC_CIRRUS bool "Build Cirrus Logic codec support" default y diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 24a2514..4b0a4bc 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -6,6 +6,9 @@ snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
+# for haswell power well +snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o
# for trace-points CFLAGS_hda_codec.o := -I$(src) CFLAGS_hda_intel.o := -I$(src) diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c new file mode 100644 index 0000000..96ca9e7 --- /dev/null +++ b/sound/pci/hda/hda_i915.c @@ -0,0 +1,92 @@ +/*
- hda_i915.c - routines for Haswell HDA controller power well
+support
- 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.
- */
+#include <linux/init.h> +#include <linux/module.h> +#include <sound/core.h> +#include <drm/i915_powerwell.h> +#include "hda_i915.h"
+static const char *module_name = "i915"; static struct module *i915; +static void (*get_power)(void); static void (*put_power)(void);
+void hda_display_power(bool enable) +{
- if (!get_power || !put_power)
return;
- snd_printk(KERN_DEBUG "HDA display power %s \n",
enable ? "Enable" : "Disable");
Use snd_printdd() for such a frequent debug message. We don't want to see this message at each time you open/close the audio stream.
Fixed.
- if (enable)
get_power();
- else
put_power();
+}
+/* in case i915 not loaded, try load i915 first */ static int +load_i915(void) {
- int err;
- mutex_lock(&module_mutex);
- i915 = find_module(module_name);
- mutex_unlock(&module_mutex);
- if (!i915) {
err = request_module(module_name);
if (err < 0)
return -ENODEV;
Why not passing the returned error code?
Fixed.
Also, you need to get i915 module again after the successful request_module(). request_module() doesn't give the module reference.
Symbol_get/put will call try_module_get() internally so it think it's not necessary get module reference again. So in above case, if request_module() finished, we can call symbol_get/put safely, Do you think so?
Looking back at the code, it looks so. But you can reduce these open codes via simple symbol_request().
Also, we can skip the whole load_i915() if i915 driver is built-in, i.e.
#ifdef CONFIG_DRM_i915_MODULE static int load_i915(void) { .... } #else #define load_i915(void) (-ENODEV) #endif
Is it really necessary here? I assume if the module is live, symbol_get() would be successful. If the symbol_get fail, the module doesnot exist. Is there any other case? i.e. module is live but symbol_get fail?
You already know that i915 is never a module but only built-in in the above. Thus request_module() and retry is simply superfluous.
- }
- return 0;
+}
+int hda_i915_init(void) +{
- get_power = symbol_get(i915_request_power_well);
- if (!get_power) {
snd_printk(KERN_WARNING "hda-i915: get symbol fail, try
again!\n");
Doesn't make sense to give a warning at this point. The fact that no symbol is taken at this point is no real error. The error should be reported when the module load failed or the symbol_get() failed twice.
Yes, fixed in next version.
if (load_i915() < 0)
return -ENODEV;
get_power = symbol_get(i915_request_power_well);
if (!get_power)
return -ENODEV;
The i915 module should be unreferenced in the error path.
- }
- put_power = symbol_get(i915_release_power_well);
- if (!put_power)
return -ENODEV;
Ditto.
Yeah, please correct me if my understanding was wrong: Symbol_get should be safe here as it would request module reference. Symbol_put is needed in exit() case, but no module_put() needed.
Right, I thought wrongly. So you just need to call symbol_put() for the leftover ones.
Takashi
if we donot use symbol finding way, we should use try_module_get() and module_put() here.
- snd_printk(KERN_INFO "HDA driver get symbol successfully from i915
+module\n");
Use snd_printd() as a debug message, if any.
Fixed.
- return 0;
+}
+int hda_i915_exit(void) +{
- if (get_power)
symbol_put(i915_request_power_well);
- if (put_power)
symbol_put(i915_release_power_well);
i915 module should be unreferenced at exit, too.
- return 0;
+} diff --git a/sound/pci/hda/hda_i915.h b/sound/pci/hda/hda_i915.h new file mode 100644 index 0000000..cad4971 --- /dev/null +++ b/sound/pci/hda/hda_i915.h @@ -0,0 +1,35 @@ +/*
- 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.
- */
+#ifndef __SOUND_HDA_I915_H +#define __SOUND_HDA_I915_H
+#ifdef CONFIG_SND_HDA_I915 +void hda_display_power(bool enable); +int hda_i915_init(void); +int hda_i915_exit(void); +#else +static inline void hda_display_power(bool enable) {} static inline +int hda_i915_init(void) {
- return 0;
+} +static inline int hda_i915_exit(void) {
- return 0;
+} +#endif
+#endif diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 418bfc0..6854a7e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -62,6 +62,7 @@ #include <linux/vga_switcheroo.h> #include <linux/firmware.h> #include "hda_codec.h" +#include "hda_i915.h"
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; @@ -594,6 +595,7 @@ enum { #define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k
boundary */
#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as
delay */
#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ +#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 power well
support */
/* quirks for Intel PCH */ #define AZX_DCAPS_INTEL_PCH_NOPM \ @@ -2869,6 +2871,8 @@ static int azx_suspend(struct device *dev) pci_disable_device(pci); pci_save_state(pci); pci_set_power_state(pci, PCI_D3hot);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
return 0;hda_display_power(false);
}
@@ -2881,6 +2885,8 @@ static int azx_resume(struct device *dev) if (chip->disabled) return 0;
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); if (pci_enable_device(pci) < 0) {hda_display_power(true);
@@ -2913,6 +2919,8 @@ static int azx_runtime_suspend(struct device *dev)
azx_stop_chip(chip); azx_clear_irq_pending(chip);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
return 0;hda_display_power(false);
}
@@ -2921,6 +2929,8 @@ static int azx_runtime_resume(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data;
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
azx_init_pci(chip); azx_init_chip(chip, 1); return 0;hda_display_power(true);
@@ -3700,6 +3710,15 @@ static int azx_probe(struct pci_dev *pci, chip->disabled = true; }
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
err = hda_i915_init();
if (err < 0) {
snd_printk(KERN_ERR "Error request power-well from i915\n");
goto out_free;
}
hda_display_power(true);
- }
- probe_now = !chip->disabled; if (probe_now) { err = azx_first_init(chip);
Missing hda_display_power(false) and hda_i915_exit() in the error path.
Okay, fixed.
@@ -3799,6 +3818,7 @@ out_free: static void azx_remove(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci);
struct azx *chip = card->private_data;
if (pci_dev_run_wake(pci)) pm_runtime_get_noresume(&pci->dev);
@@ -3806,6 +3826,10 @@ static void azx_remove(struct pci_dev *pci) if (card) snd_card_free(card); pci_set_drvdata(pci, NULL);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
hda_display_power(false);
hda_i915_exit();
- }
}
/* PCI IDs */ @@ -3835,11 +3859,14 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Haswell */ { PCI_DEVICE(0x8086, 0x0a0c),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
{ PCI_DEVICE(0x8086, 0x0c0c),AZX_DCAPS_I915_POWERWELL},
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
{ PCI_DEVICE(0x8086, 0x0d0c),AZX_DCAPS_I915_POWERWELL},
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
AZX_DCAPS_I915_POWERWELL},
Try to follow the indentation / spacing of the original code.
Fixed too.
Thanks --xingchao
participants (3)
-
Takashi Iwai
-
Wang Xingchao
-
Wang, Xingchao