/* misc flags */
atomic_t in_pm; /* suspend/resume being performed */
+ bool link_power_control:1;
/* sysfs */
struct hdac_widget_tree *widgets;
/* get a response from the last command */
int (*get_response)(struct hdac_bus *bus, unsigned int addr,
unsigned int *res);
+ /* control the link power */
+ int (*link_power)(struct hdac_bus *bus, bool enable);
};
/*
int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val);
int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
unsigned int *res);
+int snd_hdac_link_power(struct hdac_device *codec, bool enable);
bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset);
void snd_hdac_bus_stop_chip(struct hdac_bus *bus);
EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
#endif
+/*
+ * Enable/disable the link power for a codec.
+ */
+int snd_hdac_link_power(struct hdac_device *codec, bool enable)
+{
+ if (!codec->link_power_control)
+ return 0;
+
+ if (codec->bus->ops->link_power)
+ return codec->bus->ops->link_power(codec->bus, enable);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_link_power);
+
/* codec vendor labels */
struct hda_vendor_id {
unsigned int id;
return;
if (device_is_registered(hda_codec_dev(codec))) {
snd_hda_register_beep_device(codec);
+ snd_hdac_link_power(&codec->core, true);
pm_runtime_enable(hda_codec_dev(codec));
/* it was powered up in snd_hda_codec_new(), now all done */
snd_hda_power_down(codec);
struct hda_codec *codec = device->device_data;
codec->in_freeing = 1;
+ snd_hdac_link_power(&codec->core, false);
snd_hdac_device_unregister(&codec->core);
put_device(hda_codec_dev(codec));
return 0;
if (codec_has_clkstop(codec) && codec_has_epss(codec) &&
(state & AC_PWRST_CLK_STOP_OK))
snd_hdac_codec_link_down(&codec->core);
+ snd_hdac_link_power(&codec->core, false);
return 0;
}
{
struct hda_codec *codec = dev_to_hda_codec(dev);
+ snd_hdac_link_power(&codec->core, true);
snd_hdac_codec_link_up(&codec->core);
hda_call_codec_resume(codec);
pm_runtime_mark_last_busy(dev);
return azx_rirb_get_response(bus, addr, res);
}
+static int azx_link_power(struct hdac_bus *bus, bool enable)
+{
+ struct azx *chip = bus_to_azx(bus);
+
+ if (chip->ops->link_power)
+ return chip->ops->link_power(chip, enable);
+ else
+ return -EINVAL;
+}
+
static const struct hdac_bus_ops bus_core_ops = {
.command = azx_send_cmd,
.get_response = azx_get_response,
+ .link_power = azx_link_power,
};
#ifdef CONFIG_SND_HDA_DSP_LOADER
struct vm_area_struct *area);
/* Check if current position is acceptable */
int (*position_check)(struct azx *chip, struct azx_dev *azx_dev);
+ /* enable/disable the link power */
+ int (*link_power)(struct azx *chip, bool enable);
};
struct azx_pcm {
dev_dbg(&hda->chip.pci->dev, "display power %s\n",
enable ? "enable" : "disable");
- if (enable)
- acomp->ops->get_power(acomp->dev);
- else
- acomp->ops->put_power(acomp->dev);
+
+ if (enable) {
+ if (!hda->i915_power_refcount++)
+ acomp->ops->get_power(acomp->dev);
+ } else {
+ WARN_ON(!hda->i915_power_refcount);
+ if (!--hda->i915_power_refcount)
+ acomp->ops->put_power(acomp->dev);
+ }
return 0;
}
int hda_i915_exit(struct hda_intel *hda)
{
struct device *dev = &hda->chip.pci->dev;
+ struct i915_audio_component *acomp = &hda->audio_component;
+
+ WARN_ON(hda->i915_power_refcount);
+ if (hda->i915_power_refcount > 0 && acomp->ops)
+ acomp->ops->put_power(acomp->dev);
component_master_del(dev, &hda_component_master_ops);
return 0;
}
+/* Enable/disable i915 display power for the link */
+static int azx_intel_link_power(struct azx *chip, bool enable)
+{
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+
+ return hda_display_power(hda, enable);
+}
+
/*
* Check whether the current DMA position is acceptable for updating
* periods. Returns non-zero if it's OK.
if (chip->msi)
pci_disable_msi(chip->pci);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
+ && hda->need_i915_power)
hda_display_power(hda, false);
return 0;
}
if (chip->disabled || hda->init_failed)
return 0;
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
+ && hda->need_i915_power) {
hda_display_power(hda, true);
haswell_set_bclk(hda);
}
azx_stop_chip(chip);
azx_enter_link_reset(chip);
azx_clear_irq_pending(chip);
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
+ && hda->need_i915_power)
hda_display_power(hda, false);
return 0;
if (!azx_has_pm_runtime(chip))
return 0;
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
+ && hda->need_i915_power) {
hda_display_power(hda, true);
haswell_set_bclk(hda);
}
release_firmware(chip->fw);
#endif
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
- hda_display_power(hda, false);
+ if (hda->need_i915_power)
+ hda_display_power(hda, false);
hda_i915_exit(hda);
}
kfree(hda);
.substream_free_pages = substream_free_pages,
.pcm_mmap_prepare = pcm_mmap_prepare,
.position_check = azx_position_check,
+ .link_power = azx_intel_link_power,
};
static int azx_probe(struct pci_dev *pci,
int err;
hda->probe_continued = 1;
- /* Request power well for Haswell HDA controller and codec */
+
+ /* Request display power well for the HDA controller or codec. For
+ * Haswell/Broadwell, both the display HDA controller and codec need
+ * this power. For other platforms, like Baytrail/Braswell, only the
+ * display codec needs the power and it can be released after probe.
+ */
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+ /* Baytral/Braswell controllers don't need this power */
+ if (pci->device != 0x0f04 && pci->device != 0x2284)
+ hda->need_i915_power = 1;
+
+
#ifdef CONFIG_SND_HDA_I915
err = hda_i915_init(hda);
if (err < 0)
- goto out_free;
+ goto i915_power_fail;
+
err = hda_display_power(hda, true);
if (err < 0) {
dev_err(chip->card->dev,
"Cannot turn on display power on i915\n");
- goto out_free;
+ goto i915_power_fail;
}
#endif
}
pm_runtime_put_noidle(&pci->dev);
out_free:
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
+ && !hda->need_i915_power)
+ hda_display_power(hda, false);
+
+i915_power_fail:
if (err < 0)
hda->init_failed = 1;
complete_all(&hda->probe_wait);
struct dev_pm_domain hdmi_pm_domain;
/* i915 component interface */
+ bool need_i915_power:1; /* the hda controller needs i915 power */
struct i915_audio_component audio_component;
+ int i915_power_refcount;
};
#ifdef CONFIG_SND_HDA_I915
intel_haswell_fixup_enable_dp12(codec);
}
+ /* For Valleyview/Cherryview, only the display codec is in the display
+ * power well and can use link_power ops to request/release the power.
+ * For Haswell/Broadwell, the controller is also in the power well and
+ * can cover the codec power request, and so need not set this flag.
+ * For previous platforms, there is no such power well feature.
+ */
+ if (is_valleyview_plus(codec))
+ codec->core.link_power_control = 1;
+
if (is_haswell_plus(codec) || is_valleyview_plus(codec))
codec->depop_delay = 0;