diff options
Diffstat (limited to 'sound/soc/intel/skylake/skl.c')
-rw-r--r-- | sound/soc/intel/skylake/skl.c | 155 |
1 files changed, 150 insertions, 5 deletions
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index c38bf99ced10..ab5e25aaeee3 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -25,9 +25,15 @@ #include <linux/pci.h> #include <linux/pm_runtime.h> #include <linux/platform_device.h> +#include <linux/firmware.h> #include <sound/pcm.h> #include "../common/sst-acpi.h" +#include <sound/hda_register.h> +#include <sound/hdaudio.h> +#include <sound/hda_i915.h> #include "skl.h" +#include "skl-sst-dsp.h" +#include "skl-sst-ipc.h" /* * initialize the PCI registers @@ -58,6 +64,49 @@ static void skl_init_pci(struct skl *skl) skl_update_pci_byte(skl->pci, AZX_PCIREG_TCSEL, 0x07, 0); } +static void update_pci_dword(struct pci_dev *pci, + unsigned int reg, u32 mask, u32 val) +{ + u32 data = 0; + + pci_read_config_dword(pci, reg, &data); + data &= ~mask; + data |= (val & mask); + pci_write_config_dword(pci, reg, data); +} + +/* + * skl_enable_miscbdcge - enable/dsiable CGCTL.MISCBDCGE bits + * + * @dev: device pointer + * @enable: enable/disable flag + */ +static void skl_enable_miscbdcge(struct device *dev, bool enable) +{ + struct pci_dev *pci = to_pci_dev(dev); + u32 val; + + val = enable ? AZX_CGCTL_MISCBDCGE_MASK : 0; + + update_pci_dword(pci, AZX_PCIREG_CGCTL, AZX_CGCTL_MISCBDCGE_MASK, val); +} + +/* + * While performing reset, controller may not come back properly causing + * issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do reset + * (init chip) and then again set CGCTL.MISCBDCGE to 1 + */ +static int skl_init_chip(struct hdac_bus *bus, bool full_reset) +{ + int ret; + + skl_enable_miscbdcge(bus->dev, false); + ret = snd_hdac_bus_init_chip(bus, full_reset); + skl_enable_miscbdcge(bus->dev, true); + + return ret; +} + /* called from IRQ */ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) { @@ -144,7 +193,9 @@ static int _skl_suspend(struct hdac_ext_bus *ebus) return ret; snd_hdac_bus_stop_chip(bus); + skl_enable_miscbdcge(bus->dev, false); snd_hdac_bus_enter_link_reset(bus); + skl_enable_miscbdcge(bus->dev, true); return 0; } @@ -155,7 +206,7 @@ static int _skl_resume(struct hdac_ext_bus *ebus) struct hdac_bus *bus = ebus_to_hbus(ebus); skl_init_pci(skl); - snd_hdac_bus_init_chip(bus, true); + skl_init_chip(bus, true); return skl_resume_dsp(skl); } @@ -170,12 +221,15 @@ static int skl_suspend(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = ebus_to_hbus(ebus); /* * Do not suspend if streams which are marked ignore suspend are * running, we need to save the state for these and continue */ if (skl->supend_active) { + snd_hdac_ext_bus_link_power_down_all(ebus); + enable_irq_wake(bus->irq); pci_save_state(pci); pci_disable_device(pci); return 0; @@ -189,8 +243,19 @@ static int skl_resume(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = ebus_to_hbus(ebus); int ret; + /* Turned OFF in HDMI codec driver after codec reconfiguration */ + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + ret = snd_hdac_display_power(bus, true); + if (ret < 0) { + dev_err(bus->dev, + "Cannot turn on display power on i915\n"); + return ret; + } + } + /* * resume only when we are not in suspend active, otherwise need to * restore the device @@ -198,6 +263,8 @@ static int skl_resume(struct device *dev) if (skl->supend_active) { pci_restore_state(pci); ret = pci_enable_device(pci); + snd_hdac_ext_bus_link_power_up_all(ebus); + disable_irq_wake(bus->irq); } else { ret = _skl_resume(ebus); } @@ -379,7 +446,7 @@ static int skl_codec_create(struct hdac_ext_bus *ebus) * back to the sanity state. */ snd_hdac_bus_stop_chip(bus); - snd_hdac_bus_init_chip(bus, true); + skl_init_chip(bus, true); } } } @@ -427,6 +494,27 @@ static int skl_create(struct pci_dev *pci, return 0; } +static int skl_i915_init(struct hdac_bus *bus) +{ + int err; + + /* + * The HDMI codec is in GPU so we need to ensure that it is powered + * up and ready for probe + */ + err = snd_hdac_i915_init(bus); + if (err < 0) + return err; + + err = snd_hdac_display_power(bus, true); + if (err < 0) { + dev_err(bus->dev, "Cannot turn on display power on i915\n"); + return err; + } + + return err; +} + static int skl_first_init(struct hdac_ext_bus *ebus) { struct skl *skl = ebus_to_skl(ebus); @@ -489,7 +577,13 @@ static int skl_first_init(struct hdac_ext_bus *ebus) /* initialize chip */ skl_init_pci(skl); - snd_hdac_bus_init_chip(bus, true); + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + err = skl_i915_init(bus); + if (err < 0) + return err; + } + + skl_init_chip(bus, true); /* codec detection */ if (!bus->codec_mask) { @@ -519,11 +613,15 @@ static int skl_probe(struct pci_dev *pci, if (err < 0) goto out_free; + skl->pci_id = pci->device; + skl->nhlt = skl_nhlt_init(bus->dev); if (skl->nhlt == NULL) goto out_free; + skl_nhlt_update_topology_bin(skl); + pci_set_drvdata(skl->pci, ebus); /* check if dsp is there */ @@ -538,6 +636,8 @@ static int skl_probe(struct pci_dev *pci, dev_dbg(bus->dev, "error failed to register dsp\n"); goto out_mach_free; } + skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge; + } if (ebus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(ebus); @@ -557,9 +657,15 @@ static int skl_probe(struct pci_dev *pci, if (err < 0) goto out_unregister; + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + err = snd_hdac_display_power(bus, false); + if (err < 0) { + dev_err(bus->dev, "Cannot turn off display power on i915\n"); + return err; + } + } + /*configure PM */ - pm_runtime_set_autosuspend_delay(bus->dev, SKL_SUSPEND_DELAY); - pm_runtime_use_autosuspend(bus->dev); pm_runtime_put_noidle(bus->dev); pm_runtime_allow(bus->dev); @@ -580,11 +686,42 @@ out_free: return err; } +static void skl_shutdown(struct pci_dev *pci) +{ + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_stream *s; + struct hdac_ext_stream *stream; + struct skl *skl; + + if (ebus == NULL) + return; + + skl = ebus_to_skl(ebus); + + if (skl->init_failed) + return; + + snd_hdac_ext_stop_streams(ebus); + list_for_each_entry(s, &bus->stream_list, list) { + stream = stream_to_hdac_ext_stream(s); + snd_hdac_ext_stream_decouple(ebus, stream, false); + } + + snd_hdac_bus_stop_chip(bus); +} + static void skl_remove(struct pci_dev *pci) { struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct skl *skl = ebus_to_skl(ebus); + if (skl->tplg) + release_firmware(skl->tplg); + + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_i915_exit(&ebus->bus); + if (pci_dev_run_wake(pci)) pm_runtime_get_noresume(&pci->dev); pci_dev_put(pci); @@ -605,11 +742,18 @@ static struct sst_acpi_mach sst_skl_devdata[] = { {} }; +static struct sst_acpi_mach sst_bxtp_devdata[] = { + { "INT343A", "bxt_alc298s_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL }, +}; + /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ { PCI_DEVICE(0x8086, 0x9d70), .driver_data = (unsigned long)&sst_skl_devdata}, + /* BXT-P */ + { PCI_DEVICE(0x8086, 0x5a98), + .driver_data = (unsigned long)&sst_bxtp_devdata}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); @@ -620,6 +764,7 @@ static struct pci_driver skl_driver = { .id_table = skl_ids, .probe = skl_probe, .remove = skl_remove, + .shutdown = skl_shutdown, .driver = { .pm = &skl_pm, }, |