Merge branch 'topic/drm_audio_component' into for-next
Pull the generic drm_audio_component support, which will be used later for AMD/ATI and other HD-audio HDMI codec drivers. Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
commit
7abeb64da6
|
@ -23,6 +23,7 @@ config DRM_I915
|
|||
select SYNC_FILE
|
||||
select IOSF_MBI
|
||||
select CRC32
|
||||
select SND_HDA_I915 if SND_HDA_CORE
|
||||
help
|
||||
Choose this option if you have a system that has "Intel Graphics
|
||||
Media Accelerator" or "HD Graphics" integrated graphics,
|
||||
|
|
|
@ -639,11 +639,12 @@ void intel_audio_codec_enable(struct intel_encoder *encoder,
|
|||
dev_priv->av_enc_map[pipe] = encoder;
|
||||
mutex_unlock(&dev_priv->av_mutex);
|
||||
|
||||
if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) {
|
||||
if (acomp && acomp->base.audio_ops &&
|
||||
acomp->base.audio_ops->pin_eld_notify) {
|
||||
/* audio drivers expect pipe = -1 to indicate Non-MST cases */
|
||||
if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST))
|
||||
pipe = -1;
|
||||
acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr,
|
||||
acomp->base.audio_ops->pin_eld_notify(acomp->base.audio_ops->audio_ptr,
|
||||
(int) port, (int) pipe);
|
||||
}
|
||||
|
||||
|
@ -681,11 +682,12 @@ void intel_audio_codec_disable(struct intel_encoder *encoder,
|
|||
dev_priv->av_enc_map[pipe] = NULL;
|
||||
mutex_unlock(&dev_priv->av_mutex);
|
||||
|
||||
if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) {
|
||||
if (acomp && acomp->base.audio_ops &&
|
||||
acomp->base.audio_ops->pin_eld_notify) {
|
||||
/* audio drivers expect pipe = -1 to indicate Non-MST cases */
|
||||
if (!intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DP_MST))
|
||||
pipe = -1;
|
||||
acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr,
|
||||
acomp->base.audio_ops->pin_eld_notify(acomp->base.audio_ops->audio_ptr,
|
||||
(int) port, (int) pipe);
|
||||
}
|
||||
|
||||
|
@ -880,7 +882,7 @@ static int i915_audio_component_get_eld(struct device *kdev, int port,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const struct i915_audio_component_ops i915_audio_component_ops = {
|
||||
static const struct drm_audio_component_ops i915_audio_component_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.get_power = i915_audio_component_get_power,
|
||||
.put_power = i915_audio_component_put_power,
|
||||
|
@ -897,12 +899,12 @@ static int i915_audio_component_bind(struct device *i915_kdev,
|
|||
struct drm_i915_private *dev_priv = kdev_to_i915(i915_kdev);
|
||||
int i;
|
||||
|
||||
if (WARN_ON(acomp->ops || acomp->dev))
|
||||
if (WARN_ON(acomp->base.ops || acomp->base.dev))
|
||||
return -EEXIST;
|
||||
|
||||
drm_modeset_lock_all(&dev_priv->drm);
|
||||
acomp->ops = &i915_audio_component_ops;
|
||||
acomp->dev = i915_kdev;
|
||||
acomp->base.ops = &i915_audio_component_ops;
|
||||
acomp->base.dev = i915_kdev;
|
||||
BUILD_BUG_ON(MAX_PORTS != I915_MAX_PORTS);
|
||||
for (i = 0; i < ARRAY_SIZE(acomp->aud_sample_rate); i++)
|
||||
acomp->aud_sample_rate[i] = 0;
|
||||
|
@ -919,8 +921,8 @@ static void i915_audio_component_unbind(struct device *i915_kdev,
|
|||
struct drm_i915_private *dev_priv = kdev_to_i915(i915_kdev);
|
||||
|
||||
drm_modeset_lock_all(&dev_priv->drm);
|
||||
acomp->ops = NULL;
|
||||
acomp->dev = NULL;
|
||||
acomp->base.ops = NULL;
|
||||
acomp->base.dev = NULL;
|
||||
dev_priv->audio_component = NULL;
|
||||
drm_modeset_unlock_all(&dev_priv->drm);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Copyright © 2014 Intel Corporation
|
||||
|
||||
#ifndef _DRM_AUDIO_COMPONENT_H_
|
||||
#define _DRM_AUDIO_COMPONENT_H_
|
||||
|
||||
struct drm_audio_component;
|
||||
|
||||
/**
|
||||
* struct drm_audio_component_ops - Ops implemented by DRM driver, called by hda driver
|
||||
*/
|
||||
struct drm_audio_component_ops {
|
||||
/**
|
||||
* @owner: drm module to pin down
|
||||
*/
|
||||
struct module *owner;
|
||||
/**
|
||||
* @get_power: get the POWER_DOMAIN_AUDIO power well
|
||||
*
|
||||
* Request the power well to be turned on.
|
||||
*/
|
||||
void (*get_power)(struct device *);
|
||||
/**
|
||||
* @put_power: put the POWER_DOMAIN_AUDIO power well
|
||||
*
|
||||
* Allow the power well to be turned off.
|
||||
*/
|
||||
void (*put_power)(struct device *);
|
||||
/**
|
||||
* @codec_wake_override: Enable/disable codec wake signal
|
||||
*/
|
||||
void (*codec_wake_override)(struct device *, bool enable);
|
||||
/**
|
||||
* @get_cdclk_freq: Get the Core Display Clock in kHz
|
||||
*/
|
||||
int (*get_cdclk_freq)(struct device *);
|
||||
/**
|
||||
* @sync_audio_rate: set n/cts based on the sample rate
|
||||
*
|
||||
* Called from audio driver. After audio driver sets the
|
||||
* sample rate, it will call this function to set n/cts
|
||||
*/
|
||||
int (*sync_audio_rate)(struct device *, int port, int pipe, int rate);
|
||||
/**
|
||||
* @get_eld: fill the audio state and ELD bytes for the given port
|
||||
*
|
||||
* Called from audio driver to get the HDMI/DP audio state of the given
|
||||
* digital port, and also fetch ELD bytes to the given pointer.
|
||||
*
|
||||
* It returns the byte size of the original ELD (not the actually
|
||||
* copied size), zero for an invalid ELD, or a negative error code.
|
||||
*
|
||||
* Note that the returned size may be over @max_bytes. Then it
|
||||
* implies that only a part of ELD has been copied to the buffer.
|
||||
*/
|
||||
int (*get_eld)(struct device *, int port, int pipe, bool *enabled,
|
||||
unsigned char *buf, int max_bytes);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_audio_component_audio_ops - Ops implemented by hda driver, called by DRM driver
|
||||
*/
|
||||
struct drm_audio_component_audio_ops {
|
||||
/**
|
||||
* @audio_ptr: Pointer to be used in call to pin_eld_notify
|
||||
*/
|
||||
void *audio_ptr;
|
||||
/**
|
||||
* @pin_eld_notify: Notify the HDA driver that pin sense and/or ELD information has changed
|
||||
*
|
||||
* Called when the DRM driver has set up audio pipeline or has just
|
||||
* begun to tear it down. This allows the HDA driver to update its
|
||||
* status accordingly (even when the HDA controller is in power save
|
||||
* mode).
|
||||
*/
|
||||
void (*pin_eld_notify)(void *audio_ptr, int port, int pipe);
|
||||
/**
|
||||
* @pin2port: Check and convert from pin node to port number
|
||||
*
|
||||
* Called by HDA driver to check and convert from the pin widget node
|
||||
* number to a port number in the graphics side.
|
||||
*/
|
||||
int (*pin2port)(void *audio_ptr, int pin);
|
||||
/**
|
||||
* @master_bind: (Optional) component master bind callback
|
||||
*
|
||||
* Called at binding master component, for HDA codec-specific
|
||||
* handling of dynamic binding.
|
||||
*/
|
||||
int (*master_bind)(struct device *dev, struct drm_audio_component *);
|
||||
/**
|
||||
* @master_unbind: (Optional) component master unbind callback
|
||||
*
|
||||
* Called at unbinding master component, for HDA codec-specific
|
||||
* handling of dynamic unbinding.
|
||||
*/
|
||||
void (*master_unbind)(struct device *dev, struct drm_audio_component *);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_audio_component - Used for direct communication between DRM and hda drivers
|
||||
*/
|
||||
struct drm_audio_component {
|
||||
/**
|
||||
* @dev: DRM device, used as parameter for ops
|
||||
*/
|
||||
struct device *dev;
|
||||
/**
|
||||
* @ops: Ops implemented by DRM driver, called by hda driver
|
||||
*/
|
||||
const struct drm_audio_component_ops *ops;
|
||||
/**
|
||||
* @audio_ops: Ops implemented by hda driver, called by DRM driver
|
||||
*/
|
||||
const struct drm_audio_component_audio_ops *audio_ops;
|
||||
};
|
||||
|
||||
#endif /* _DRM_AUDIO_COMPONENT_H_ */
|
|
@ -24,101 +24,26 @@
|
|||
#ifndef _I915_COMPONENT_H_
|
||||
#define _I915_COMPONENT_H_
|
||||
|
||||
#include "drm_audio_component.h"
|
||||
|
||||
/* MAX_PORT is the number of port
|
||||
* It must be sync with I915_MAX_PORTS defined i915_drv.h
|
||||
*/
|
||||
#define MAX_PORTS 6
|
||||
|
||||
/**
|
||||
* struct i915_audio_component_ops - Ops implemented by i915 driver, called by hda driver
|
||||
*/
|
||||
struct i915_audio_component_ops {
|
||||
/**
|
||||
* @owner: i915 module
|
||||
*/
|
||||
struct module *owner;
|
||||
/**
|
||||
* @get_power: get the POWER_DOMAIN_AUDIO power well
|
||||
*
|
||||
* Request the power well to be turned on.
|
||||
*/
|
||||
void (*get_power)(struct device *);
|
||||
/**
|
||||
* @put_power: put the POWER_DOMAIN_AUDIO power well
|
||||
*
|
||||
* Allow the power well to be turned off.
|
||||
*/
|
||||
void (*put_power)(struct device *);
|
||||
/**
|
||||
* @codec_wake_override: Enable/disable codec wake signal
|
||||
*/
|
||||
void (*codec_wake_override)(struct device *, bool enable);
|
||||
/**
|
||||
* @get_cdclk_freq: Get the Core Display Clock in kHz
|
||||
*/
|
||||
int (*get_cdclk_freq)(struct device *);
|
||||
/**
|
||||
* @sync_audio_rate: set n/cts based on the sample rate
|
||||
*
|
||||
* Called from audio driver. After audio driver sets the
|
||||
* sample rate, it will call this function to set n/cts
|
||||
*/
|
||||
int (*sync_audio_rate)(struct device *, int port, int pipe, int rate);
|
||||
/**
|
||||
* @get_eld: fill the audio state and ELD bytes for the given port
|
||||
*
|
||||
* Called from audio driver to get the HDMI/DP audio state of the given
|
||||
* digital port, and also fetch ELD bytes to the given pointer.
|
||||
*
|
||||
* It returns the byte size of the original ELD (not the actually
|
||||
* copied size), zero for an invalid ELD, or a negative error code.
|
||||
*
|
||||
* Note that the returned size may be over @max_bytes. Then it
|
||||
* implies that only a part of ELD has been copied to the buffer.
|
||||
*/
|
||||
int (*get_eld)(struct device *, int port, int pipe, bool *enabled,
|
||||
unsigned char *buf, int max_bytes);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct i915_audio_component_audio_ops - Ops implemented by hda driver, called by i915 driver
|
||||
*/
|
||||
struct i915_audio_component_audio_ops {
|
||||
/**
|
||||
* @audio_ptr: Pointer to be used in call to pin_eld_notify
|
||||
*/
|
||||
void *audio_ptr;
|
||||
/**
|
||||
* @pin_eld_notify: Notify the HDA driver that pin sense and/or ELD information has changed
|
||||
*
|
||||
* Called when the i915 driver has set up audio pipeline or has just
|
||||
* begun to tear it down. This allows the HDA driver to update its
|
||||
* status accordingly (even when the HDA controller is in power save
|
||||
* mode).
|
||||
*/
|
||||
void (*pin_eld_notify)(void *audio_ptr, int port, int pipe);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct i915_audio_component - Used for direct communication between i915 and hda drivers
|
||||
*/
|
||||
struct i915_audio_component {
|
||||
/**
|
||||
* @dev: i915 device, used as parameter for ops
|
||||
* @base: the drm_audio_component base class
|
||||
*/
|
||||
struct device *dev;
|
||||
struct drm_audio_component base;
|
||||
|
||||
/**
|
||||
* @aud_sample_rate: the array of audio sample rate per port
|
||||
*/
|
||||
int aud_sample_rate[MAX_PORTS];
|
||||
/**
|
||||
* @ops: Ops implemented by i915 driver, called by hda driver
|
||||
*/
|
||||
const struct i915_audio_component_ops *ops;
|
||||
/**
|
||||
* @audio_ops: Ops implemented by hda driver, called by i915 driver
|
||||
*/
|
||||
const struct i915_audio_component_audio_ops *audio_ops;
|
||||
};
|
||||
|
||||
#endif /* _I915_COMPONENT_H_ */
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// HD-Audio helpers to sync with DRM driver
|
||||
|
||||
#ifndef __SOUND_HDA_COMPONENT_H
|
||||
#define __SOUND_HDA_COMPONENT_H
|
||||
|
||||
#include <drm/drm_audio_component.h>
|
||||
|
||||
#ifdef CONFIG_SND_HDA_COMPONENT
|
||||
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_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid,
|
||||
int dev_id, int rate);
|
||||
int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id,
|
||||
bool *audio_enabled, char *buffer, int max_bytes);
|
||||
int snd_hdac_acomp_init(struct hdac_bus *bus,
|
||||
const struct drm_audio_component_audio_ops *aops,
|
||||
int (*match_master)(struct device *, void *),
|
||||
size_t extra_size);
|
||||
int snd_hdac_acomp_exit(struct hdac_bus *bus);
|
||||
int snd_hdac_acomp_register_notifier(struct hdac_bus *bus,
|
||||
const struct drm_audio_component_audio_ops *ops);
|
||||
#else
|
||||
static inline 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_sync_audio_rate(struct hdac_device *codec,
|
||||
hda_nid_t nid, int dev_id, int rate)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
|
||||
int dev_id, bool *audio_enabled,
|
||||
char *buffer, int max_bytes)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int snd_hdac_acomp_init(struct hdac_bus *bus,
|
||||
const struct drm_audio_component_audio_ops *aops,
|
||||
int (*match_master)(struct device *, void *),
|
||||
size_t extra_size)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int snd_hdac_acomp_exit(struct hdac_bus *bus)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int snd_hdac_acomp_register_notifier(struct hdac_bus *bus,
|
||||
const struct drm_audio_component_audio_ops *ops)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SOUND_HDA_COMPONENT_H */
|
|
@ -5,54 +5,23 @@
|
|||
#ifndef __SOUND_HDA_I915_H
|
||||
#define __SOUND_HDA_I915_H
|
||||
|
||||
#include <drm/i915_component.h>
|
||||
#include "hda_component.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);
|
||||
void snd_hdac_i915_set_bclk(struct hdac_bus *bus);
|
||||
int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid,
|
||||
int dev_id, int rate);
|
||||
int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id,
|
||||
bool *audio_enabled, char *buffer, int max_bytes);
|
||||
int snd_hdac_i915_init(struct hdac_bus *bus);
|
||||
int snd_hdac_i915_exit(struct hdac_bus *bus);
|
||||
int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *);
|
||||
#else
|
||||
static inline 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 void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
|
||||
{
|
||||
}
|
||||
static inline int snd_hdac_sync_audio_rate(struct hdac_device *codec,
|
||||
hda_nid_t nid, int dev_id, int rate)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
|
||||
int dev_id, bool *audio_enabled,
|
||||
char *buffer, int max_bytes)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int snd_hdac_i915_init(struct hdac_bus *bus)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
static inline int snd_hdac_i915_exit(struct hdac_bus *bus)
|
||||
{
|
||||
return 0;
|
||||
return snd_hdac_acomp_exit(bus);
|
||||
}
|
||||
static inline int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *ops)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SOUND_HDA_I915_H */
|
||||
|
|
|
@ -360,9 +360,9 @@ struct hdac_bus {
|
|||
spinlock_t reg_lock;
|
||||
struct mutex cmd_mutex;
|
||||
|
||||
/* i915 component interface */
|
||||
struct i915_audio_component *audio_component;
|
||||
int i915_power_refcount;
|
||||
/* DRM component interface */
|
||||
struct drm_audio_component *audio_component;
|
||||
int drm_power_refcount;
|
||||
|
||||
/* parameters required for enhanced capabilities */
|
||||
int num_streams;
|
||||
|
|
|
@ -5,11 +5,12 @@ config SND_HDA_CORE
|
|||
config SND_HDA_DSP_LOADER
|
||||
bool
|
||||
|
||||
config SND_HDA_COMPONENT
|
||||
bool
|
||||
|
||||
config SND_HDA_I915
|
||||
bool
|
||||
default y
|
||||
depends on DRM_I915
|
||||
depends on SND_HDA_CORE
|
||||
select SND_HDA_COMPONENT
|
||||
|
||||
config SND_HDA_EXT_CORE
|
||||
tristate
|
||||
|
|
|
@ -6,6 +6,7 @@ snd-hda-core-objs += trace.o
|
|||
CFLAGS_trace.o := -I$(src)
|
||||
|
||||
# for sync with i915 gfx driver
|
||||
snd-hda-core-$(CONFIG_SND_HDA_COMPONENT) += hdac_component.o
|
||||
snd-hda-core-$(CONFIG_SND_HDA_I915) += hdac_i915.o
|
||||
|
||||
obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
|
||||
|
|
|
@ -0,0 +1,335 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// hdac_component.c - routines for sync between HD-A core and DRM driver
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/component.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/hdaudio.h>
|
||||
#include <sound/hda_component.h>
|
||||
#include <sound/hda_register.h>
|
||||
|
||||
static void hdac_acomp_release(struct device *dev, void *res)
|
||||
{
|
||||
}
|
||||
|
||||
static struct drm_audio_component *hdac_get_acomp(struct device *dev)
|
||||
{
|
||||
return devres_find(dev, hdac_acomp_release, NULL, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup
|
||||
* @bus: HDA core bus
|
||||
* @enable: enable or disable the wakeup
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with graphics driver.
|
||||
*
|
||||
* This function should be called during the chip reset, also called at
|
||||
* resume for updating STATESTS register read.
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
|
||||
{
|
||||
struct drm_audio_component *acomp = bus->audio_component;
|
||||
|
||||
if (!acomp || !acomp->ops)
|
||||
return -ENODEV;
|
||||
|
||||
if (!acomp->ops->codec_wake_override)
|
||||
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);
|
||||
|
||||
/**
|
||||
* snd_hdac_display_power - Power up / down the power refcount
|
||||
* @bus: HDA core bus
|
||||
* @enable: power up or down
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with graphics driver.
|
||||
*
|
||||
* This function manages a refcount and calls the get_power() and
|
||||
* put_power() ops accordingly, toggling the codec wakeup, too.
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
|
||||
{
|
||||
struct drm_audio_component *acomp = bus->audio_component;
|
||||
|
||||
if (!acomp || !acomp->ops)
|
||||
return -ENODEV;
|
||||
|
||||
dev_dbg(bus->dev, "display power %s\n",
|
||||
enable ? "enable" : "disable");
|
||||
|
||||
if (enable) {
|
||||
if (!bus->drm_power_refcount++) {
|
||||
if (acomp->ops->get_power)
|
||||
acomp->ops->get_power(acomp->dev);
|
||||
snd_hdac_set_codec_wakeup(bus, true);
|
||||
snd_hdac_set_codec_wakeup(bus, false);
|
||||
}
|
||||
} else {
|
||||
WARN_ON(!bus->drm_power_refcount);
|
||||
if (!--bus->drm_power_refcount)
|
||||
if (acomp->ops->put_power)
|
||||
acomp->ops->put_power(acomp->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_display_power);
|
||||
|
||||
/**
|
||||
* snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
|
||||
* @codec: HDA codec
|
||||
* @nid: the pin widget NID
|
||||
* @dev_id: device identifier
|
||||
* @rate: the sample rate to set
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with graphics driver.
|
||||
*
|
||||
* This function sets N/CTS value based on the given sample rate.
|
||||
* Returns zero for success, or a negative error code.
|
||||
*/
|
||||
int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid,
|
||||
int dev_id, int rate)
|
||||
{
|
||||
struct hdac_bus *bus = codec->bus;
|
||||
struct drm_audio_component *acomp = bus->audio_component;
|
||||
int port, pipe;
|
||||
|
||||
if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
|
||||
return -ENODEV;
|
||||
port = nid;
|
||||
if (acomp->audio_ops && acomp->audio_ops->pin2port) {
|
||||
port = acomp->audio_ops->pin2port(codec, nid);
|
||||
if (port < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
pipe = dev_id;
|
||||
return acomp->ops->sync_audio_rate(acomp->dev, port, pipe, rate);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
|
||||
|
||||
/**
|
||||
* snd_hdac_acomp_get_eld - Get the audio state and ELD via component
|
||||
* @codec: HDA codec
|
||||
* @nid: the pin widget NID
|
||||
* @dev_id: device identifier
|
||||
* @audio_enabled: the pointer to store the current audio state
|
||||
* @buffer: the buffer pointer to store ELD bytes
|
||||
* @max_bytes: the max bytes to be stored on @buffer
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with graphics driver.
|
||||
*
|
||||
* This function queries the current state of the audio on the given
|
||||
* digital port and fetches the ELD bytes onto the given buffer.
|
||||
* It returns the number of bytes for the total ELD data, zero for
|
||||
* invalid ELD, or a negative error code.
|
||||
*
|
||||
* The return size is the total bytes required for the whole ELD bytes,
|
||||
* thus it may be over @max_bytes. If it's over @max_bytes, it implies
|
||||
* that only a part of ELD bytes have been fetched.
|
||||
*/
|
||||
int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id,
|
||||
bool *audio_enabled, char *buffer, int max_bytes)
|
||||
{
|
||||
struct hdac_bus *bus = codec->bus;
|
||||
struct drm_audio_component *acomp = bus->audio_component;
|
||||
int port, pipe;
|
||||
|
||||
if (!acomp || !acomp->ops || !acomp->ops->get_eld)
|
||||
return -ENODEV;
|
||||
|
||||
port = nid;
|
||||
if (acomp->audio_ops && acomp->audio_ops->pin2port) {
|
||||
port = acomp->audio_ops->pin2port(codec, nid);
|
||||
if (port < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
pipe = dev_id;
|
||||
return acomp->ops->get_eld(acomp->dev, port, pipe, audio_enabled,
|
||||
buffer, max_bytes);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);
|
||||
|
||||
static int hdac_component_master_bind(struct device *dev)
|
||||
{
|
||||
struct drm_audio_component *acomp = hdac_get_acomp(dev);
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!acomp))
|
||||
return -EINVAL;
|
||||
|
||||
ret = component_bind_all(dev, acomp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (WARN_ON(!(acomp->dev && acomp->ops))) {
|
||||
ret = -EINVAL;
|
||||
goto out_unbind;
|
||||
}
|
||||
|
||||
/* pin the module to avoid dynamic unbinding, but only if given */
|
||||
if (!try_module_get(acomp->ops->owner)) {
|
||||
ret = -ENODEV;
|
||||
goto out_unbind;
|
||||
}
|
||||
|
||||
if (acomp->audio_ops && acomp->audio_ops->master_bind) {
|
||||
ret = acomp->audio_ops->master_bind(dev, acomp);
|
||||
if (ret < 0)
|
||||
goto module_put;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
module_put:
|
||||
module_put(acomp->ops->owner);
|
||||
out_unbind:
|
||||
component_unbind_all(dev, acomp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hdac_component_master_unbind(struct device *dev)
|
||||
{
|
||||
struct drm_audio_component *acomp = hdac_get_acomp(dev);
|
||||
|
||||
if (acomp->audio_ops && acomp->audio_ops->master_unbind)
|
||||
acomp->audio_ops->master_unbind(dev, 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,
|
||||
};
|
||||
|
||||
/**
|
||||
* snd_hdac_acomp_register_notifier - Register audio component ops
|
||||
* @bus: HDA core bus
|
||||
* @aops: audio component ops
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with graphics driver.
|
||||
*
|
||||
* This function sets the given ops to be called by the graphics driver.
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_acomp_register_notifier(struct hdac_bus *bus,
|
||||
const struct drm_audio_component_audio_ops *aops)
|
||||
{
|
||||
if (!bus->audio_component)
|
||||
return -ENODEV;
|
||||
|
||||
bus->audio_component->audio_ops = aops;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_acomp_register_notifier);
|
||||
|
||||
/**
|
||||
* snd_hdac_acomp_init - Initialize audio component
|
||||
* @bus: HDA core bus
|
||||
* @match_master: match function for finding components
|
||||
* @extra_size: Extra bytes to allocate
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with graphics driver.
|
||||
*
|
||||
* This function initializes and sets up the audio component to communicate
|
||||
* with graphics driver.
|
||||
*
|
||||
* Unlike snd_hdac_i915_init(), this function doesn't synchronize with the
|
||||
* binding with the DRM component. Each caller needs to sync via master_bind
|
||||
* audio_ops.
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_acomp_init(struct hdac_bus *bus,
|
||||
const struct drm_audio_component_audio_ops *aops,
|
||||
int (*match_master)(struct device *, void *),
|
||||
size_t extra_size)
|
||||
{
|
||||
struct component_match *match = NULL;
|
||||
struct device *dev = bus->dev;
|
||||
struct drm_audio_component *acomp;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(hdac_get_acomp(dev)))
|
||||
return -EBUSY;
|
||||
|
||||
acomp = devres_alloc(hdac_acomp_release, sizeof(*acomp) + extra_size,
|
||||
GFP_KERNEL);
|
||||
if (!acomp)
|
||||
return -ENOMEM;
|
||||
acomp->audio_ops = aops;
|
||||
bus->audio_component = acomp;
|
||||
devres_add(dev, acomp);
|
||||
|
||||
component_match_add(dev, &match, match_master, bus);
|
||||
ret = component_master_add_with_match(dev, &hdac_component_master_ops,
|
||||
match);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
bus->audio_component = NULL;
|
||||
devres_destroy(dev, hdac_acomp_release, NULL, NULL);
|
||||
dev_info(dev, "failed to add audio component master (%d)\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_acomp_init);
|
||||
|
||||
/**
|
||||
* snd_hdac_acomp_exit - Finalize audio component
|
||||
* @bus: HDA core bus
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with graphics driver.
|
||||
*
|
||||
* This function releases the audio component that has been used.
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_acomp_exit(struct hdac_bus *bus)
|
||||
{
|
||||
struct device *dev = bus->dev;
|
||||
struct drm_audio_component *acomp = bus->audio_component;
|
||||
|
||||
if (!acomp)
|
||||
return 0;
|
||||
|
||||
WARN_ON(bus->drm_power_refcount);
|
||||
if (bus->drm_power_refcount > 0 && acomp->ops)
|
||||
acomp->ops->put_power(acomp->dev);
|
||||
|
||||
component_master_del(dev, &hdac_component_master_ops);
|
||||
|
||||
bus->audio_component = NULL;
|
||||
devres_destroy(dev, hdac_acomp_release, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_acomp_exit);
|
|
@ -15,89 +15,11 @@
|
|||
#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>
|
||||
#include <sound/hda_register.h>
|
||||
|
||||
static struct i915_audio_component *hdac_acomp;
|
||||
|
||||
/**
|
||||
* snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup
|
||||
* @bus: HDA core bus
|
||||
* @enable: enable or disable the wakeup
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with i915 graphics.
|
||||
*
|
||||
* This function should be called during the chip reset, also called at
|
||||
* resume for updating STATESTS register read.
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
|
||||
{
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
|
||||
if (!acomp || !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);
|
||||
|
||||
/**
|
||||
* snd_hdac_display_power - Power up / down the power refcount
|
||||
* @bus: HDA core bus
|
||||
* @enable: power up or down
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with i915 graphics.
|
||||
*
|
||||
* This function manages a refcount and calls the i915 get_power() and
|
||||
* put_power() ops accordingly, toggling the codec wakeup, too.
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
|
||||
{
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
|
||||
if (!acomp || !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);
|
||||
snd_hdac_set_codec_wakeup(bus, true);
|
||||
snd_hdac_set_codec_wakeup(bus, false);
|
||||
}
|
||||
} 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);
|
||||
|
||||
#define CONTROLLER_IN_GPU(pci) (((pci)->device == 0x0a0c) || \
|
||||
((pci)->device == 0x0c0c) || \
|
||||
((pci)->device == 0x0d0c) || \
|
||||
|
@ -119,7 +41,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_display_power);
|
|||
*/
|
||||
void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
|
||||
{
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
struct drm_audio_component *acomp = bus->audio_component;
|
||||
struct pci_dev *pci = to_pci_dev(bus->dev);
|
||||
int cdclk_freq;
|
||||
unsigned int bclk_m, bclk_n;
|
||||
|
@ -158,181 +80,11 @@ void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk);
|
||||
|
||||
/* There is a fixed mapping between audio pin node and display port.
|
||||
* on SNB, IVY, HSW, BSW, SKL, BXT, KBL:
|
||||
* Pin Widget 5 - PORT B (port = 1 in i915 driver)
|
||||
* Pin Widget 6 - PORT C (port = 2 in i915 driver)
|
||||
* Pin Widget 7 - PORT D (port = 3 in i915 driver)
|
||||
*
|
||||
* on VLV, ILK:
|
||||
* Pin Widget 4 - PORT B (port = 1 in i915 driver)
|
||||
* Pin Widget 5 - PORT C (port = 2 in i915 driver)
|
||||
* Pin Widget 6 - PORT D (port = 3 in i915 driver)
|
||||
*/
|
||||
static int pin2port(struct hdac_device *codec, hda_nid_t pin_nid)
|
||||
static int i915_component_master_match(struct device *dev, void *data)
|
||||
{
|
||||
int base_nid;
|
||||
|
||||
switch (codec->vendor_id) {
|
||||
case 0x80860054: /* ILK */
|
||||
case 0x80862804: /* ILK */
|
||||
case 0x80862882: /* VLV */
|
||||
base_nid = 3;
|
||||
break;
|
||||
default:
|
||||
base_nid = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (WARN_ON(pin_nid <= base_nid || pin_nid > base_nid + 3))
|
||||
return -1;
|
||||
return pin_nid - base_nid;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
|
||||
* @codec: HDA codec
|
||||
* @nid: the pin widget NID
|
||||
* @dev_id: device identifier
|
||||
* @rate: the sample rate to set
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with i915 graphics.
|
||||
*
|
||||
* This function sets N/CTS value based on the given sample rate.
|
||||
* Returns zero for success, or a negative error code.
|
||||
*/
|
||||
int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid,
|
||||
int dev_id, int rate)
|
||||
{
|
||||
struct hdac_bus *bus = codec->bus;
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
int port, pipe;
|
||||
|
||||
if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
|
||||
return -ENODEV;
|
||||
port = pin2port(codec, nid);
|
||||
if (port < 0)
|
||||
return -EINVAL;
|
||||
pipe = dev_id;
|
||||
return acomp->ops->sync_audio_rate(acomp->dev, port, pipe, rate);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
|
||||
|
||||
/**
|
||||
* snd_hdac_acomp_get_eld - Get the audio state and ELD via component
|
||||
* @codec: HDA codec
|
||||
* @nid: the pin widget NID
|
||||
* @dev_id: device identifier
|
||||
* @audio_enabled: the pointer to store the current audio state
|
||||
* @buffer: the buffer pointer to store ELD bytes
|
||||
* @max_bytes: the max bytes to be stored on @buffer
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with i915 graphics.
|
||||
*
|
||||
* This function queries the current state of the audio on the given
|
||||
* digital port and fetches the ELD bytes onto the given buffer.
|
||||
* It returns the number of bytes for the total ELD data, zero for
|
||||
* invalid ELD, or a negative error code.
|
||||
*
|
||||
* The return size is the total bytes required for the whole ELD bytes,
|
||||
* thus it may be over @max_bytes. If it's over @max_bytes, it implies
|
||||
* that only a part of ELD bytes have been fetched.
|
||||
*/
|
||||
int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id,
|
||||
bool *audio_enabled, char *buffer, int max_bytes)
|
||||
{
|
||||
struct hdac_bus *bus = codec->bus;
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
int port, pipe;
|
||||
|
||||
if (!acomp || !acomp->ops || !acomp->ops->get_eld)
|
||||
return -ENODEV;
|
||||
|
||||
port = pin2port(codec, nid);
|
||||
if (port < 0)
|
||||
return -EINVAL;
|
||||
|
||||
pipe = dev_id;
|
||||
return acomp->ops->get_eld(acomp->dev, port, pipe, audio_enabled,
|
||||
buffer, max_bytes);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_i915_register_notifier - Register i915 audio component ops
|
||||
* @aops: i915 audio component ops
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with i915 graphics.
|
||||
*
|
||||
* This function sets the given ops to be called by the i915 graphics driver.
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *aops)
|
||||
{
|
||||
if (!hdac_acomp)
|
||||
return -ENODEV;
|
||||
|
||||
hdac_acomp->audio_ops = aops;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_i915_register_notifier);
|
||||
|
||||
/* check whether intel graphics is present */
|
||||
static bool i915_gfx_present(void)
|
||||
{
|
||||
|
@ -359,83 +111,26 @@ static bool i915_gfx_present(void)
|
|||
*/
|
||||
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;
|
||||
|
||||
if (WARN_ON(hdac_acomp))
|
||||
return -EBUSY;
|
||||
struct drm_audio_component *acomp;
|
||||
int err;
|
||||
|
||||
if (!i915_gfx_present())
|
||||
return -ENODEV;
|
||||
|
||||
acomp = kzalloc(sizeof(*acomp), GFP_KERNEL);
|
||||
err = snd_hdac_acomp_init(bus, NULL,
|
||||
i915_component_master_match,
|
||||
sizeof(struct i915_audio_component) - sizeof(*acomp));
|
||||
if (err < 0)
|
||||
return err;
|
||||
acomp = bus->audio_component;
|
||||
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");
|
||||
|
||||
return -ENODEV;
|
||||
if (!acomp->ops)
|
||||
request_module("i915");
|
||||
if (!acomp->ops) {
|
||||
ret = -ENODEV;
|
||||
goto out_master_del;
|
||||
snd_hdac_acomp_exit(bus);
|
||||
return -ENODEV;
|
||||
}
|
||||
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;
|
||||
hdac_acomp = NULL;
|
||||
dev_info(dev, "failed to add i915 component master (%d)\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_i915_init);
|
||||
|
||||
/**
|
||||
* snd_hdac_i915_exit - Finalize i915 audio component
|
||||
* @bus: HDA core bus
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with i915 graphics.
|
||||
*
|
||||
* This function releases the i915 audio component that has been used.
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_i915_exit(struct hdac_bus *bus)
|
||||
{
|
||||
struct device *dev = bus->dev;
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
|
||||
if (!acomp)
|
||||
return 0;
|
||||
|
||||
WARN_ON(bus->i915_power_refcount);
|
||||
if (bus->i915_power_refcount > 0 && acomp->ops)
|
||||
acomp->ops->put_power(acomp->dev);
|
||||
|
||||
component_master_del(dev, &hdac_component_master_ops);
|
||||
|
||||
kfree(acomp);
|
||||
bus->audio_component = NULL;
|
||||
hdac_acomp = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_i915_exit);
|
||||
|
|
|
@ -177,13 +177,13 @@ struct hdmi_spec {
|
|||
|
||||
/* i915/powerwell (Haswell+/Valleyview+) specific */
|
||||
bool use_acomp_notifier; /* use i915 eld_notify callback for hotplug */
|
||||
struct i915_audio_component_audio_ops i915_audio_ops;
|
||||
struct drm_audio_component_audio_ops drm_audio_ops;
|
||||
|
||||
struct hdac_chmap chmap;
|
||||
hda_nid_t vendor_nid;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SND_HDA_I915
|
||||
#ifdef CONFIG_SND_HDA_COMPONENT
|
||||
static inline bool codec_has_acomp(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
@ -2288,7 +2288,7 @@ static void generic_hdmi_free(struct hda_codec *codec)
|
|||
int pin_idx, pcm_idx;
|
||||
|
||||
if (codec_has_acomp(codec))
|
||||
snd_hdac_i915_register_notifier(NULL);
|
||||
snd_hdac_acomp_register_notifier(&codec->bus->core, NULL);
|
||||
|
||||
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
||||
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
||||
|
@ -2471,6 +2471,38 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg,
|
|||
snd_hda_codec_set_power_to_all(codec, fg, power_state);
|
||||
}
|
||||
|
||||
/* There is a fixed mapping between audio pin node and display port.
|
||||
* on SNB, IVY, HSW, BSW, SKL, BXT, KBL:
|
||||
* Pin Widget 5 - PORT B (port = 1 in i915 driver)
|
||||
* Pin Widget 6 - PORT C (port = 2 in i915 driver)
|
||||
* Pin Widget 7 - PORT D (port = 3 in i915 driver)
|
||||
*
|
||||
* on VLV, ILK:
|
||||
* Pin Widget 4 - PORT B (port = 1 in i915 driver)
|
||||
* Pin Widget 5 - PORT C (port = 2 in i915 driver)
|
||||
* Pin Widget 6 - PORT D (port = 3 in i915 driver)
|
||||
*/
|
||||
static int intel_base_nid(struct hda_codec *codec)
|
||||
{
|
||||
switch (codec->core.vendor_id) {
|
||||
case 0x80860054: /* ILK */
|
||||
case 0x80862804: /* ILK */
|
||||
case 0x80862882: /* VLV */
|
||||
return 4;
|
||||
default:
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
static int intel_pin2port(void *audio_ptr, int pin_nid)
|
||||
{
|
||||
int base_nid = intel_base_nid(audio_ptr);
|
||||
|
||||
if (WARN_ON(pin_nid < base_nid || pin_nid >= base_nid + 3))
|
||||
return -1;
|
||||
return pin_nid - base_nid + 1; /* intel port is 1-based */
|
||||
}
|
||||
|
||||
static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe)
|
||||
{
|
||||
struct hda_codec *codec = audio_ptr;
|
||||
|
@ -2481,16 +2513,7 @@ static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe)
|
|||
if (port < 1 || port > 3)
|
||||
return;
|
||||
|
||||
switch (codec->core.vendor_id) {
|
||||
case 0x80860054: /* ILK */
|
||||
case 0x80862804: /* ILK */
|
||||
case 0x80862882: /* VLV */
|
||||
pin_nid = port + 0x03;
|
||||
break;
|
||||
default:
|
||||
pin_nid = port + 0x04;
|
||||
break;
|
||||
}
|
||||
pin_nid = port + intel_base_nid(codec) - 1; /* intel port is 1-based */
|
||||
|
||||
/* skip notification during system suspend (but not in runtime PM);
|
||||
* the state will be updated at resume
|
||||
|
@ -2511,14 +2534,16 @@ static void register_i915_notifier(struct hda_codec *codec)
|
|||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
spec->use_acomp_notifier = true;
|
||||
spec->i915_audio_ops.audio_ptr = codec;
|
||||
spec->drm_audio_ops.audio_ptr = codec;
|
||||
/* intel_audio_codec_enable() or intel_audio_codec_disable()
|
||||
* will call pin_eld_notify with using audio_ptr pointer
|
||||
* We need make sure audio_ptr is really setup
|
||||
*/
|
||||
wmb();
|
||||
spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
|
||||
snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
|
||||
spec->drm_audio_ops.pin2port = intel_pin2port;
|
||||
spec->drm_audio_ops.pin_eld_notify = intel_pin_eld_notify;
|
||||
snd_hdac_acomp_register_notifier(&codec->bus->core,
|
||||
&spec->drm_audio_ops);
|
||||
}
|
||||
|
||||
/* setup_stream ops override for HSW+ */
|
||||
|
|
|
@ -1530,6 +1530,11 @@ free_widgets:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_pin2port(void *aptr, int pin)
|
||||
{
|
||||
return pin - 4; /* map NID 0x05 -> port #1 */
|
||||
}
|
||||
|
||||
static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
|
||||
{
|
||||
struct hdac_device *hdev = aptr;
|
||||
|
@ -1583,7 +1588,8 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
|
|||
|
||||
}
|
||||
|
||||
static struct i915_audio_component_audio_ops aops = {
|
||||
static struct drm_audio_component_audio_ops aops = {
|
||||
.pin2port = hdac_hdmi_pin2port,
|
||||
.pin_eld_notify = hdac_hdmi_eld_notify_cb,
|
||||
};
|
||||
|
||||
|
@ -1812,7 +1818,7 @@ static int hdmi_codec_probe(struct snd_soc_component *component)
|
|||
return ret;
|
||||
|
||||
aops.audio_ptr = hdev;
|
||||
ret = snd_hdac_i915_register_notifier(&aops);
|
||||
ret = snd_hdac_acomp_register_notifier(hdev->bus, &aops);
|
||||
if (ret < 0) {
|
||||
dev_err(&hdev->dev, "notifier register failed: err: %d\n", ret);
|
||||
return ret;
|
||||
|
|
Loading…
Reference in New Issue