Skip to content

Commit

Permalink
ALSA: hda - Move hda_i915.c from sound/pci/hda to sound/hda
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Mengdong Lin authored and Takashi Iwai committed May 20, 2015
1 parent 4214c53 commit 98d8fc6
Show file tree
Hide file tree
Showing 11 changed files with 313 additions and 280 deletions.
36 changes: 36 additions & 0 deletions include/sound/hda_i915.h
Original file line number Diff line number Diff line change
@@ -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 */
4 changes: 4 additions & 0 deletions include/sound/hda_register.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 5 additions & 0 deletions include/sound/hdaudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions sound/hda/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions sound/hda/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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) += hdac_i915.o

obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
193 changes: 193 additions & 0 deletions sound/hda/hdac_i915.c
Original file line number Diff line number Diff line change
@@ -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);
5 changes: 0 additions & 5 deletions sound/pci/hda/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions sound/pci/hda/Makefile
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading

0 comments on commit 98d8fc6

Please sign in to comment.