summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/i915/Kconfig1
-rw-r--r--include/drm/drm_audio_component.h23
-rw-r--r--include/sound/hda_component.h61
-rw-r--r--include/sound/hda_i915.h39
-rw-r--r--sound/hda/Kconfig7
-rw-r--r--sound/hda/Makefile1
-rw-r--r--sound/hda/hdac_component.c335
-rw-r--r--sound/hda/hdac_i915.c343
-rw-r--r--sound/pci/hda/patch_hdmi.c50
-rw-r--r--sound/soc/codecs/hdac_hdmi.c8
10 files changed, 486 insertions, 382 deletions
diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig
index dfd95889f4b7..5c607f2c707b 100644
--- a/drivers/gpu/drm/i915/Kconfig
+++ b/drivers/gpu/drm/i915/Kconfig
@@ -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,
diff --git a/include/drm/drm_audio_component.h b/include/drm/drm_audio_component.h
index e85689f212c2..4923b00328c1 100644
--- a/include/drm/drm_audio_component.h
+++ b/include/drm/drm_audio_component.h
@@ -4,6 +4,8 @@
#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
*/
@@ -72,6 +74,27 @@ struct drm_audio_component_audio_ops {
* 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 *);
};
/**
diff --git a/include/sound/hda_component.h b/include/sound/hda_component.h
new file mode 100644
index 000000000000..78626cde7081
--- /dev/null
+++ b/include/sound/hda_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 */
diff --git a/include/sound/hda_i915.h b/include/sound/hda_i915.h
index 648263791559..6b79614a893b 100644
--- a/include/sound/hda_i915.h
+++ b/include/sound/hda_i915.h
@@ -5,56 +5,23 @@
#ifndef __SOUND_HDA_I915_H
#define __SOUND_HDA_I915_H
-#include <drm/drm_audio_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(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 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(struct hdac_bus *bus,
- const struct drm_audio_component_audio_ops *ops)
-{
- return -ENODEV;
-}
-#endif
#endif /* __SOUND_HDA_I915_H */
diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig
index 3129546398d0..2d90e11b3eaa 100644
--- a/sound/hda/Kconfig
+++ b/sound/hda/Kconfig
@@ -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
diff --git a/sound/hda/Makefile b/sound/hda/Makefile
index e4e726f2ce98..2160202e2dc1 100644
--- a/sound/hda/Makefile
+++ b/sound/hda/Makefile
@@ -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
diff --git a/sound/hda/hdac_component.c b/sound/hda/hdac_component.c
new file mode 100644
index 000000000000..6e46a9c73aed
--- /dev/null
+++ b/sound/hda/hdac_component.c
@@ -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);
diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c
index 861b77bbc7df..8f2aa8bc1185 100644
--- a/sound/hda/hdac_i915.c
+++ b/sound/hda/hdac_i915.c
@@ -15,96 +15,11 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/component.h>
-#include <drm/drm_audio_component.h>
#include <sound/core.h>
#include <sound/hdaudio.h>
#include <sound/hda_i915.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 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 drm_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 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++) {
- 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)
- 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) || \
@@ -165,183 +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)
-{
- 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)
+static int i915_component_master_match(struct device *dev, void *data)
{
- 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 = 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 drm_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 drm_audio_component *acomp = hdac_get_acomp(dev);
- 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 drm_audio_component *acomp = hdac_get_acomp(dev);
-
- 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
- * @bus: HDA core bus
- * @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(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_i915_register_notifier);
-
/* check whether intel graphics is present */
static bool i915_gfx_present(void)
{
@@ -368,84 +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 *i915_acomp;
struct drm_audio_component *acomp;
- int ret;
-
- if (WARN_ON(hdac_get_acomp(dev)))
- return -EBUSY;
+ int err;
if (!i915_gfx_present())
return -ENODEV;
- i915_acomp = devres_alloc(hdac_acomp_release, sizeof(*i915_acomp),
- GFP_KERNEL);
- if (!i915_acomp)
- return -ENOMEM;
- acomp = &i915_acomp->base;
- bus->audio_component = acomp;
- devres_add(dev, 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");
-
+ 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 -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:
- bus->audio_component = NULL;
- devres_destroy(dev, hdac_acomp_release, NULL, 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 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_i915_exit);
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index bf174a013f2d..1de5491fb9bf 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -183,7 +183,7 @@ struct hdmi_spec {
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(&codec->bus->core, 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
@@ -2517,8 +2540,9 @@ static void register_i915_notifier(struct hda_codec *codec)
* We need make sure audio_ptr is really setup
*/
wmb();
+ spec->drm_audio_ops.pin2port = intel_pin2port;
spec->drm_audio_ops.pin_eld_notify = intel_pin_eld_notify;
- snd_hdac_i915_register_notifier(&codec->bus->core,
+ snd_hdac_acomp_register_notifier(&codec->bus->core,
&spec->drm_audio_ops);
}
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 2b7c33db4ded..4748a9d5de3b 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -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;
@@ -1584,6 +1589,7 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
}
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(hdev->bus, &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;