Commit 85abf3ec authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'topic/hda' into for-next

parents 49c4a4c5 2bd1f73f
...@@ -74,6 +74,7 @@ struct hdac_device { ...@@ -74,6 +74,7 @@ struct hdac_device {
/* misc flags */ /* misc flags */
atomic_t in_pm; /* suspend/resume being performed */ atomic_t in_pm; /* suspend/resume being performed */
bool link_power_control:1;
/* sysfs */ /* sysfs */
struct hdac_widget_tree *widgets; struct hdac_widget_tree *widgets;
...@@ -184,6 +185,8 @@ struct hdac_bus_ops { ...@@ -184,6 +185,8 @@ struct hdac_bus_ops {
/* get a response from the last command */ /* get a response from the last command */
int (*get_response)(struct hdac_bus *bus, unsigned int addr, int (*get_response)(struct hdac_bus *bus, unsigned int addr,
unsigned int *res); unsigned int *res);
/* control the link power */
int (*link_power)(struct hdac_bus *bus, bool enable);
}; };
/* /*
...@@ -311,6 +314,7 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec) ...@@ -311,6 +314,7 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec)
int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val); 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, int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
unsigned int *res); 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); bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset);
void snd_hdac_bus_stop_chip(struct hdac_bus *bus); void snd_hdac_bus_stop_chip(struct hdac_bus *bus);
......
...@@ -552,6 +552,21 @@ void snd_hdac_power_down_pm(struct hdac_device *codec) ...@@ -552,6 +552,21 @@ void snd_hdac_power_down_pm(struct hdac_device *codec)
EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm); EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
#endif #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 */ /* codec vendor labels */
struct hda_vendor_id { struct hda_vendor_id {
unsigned int id; unsigned int id;
......
...@@ -858,6 +858,7 @@ void snd_hda_codec_register(struct hda_codec *codec) ...@@ -858,6 +858,7 @@ void snd_hda_codec_register(struct hda_codec *codec)
return; return;
if (device_is_registered(hda_codec_dev(codec))) { if (device_is_registered(hda_codec_dev(codec))) {
snd_hda_register_beep_device(codec); snd_hda_register_beep_device(codec);
snd_hdac_link_power(&codec->core, true);
pm_runtime_enable(hda_codec_dev(codec)); pm_runtime_enable(hda_codec_dev(codec));
/* it was powered up in snd_hda_codec_new(), now all done */ /* it was powered up in snd_hda_codec_new(), now all done */
snd_hda_power_down(codec); snd_hda_power_down(codec);
...@@ -884,6 +885,7 @@ static int snd_hda_codec_dev_free(struct snd_device *device) ...@@ -884,6 +885,7 @@ static int snd_hda_codec_dev_free(struct snd_device *device)
struct hda_codec *codec = device->device_data; struct hda_codec *codec = device->device_data;
codec->in_freeing = 1; codec->in_freeing = 1;
snd_hdac_link_power(&codec->core, false);
snd_hdac_device_unregister(&codec->core); snd_hdac_device_unregister(&codec->core);
put_device(hda_codec_dev(codec)); put_device(hda_codec_dev(codec));
return 0; return 0;
...@@ -3106,6 +3108,7 @@ static int hda_codec_runtime_suspend(struct device *dev) ...@@ -3106,6 +3108,7 @@ static int hda_codec_runtime_suspend(struct device *dev)
if (codec_has_clkstop(codec) && codec_has_epss(codec) && if (codec_has_clkstop(codec) && codec_has_epss(codec) &&
(state & AC_PWRST_CLK_STOP_OK)) (state & AC_PWRST_CLK_STOP_OK))
snd_hdac_codec_link_down(&codec->core); snd_hdac_codec_link_down(&codec->core);
snd_hdac_link_power(&codec->core, false);
return 0; return 0;
} }
...@@ -3113,6 +3116,7 @@ static int hda_codec_runtime_resume(struct device *dev) ...@@ -3113,6 +3116,7 @@ static int hda_codec_runtime_resume(struct device *dev)
{ {
struct hda_codec *codec = dev_to_hda_codec(dev); struct hda_codec *codec = dev_to_hda_codec(dev);
snd_hdac_link_power(&codec->core, true);
snd_hdac_codec_link_up(&codec->core); snd_hdac_codec_link_up(&codec->core);
hda_call_codec_resume(codec); hda_call_codec_resume(codec);
pm_runtime_mark_last_busy(dev); pm_runtime_mark_last_busy(dev);
......
...@@ -775,9 +775,20 @@ static int azx_get_response(struct hdac_bus *bus, unsigned int addr, ...@@ -775,9 +775,20 @@ static int azx_get_response(struct hdac_bus *bus, unsigned int addr,
return azx_rirb_get_response(bus, addr, res); 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 = { static const struct hdac_bus_ops bus_core_ops = {
.command = azx_send_cmd, .command = azx_send_cmd,
.get_response = azx_get_response, .get_response = azx_get_response,
.link_power = azx_link_power,
}; };
#ifdef CONFIG_SND_HDA_DSP_LOADER #ifdef CONFIG_SND_HDA_DSP_LOADER
......
...@@ -89,6 +89,8 @@ struct hda_controller_ops { ...@@ -89,6 +89,8 @@ struct hda_controller_ops {
struct vm_area_struct *area); struct vm_area_struct *area);
/* Check if current position is acceptable */ /* Check if current position is acceptable */
int (*position_check)(struct azx *chip, struct azx_dev *azx_dev); 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 { struct azx_pcm {
......
...@@ -42,10 +42,15 @@ int hda_display_power(struct hda_intel *hda, bool enable) ...@@ -42,10 +42,15 @@ int hda_display_power(struct hda_intel *hda, bool enable)
dev_dbg(&hda->chip.pci->dev, "display power %s\n", dev_dbg(&hda->chip.pci->dev, "display power %s\n",
enable ? "enable" : "disable"); enable ? "enable" : "disable");
if (enable)
acomp->ops->get_power(acomp->dev); if (enable) {
else if (!hda->i915_power_refcount++)
acomp->ops->put_power(acomp->dev); 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; return 0;
} }
...@@ -189,6 +194,11 @@ int hda_i915_init(struct hda_intel *hda) ...@@ -189,6 +194,11 @@ int hda_i915_init(struct hda_intel *hda)
int hda_i915_exit(struct hda_intel *hda) int hda_i915_exit(struct hda_intel *hda)
{ {
struct device *dev = &hda->chip.pci->dev; 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); component_master_del(dev, &hda_component_master_ops);
......
...@@ -543,6 +543,14 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev) ...@@ -543,6 +543,14 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
return 0; 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 * Check whether the current DMA position is acceptable for updating
* periods. Returns non-zero if it's OK. * periods. Returns non-zero if it's OK.
...@@ -809,7 +817,8 @@ static int azx_suspend(struct device *dev) ...@@ -809,7 +817,8 @@ static int azx_suspend(struct device *dev)
if (chip->msi) if (chip->msi)
pci_disable_msi(chip->pci); 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); hda_display_power(hda, false);
return 0; return 0;
} }
...@@ -829,7 +838,8 @@ static int azx_resume(struct device *dev) ...@@ -829,7 +838,8 @@ static int azx_resume(struct device *dev)
if (chip->disabled || hda->init_failed) if (chip->disabled || hda->init_failed)
return 0; 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); hda_display_power(hda, true);
haswell_set_bclk(hda); haswell_set_bclk(hda);
} }
...@@ -872,7 +882,8 @@ static int azx_runtime_suspend(struct device *dev) ...@@ -872,7 +882,8 @@ static int azx_runtime_suspend(struct device *dev)
azx_stop_chip(chip); azx_stop_chip(chip);
azx_enter_link_reset(chip); azx_enter_link_reset(chip);
azx_clear_irq_pending(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); hda_display_power(hda, false);
return 0; return 0;
...@@ -897,7 +908,8 @@ static int azx_runtime_resume(struct device *dev) ...@@ -897,7 +908,8 @@ static int azx_runtime_resume(struct device *dev)
if (!azx_has_pm_runtime(chip)) if (!azx_has_pm_runtime(chip))
return 0; 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); hda_display_power(hda, true);
haswell_set_bclk(hda); haswell_set_bclk(hda);
} }
...@@ -1118,7 +1130,8 @@ static int azx_free(struct azx *chip) ...@@ -1118,7 +1130,8 @@ static int azx_free(struct azx *chip)
release_firmware(chip->fw); release_firmware(chip->fw);
#endif #endif
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { 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); hda_i915_exit(hda);
} }
kfree(hda); kfree(hda);
...@@ -1789,6 +1802,7 @@ static const struct hda_controller_ops pci_hda_ops = { ...@@ -1789,6 +1802,7 @@ static const struct hda_controller_ops pci_hda_ops = {
.substream_free_pages = substream_free_pages, .substream_free_pages = substream_free_pages,
.pcm_mmap_prepare = pcm_mmap_prepare, .pcm_mmap_prepare = pcm_mmap_prepare,
.position_check = azx_position_check, .position_check = azx_position_check,
.link_power = azx_intel_link_power,
}; };
static int azx_probe(struct pci_dev *pci, static int azx_probe(struct pci_dev *pci,
...@@ -1882,17 +1896,28 @@ static int azx_probe_continue(struct azx *chip) ...@@ -1882,17 +1896,28 @@ static int azx_probe_continue(struct azx *chip)
int err; int err;
hda->probe_continued = 1; 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) { 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 #ifdef CONFIG_SND_HDA_I915
err = hda_i915_init(hda); err = hda_i915_init(hda);
if (err < 0) if (err < 0)
goto out_free; goto i915_power_fail;
err = hda_display_power(hda, true); err = hda_display_power(hda, true);
if (err < 0) { if (err < 0) {
dev_err(chip->card->dev, dev_err(chip->card->dev,
"Cannot turn on display power on i915\n"); "Cannot turn on display power on i915\n");
goto out_free; goto i915_power_fail;
} }
#endif #endif
} }
...@@ -1939,6 +1964,11 @@ static int azx_probe_continue(struct azx *chip) ...@@ -1939,6 +1964,11 @@ static int azx_probe_continue(struct azx *chip)
pm_runtime_put_noidle(&pci->dev); pm_runtime_put_noidle(&pci->dev);
out_free: 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) if (err < 0)
hda->init_failed = 1; hda->init_failed = 1;
complete_all(&hda->probe_wait); complete_all(&hda->probe_wait);
......
...@@ -45,7 +45,9 @@ struct hda_intel { ...@@ -45,7 +45,9 @@ struct hda_intel {
struct dev_pm_domain hdmi_pm_domain; struct dev_pm_domain hdmi_pm_domain;
/* i915 component interface */ /* i915 component interface */
bool need_i915_power:1; /* the hda controller needs i915 power */
struct i915_audio_component audio_component; struct i915_audio_component audio_component;
int i915_power_refcount;
}; };
#ifdef CONFIG_SND_HDA_I915 #ifdef CONFIG_SND_HDA_I915
......
...@@ -2335,6 +2335,15 @@ static int patch_generic_hdmi(struct hda_codec *codec) ...@@ -2335,6 +2335,15 @@ static int patch_generic_hdmi(struct hda_codec *codec)
intel_haswell_fixup_enable_dp12(codec); 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)) if (is_haswell_plus(codec) || is_valleyview_plus(codec))
codec->depop_delay = 0; codec->depop_delay = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment