diff mbox

[RFC] ALSA: hda - Move hda_i915.c from sound/pci/hda to sound/hda

Message ID 1431519676-3628-1-git-send-email-mengdong.lin@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Lin, Mengdong May 13, 2015, 12:21 p.m. UTC
From: Mengdong Lin <mengdong.lin@intel.com>

The file is moved to hda core, so can be used by both legacy HDA driver and
new Skylake audio driver.

- The i915 audio component is moved to core bus and dynamically allocated.
- A static pointer hda_acomp is used to help bind/unbind callbacks to get
  this component, because the sound card's private_data is used by the azx
  chip pointer, which is a legacy structure. It could be removed if private
  _data changes to some core structure which can be extended to find the
  bus.
- hda_get_display_clk() is added to get the display core clock for HSW/BDW.
- haswell_set_bclk() is moved to hda_intel.c because it needs to write the
  controller registers EM4/EM5.
- Move definition of HSW/BDW-specific registers EM4/EM5 to hda_register.h
  and rename them to HSW_EM4/HSW_EM5, because other HD-A controllers have
  different layout for the extended mode registers.

Signed-off-by: Mengdong Lin <mengdong.lin@intel.com>

Comments

Takashi Iwai May 18, 2015, 8:20 a.m. UTC | #1
At Wed, 13 May 2015 20:21:16 +0800,
mengdong.lin@intel.com wrote:
> 
> From: Mengdong Lin <mengdong.lin@intel.com>
> 
> The file is moved to hda core, so can be used by both legacy HDA driver and
> new Skylake audio driver.
> 
> - The i915 audio component is moved to core bus and dynamically allocated.
> - A static pointer hda_acomp is used to help bind/unbind callbacks to get
>   this component, because the sound card's private_data is used by the azx
>   chip pointer, which is a legacy structure. It could be removed if private
>   _data changes to some core structure which can be extended to find the
>   bus.
> - hda_get_display_clk() is added to get the display core clock for HSW/BDW.
> - haswell_set_bclk() is moved to hda_intel.c because it needs to write the
>   controller registers EM4/EM5.
> - Move definition of HSW/BDW-specific registers EM4/EM5 to hda_register.h
>   and rename them to HSW_EM4/HSW_EM5, because other HD-A controllers have
>   different layout for the extended mode registers.
> 
> Signed-off-by: Mengdong Lin <mengdong.lin@intel.com>

When moving to include/sound/*.h as a public API, better to add snd_
prefix.  And, it's no longer HDA legacy driver, snd_hdac_ prefix would
be more appropriate.

Could you change in that way and rebase to the latest for-next branch?


thanks,

Takashi

> 
> diff --git a/include/sound/hda_i915.h b/include/sound/hda_i915.h
> new file mode 100644
> index 0000000..440e4c0
> --- /dev/null
> +++ b/include/sound/hda_i915.h
> @@ -0,0 +1,36 @@
> +/*
> + * HD-Audio helpers to sync with i915 driver
> + */
> +#ifndef __SOUND_HDA_I915_H
> +#define __SOUND_HDA_I915_H
> +
> +#ifdef CONFIG_SND_HDA_I915
> +int hda_set_codec_wakeup(struct hdac_bus *bus, bool enable);
> +int hda_display_power(struct hdac_bus *bus, bool enable);
> +int hda_get_display_clk(struct hdac_bus *bus);
> +int hda_i915_init(struct hdac_bus *bus);
> +int hda_i915_exit(struct hdac_bus *bus);
> +#else
> +static int hda_set_codec_wakeup(struct hdac_bus *bus, bool enable)
> +{
> +	return 0;
> +}
> +static inline int hda_display_power(struct hdac_bus *bus, bool enable)
> +{
> +	return 0;
> +}
> +static inline int hda_get_display_clk(struct hdac_bus *bus)
> +{
> +	return 0;
> +}
> +static inline int hda_i915_init(struct hdac_bus *bus)
> +{
> +	return -ENODEV;
> +}
> +static inline int hda_i915_exit(struct hdac_bus *bus)
> +{
> +	return 0;
> +}
> +#endif
> +
> +#endif /* __SOUND_HDA_I915_H */
> diff --git a/include/sound/hda_register.h b/include/sound/hda_register.h
> index 4f6d3fc..0c7536e 100644
> --- a/include/sound/hda_register.h
> +++ b/include/sound/hda_register.h
> @@ -84,6 +84,10 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
>  #define AZX_REG_SD_BDLPL		0x18
>  #define AZX_REG_SD_BDLPU		0x1c
>  
> +/* Haswell/Broadwell display HD-A controller Extended Mode registers */
> +#define AZX_REG_HSW_EM4			0x100c
> +#define AZX_REG_HSW_EM5			0x1010
> +
>  /* PCI space */
>  #define AZX_PCIREG_TCSEL		0x44
>  
> diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h
> index b97c59e..64fff4d 100644
> --- a/include/sound/hdaudio.h
> +++ b/include/sound/hdaudio.h
> @@ -11,6 +11,7 @@
>  #include <sound/core.h>
>  #include <sound/memalloc.h>
>  #include <sound/hda_verbs.h>
> +#include <drm/i915_component.h>
>  
>  /* codec node id */
>  typedef u16 hda_nid_t;
> @@ -285,6 +286,10 @@ struct hdac_bus {
>  	/* locks */
>  	spinlock_t reg_lock;
>  	struct mutex cmd_mutex;
> +
> +	/* i915 component interface */
> +	struct i915_audio_component *audio_component;
> +	int i915_power_refcount;
>  };
>  
>  int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
> diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig
> index 7a17fca..ac5ffac 100644
> --- a/sound/hda/Kconfig
> +++ b/sound/hda/Kconfig
> @@ -4,3 +4,9 @@ config SND_HDA_CORE
>  
>  config SND_HDA_DSP_LOADER
>  	bool
> +
> +config SND_HDA_I915
> +	bool
> +	default y
> +	depends on DRM_I915
> +	depends on SND_HDA_CORE
> diff --git a/sound/hda/Makefile b/sound/hda/Makefile
> index 5b4bb47..1ce78c6 100644
> --- a/sound/hda/Makefile
> +++ b/sound/hda/Makefile
> @@ -4,4 +4,7 @@ snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \
>  snd-hda-core-objs += trace.o
>  CFLAGS_trace.o := -I$(src)
>  
> +# for sync with i915 gfx driver
> +snd-hda-core-$(CONFIG_SND_HDA_I915) += hda_i915.o
> +
>  obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
> diff --git a/sound/hda/hda_i915.c b/sound/hda/hda_i915.c
> new file mode 100644
> index 0000000..5c87938
> --- /dev/null
> +++ b/sound/hda/hda_i915.c
> @@ -0,0 +1,193 @@
> +/*
> + *  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.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/component.h>
> +#include <drm/i915_component.h>
> +#include <sound/core.h>
> +#include <sound/hdaudio.h>
> +#include <sound/hda_i915.h>
> +
> +static struct i915_audio_component *hda_acomp;
> +
> +int hda_set_codec_wakeup(struct hdac_bus *bus, bool enable)
> +{
> +	struct i915_audio_component *acomp = bus->audio_component;
> +
> +	if (!acomp->ops)
> +		return -ENODEV;
> +
> +	if (!acomp->ops->codec_wake_override) {
> +		dev_warn(bus->dev,
> +			"Invalid codec wake callback\n");
> +		return 0;
> +	}
> +
> +	dev_dbg(bus->dev, "%s codec wakeup\n",
> +		enable ? "enable" : "disable");
> +
> +	acomp->ops->codec_wake_override(acomp->dev, enable);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(hda_set_codec_wakeup);
> +
> +int hda_display_power(struct hdac_bus *bus, bool enable)
> +{
> +	struct i915_audio_component *acomp = bus->audio_component;
> +
> +	if (!acomp->ops)
> +		return -ENODEV;
> +
> +	dev_dbg(bus->dev, "display power %s\n",
> +		enable ? "enable" : "disable");
> +
> +	if (enable) {
> +		if (!bus->i915_power_refcount++)
> +			acomp->ops->get_power(acomp->dev);
> +	} else {
> +		WARN_ON(!bus->i915_power_refcount);
> +		if (!--bus->i915_power_refcount)
> +			acomp->ops->put_power(acomp->dev);
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(hda_display_power);
> +
> +int hda_get_display_clk(struct hdac_bus *bus)
> +{
> +	struct i915_audio_component *acomp = bus->audio_component;
> +
> +	if (!acomp->ops)
> +		return -ENODEV;
> +
> +	return acomp->ops->get_cdclk_freq(acomp->dev);
> +}
> +EXPORT_SYMBOL_GPL(hda_get_display_clk);
> +
> +static int hda_component_master_bind(struct device *dev)
> +{
> +	struct i915_audio_component *acomp = hda_acomp;
> +	int ret;
> +
> +	ret = component_bind_all(dev, acomp);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (WARN_ON(!(acomp->dev && acomp->ops && acomp->ops->get_power &&
> +		      acomp->ops->put_power && acomp->ops->get_cdclk_freq))) {
> +		ret = -EINVAL;
> +		goto out_unbind;
> +	}
> +
> +	/*
> +	 * Atm, we don't support dynamic unbinding initiated by the child
> +	 * component, so pin its containing module until we unbind.
> +	 */
> +	if (!try_module_get(acomp->ops->owner)) {
> +		ret = -ENODEV;
> +		goto out_unbind;
> +	}
> +
> +	return 0;
> +
> +out_unbind:
> +	component_unbind_all(dev, acomp);
> +
> +	return ret;
> +}
> +
> +static void hda_component_master_unbind(struct device *dev)
> +{
> +	struct i915_audio_component *acomp = hda_acomp;
> +
> +	module_put(acomp->ops->owner);
> +	component_unbind_all(dev, acomp);
> +	WARN_ON(acomp->ops || acomp->dev);
> +}
> +
> +static const struct component_master_ops hda_component_master_ops = {
> +	.bind = hda_component_master_bind,
> +	.unbind = hda_component_master_unbind,
> +};
> +
> +static int hda_component_master_match(struct device *dev, void *data)
> +{
> +	/* i915 is the only supported component */
> +	return !strcmp(dev->driver->name, "i915");
> +}
> +
> +int hda_i915_init(struct hdac_bus *bus)
> +{
> +	struct component_match *match = NULL;
> +	struct device *dev = bus->dev;
> +	struct i915_audio_component *acomp;
> +	int ret;
> +
> +	acomp = kzalloc(sizeof(*acomp), GFP_KERNEL);
> +	if (!acomp)
> +		return -ENOMEM;
> +	bus->audio_component = acomp;
> +	hda_acomp = acomp;
> +
> +	component_match_add(dev, &match, hda_component_master_match, bus);
> +	ret = component_master_add_with_match(dev, &hda_component_master_ops,
> +					      match);
> +	if (ret < 0)
> +		goto out_err;
> +
> +	/*
> +	 * Atm, we don't support deferring the component binding, so make sure
> +	 * i915 is loaded and that the binding successfully completes.
> +	 */
> +	request_module("i915");
> +
> +	if (!acomp->ops) {
> +		ret = -ENODEV;
> +		goto out_master_del;
> +	}
> +	dev_dbg(dev, "bound to i915 component master\n");
> +
> +	return 0;
> +out_master_del:
> +	component_master_del(dev, &hda_component_master_ops);
> +out_err:
> +	kfree(acomp);
> +	bus->audio_component = NULL;
> +	dev_err(dev, "failed to add i915 component master (%d)\n", ret);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(hda_i915_init);
> +
> +int hda_i915_exit(struct hdac_bus *bus)
> +{
> +	struct device *dev = bus->dev;
> +	struct i915_audio_component *acomp = bus->audio_component;
> +
> +	WARN_ON(bus->i915_power_refcount);
> +	if (bus->i915_power_refcount > 0 && acomp && acomp->ops)
> +		acomp->ops->put_power(acomp->dev);
> +
> +	component_master_del(dev, &hda_component_master_ops);
> +
> +	kfree(acomp);
> +	bus->audio_component = NULL;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(hda_i915_exit);
> diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
> index 117bf5c..98ced97 100644
> --- a/sound/pci/hda/Kconfig
> +++ b/sound/pci/hda/Kconfig
> @@ -145,11 +145,6 @@ config SND_HDA_CODEC_HDMI
>  comment "Set to Y if you want auto-loading the codec driver"
>  	depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m
>  
> -config SND_HDA_I915
> -	bool
> -	default y
> -	depends on DRM_I915
> -
>  config SND_HDA_CODEC_CIRRUS
>  	tristate "Build Cirrus Logic codec support"
>  	select SND_HDA_GENERIC
> diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
> index c5e6651..a32ab83 100644
> --- a/sound/pci/hda/Makefile
> +++ b/sound/pci/hda/Makefile
> @@ -1,7 +1,5 @@
>  snd-hda-intel-objs := hda_intel.o
>  snd-hda-tegra-objs := hda_tegra.o
> -# for haswell power well
> -snd-hda-intel-$(CONFIG_SND_HDA_I915) +=	hda_i915.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
> diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c
> deleted file mode 100644
> index 8521702..0000000
> --- a/sound/pci/hda/hda_i915.c
> +++ /dev/null
> @@ -1,227 +0,0 @@
> -/*
> - *  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 <linux/pci.h>
> -#include <linux/component.h>
> -#include <drm/i915_component.h>
> -#include <sound/core.h>
> -#include "hda_controller.h"
> -#include "hda_intel.h"
> -
> -/* 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 AZX_REG_EM4			0x100c
> -#define AZX_REG_EM5			0x1010
> -
> -int hda_set_codec_wakeup(struct hda_intel *hda, bool enable)
> -{
> -	struct i915_audio_component *acomp = &hda->audio_component;
> -
> -	if (!acomp->ops)
> -		return -ENODEV;
> -
> -	if (!acomp->ops->codec_wake_override) {
> -		dev_warn(&hda->chip.pci->dev,
> -			"Invalid codec wake callback\n");
> -		return 0;
> -	}
> -
> -	dev_dbg(&hda->chip.pci->dev, "%s codec wakeup\n",
> -		enable ? "enable" : "disable");
> -
> -	acomp->ops->codec_wake_override(acomp->dev, enable);
> -
> -	return 0;
> -}
> -
> -int hda_display_power(struct hda_intel *hda, bool enable)
> -{
> -	struct i915_audio_component *acomp = &hda->audio_component;
> -
> -	if (!acomp->ops)
> -		return -ENODEV;
> -
> -	dev_dbg(&hda->chip.pci->dev, "display power %s\n",
> -		enable ? "enable" : "disable");
> -
> -	if (enable) {
> -		if (!hda->i915_power_refcount++)
> -			acomp->ops->get_power(acomp->dev);
> -	} else {
> -		WARN_ON(!hda->i915_power_refcount);
> -		if (!--hda->i915_power_refcount)
> -			acomp->ops->put_power(acomp->dev);
> -	}
> -
> -	return 0;
> -}
> -
> -void haswell_set_bclk(struct hda_intel *hda)
> -{
> -	int cdclk_freq;
> -	unsigned int bclk_m, bclk_n;
> -	struct i915_audio_component *acomp = &hda->audio_component;
> -	struct pci_dev *pci = hda->chip.pci;
> -
> -	/* Only Haswell/Broadwell need set BCLK */
> -	if (pci->device != 0x0a0c && pci->device != 0x0c0c
> -	   && pci->device != 0x0d0c && pci->device != 0x160c)
> -		return;
> -
> -	if (!acomp->ops)
> -		return;
> -
> -	cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev);
> -	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(&hda->chip, EM4, bclk_m);
> -	azx_writew(&hda->chip, EM5, bclk_n);
> -}
> -
> -static int hda_component_master_bind(struct device *dev)
> -{
> -	struct snd_card *card = dev_get_drvdata(dev);
> -	struct azx *chip = card->private_data;
> -	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
> -	struct i915_audio_component *acomp = &hda->audio_component;
> -	int ret;
> -
> -	ret = component_bind_all(dev, acomp);
> -	if (ret < 0)
> -		return ret;
> -
> -	if (WARN_ON(!(acomp->dev && acomp->ops && acomp->ops->get_power &&
> -		      acomp->ops->put_power && acomp->ops->get_cdclk_freq))) {
> -		ret = -EINVAL;
> -		goto out_unbind;
> -	}
> -
> -	/*
> -	 * Atm, we don't support dynamic unbinding initiated by the child
> -	 * component, so pin its containing module until we unbind.
> -	 */
> -	if (!try_module_get(acomp->ops->owner)) {
> -		ret = -ENODEV;
> -		goto out_unbind;
> -	}
> -
> -	return 0;
> -
> -out_unbind:
> -	component_unbind_all(dev, acomp);
> -
> -	return ret;
> -}
> -
> -static void hda_component_master_unbind(struct device *dev)
> -{
> -	struct snd_card *card = dev_get_drvdata(dev);
> -	struct azx *chip = card->private_data;
> -	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
> -	struct i915_audio_component *acomp = &hda->audio_component;
> -
> -	module_put(acomp->ops->owner);
> -	component_unbind_all(dev, acomp);
> -	WARN_ON(acomp->ops || acomp->dev);
> -}
> -
> -static const struct component_master_ops hda_component_master_ops = {
> -	.bind = hda_component_master_bind,
> -	.unbind = hda_component_master_unbind,
> -};
> -
> -static int hda_component_master_match(struct device *dev, void *data)
> -{
> -	/* i915 is the only supported component */
> -	return !strcmp(dev->driver->name, "i915");
> -}
> -
> -int hda_i915_init(struct hda_intel *hda)
> -{
> -	struct component_match *match = NULL;
> -	struct device *dev = &hda->chip.pci->dev;
> -	struct i915_audio_component *acomp = &hda->audio_component;
> -	int ret;
> -
> -	component_match_add(dev, &match, hda_component_master_match, hda);
> -	ret = component_master_add_with_match(dev, &hda_component_master_ops,
> -					      match);
> -	if (ret < 0)
> -		goto out_err;
> -
> -	/*
> -	 * Atm, we don't support deferring the component binding, so make sure
> -	 * i915 is loaded and that the binding successfully completes.
> -	 */
> -	request_module("i915");
> -
> -	if (!acomp->ops) {
> -		ret = -ENODEV;
> -		goto out_master_del;
> -	}
> -
> -	dev_dbg(dev, "bound to i915 component master\n");
> -
> -	return 0;
> -out_master_del:
> -	component_master_del(dev, &hda_component_master_ops);
> -out_err:
> -	dev_err(dev, "failed to add i915 component master (%d)\n", ret);
> -
> -	return ret;
> -}
> -
> -int hda_i915_exit(struct hda_intel *hda)
> -{
> -	struct device *dev = &hda->chip.pci->dev;
> -	struct i915_audio_component *acomp = &hda->audio_component;
> -
> -	WARN_ON(hda->i915_power_refcount);
> -	if (hda->i915_power_refcount > 0 && acomp->ops)
> -		acomp->ops->put_power(acomp->dev);
> -
> -	component_master_del(dev, &hda_component_master_ops);
> -
> -	return 0;
> -}
> diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
> index 706879a..229a1e4 100644
> --- a/sound/pci/hda/hda_intel.c
> +++ b/sound/pci/hda/hda_intel.c
> @@ -57,6 +57,8 @@
>  #endif
>  #include <sound/core.h>
>  #include <sound/initval.h>
> +#include <sound/hdaudio.h>
> +#include <sound/hda_i915.h>
>  #include <linux/vgaarb.h>
>  #include <linux/vga_switcheroo.h>
>  #include <linux/firmware.h>
> @@ -493,13 +495,13 @@ static void azx_init_pci(struct azx *chip)
>  
>  static void hda_intel_init_chip(struct azx *chip, bool full_reset)
>  {
> -	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
> +	struct hdac_bus *bus = azx_bus(chip);
>  
>  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
> -		hda_set_codec_wakeup(hda, true);
> +		hda_set_codec_wakeup(bus, true);
>  	azx_init_chip(chip, full_reset);
>  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
> -		hda_set_codec_wakeup(hda, false);
> +		hda_set_codec_wakeup(bus, false);
>  }
>  
>  /* calculate runtime delay from LPIB */
> @@ -557,9 +559,9 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
>  /* Enable/disable i915 display power for the link */
>  static int azx_intel_link_power(struct azx *chip, bool enable)
>  {
> -	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
> +	struct hdac_bus *bus = azx_bus(chip);
>  
> -	return hda_display_power(hda, enable);
> +	return hda_display_power(bus, enable);
>  }
>  
>  /*
> @@ -797,6 +799,50 @@ static int param_set_xint(const char *val, const struct kernel_param *kp)
>  #define azx_del_card_list(chip) /* NOP */
>  #endif /* CONFIG_PM */
>  
> +/* Intel HSW/BDW display HDA controller is in GPU. Both its power and link BCLK
> + * depends on GPU. Two 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 and need to
> + * be restored to avoid abnormal playback speed.
> + */
> +static void haswell_set_bclk(struct hda_intel *hda)
> +{
> +	struct azx *chip = &hda->chip;
> +	int cdclk_freq;
> +	unsigned int bclk_m, bclk_n;
> +
> +	if (!hda->need_i915_power)
> +		return;
> +
> +	cdclk_freq = hda_get_display_clk(azx_bus(chip));
> +	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, HSW_EM4, bclk_m);
> +	azx_writew(chip, HSW_EM5, bclk_n);
> +}
> +
>  #if defined(CONFIG_PM_SLEEP) || defined(SUPPORT_VGA_SWITCHEROO)
>  /*
>   * power management
> @@ -830,7 +876,7 @@ static int azx_suspend(struct device *dev)
>  		pci_disable_msi(chip->pci);
>  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
>  		&& hda->need_i915_power)
> -		hda_display_power(hda, false);
> +		hda_display_power(bus, false);
>  	return 0;
>  }
>  
> @@ -851,7 +897,7 @@ static int azx_resume(struct device *dev)
>  
>  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
>  		&& hda->need_i915_power) {
> -		hda_display_power(hda, true);
> +		hda_display_power(azx_bus(chip), true);
>  		haswell_set_bclk(hda);
>  	}
>  	if (chip->msi)
> @@ -895,7 +941,7 @@ static int azx_runtime_suspend(struct device *dev)
>  	azx_clear_irq_pending(chip);
>  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
>  		&& hda->need_i915_power)
> -		hda_display_power(hda, false);
> +		hda_display_power(azx_bus(chip), false);
>  
>  	return 0;
>  }
> @@ -905,6 +951,7 @@ static int azx_runtime_resume(struct device *dev)
>  	struct snd_card *card = dev_get_drvdata(dev);
>  	struct azx *chip;
>  	struct hda_intel *hda;
> +	struct hdac_bus *bus;
>  	struct hda_codec *codec;
>  	int status;
>  
> @@ -921,11 +968,12 @@ static int azx_runtime_resume(struct device *dev)
>  
>  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
>  		&& hda->need_i915_power) {
> -		hda_display_power(hda, true);
> +		bus =  azx_bus(chip);
> +		hda_display_power(bus, true);
>  		haswell_set_bclk(hda);
>  		/* toggle codec wakeup bit for STATESTS read */
> -		hda_set_codec_wakeup(hda, true);
> -		hda_set_codec_wakeup(hda, false);
> +		hda_set_codec_wakeup(bus, true);
> +		hda_set_codec_wakeup(bus, false);
>  	}
>  
>  	/* Read STATESTS before controller reset */
> @@ -1143,10 +1191,11 @@ static int azx_free(struct azx *chip)
>  #ifdef CONFIG_SND_HDA_PATCH_LOADER
>  	release_firmware(chip->fw);
>  #endif
> +
>  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
>  		if (hda->need_i915_power)
> -			hda_display_power(hda, false);
> -		hda_i915_exit(hda);
> +			hda_display_power(bus, false);
> +		hda_i915_exit(bus);
>  	}
>  	kfree(hda);
>  
> @@ -1905,6 +1954,7 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {
>  static int azx_probe_continue(struct azx *chip)
>  {
>  	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
> +	struct hdac_bus *bus = azx_bus(chip);
>  	struct pci_dev *pci = chip->pci;
>  	int dev = chip->dev_index;
>  	int err;
> @@ -1921,11 +1971,11 @@ static int azx_probe_continue(struct azx *chip)
>  		if (pci->device != 0x0f04 && pci->device != 0x2284)
>  			hda->need_i915_power = 1;
>  
> -		err = hda_i915_init(hda);
> +		err = hda_i915_init(bus);
>  		if (err < 0)
>  			goto i915_power_fail;
>  
> -		err = hda_display_power(hda, true);
> +		err = hda_display_power(bus, true);
>  		if (err < 0) {
>  			dev_err(chip->card->dev,
>  				"Cannot turn on display power on i915\n");
> @@ -1977,7 +2027,7 @@ static int azx_probe_continue(struct azx *chip)
>  out_free:
>  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
>  		&& !hda->need_i915_power)
> -		hda_display_power(hda, false);
> +		hda_display_power(bus, false);
>  
>  i915_power_fail:
>  	if (err < 0)
> diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h
> index 7fd3254..354f0bb 100644
> --- a/sound/pci/hda/hda_intel.h
> +++ b/sound/pci/hda/hda_intel.h
> @@ -16,7 +16,6 @@
>  #ifndef __SOUND_HDA_INTEL_H
>  #define __SOUND_HDA_INTEL_H
>  
> -#include <drm/i915_component.h>
>  #include "hda_controller.h"
>  
>  struct hda_intel {
> @@ -44,36 +43,7 @@ struct hda_intel {
>  	/* secondary power domain for hdmi audio under vga device */
>  	struct dev_pm_domain hdmi_pm_domain;
>  
> -	/* i915 component interface */
>  	bool need_i915_power:1; /* the hda controller needs i915 power */
> -	struct i915_audio_component audio_component;
> -	int i915_power_refcount;
>  };
>  
> -#ifdef CONFIG_SND_HDA_I915
> -int hda_set_codec_wakeup(struct hda_intel *hda, bool enable);
> -int hda_display_power(struct hda_intel *hda, bool enable);
> -void haswell_set_bclk(struct hda_intel *hda);
> -int hda_i915_init(struct hda_intel *hda);
> -int hda_i915_exit(struct hda_intel *hda);
> -#else
> -static inline int hda_set_codec_wakeup(struct hda_intel *hda, bool enable)
> -{
> -	return 0;
> -}
> -static inline int hda_display_power(struct hda_intel *hda, bool enable)
> -{
> -	return 0;
> -}
> -static inline void haswell_set_bclk(struct hda_intel *hda) { return; }
> -static inline int hda_i915_init(struct hda_intel *hda)
> -{
> -	return 0;
> -}
> -static inline int hda_i915_exit(struct hda_intel *hda)
> -{
> -	return 0;
> -}
> -#endif
> -
>  #endif
> -- 
> 1.9.1
>
Lin, Mengdong May 18, 2015, 8:26 a.m. UTC | #2
> -----Original Message-----
> From: Takashi Iwai [mailto:tiwai@suse.de]
> Sent: Monday, May 18, 2015 4:20 PM
> To: Lin, Mengdong
> Cc: alsa-devel@alsa-project.org
> Subject: Re: [RFC PATCH] ALSA: hda - Move hda_i915.c from sound/pci/hda to
> sound/hda
> 
> At Wed, 13 May 2015 20:21:16 +0800,
> mengdong.lin@intel.com wrote:
> >
> > From: Mengdong Lin <mengdong.lin@intel.com>
> >
> > The file is moved to hda core, so can be used by both legacy HDA
> > driver and new Skylake audio driver.
> >
> > - The i915 audio component is moved to core bus and dynamically allocated.
> > - A static pointer hda_acomp is used to help bind/unbind callbacks to get
> >   this component, because the sound card's private_data is used by the azx
> >   chip pointer, which is a legacy structure. It could be removed if private
> >   _data changes to some core structure which can be extended to find the
> >   bus.
> > - hda_get_display_clk() is added to get the display core clock for HSW/BDW.
> > - haswell_set_bclk() is moved to hda_intel.c because it needs to write the
> >   controller registers EM4/EM5.
> > - Move definition of HSW/BDW-specific registers EM4/EM5 to hda_register.h
> >   and rename them to HSW_EM4/HSW_EM5, because other HD-A
> controllers have
> >   different layout for the extended mode registers.
> >
> > Signed-off-by: Mengdong Lin <mengdong.lin@intel.com>
> 
> When moving to include/sound/*.h as a public API, better to add snd_ prefix.
> And, it's no longer HDA legacy driver, snd_hdac_ prefix would be more
> appropriate.
> 
> Could you change in that way and rebase to the latest for-next branch?

Thanks for your review! I'll add snd_hdac_ prefix to these APIs and rebase the patch. 

Regards
Mengdong

> 
> 
> thanks,
> 
> Takashi
> 
> >
> > diff --git a/include/sound/hda_i915.h b/include/sound/hda_i915.h new
> > file mode 100644 index 0000000..440e4c0
> > --- /dev/null
> > +++ b/include/sound/hda_i915.h
> > @@ -0,0 +1,36 @@
> > +/*
> > + * HD-Audio helpers to sync with i915 driver  */ #ifndef
> > +__SOUND_HDA_I915_H #define __SOUND_HDA_I915_H
> > +
> > +#ifdef CONFIG_SND_HDA_I915
> > +int hda_set_codec_wakeup(struct hdac_bus *bus, bool enable); int
> > +hda_display_power(struct hdac_bus *bus, bool enable); int
> > +hda_get_display_clk(struct hdac_bus *bus); int hda_i915_init(struct
> > +hdac_bus *bus); int hda_i915_exit(struct hdac_bus *bus); #else static
> > +int hda_set_codec_wakeup(struct hdac_bus *bus, bool enable) {
> > +	return 0;
> > +}
> > +static inline int hda_display_power(struct hdac_bus *bus, bool
> > +enable) {
> > +	return 0;
> > +}
> > +static inline int hda_get_display_clk(struct hdac_bus *bus) {
> > +	return 0;
> > +}
> > +static inline int hda_i915_init(struct hdac_bus *bus) {
> > +	return -ENODEV;
> > +}
> > +static inline int hda_i915_exit(struct hdac_bus *bus) {
> > +	return 0;
> > +}
> > +#endif
> > +
> > +#endif /* __SOUND_HDA_I915_H */
> > diff --git a/include/sound/hda_register.h
> > b/include/sound/hda_register.h index 4f6d3fc..0c7536e 100644
> > --- a/include/sound/hda_register.h
> > +++ b/include/sound/hda_register.h
> > @@ -84,6 +84,10 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2,
> SDO3 };
> >  #define AZX_REG_SD_BDLPL		0x18
> >  #define AZX_REG_SD_BDLPU		0x1c
> >
> > +/* Haswell/Broadwell display HD-A controller Extended Mode registers */
> > +#define AZX_REG_HSW_EM4			0x100c
> > +#define AZX_REG_HSW_EM5			0x1010
> > +
> >  /* PCI space */
> >  #define AZX_PCIREG_TCSEL		0x44
> >
> > diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index
> > b97c59e..64fff4d 100644
> > --- a/include/sound/hdaudio.h
> > +++ b/include/sound/hdaudio.h
> > @@ -11,6 +11,7 @@
> >  #include <sound/core.h>
> >  #include <sound/memalloc.h>
> >  #include <sound/hda_verbs.h>
> > +#include <drm/i915_component.h>
> >
> >  /* codec node id */
> >  typedef u16 hda_nid_t;
> > @@ -285,6 +286,10 @@ struct hdac_bus {
> >  	/* locks */
> >  	spinlock_t reg_lock;
> >  	struct mutex cmd_mutex;
> > +
> > +	/* i915 component interface */
> > +	struct i915_audio_component *audio_component;
> > +	int i915_power_refcount;
> >  };
> >
> >  int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, diff
> > --git a/sound/hda/Kconfig b/sound/hda/Kconfig index 7a17fca..ac5ffac
> > 100644
> > --- a/sound/hda/Kconfig
> > +++ b/sound/hda/Kconfig
> > @@ -4,3 +4,9 @@ config SND_HDA_CORE
> >
> >  config SND_HDA_DSP_LOADER
> >  	bool
> > +
> > +config SND_HDA_I915
> > +	bool
> > +	default y
> > +	depends on DRM_I915
> > +	depends on SND_HDA_CORE
> > diff --git a/sound/hda/Makefile b/sound/hda/Makefile index
> > 5b4bb47..1ce78c6 100644
> > --- a/sound/hda/Makefile
> > +++ b/sound/hda/Makefile
> > @@ -4,4 +4,7 @@ snd-hda-core-objs := hda_bus_type.o hdac_bus.o
> > hdac_device.o hdac_sysfs.o \  snd-hda-core-objs += trace.o
> > CFLAGS_trace.o := -I$(src)
> >
> > +# for sync with i915 gfx driver
> > +snd-hda-core-$(CONFIG_SND_HDA_I915) += hda_i915.o
> > +
> >  obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o diff --git
> > a/sound/hda/hda_i915.c b/sound/hda/hda_i915.c new file mode 100644
> > index 0000000..5c87938
> > --- /dev/null
> > +++ b/sound/hda/hda_i915.c
> > @@ -0,0 +1,193 @@
> > +/*
> > + *  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.
> > + */
> > +
> > +#include <linux/init.h>
> > +#include <linux/module.h>
> > +#include <linux/pci.h>
> > +#include <linux/component.h>
> > +#include <drm/i915_component.h>
> > +#include <sound/core.h>
> > +#include <sound/hdaudio.h>
> > +#include <sound/hda_i915.h>
> > +
> > +static struct i915_audio_component *hda_acomp;
> > +
> > +int hda_set_codec_wakeup(struct hdac_bus *bus, bool enable) {
> > +	struct i915_audio_component *acomp = bus->audio_component;
> > +
> > +	if (!acomp->ops)
> > +		return -ENODEV;
> > +
> > +	if (!acomp->ops->codec_wake_override) {
> > +		dev_warn(bus->dev,
> > +			"Invalid codec wake callback\n");
> > +		return 0;
> > +	}
> > +
> > +	dev_dbg(bus->dev, "%s codec wakeup\n",
> > +		enable ? "enable" : "disable");
> > +
> > +	acomp->ops->codec_wake_override(acomp->dev, enable);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(hda_set_codec_wakeup);
> > +
> > +int hda_display_power(struct hdac_bus *bus, bool enable) {
> > +	struct i915_audio_component *acomp = bus->audio_component;
> > +
> > +	if (!acomp->ops)
> > +		return -ENODEV;
> > +
> > +	dev_dbg(bus->dev, "display power %s\n",
> > +		enable ? "enable" : "disable");
> > +
> > +	if (enable) {
> > +		if (!bus->i915_power_refcount++)
> > +			acomp->ops->get_power(acomp->dev);
> > +	} else {
> > +		WARN_ON(!bus->i915_power_refcount);
> > +		if (!--bus->i915_power_refcount)
> > +			acomp->ops->put_power(acomp->dev);
> > +	}
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(hda_display_power);
> > +
> > +int hda_get_display_clk(struct hdac_bus *bus) {
> > +	struct i915_audio_component *acomp = bus->audio_component;
> > +
> > +	if (!acomp->ops)
> > +		return -ENODEV;
> > +
> > +	return acomp->ops->get_cdclk_freq(acomp->dev);
> > +}
> > +EXPORT_SYMBOL_GPL(hda_get_display_clk);
> > +
> > +static int hda_component_master_bind(struct device *dev) {
> > +	struct i915_audio_component *acomp = hda_acomp;
> > +	int ret;
> > +
> > +	ret = component_bind_all(dev, acomp);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	if (WARN_ON(!(acomp->dev && acomp->ops &&
> acomp->ops->get_power &&
> > +		      acomp->ops->put_power && acomp->ops->get_cdclk_freq)))
> {
> > +		ret = -EINVAL;
> > +		goto out_unbind;
> > +	}
> > +
> > +	/*
> > +	 * Atm, we don't support dynamic unbinding initiated by the child
> > +	 * component, so pin its containing module until we unbind.
> > +	 */
> > +	if (!try_module_get(acomp->ops->owner)) {
> > +		ret = -ENODEV;
> > +		goto out_unbind;
> > +	}
> > +
> > +	return 0;
> > +
> > +out_unbind:
> > +	component_unbind_all(dev, acomp);
> > +
> > +	return ret;
> > +}
> > +
> > +static void hda_component_master_unbind(struct device *dev) {
> > +	struct i915_audio_component *acomp = hda_acomp;
> > +
> > +	module_put(acomp->ops->owner);
> > +	component_unbind_all(dev, acomp);
> > +	WARN_ON(acomp->ops || acomp->dev);
> > +}
> > +
> > +static const struct component_master_ops hda_component_master_ops = {
> > +	.bind = hda_component_master_bind,
> > +	.unbind = hda_component_master_unbind, };
> > +
> > +static int hda_component_master_match(struct device *dev, void *data)
> > +{
> > +	/* i915 is the only supported component */
> > +	return !strcmp(dev->driver->name, "i915"); }
> > +
> > +int hda_i915_init(struct hdac_bus *bus) {
> > +	struct component_match *match = NULL;
> > +	struct device *dev = bus->dev;
> > +	struct i915_audio_component *acomp;
> > +	int ret;
> > +
> > +	acomp = kzalloc(sizeof(*acomp), GFP_KERNEL);
> > +	if (!acomp)
> > +		return -ENOMEM;
> > +	bus->audio_component = acomp;
> > +	hda_acomp = acomp;
> > +
> > +	component_match_add(dev, &match, hda_component_master_match,
> bus);
> > +	ret = component_master_add_with_match(dev,
> &hda_component_master_ops,
> > +					      match);
> > +	if (ret < 0)
> > +		goto out_err;
> > +
> > +	/*
> > +	 * Atm, we don't support deferring the component binding, so make sure
> > +	 * i915 is loaded and that the binding successfully completes.
> > +	 */
> > +	request_module("i915");
> > +
> > +	if (!acomp->ops) {
> > +		ret = -ENODEV;
> > +		goto out_master_del;
> > +	}
> > +	dev_dbg(dev, "bound to i915 component master\n");
> > +
> > +	return 0;
> > +out_master_del:
> > +	component_master_del(dev, &hda_component_master_ops);
> > +out_err:
> > +	kfree(acomp);
> > +	bus->audio_component = NULL;
> > +	dev_err(dev, "failed to add i915 component master (%d)\n", ret);
> > +
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(hda_i915_init);
> > +
> > +int hda_i915_exit(struct hdac_bus *bus) {
> > +	struct device *dev = bus->dev;
> > +	struct i915_audio_component *acomp = bus->audio_component;
> > +
> > +	WARN_ON(bus->i915_power_refcount);
> > +	if (bus->i915_power_refcount > 0 && acomp && acomp->ops)
> > +		acomp->ops->put_power(acomp->dev);
> > +
> > +	component_master_del(dev, &hda_component_master_ops);
> > +
> > +	kfree(acomp);
> > +	bus->audio_component = NULL;
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(hda_i915_exit);
> > diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index
> > 117bf5c..98ced97 100644
> > --- a/sound/pci/hda/Kconfig
> > +++ b/sound/pci/hda/Kconfig
> > @@ -145,11 +145,6 @@ config SND_HDA_CODEC_HDMI  comment "Set to
> Y if
> > you want auto-loading the codec driver"
> >  	depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m
> >
> > -config SND_HDA_I915
> > -	bool
> > -	default y
> > -	depends on DRM_I915
> > -
> >  config SND_HDA_CODEC_CIRRUS
> >  	tristate "Build Cirrus Logic codec support"
> >  	select SND_HDA_GENERIC
> > diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index
> > c5e6651..a32ab83 100644
> > --- a/sound/pci/hda/Makefile
> > +++ b/sound/pci/hda/Makefile
> > @@ -1,7 +1,5 @@
> >  snd-hda-intel-objs := hda_intel.o
> >  snd-hda-tegra-objs := hda_tegra.o
> > -# for haswell power well
> > -snd-hda-intel-$(CONFIG_SND_HDA_I915) +=	hda_i915.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
> > diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c
> > deleted file mode 100644 index 8521702..0000000
> > --- a/sound/pci/hda/hda_i915.c
> > +++ /dev/null
> > @@ -1,227 +0,0 @@
> > -/*
> > - *  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 <linux/pci.h>
> > -#include <linux/component.h>
> > -#include <drm/i915_component.h>
> > -#include <sound/core.h>
> > -#include "hda_controller.h"
> > -#include "hda_intel.h"
> > -
> > -/* 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 AZX_REG_EM4			0x100c
> > -#define AZX_REG_EM5			0x1010
> > -
> > -int hda_set_codec_wakeup(struct hda_intel *hda, bool enable) -{
> > -	struct i915_audio_component *acomp = &hda->audio_component;
> > -
> > -	if (!acomp->ops)
> > -		return -ENODEV;
> > -
> > -	if (!acomp->ops->codec_wake_override) {
> > -		dev_warn(&hda->chip.pci->dev,
> > -			"Invalid codec wake callback\n");
> > -		return 0;
> > -	}
> > -
> > -	dev_dbg(&hda->chip.pci->dev, "%s codec wakeup\n",
> > -		enable ? "enable" : "disable");
> > -
> > -	acomp->ops->codec_wake_override(acomp->dev, enable);
> > -
> > -	return 0;
> > -}
> > -
> > -int hda_display_power(struct hda_intel *hda, bool enable) -{
> > -	struct i915_audio_component *acomp = &hda->audio_component;
> > -
> > -	if (!acomp->ops)
> > -		return -ENODEV;
> > -
> > -	dev_dbg(&hda->chip.pci->dev, "display power %s\n",
> > -		enable ? "enable" : "disable");
> > -
> > -	if (enable) {
> > -		if (!hda->i915_power_refcount++)
> > -			acomp->ops->get_power(acomp->dev);
> > -	} else {
> > -		WARN_ON(!hda->i915_power_refcount);
> > -		if (!--hda->i915_power_refcount)
> > -			acomp->ops->put_power(acomp->dev);
> > -	}
> > -
> > -	return 0;
> > -}
> > -
> > -void haswell_set_bclk(struct hda_intel *hda) -{
> > -	int cdclk_freq;
> > -	unsigned int bclk_m, bclk_n;
> > -	struct i915_audio_component *acomp = &hda->audio_component;
> > -	struct pci_dev *pci = hda->chip.pci;
> > -
> > -	/* Only Haswell/Broadwell need set BCLK */
> > -	if (pci->device != 0x0a0c && pci->device != 0x0c0c
> > -	   && pci->device != 0x0d0c && pci->device != 0x160c)
> > -		return;
> > -
> > -	if (!acomp->ops)
> > -		return;
> > -
> > -	cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev);
> > -	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(&hda->chip, EM4, bclk_m);
> > -	azx_writew(&hda->chip, EM5, bclk_n);
> > -}
> > -
> > -static int hda_component_master_bind(struct device *dev) -{
> > -	struct snd_card *card = dev_get_drvdata(dev);
> > -	struct azx *chip = card->private_data;
> > -	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
> > -	struct i915_audio_component *acomp = &hda->audio_component;
> > -	int ret;
> > -
> > -	ret = component_bind_all(dev, acomp);
> > -	if (ret < 0)
> > -		return ret;
> > -
> > -	if (WARN_ON(!(acomp->dev && acomp->ops &&
> acomp->ops->get_power &&
> > -		      acomp->ops->put_power && acomp->ops->get_cdclk_freq)))
> {
> > -		ret = -EINVAL;
> > -		goto out_unbind;
> > -	}
> > -
> > -	/*
> > -	 * Atm, we don't support dynamic unbinding initiated by the child
> > -	 * component, so pin its containing module until we unbind.
> > -	 */
> > -	if (!try_module_get(acomp->ops->owner)) {
> > -		ret = -ENODEV;
> > -		goto out_unbind;
> > -	}
> > -
> > -	return 0;
> > -
> > -out_unbind:
> > -	component_unbind_all(dev, acomp);
> > -
> > -	return ret;
> > -}
> > -
> > -static void hda_component_master_unbind(struct device *dev) -{
> > -	struct snd_card *card = dev_get_drvdata(dev);
> > -	struct azx *chip = card->private_data;
> > -	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
> > -	struct i915_audio_component *acomp = &hda->audio_component;
> > -
> > -	module_put(acomp->ops->owner);
> > -	component_unbind_all(dev, acomp);
> > -	WARN_ON(acomp->ops || acomp->dev);
> > -}
> > -
> > -static const struct component_master_ops hda_component_master_ops = {
> > -	.bind = hda_component_master_bind,
> > -	.unbind = hda_component_master_unbind,
> > -};
> > -
> > -static int hda_component_master_match(struct device *dev, void *data)
> > -{
> > -	/* i915 is the only supported component */
> > -	return !strcmp(dev->driver->name, "i915");
> > -}
> > -
> > -int hda_i915_init(struct hda_intel *hda) -{
> > -	struct component_match *match = NULL;
> > -	struct device *dev = &hda->chip.pci->dev;
> > -	struct i915_audio_component *acomp = &hda->audio_component;
> > -	int ret;
> > -
> > -	component_match_add(dev, &match, hda_component_master_match,
> hda);
> > -	ret = component_master_add_with_match(dev,
> &hda_component_master_ops,
> > -					      match);
> > -	if (ret < 0)
> > -		goto out_err;
> > -
> > -	/*
> > -	 * Atm, we don't support deferring the component binding, so make sure
> > -	 * i915 is loaded and that the binding successfully completes.
> > -	 */
> > -	request_module("i915");
> > -
> > -	if (!acomp->ops) {
> > -		ret = -ENODEV;
> > -		goto out_master_del;
> > -	}
> > -
> > -	dev_dbg(dev, "bound to i915 component master\n");
> > -
> > -	return 0;
> > -out_master_del:
> > -	component_master_del(dev, &hda_component_master_ops);
> > -out_err:
> > -	dev_err(dev, "failed to add i915 component master (%d)\n", ret);
> > -
> > -	return ret;
> > -}
> > -
> > -int hda_i915_exit(struct hda_intel *hda) -{
> > -	struct device *dev = &hda->chip.pci->dev;
> > -	struct i915_audio_component *acomp = &hda->audio_component;
> > -
> > -	WARN_ON(hda->i915_power_refcount);
> > -	if (hda->i915_power_refcount > 0 && acomp->ops)
> > -		acomp->ops->put_power(acomp->dev);
> > -
> > -	component_master_del(dev, &hda_component_master_ops);
> > -
> > -	return 0;
> > -}
> > diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
> > index 706879a..229a1e4 100644
> > --- a/sound/pci/hda/hda_intel.c
> > +++ b/sound/pci/hda/hda_intel.c
> > @@ -57,6 +57,8 @@
> >  #endif
> >  #include <sound/core.h>
> >  #include <sound/initval.h>
> > +#include <sound/hdaudio.h>
> > +#include <sound/hda_i915.h>
> >  #include <linux/vgaarb.h>
> >  #include <linux/vga_switcheroo.h>
> >  #include <linux/firmware.h>
> > @@ -493,13 +495,13 @@ static void azx_init_pci(struct azx *chip)
> >
> >  static void hda_intel_init_chip(struct azx *chip, bool full_reset)  {
> > -	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
> > +	struct hdac_bus *bus = azx_bus(chip);
> >
> >  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
> > -		hda_set_codec_wakeup(hda, true);
> > +		hda_set_codec_wakeup(bus, true);
> >  	azx_init_chip(chip, full_reset);
> >  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
> > -		hda_set_codec_wakeup(hda, false);
> > +		hda_set_codec_wakeup(bus, false);
> >  }
> >
> >  /* calculate runtime delay from LPIB */ @@ -557,9 +559,9 @@ static
> > int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
> >  /* Enable/disable i915 display power for the link */  static int
> > azx_intel_link_power(struct azx *chip, bool enable)  {
> > -	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
> > +	struct hdac_bus *bus = azx_bus(chip);
> >
> > -	return hda_display_power(hda, enable);
> > +	return hda_display_power(bus, enable);
> >  }
> >
> >  /*
> > @@ -797,6 +799,50 @@ static int param_set_xint(const char *val, const
> > struct kernel_param *kp)  #define azx_del_card_list(chip) /* NOP */
> > #endif /* CONFIG_PM */
> >
> > +/* Intel HSW/BDW display HDA controller is in GPU. Both its power and
> > +link BCLK
> > + * depends on GPU. Two 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
> > +and need to
> > + * be restored to avoid abnormal playback speed.
> > + */
> > +static void haswell_set_bclk(struct hda_intel *hda) {
> > +	struct azx *chip = &hda->chip;
> > +	int cdclk_freq;
> > +	unsigned int bclk_m, bclk_n;
> > +
> > +	if (!hda->need_i915_power)
> > +		return;
> > +
> > +	cdclk_freq = hda_get_display_clk(azx_bus(chip));
> > +	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, HSW_EM4, bclk_m);
> > +	azx_writew(chip, HSW_EM5, bclk_n);
> > +}
> > +
> >  #if defined(CONFIG_PM_SLEEP) || defined(SUPPORT_VGA_SWITCHEROO)
> >  /*
> >   * power management
> > @@ -830,7 +876,7 @@ static int azx_suspend(struct device *dev)
> >  		pci_disable_msi(chip->pci);
> >  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
> >  		&& hda->need_i915_power)
> > -		hda_display_power(hda, false);
> > +		hda_display_power(bus, false);
> >  	return 0;
> >  }
> >
> > @@ -851,7 +897,7 @@ static int azx_resume(struct device *dev)
> >
> >  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
> >  		&& hda->need_i915_power) {
> > -		hda_display_power(hda, true);
> > +		hda_display_power(azx_bus(chip), true);
> >  		haswell_set_bclk(hda);
> >  	}
> >  	if (chip->msi)
> > @@ -895,7 +941,7 @@ static int azx_runtime_suspend(struct device *dev)
> >  	azx_clear_irq_pending(chip);
> >  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
> >  		&& hda->need_i915_power)
> > -		hda_display_power(hda, false);
> > +		hda_display_power(azx_bus(chip), false);
> >
> >  	return 0;
> >  }
> > @@ -905,6 +951,7 @@ static int azx_runtime_resume(struct device *dev)
> >  	struct snd_card *card = dev_get_drvdata(dev);
> >  	struct azx *chip;
> >  	struct hda_intel *hda;
> > +	struct hdac_bus *bus;
> >  	struct hda_codec *codec;
> >  	int status;
> >
> > @@ -921,11 +968,12 @@ static int azx_runtime_resume(struct device
> > *dev)
> >
> >  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
> >  		&& hda->need_i915_power) {
> > -		hda_display_power(hda, true);
> > +		bus =  azx_bus(chip);
> > +		hda_display_power(bus, true);
> >  		haswell_set_bclk(hda);
> >  		/* toggle codec wakeup bit for STATESTS read */
> > -		hda_set_codec_wakeup(hda, true);
> > -		hda_set_codec_wakeup(hda, false);
> > +		hda_set_codec_wakeup(bus, true);
> > +		hda_set_codec_wakeup(bus, false);
> >  	}
> >
> >  	/* Read STATESTS before controller reset */ @@ -1143,10 +1191,11 @@
> > static int azx_free(struct azx *chip)  #ifdef
> > CONFIG_SND_HDA_PATCH_LOADER
> >  	release_firmware(chip->fw);
> >  #endif
> > +
> >  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
> >  		if (hda->need_i915_power)
> > -			hda_display_power(hda, false);
> > -		hda_i915_exit(hda);
> > +			hda_display_power(bus, false);
> > +		hda_i915_exit(bus);
> >  	}
> >  	kfree(hda);
> >
> > @@ -1905,6 +1954,7 @@ static unsigned int
> > azx_max_codecs[AZX_NUM_DRIVERS] = {  static int
> > azx_probe_continue(struct azx *chip)  {
> >  	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
> > +	struct hdac_bus *bus = azx_bus(chip);
> >  	struct pci_dev *pci = chip->pci;
> >  	int dev = chip->dev_index;
> >  	int err;
> > @@ -1921,11 +1971,11 @@ static int azx_probe_continue(struct azx *chip)
> >  		if (pci->device != 0x0f04 && pci->device != 0x2284)
> >  			hda->need_i915_power = 1;
> >
> > -		err = hda_i915_init(hda);
> > +		err = hda_i915_init(bus);
> >  		if (err < 0)
> >  			goto i915_power_fail;
> >
> > -		err = hda_display_power(hda, true);
> > +		err = hda_display_power(bus, true);
> >  		if (err < 0) {
> >  			dev_err(chip->card->dev,
> >  				"Cannot turn on display power on i915\n"); @@ -1977,7
> +2027,7 @@
> > static int azx_probe_continue(struct azx *chip)
> >  out_free:
> >  	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
> >  		&& !hda->need_i915_power)
> > -		hda_display_power(hda, false);
> > +		hda_display_power(bus, false);
> >
> >  i915_power_fail:
> >  	if (err < 0)
> > diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h
> > index 7fd3254..354f0bb 100644
> > --- a/sound/pci/hda/hda_intel.h
> > +++ b/sound/pci/hda/hda_intel.h
> > @@ -16,7 +16,6 @@
> >  #ifndef __SOUND_HDA_INTEL_H
> >  #define __SOUND_HDA_INTEL_H
> >
> > -#include <drm/i915_component.h>
> >  #include "hda_controller.h"
> >
> >  struct hda_intel {
> > @@ -44,36 +43,7 @@ struct hda_intel {
> >  	/* secondary power domain for hdmi audio under vga device */
> >  	struct dev_pm_domain hdmi_pm_domain;
> >
> > -	/* i915 component interface */
> >  	bool need_i915_power:1; /* the hda controller needs i915 power */
> > -	struct i915_audio_component audio_component;
> > -	int i915_power_refcount;
> >  };
> >
> > -#ifdef CONFIG_SND_HDA_I915
> > -int hda_set_codec_wakeup(struct hda_intel *hda, bool enable); -int
> > hda_display_power(struct hda_intel *hda, bool enable); -void
> > haswell_set_bclk(struct hda_intel *hda); -int hda_i915_init(struct
> > hda_intel *hda); -int hda_i915_exit(struct hda_intel *hda); -#else
> > -static inline int hda_set_codec_wakeup(struct hda_intel *hda, bool
> > enable) -{
> > -	return 0;
> > -}
> > -static inline int hda_display_power(struct hda_intel *hda, bool
> > enable) -{
> > -	return 0;
> > -}
> > -static inline void haswell_set_bclk(struct hda_intel *hda) { return;
> > } -static inline int hda_i915_init(struct hda_intel *hda) -{
> > -	return 0;
> > -}
> > -static inline int hda_i915_exit(struct hda_intel *hda) -{
> > -	return 0;
> > -}
> > -#endif
> > -
> >  #endif
> > --
> > 1.9.1
> >
diff mbox

Patch

diff --git a/include/sound/hda_i915.h b/include/sound/hda_i915.h
new file mode 100644
index 0000000..440e4c0
--- /dev/null
+++ b/include/sound/hda_i915.h
@@ -0,0 +1,36 @@ 
+/*
+ * HD-Audio helpers to sync with i915 driver
+ */
+#ifndef __SOUND_HDA_I915_H
+#define __SOUND_HDA_I915_H
+
+#ifdef CONFIG_SND_HDA_I915
+int hda_set_codec_wakeup(struct hdac_bus *bus, bool enable);
+int hda_display_power(struct hdac_bus *bus, bool enable);
+int hda_get_display_clk(struct hdac_bus *bus);
+int hda_i915_init(struct hdac_bus *bus);
+int hda_i915_exit(struct hdac_bus *bus);
+#else
+static int hda_set_codec_wakeup(struct hdac_bus *bus, bool enable)
+{
+	return 0;
+}
+static inline int hda_display_power(struct hdac_bus *bus, bool enable)
+{
+	return 0;
+}
+static inline int hda_get_display_clk(struct hdac_bus *bus)
+{
+	return 0;
+}
+static inline int hda_i915_init(struct hdac_bus *bus)
+{
+	return -ENODEV;
+}
+static inline int hda_i915_exit(struct hdac_bus *bus)
+{
+	return 0;
+}
+#endif
+
+#endif /* __SOUND_HDA_I915_H */
diff --git a/include/sound/hda_register.h b/include/sound/hda_register.h
index 4f6d3fc..0c7536e 100644
--- a/include/sound/hda_register.h
+++ b/include/sound/hda_register.h
@@ -84,6 +84,10 @@  enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 #define AZX_REG_SD_BDLPL		0x18
 #define AZX_REG_SD_BDLPU		0x1c
 
+/* Haswell/Broadwell display HD-A controller Extended Mode registers */
+#define AZX_REG_HSW_EM4			0x100c
+#define AZX_REG_HSW_EM5			0x1010
+
 /* PCI space */
 #define AZX_PCIREG_TCSEL		0x44
 
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h
index b97c59e..64fff4d 100644
--- a/include/sound/hdaudio.h
+++ b/include/sound/hdaudio.h
@@ -11,6 +11,7 @@ 
 #include <sound/core.h>
 #include <sound/memalloc.h>
 #include <sound/hda_verbs.h>
+#include <drm/i915_component.h>
 
 /* codec node id */
 typedef u16 hda_nid_t;
@@ -285,6 +286,10 @@  struct hdac_bus {
 	/* locks */
 	spinlock_t reg_lock;
 	struct mutex cmd_mutex;
+
+	/* i915 component interface */
+	struct i915_audio_component *audio_component;
+	int i915_power_refcount;
 };
 
 int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig
index 7a17fca..ac5ffac 100644
--- a/sound/hda/Kconfig
+++ b/sound/hda/Kconfig
@@ -4,3 +4,9 @@  config SND_HDA_CORE
 
 config SND_HDA_DSP_LOADER
 	bool
+
+config SND_HDA_I915
+	bool
+	default y
+	depends on DRM_I915
+	depends on SND_HDA_CORE
diff --git a/sound/hda/Makefile b/sound/hda/Makefile
index 5b4bb47..1ce78c6 100644
--- a/sound/hda/Makefile
+++ b/sound/hda/Makefile
@@ -4,4 +4,7 @@  snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \
 snd-hda-core-objs += trace.o
 CFLAGS_trace.o := -I$(src)
 
+# for sync with i915 gfx driver
+snd-hda-core-$(CONFIG_SND_HDA_I915) += hda_i915.o
+
 obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
diff --git a/sound/hda/hda_i915.c b/sound/hda/hda_i915.c
new file mode 100644
index 0000000..5c87938
--- /dev/null
+++ b/sound/hda/hda_i915.c
@@ -0,0 +1,193 @@ 
+/*
+ *  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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/component.h>
+#include <drm/i915_component.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
+
+static struct i915_audio_component *hda_acomp;
+
+int hda_set_codec_wakeup(struct hdac_bus *bus, bool enable)
+{
+	struct i915_audio_component *acomp = bus->audio_component;
+
+	if (!acomp->ops)
+		return -ENODEV;
+
+	if (!acomp->ops->codec_wake_override) {
+		dev_warn(bus->dev,
+			"Invalid codec wake callback\n");
+		return 0;
+	}
+
+	dev_dbg(bus->dev, "%s codec wakeup\n",
+		enable ? "enable" : "disable");
+
+	acomp->ops->codec_wake_override(acomp->dev, enable);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hda_set_codec_wakeup);
+
+int hda_display_power(struct hdac_bus *bus, bool enable)
+{
+	struct i915_audio_component *acomp = bus->audio_component;
+
+	if (!acomp->ops)
+		return -ENODEV;
+
+	dev_dbg(bus->dev, "display power %s\n",
+		enable ? "enable" : "disable");
+
+	if (enable) {
+		if (!bus->i915_power_refcount++)
+			acomp->ops->get_power(acomp->dev);
+	} else {
+		WARN_ON(!bus->i915_power_refcount);
+		if (!--bus->i915_power_refcount)
+			acomp->ops->put_power(acomp->dev);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hda_display_power);
+
+int hda_get_display_clk(struct hdac_bus *bus)
+{
+	struct i915_audio_component *acomp = bus->audio_component;
+
+	if (!acomp->ops)
+		return -ENODEV;
+
+	return acomp->ops->get_cdclk_freq(acomp->dev);
+}
+EXPORT_SYMBOL_GPL(hda_get_display_clk);
+
+static int hda_component_master_bind(struct device *dev)
+{
+	struct i915_audio_component *acomp = hda_acomp;
+	int ret;
+
+	ret = component_bind_all(dev, acomp);
+	if (ret < 0)
+		return ret;
+
+	if (WARN_ON(!(acomp->dev && acomp->ops && acomp->ops->get_power &&
+		      acomp->ops->put_power && acomp->ops->get_cdclk_freq))) {
+		ret = -EINVAL;
+		goto out_unbind;
+	}
+
+	/*
+	 * Atm, we don't support dynamic unbinding initiated by the child
+	 * component, so pin its containing module until we unbind.
+	 */
+	if (!try_module_get(acomp->ops->owner)) {
+		ret = -ENODEV;
+		goto out_unbind;
+	}
+
+	return 0;
+
+out_unbind:
+	component_unbind_all(dev, acomp);
+
+	return ret;
+}
+
+static void hda_component_master_unbind(struct device *dev)
+{
+	struct i915_audio_component *acomp = hda_acomp;
+
+	module_put(acomp->ops->owner);
+	component_unbind_all(dev, acomp);
+	WARN_ON(acomp->ops || acomp->dev);
+}
+
+static const struct component_master_ops hda_component_master_ops = {
+	.bind = hda_component_master_bind,
+	.unbind = hda_component_master_unbind,
+};
+
+static int hda_component_master_match(struct device *dev, void *data)
+{
+	/* i915 is the only supported component */
+	return !strcmp(dev->driver->name, "i915");
+}
+
+int hda_i915_init(struct hdac_bus *bus)
+{
+	struct component_match *match = NULL;
+	struct device *dev = bus->dev;
+	struct i915_audio_component *acomp;
+	int ret;
+
+	acomp = kzalloc(sizeof(*acomp), GFP_KERNEL);
+	if (!acomp)
+		return -ENOMEM;
+	bus->audio_component = acomp;
+	hda_acomp = acomp;
+
+	component_match_add(dev, &match, hda_component_master_match, bus);
+	ret = component_master_add_with_match(dev, &hda_component_master_ops,
+					      match);
+	if (ret < 0)
+		goto out_err;
+
+	/*
+	 * Atm, we don't support deferring the component binding, so make sure
+	 * i915 is loaded and that the binding successfully completes.
+	 */
+	request_module("i915");
+
+	if (!acomp->ops) {
+		ret = -ENODEV;
+		goto out_master_del;
+	}
+	dev_dbg(dev, "bound to i915 component master\n");
+
+	return 0;
+out_master_del:
+	component_master_del(dev, &hda_component_master_ops);
+out_err:
+	kfree(acomp);
+	bus->audio_component = NULL;
+	dev_err(dev, "failed to add i915 component master (%d)\n", ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(hda_i915_init);
+
+int hda_i915_exit(struct hdac_bus *bus)
+{
+	struct device *dev = bus->dev;
+	struct i915_audio_component *acomp = bus->audio_component;
+
+	WARN_ON(bus->i915_power_refcount);
+	if (bus->i915_power_refcount > 0 && acomp && acomp->ops)
+		acomp->ops->put_power(acomp->dev);
+
+	component_master_del(dev, &hda_component_master_ops);
+
+	kfree(acomp);
+	bus->audio_component = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hda_i915_exit);
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 117bf5c..98ced97 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -145,11 +145,6 @@  config SND_HDA_CODEC_HDMI
 comment "Set to Y if you want auto-loading the codec driver"
 	depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m
 
-config SND_HDA_I915
-	bool
-	default y
-	depends on DRM_I915
-
 config SND_HDA_CODEC_CIRRUS
 	tristate "Build Cirrus Logic codec support"
 	select SND_HDA_GENERIC
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index c5e6651..a32ab83 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -1,7 +1,5 @@ 
 snd-hda-intel-objs := hda_intel.o
 snd-hda-tegra-objs := hda_tegra.o
-# for haswell power well
-snd-hda-intel-$(CONFIG_SND_HDA_I915) +=	hda_i915.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
diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c
deleted file mode 100644
index 8521702..0000000
--- a/sound/pci/hda/hda_i915.c
+++ /dev/null
@@ -1,227 +0,0 @@ 
-/*
- *  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 <linux/pci.h>
-#include <linux/component.h>
-#include <drm/i915_component.h>
-#include <sound/core.h>
-#include "hda_controller.h"
-#include "hda_intel.h"
-
-/* 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 AZX_REG_EM4			0x100c
-#define AZX_REG_EM5			0x1010
-
-int hda_set_codec_wakeup(struct hda_intel *hda, bool enable)
-{
-	struct i915_audio_component *acomp = &hda->audio_component;
-
-	if (!acomp->ops)
-		return -ENODEV;
-
-	if (!acomp->ops->codec_wake_override) {
-		dev_warn(&hda->chip.pci->dev,
-			"Invalid codec wake callback\n");
-		return 0;
-	}
-
-	dev_dbg(&hda->chip.pci->dev, "%s codec wakeup\n",
-		enable ? "enable" : "disable");
-
-	acomp->ops->codec_wake_override(acomp->dev, enable);
-
-	return 0;
-}
-
-int hda_display_power(struct hda_intel *hda, bool enable)
-{
-	struct i915_audio_component *acomp = &hda->audio_component;
-
-	if (!acomp->ops)
-		return -ENODEV;
-
-	dev_dbg(&hda->chip.pci->dev, "display power %s\n",
-		enable ? "enable" : "disable");
-
-	if (enable) {
-		if (!hda->i915_power_refcount++)
-			acomp->ops->get_power(acomp->dev);
-	} else {
-		WARN_ON(!hda->i915_power_refcount);
-		if (!--hda->i915_power_refcount)
-			acomp->ops->put_power(acomp->dev);
-	}
-
-	return 0;
-}
-
-void haswell_set_bclk(struct hda_intel *hda)
-{
-	int cdclk_freq;
-	unsigned int bclk_m, bclk_n;
-	struct i915_audio_component *acomp = &hda->audio_component;
-	struct pci_dev *pci = hda->chip.pci;
-
-	/* Only Haswell/Broadwell need set BCLK */
-	if (pci->device != 0x0a0c && pci->device != 0x0c0c
-	   && pci->device != 0x0d0c && pci->device != 0x160c)
-		return;
-
-	if (!acomp->ops)
-		return;
-
-	cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev);
-	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(&hda->chip, EM4, bclk_m);
-	azx_writew(&hda->chip, EM5, bclk_n);
-}
-
-static int hda_component_master_bind(struct device *dev)
-{
-	struct snd_card *card = dev_get_drvdata(dev);
-	struct azx *chip = card->private_data;
-	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
-	struct i915_audio_component *acomp = &hda->audio_component;
-	int ret;
-
-	ret = component_bind_all(dev, acomp);
-	if (ret < 0)
-		return ret;
-
-	if (WARN_ON(!(acomp->dev && acomp->ops && acomp->ops->get_power &&
-		      acomp->ops->put_power && acomp->ops->get_cdclk_freq))) {
-		ret = -EINVAL;
-		goto out_unbind;
-	}
-
-	/*
-	 * Atm, we don't support dynamic unbinding initiated by the child
-	 * component, so pin its containing module until we unbind.
-	 */
-	if (!try_module_get(acomp->ops->owner)) {
-		ret = -ENODEV;
-		goto out_unbind;
-	}
-
-	return 0;
-
-out_unbind:
-	component_unbind_all(dev, acomp);
-
-	return ret;
-}
-
-static void hda_component_master_unbind(struct device *dev)
-{
-	struct snd_card *card = dev_get_drvdata(dev);
-	struct azx *chip = card->private_data;
-	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
-	struct i915_audio_component *acomp = &hda->audio_component;
-
-	module_put(acomp->ops->owner);
-	component_unbind_all(dev, acomp);
-	WARN_ON(acomp->ops || acomp->dev);
-}
-
-static const struct component_master_ops hda_component_master_ops = {
-	.bind = hda_component_master_bind,
-	.unbind = hda_component_master_unbind,
-};
-
-static int hda_component_master_match(struct device *dev, void *data)
-{
-	/* i915 is the only supported component */
-	return !strcmp(dev->driver->name, "i915");
-}
-
-int hda_i915_init(struct hda_intel *hda)
-{
-	struct component_match *match = NULL;
-	struct device *dev = &hda->chip.pci->dev;
-	struct i915_audio_component *acomp = &hda->audio_component;
-	int ret;
-
-	component_match_add(dev, &match, hda_component_master_match, hda);
-	ret = component_master_add_with_match(dev, &hda_component_master_ops,
-					      match);
-	if (ret < 0)
-		goto out_err;
-
-	/*
-	 * Atm, we don't support deferring the component binding, so make sure
-	 * i915 is loaded and that the binding successfully completes.
-	 */
-	request_module("i915");
-
-	if (!acomp->ops) {
-		ret = -ENODEV;
-		goto out_master_del;
-	}
-
-	dev_dbg(dev, "bound to i915 component master\n");
-
-	return 0;
-out_master_del:
-	component_master_del(dev, &hda_component_master_ops);
-out_err:
-	dev_err(dev, "failed to add i915 component master (%d)\n", ret);
-
-	return ret;
-}
-
-int hda_i915_exit(struct hda_intel *hda)
-{
-	struct device *dev = &hda->chip.pci->dev;
-	struct i915_audio_component *acomp = &hda->audio_component;
-
-	WARN_ON(hda->i915_power_refcount);
-	if (hda->i915_power_refcount > 0 && acomp->ops)
-		acomp->ops->put_power(acomp->dev);
-
-	component_master_del(dev, &hda_component_master_ops);
-
-	return 0;
-}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 706879a..229a1e4 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -57,6 +57,8 @@ 
 #endif
 #include <sound/core.h>
 #include <sound/initval.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
 #include <linux/vgaarb.h>
 #include <linux/vga_switcheroo.h>
 #include <linux/firmware.h>
@@ -493,13 +495,13 @@  static void azx_init_pci(struct azx *chip)
 
 static void hda_intel_init_chip(struct azx *chip, bool full_reset)
 {
-	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+	struct hdac_bus *bus = azx_bus(chip);
 
 	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
-		hda_set_codec_wakeup(hda, true);
+		hda_set_codec_wakeup(bus, true);
 	azx_init_chip(chip, full_reset);
 	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
-		hda_set_codec_wakeup(hda, false);
+		hda_set_codec_wakeup(bus, false);
 }
 
 /* calculate runtime delay from LPIB */
@@ -557,9 +559,9 @@  static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
 /* Enable/disable i915 display power for the link */
 static int azx_intel_link_power(struct azx *chip, bool enable)
 {
-	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+	struct hdac_bus *bus = azx_bus(chip);
 
-	return hda_display_power(hda, enable);
+	return hda_display_power(bus, enable);
 }
 
 /*
@@ -797,6 +799,50 @@  static int param_set_xint(const char *val, const struct kernel_param *kp)
 #define azx_del_card_list(chip) /* NOP */
 #endif /* CONFIG_PM */
 
+/* Intel HSW/BDW display HDA controller is in GPU. Both its power and link BCLK
+ * depends on GPU. Two 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 and need to
+ * be restored to avoid abnormal playback speed.
+ */
+static void haswell_set_bclk(struct hda_intel *hda)
+{
+	struct azx *chip = &hda->chip;
+	int cdclk_freq;
+	unsigned int bclk_m, bclk_n;
+
+	if (!hda->need_i915_power)
+		return;
+
+	cdclk_freq = hda_get_display_clk(azx_bus(chip));
+	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, HSW_EM4, bclk_m);
+	azx_writew(chip, HSW_EM5, bclk_n);
+}
+
 #if defined(CONFIG_PM_SLEEP) || defined(SUPPORT_VGA_SWITCHEROO)
 /*
  * power management
@@ -830,7 +876,7 @@  static int azx_suspend(struct device *dev)
 		pci_disable_msi(chip->pci);
 	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
 		&& hda->need_i915_power)
-		hda_display_power(hda, false);
+		hda_display_power(bus, false);
 	return 0;
 }
 
@@ -851,7 +897,7 @@  static int azx_resume(struct device *dev)
 
 	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
 		&& hda->need_i915_power) {
-		hda_display_power(hda, true);
+		hda_display_power(azx_bus(chip), true);
 		haswell_set_bclk(hda);
 	}
 	if (chip->msi)
@@ -895,7 +941,7 @@  static int azx_runtime_suspend(struct device *dev)
 	azx_clear_irq_pending(chip);
 	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
 		&& hda->need_i915_power)
-		hda_display_power(hda, false);
+		hda_display_power(azx_bus(chip), false);
 
 	return 0;
 }
@@ -905,6 +951,7 @@  static int azx_runtime_resume(struct device *dev)
 	struct snd_card *card = dev_get_drvdata(dev);
 	struct azx *chip;
 	struct hda_intel *hda;
+	struct hdac_bus *bus;
 	struct hda_codec *codec;
 	int status;
 
@@ -921,11 +968,12 @@  static int azx_runtime_resume(struct device *dev)
 
 	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
 		&& hda->need_i915_power) {
-		hda_display_power(hda, true);
+		bus =  azx_bus(chip);
+		hda_display_power(bus, true);
 		haswell_set_bclk(hda);
 		/* toggle codec wakeup bit for STATESTS read */
-		hda_set_codec_wakeup(hda, true);
-		hda_set_codec_wakeup(hda, false);
+		hda_set_codec_wakeup(bus, true);
+		hda_set_codec_wakeup(bus, false);
 	}
 
 	/* Read STATESTS before controller reset */
@@ -1143,10 +1191,11 @@  static int azx_free(struct azx *chip)
 #ifdef CONFIG_SND_HDA_PATCH_LOADER
 	release_firmware(chip->fw);
 #endif
+
 	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
 		if (hda->need_i915_power)
-			hda_display_power(hda, false);
-		hda_i915_exit(hda);
+			hda_display_power(bus, false);
+		hda_i915_exit(bus);
 	}
 	kfree(hda);
 
@@ -1905,6 +1954,7 @@  static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {
 static int azx_probe_continue(struct azx *chip)
 {
 	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+	struct hdac_bus *bus = azx_bus(chip);
 	struct pci_dev *pci = chip->pci;
 	int dev = chip->dev_index;
 	int err;
@@ -1921,11 +1971,11 @@  static int azx_probe_continue(struct azx *chip)
 		if (pci->device != 0x0f04 && pci->device != 0x2284)
 			hda->need_i915_power = 1;
 
-		err = hda_i915_init(hda);
+		err = hda_i915_init(bus);
 		if (err < 0)
 			goto i915_power_fail;
 
-		err = hda_display_power(hda, true);
+		err = hda_display_power(bus, true);
 		if (err < 0) {
 			dev_err(chip->card->dev,
 				"Cannot turn on display power on i915\n");
@@ -1977,7 +2027,7 @@  static int azx_probe_continue(struct azx *chip)
 out_free:
 	if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
 		&& !hda->need_i915_power)
-		hda_display_power(hda, false);
+		hda_display_power(bus, false);
 
 i915_power_fail:
 	if (err < 0)
diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h
index 7fd3254..354f0bb 100644
--- a/sound/pci/hda/hda_intel.h
+++ b/sound/pci/hda/hda_intel.h
@@ -16,7 +16,6 @@ 
 #ifndef __SOUND_HDA_INTEL_H
 #define __SOUND_HDA_INTEL_H
 
-#include <drm/i915_component.h>
 #include "hda_controller.h"
 
 struct hda_intel {
@@ -44,36 +43,7 @@  struct hda_intel {
 	/* secondary power domain for hdmi audio under vga device */
 	struct dev_pm_domain hdmi_pm_domain;
 
-	/* i915 component interface */
 	bool need_i915_power:1; /* the hda controller needs i915 power */
-	struct i915_audio_component audio_component;
-	int i915_power_refcount;
 };
 
-#ifdef CONFIG_SND_HDA_I915
-int hda_set_codec_wakeup(struct hda_intel *hda, bool enable);
-int hda_display_power(struct hda_intel *hda, bool enable);
-void haswell_set_bclk(struct hda_intel *hda);
-int hda_i915_init(struct hda_intel *hda);
-int hda_i915_exit(struct hda_intel *hda);
-#else
-static inline int hda_set_codec_wakeup(struct hda_intel *hda, bool enable)
-{
-	return 0;
-}
-static inline int hda_display_power(struct hda_intel *hda, bool enable)
-{
-	return 0;
-}
-static inline void haswell_set_bclk(struct hda_intel *hda) { return; }
-static inline int hda_i915_init(struct hda_intel *hda)
-{
-	return 0;
-}
-static inline int hda_i915_exit(struct hda_intel *hda)
-{
-	return 0;
-}
-#endif
-
 #endif