ALSA: hda - Move hda_i915.c from sound/pci/hda to sound/hda
The file is moved to hda core and renamed to hdac_i915.c, so can be used by both legacy HDA driver and new Skylake audio driver. - Add snd_hdac_ prefix to the public APIs. - The i915 audio component is moved to core bus and dynamically allocated. - A static pointer hdac_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. - snd_hdac_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, and only legacy HD-A needs it for HSW/BDW. - 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> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
4214c5349c
commit
98d8fc6c5d
|
@ -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 snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable);
|
||||||
|
int snd_hdac_display_power(struct hdac_bus *bus, bool enable);
|
||||||
|
int snd_hdac_get_display_clk(struct hdac_bus *bus);
|
||||||
|
int snd_hdac_i915_init(struct hdac_bus *bus);
|
||||||
|
int snd_hdac_i915_exit(struct hdac_bus *bus);
|
||||||
|
#else
|
||||||
|
static int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static inline int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static inline int snd_hdac_get_display_clk(struct hdac_bus *bus)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static inline int snd_hdac_i915_init(struct hdac_bus *bus)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
static inline int snd_hdac_i915_exit(struct hdac_bus *bus)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SOUND_HDA_I915_H */
|
|
@ -84,6 +84,10 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
|
||||||
#define AZX_REG_SD_BDLPL 0x18
|
#define AZX_REG_SD_BDLPL 0x18
|
||||||
#define AZX_REG_SD_BDLPU 0x1c
|
#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 */
|
/* PCI space */
|
||||||
#define AZX_PCIREG_TCSEL 0x44
|
#define AZX_PCIREG_TCSEL 0x44
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/memalloc.h>
|
#include <sound/memalloc.h>
|
||||||
#include <sound/hda_verbs.h>
|
#include <sound/hda_verbs.h>
|
||||||
|
#include <drm/i915_component.h>
|
||||||
|
|
||||||
/* codec node id */
|
/* codec node id */
|
||||||
typedef u16 hda_nid_t;
|
typedef u16 hda_nid_t;
|
||||||
|
@ -285,6 +286,10 @@ struct hdac_bus {
|
||||||
/* locks */
|
/* locks */
|
||||||
spinlock_t reg_lock;
|
spinlock_t reg_lock;
|
||||||
struct mutex cmd_mutex;
|
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,
|
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
|
||||||
|
|
|
@ -4,3 +4,9 @@ config SND_HDA_CORE
|
||||||
|
|
||||||
config SND_HDA_DSP_LOADER
|
config SND_HDA_DSP_LOADER
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config SND_HDA_I915
|
||||||
|
bool
|
||||||
|
default y
|
||||||
|
depends on DRM_I915
|
||||||
|
depends on SND_HDA_CORE
|
||||||
|
|
|
@ -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
|
snd-hda-core-objs += trace.o
|
||||||
CFLAGS_trace.o := -I$(src)
|
CFLAGS_trace.o := -I$(src)
|
||||||
|
|
||||||
|
# for sync with i915 gfx driver
|
||||||
|
snd-hda-core-$(CONFIG_SND_HDA_I915) += hdac_i915.o
|
||||||
|
|
||||||
obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
|
obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
/*
|
||||||
|
* hdac_i915.c - routines for sync between HD-A core and i915 display driver
|
||||||
|
*
|
||||||
|
* 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 *hdac_acomp;
|
||||||
|
|
||||||
|
int snd_hdac_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(snd_hdac_set_codec_wakeup);
|
||||||
|
|
||||||
|
int snd_hdac_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(snd_hdac_display_power);
|
||||||
|
|
||||||
|
int snd_hdac_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(snd_hdac_get_display_clk);
|
||||||
|
|
||||||
|
static int hdac_component_master_bind(struct device *dev)
|
||||||
|
{
|
||||||
|
struct i915_audio_component *acomp = hdac_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 hdac_component_master_unbind(struct device *dev)
|
||||||
|
{
|
||||||
|
struct i915_audio_component *acomp = hdac_acomp;
|
||||||
|
|
||||||
|
module_put(acomp->ops->owner);
|
||||||
|
component_unbind_all(dev, acomp);
|
||||||
|
WARN_ON(acomp->ops || acomp->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct component_master_ops hdac_component_master_ops = {
|
||||||
|
.bind = hdac_component_master_bind,
|
||||||
|
.unbind = hdac_component_master_unbind,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int hdac_component_master_match(struct device *dev, void *data)
|
||||||
|
{
|
||||||
|
/* i915 is the only supported component */
|
||||||
|
return !strcmp(dev->driver->name, "i915");
|
||||||
|
}
|
||||||
|
|
||||||
|
int snd_hdac_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;
|
||||||
|
hdac_acomp = acomp;
|
||||||
|
|
||||||
|
component_match_add(dev, &match, hdac_component_master_match, bus);
|
||||||
|
ret = component_master_add_with_match(dev, &hdac_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, &hdac_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(snd_hdac_i915_init);
|
||||||
|
|
||||||
|
int snd_hdac_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, &hdac_component_master_ops);
|
||||||
|
|
||||||
|
kfree(acomp);
|
||||||
|
bus->audio_component = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(snd_hdac_i915_exit);
|
|
@ -145,11 +145,6 @@ config SND_HDA_CODEC_HDMI
|
||||||
comment "Set to Y if you want auto-loading the codec driver"
|
comment "Set to Y if you want auto-loading the codec driver"
|
||||||
depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m
|
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
|
config SND_HDA_CODEC_CIRRUS
|
||||||
tristate "Build Cirrus Logic codec support"
|
tristate "Build Cirrus Logic codec support"
|
||||||
select SND_HDA_GENERIC
|
select SND_HDA_GENERIC
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
snd-hda-intel-objs := hda_intel.o
|
snd-hda-intel-objs := hda_intel.o
|
||||||
snd-hda-tegra-objs := hda_tegra.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_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
|
||||||
snd-hda-codec-y += hda_controller.o
|
snd-hda-codec-y += hda_controller.o
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -57,6 +57,8 @@
|
||||||
#endif
|
#endif
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/initval.h>
|
#include <sound/initval.h>
|
||||||
|
#include <sound/hdaudio.h>
|
||||||
|
#include <sound/hda_i915.h>
|
||||||
#include <linux/vgaarb.h>
|
#include <linux/vgaarb.h>
|
||||||
#include <linux/vga_switcheroo.h>
|
#include <linux/vga_switcheroo.h>
|
||||||
#include <linux/firmware.h>
|
#include <linux/firmware.h>
|
||||||
|
@ -496,13 +498,13 @@ static void azx_init_pci(struct azx *chip)
|
||||||
|
|
||||||
static void hda_intel_init_chip(struct azx *chip, bool full_reset)
|
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)
|
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
|
||||||
hda_set_codec_wakeup(hda, true);
|
snd_hdac_set_codec_wakeup(bus, true);
|
||||||
azx_init_chip(chip, full_reset);
|
azx_init_chip(chip, full_reset);
|
||||||
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
|
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
|
||||||
hda_set_codec_wakeup(hda, false);
|
snd_hdac_set_codec_wakeup(bus, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* calculate runtime delay from LPIB */
|
/* calculate runtime delay from LPIB */
|
||||||
|
@ -560,9 +562,9 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
|
||||||
/* Enable/disable i915 display power for the link */
|
/* Enable/disable i915 display power for the link */
|
||||||
static int azx_intel_link_power(struct azx *chip, bool enable)
|
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 snd_hdac_display_power(bus, enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -800,6 +802,50 @@ static int param_set_xint(const char *val, const struct kernel_param *kp)
|
||||||
#define azx_del_card_list(chip) /* NOP */
|
#define azx_del_card_list(chip) /* NOP */
|
||||||
#endif /* CONFIG_PM */
|
#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 = snd_hdac_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)
|
#if defined(CONFIG_PM_SLEEP) || defined(SUPPORT_VGA_SWITCHEROO)
|
||||||
/*
|
/*
|
||||||
* power management
|
* power management
|
||||||
|
@ -833,7 +879,7 @@ static int azx_suspend(struct device *dev)
|
||||||
pci_disable_msi(chip->pci);
|
pci_disable_msi(chip->pci);
|
||||||
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
|
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
|
||||||
&& hda->need_i915_power)
|
&& hda->need_i915_power)
|
||||||
hda_display_power(hda, false);
|
snd_hdac_display_power(bus, false);
|
||||||
|
|
||||||
trace_azx_suspend(chip);
|
trace_azx_suspend(chip);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -856,7 +902,7 @@ static int azx_resume(struct device *dev)
|
||||||
|
|
||||||
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
|
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
|
||||||
&& hda->need_i915_power) {
|
&& hda->need_i915_power) {
|
||||||
hda_display_power(hda, true);
|
snd_hdac_display_power(azx_bus(chip), true);
|
||||||
haswell_set_bclk(hda);
|
haswell_set_bclk(hda);
|
||||||
}
|
}
|
||||||
if (chip->msi)
|
if (chip->msi)
|
||||||
|
@ -902,7 +948,7 @@ static int azx_runtime_suspend(struct device *dev)
|
||||||
azx_clear_irq_pending(chip);
|
azx_clear_irq_pending(chip);
|
||||||
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
|
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
|
||||||
&& hda->need_i915_power)
|
&& hda->need_i915_power)
|
||||||
hda_display_power(hda, false);
|
snd_hdac_display_power(azx_bus(chip), false);
|
||||||
|
|
||||||
trace_azx_runtime_suspend(chip);
|
trace_azx_runtime_suspend(chip);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -913,6 +959,7 @@ static int azx_runtime_resume(struct device *dev)
|
||||||
struct snd_card *card = dev_get_drvdata(dev);
|
struct snd_card *card = dev_get_drvdata(dev);
|
||||||
struct azx *chip;
|
struct azx *chip;
|
||||||
struct hda_intel *hda;
|
struct hda_intel *hda;
|
||||||
|
struct hdac_bus *bus;
|
||||||
struct hda_codec *codec;
|
struct hda_codec *codec;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
@ -929,11 +976,12 @@ static int azx_runtime_resume(struct device *dev)
|
||||||
|
|
||||||
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
|
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
|
||||||
&& hda->need_i915_power) {
|
&& hda->need_i915_power) {
|
||||||
hda_display_power(hda, true);
|
bus = azx_bus(chip);
|
||||||
|
snd_hdac_display_power(bus, true);
|
||||||
haswell_set_bclk(hda);
|
haswell_set_bclk(hda);
|
||||||
/* toggle codec wakeup bit for STATESTS read */
|
/* toggle codec wakeup bit for STATESTS read */
|
||||||
hda_set_codec_wakeup(hda, true);
|
snd_hdac_set_codec_wakeup(bus, true);
|
||||||
hda_set_codec_wakeup(hda, false);
|
snd_hdac_set_codec_wakeup(bus, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read STATESTS before controller reset */
|
/* Read STATESTS before controller reset */
|
||||||
|
@ -1152,10 +1200,11 @@ static int azx_free(struct azx *chip)
|
||||||
#ifdef CONFIG_SND_HDA_PATCH_LOADER
|
#ifdef CONFIG_SND_HDA_PATCH_LOADER
|
||||||
release_firmware(chip->fw);
|
release_firmware(chip->fw);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
|
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
|
||||||
if (hda->need_i915_power)
|
if (hda->need_i915_power)
|
||||||
hda_display_power(hda, false);
|
snd_hdac_display_power(bus, false);
|
||||||
hda_i915_exit(hda);
|
snd_hdac_i915_exit(bus);
|
||||||
}
|
}
|
||||||
kfree(hda);
|
kfree(hda);
|
||||||
|
|
||||||
|
@ -1914,6 +1963,7 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {
|
||||||
static int azx_probe_continue(struct azx *chip)
|
static int azx_probe_continue(struct azx *chip)
|
||||||
{
|
{
|
||||||
struct hda_intel *hda = container_of(chip, struct hda_intel, 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;
|
struct pci_dev *pci = chip->pci;
|
||||||
int dev = chip->dev_index;
|
int dev = chip->dev_index;
|
||||||
int err;
|
int err;
|
||||||
|
@ -1930,11 +1980,11 @@ static int azx_probe_continue(struct azx *chip)
|
||||||
if (pci->device != 0x0f04 && pci->device != 0x2284)
|
if (pci->device != 0x0f04 && pci->device != 0x2284)
|
||||||
hda->need_i915_power = 1;
|
hda->need_i915_power = 1;
|
||||||
|
|
||||||
err = hda_i915_init(hda);
|
err = snd_hdac_i915_init(bus);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto i915_power_fail;
|
goto i915_power_fail;
|
||||||
|
|
||||||
err = hda_display_power(hda, true);
|
err = snd_hdac_display_power(bus, true);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(chip->card->dev,
|
dev_err(chip->card->dev,
|
||||||
"Cannot turn on display power on i915\n");
|
"Cannot turn on display power on i915\n");
|
||||||
|
@ -1986,7 +2036,7 @@ static int azx_probe_continue(struct azx *chip)
|
||||||
out_free:
|
out_free:
|
||||||
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
|
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
|
||||||
&& !hda->need_i915_power)
|
&& !hda->need_i915_power)
|
||||||
hda_display_power(hda, false);
|
snd_hdac_display_power(bus, false);
|
||||||
|
|
||||||
i915_power_fail:
|
i915_power_fail:
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#ifndef __SOUND_HDA_INTEL_H
|
#ifndef __SOUND_HDA_INTEL_H
|
||||||
#define __SOUND_HDA_INTEL_H
|
#define __SOUND_HDA_INTEL_H
|
||||||
|
|
||||||
#include <drm/i915_component.h>
|
|
||||||
#include "hda_controller.h"
|
#include "hda_controller.h"
|
||||||
|
|
||||||
struct hda_intel {
|
struct hda_intel {
|
||||||
|
@ -44,36 +43,7 @@ struct hda_intel {
|
||||||
/* secondary power domain for hdmi audio under vga device */
|
/* secondary power domain for hdmi audio under vga device */
|
||||||
struct dev_pm_domain hdmi_pm_domain;
|
struct dev_pm_domain hdmi_pm_domain;
|
||||||
|
|
||||||
/* i915 component interface */
|
|
||||||
bool need_i915_power:1; /* the hda controller needs i915 power */
|
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
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue