diff options
Diffstat (limited to 'sound/pci/hda/hda_i915.c')
-rw-r--r-- | sound/pci/hda/hda_i915.c | 154 |
1 files changed, 107 insertions, 47 deletions
diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c index d4d0375ac181..714894527e06 100644 --- a/sound/pci/hda/hda_i915.c +++ b/sound/pci/hda/hda_i915.c @@ -18,10 +18,12 @@ #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 <drm/i915_powerwell.h> #include "hda_priv.h" -#include "hda_i915.h" +#include "hda_intel.h" /* Intel HSW/BDW display HDA controller Extended Mode registers. * EM4 (M value) and EM5 (N Value) are used to convert CDClk (Core Display @@ -31,32 +33,33 @@ #define AZX_REG_EM4 0x100c #define AZX_REG_EM5 0x1010 -static int (*get_power)(void); -static int (*put_power)(void); -static int (*get_cdclk)(void); - -int hda_display_power(bool enable) +int hda_display_power(struct hda_intel *hda, bool enable) { - if (!get_power || !put_power) + struct i915_audio_component *acomp = &hda->audio_component; + + if (!acomp->ops) return -ENODEV; - pr_debug("HDA display power %s \n", - enable ? "Enable" : "Disable"); + dev_dbg(&hda->chip.pci->dev, "display power %s\n", + enable ? "enable" : "disable"); if (enable) - return get_power(); + acomp->ops->get_power(acomp->dev); else - return put_power(); + acomp->ops->put_power(acomp->dev); + + return 0; } -void haswell_set_bclk(struct azx *chip) +void haswell_set_bclk(struct hda_intel *hda) { int cdclk_freq; unsigned int bclk_m, bclk_n; + struct i915_audio_component *acomp = &hda->audio_component; - if (!get_cdclk) + if (!acomp->ops) return; - cdclk_freq = get_cdclk(); + cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev); switch (cdclk_freq) { case 337500: bclk_m = 16; @@ -80,51 +83,108 @@ void haswell_set_bclk(struct azx *chip) break; } - azx_writew(chip, EM4, bclk_m); - azx_writew(chip, EM5, bclk_n); + azx_writew(&hda->chip, EM4, bclk_m); + azx_writew(&hda->chip, EM5, bclk_n); } - -int hda_i915_init(void) +static int hda_component_master_bind(struct device *dev) { - int err = 0; - - get_power = symbol_request(i915_request_power_well); - if (!get_power) { - pr_warn("hda-i915: get_power symbol get fail\n"); - return -ENODEV; + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct hda_intel *hda = container_of(chip, struct hda_intel, chip); + struct i915_audio_component *acomp = &hda->audio_component; + int ret; + + ret = component_bind_all(dev, acomp); + if (ret < 0) + return ret; + + if (WARN_ON(!(acomp->dev && acomp->ops && acomp->ops->get_power && + acomp->ops->put_power && acomp->ops->get_cdclk_freq))) { + ret = -EINVAL; + goto out_unbind; } - put_power = symbol_request(i915_release_power_well); - if (!put_power) { - symbol_put(i915_request_power_well); - get_power = NULL; - return -ENODEV; + /* + * 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; } - get_cdclk = symbol_request(i915_get_cdclk_freq); - if (!get_cdclk) /* may have abnormal BCLK and audio playback rate */ - pr_warn("hda-i915: get_cdclk symbol get fail\n"); + return 0; - pr_debug("HDA driver get symbol successfully from i915 module\n"); +out_unbind: + component_unbind_all(dev, acomp); - return err; + return ret; } -int hda_i915_exit(void) +static void hda_component_master_unbind(struct device *dev) { - if (get_power) { - symbol_put(i915_request_power_well); - get_power = NULL; - } - if (put_power) { - symbol_put(i915_release_power_well); - put_power = NULL; - } - if (get_cdclk) { - symbol_put(i915_get_cdclk_freq); - get_cdclk = NULL; + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct hda_intel *hda = container_of(chip, struct hda_intel, chip); + struct i915_audio_component *acomp = &hda->audio_component; + + module_put(acomp->ops->owner); + component_unbind_all(dev, acomp); + WARN_ON(acomp->ops || acomp->dev); +} + +static const struct component_master_ops hda_component_master_ops = { + .bind = hda_component_master_bind, + .unbind = hda_component_master_unbind, +}; + +static int hda_component_master_match(struct device *dev, void *data) +{ + /* i915 is the only supported component */ + return !strcmp(dev->driver->name, "i915"); +} + +int hda_i915_init(struct hda_intel *hda) +{ + struct component_match *match = NULL; + struct device *dev = &hda->chip.pci->dev; + struct i915_audio_component *acomp = &hda->audio_component; + int ret; + + component_match_add(dev, &match, hda_component_master_match, hda); + ret = component_master_add_with_match(dev, &hda_component_master_ops, + match); + if (ret < 0) + goto out_err; + + /* + * Atm, we don't support deferring the component binding, so make sure + * i915 is loaded and that the binding successfully completes. + */ + request_module("i915"); + + if (!acomp->ops) { + ret = -ENODEV; + goto out_master_del; } + dev_dbg(dev, "bound to i915 component master\n"); + + return 0; +out_master_del: + component_master_del(dev, &hda_component_master_ops); +out_err: + dev_err(dev, "failed to add i915 component master (%d)\n", ret); + + return ret; +} + +int hda_i915_exit(struct hda_intel *hda) +{ + struct device *dev = &hda->chip.pci->dev; + + component_master_del(dev, &hda_component_master_ops); + return 0; } |