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,
 
 #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
  */
         * 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 *);
 };
 
 /**
 
--- /dev/null
+// 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 */
 
 #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 */
 
 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
 
 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
 
--- /dev/null
+// 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);
 
 #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) || \
 }
 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)
 {
  */
 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);
 
        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;
        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);
        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;
        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
         * 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);
 }
 
 
        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;
 }
 
 static struct drm_audio_component_audio_ops aops = {
+       .pin2port       = hdac_hdmi_pin2port,
        .pin_eld_notify = hdac_hdmi_eld_notify_cb,
 };
 
                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;