Merge branch 'fix/intel' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[sfrench/cifs-2.6.git] / sound / soc / intel / skylake / skl.c
index 1d36b28d64895eb682d9549a79c228ad3db4fb2d..092705e73db497fb1d75c1c9b45a3c2aaaa34507 100644 (file)
 #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 "skl.h"
+#include "skl-sst-dsp.h"
+#include "skl-sst-ipc.h"
 
 /*
  * initialize the PCI registers
@@ -58,6 +61,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 +190,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 +203,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 +218,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,6 +240,7 @@ 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;
 
        /*
@@ -198,6 +250,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 +433,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);
                        }
                }
        }
@@ -489,7 +543,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus)
        /* initialize chip */
        skl_init_pci(skl);
 
-       snd_hdac_bus_init_chip(bus, true);
+       skl_init_chip(bus, true);
 
        /* codec detection */
        if (!bus->codec_mask) {
@@ -538,6 +592,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);
@@ -583,6 +639,9 @@ 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 (pci_dev_run_wake(pci))
                pm_runtime_get_noresume(&pci->dev);
        pci_dev_put(pci);