summaryrefslogtreecommitdiff
path: root/sound/soc/intel/skylake/skl.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-01-17 12:05:31 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2016-01-17 12:05:31 -0800
commita016af2e70bfca23f2f5de7d8708157b86ea374d (patch)
treebfe3c0c6ea9d52d4ec6ea021b0626a53c83e7d9f /sound/soc/intel/skylake/skl.c
parente535d74bc50df2357d3253f8f3ca48c66d0d892a (diff)
parentc3b1681375dc6e71d89a3ae00cc3ce9e775a8917 (diff)
Merge tag 'sound-4.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "We've had quite busy weeks in this cycle. Looking at ALSA core, the significant changes are a few fixes wrt timer and sequencer ioctls that have been revealed by fuzzer recently. Other than that, ASoC core got a few updates about DAI link handling, but these are rather straightforward refactoring. In drivers scene, ASoC received quite lots of new drivers in addition to bunch of updates for still ongoing Intel Skylake support and topology API. HD-audio gained a new HDMI/DP hotplug notification via component. FireWire got a pile of code refactoring/updates with SCS.1x driver integration. More highlights are shown below. [ NOTE: this contains also many commits for DRM. This is due to the pull of drm stable branch into sound tree, as the base of i915 audio component work for HD-audio. The highlights below don't contain these DRM changes, as these are supposed to be pulled via drm tree in anyway sooner or later. ] Core: - Handful fixes to harden ALSA timer and sequencer ioctls against races reported by syzkaller fuzzer - Irq description string can be unique to each card; only for HD-audio for now ASoC: - Conversion of the array of DAI links to a list for supporting dynamically adding and removing DAI links - Topology API enhancements to make everything more component based and being able to specify PCM links via topology - Some more fixes for the topology code, though it is still not final and ready for enabling in production; we really need to get to the point where that can be done - A pile of changes for Intel SkyLake drivers which hopefully deliver some useful initial functionality for systems with this chipset, though there is more work still to come - Lots of new features and cleanups for the Renesas drivers - ANC support for WM5110 - New drivers: Imagination Technologies IPs, Atmel class D speaker, Cirrus CS47L24 and WM1831, Dialog DA7128, Realtek RT5659 and RT56156, Rockchip RK3036, TI PC3168A, and AMD ACP - Rename PCM1792a driver to be generic pcm179x HD-Audio: - Use audio component for i915 HDMI/DP hotplug handling - On-demand binding with i915 driver - bdl_pos_adj parameter adjustment for Baytrail controllers - Enable power_save_node for CX20722; this shouldn't lead to regression, hopefully - Kabylake HDMI/DP codec support - Quirks for Lenovo E50-80, Dell Latitude E-series, and other Dell machines - A few code refactoring FireWire: - Lots of code cleanup and refactoring - Integrate the support of SCS.1x devices into snd-oxfw driver; snd-scs1x driver is obsoleted USB-audio: - Fix possible NULL dereference at disconnection - A regression fix for Native Instruments devices Misc: - A few code cleanups of fm801 driver" * tag 'sound-4.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (722 commits) ALSA: timer: Code cleanup ALSA: timer: Harden slave timer list handling ALSA: hda - Add fixup for Dell Latitidue E6540 ALSA: timer: Fix race among timer ioctls ALSA: hda - add codec support for Kabylake display audio codec ALSA: timer: Fix double unlink of active_list ALSA: usb-audio: Fix mixer ctl regression of Native Instrument devices ALSA: hda - fix the headset mic detection problem for a Dell laptop ALSA: hda - Fix white noise on Dell Latitude E5550 ALSA: hda_intel: add card number to irq description ALSA: seq: Fix race at timer setup and close ALSA: seq: Fix missing NULL check at remove_events ioctl ALSA: usb-audio: Avoid calling usb_autopm_put_interface() at disconnect ASoC: hdac_hdmi: remove unused hdac_hdmi_query_pin_connlist ASoC: AMD: Add missing include file ALSA: hda - Fixup inverted internal mic for Lenovo E50-80 ALSA: usb: Add native DSD support for Oppo HA-1 ASoC: Make aux_dev more like a generic component ASoC: bcm2835: cleanup includes by ordering them alphabetically ASoC: AMD: Manage ACP 2.x SRAM banks power ...
Diffstat (limited to 'sound/soc/intel/skylake/skl.c')
-rw-r--r--sound/soc/intel/skylake/skl.c213
1 files changed, 172 insertions, 41 deletions
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index caa69c4598a6..443a15de94b5 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -27,7 +27,10 @@
#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)
{
@@ -130,6 +176,39 @@ static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect)
return 0;
}
+#ifdef CONFIG_PM
+static int _skl_suspend(struct hdac_ext_bus *ebus)
+{
+ struct skl *skl = ebus_to_skl(ebus);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ int ret;
+
+ snd_hdac_ext_bus_link_power_down_all(ebus);
+
+ ret = skl_suspend_dsp(skl);
+ if (ret < 0)
+ 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;
+}
+
+static int _skl_resume(struct hdac_ext_bus *ebus)
+{
+ struct skl *skl = ebus_to_skl(ebus);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+
+ skl_init_pci(skl);
+ skl_init_chip(bus, true);
+
+ return skl_resume_dsp(skl);
+}
+#endif
+
#ifdef CONFIG_PM_SLEEP
/*
* power management
@@ -138,26 +217,46 @@ 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);
- snd_hdac_bus_stop_chip(bus);
- snd_hdac_bus_enter_link_reset(bus);
-
- return 0;
+ /*
+ * 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;
+ } else {
+ return _skl_suspend(ebus);
+ }
}
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);
- struct skl *hda = ebus_to_skl(ebus);
-
- skl_init_pci(hda);
+ int ret;
- snd_hdac_bus_init_chip(bus, 1);
+ /*
+ * resume only when we are not in suspend active, otherwise need to
+ * restore the device
+ */
+ 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);
+ }
- return 0;
+ return ret;
}
#endif /* CONFIG_PM_SLEEP */
@@ -167,24 +266,10 @@ static int skl_runtime_suspend(struct device *dev)
struct pci_dev *pci = to_pci_dev(dev);
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
struct hdac_bus *bus = ebus_to_hbus(ebus);
- struct skl *skl = ebus_to_skl(ebus);
- int ret;
dev_dbg(bus->dev, "in %s\n", __func__);
- /* enable controller wake up event */
- snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK);
-
- snd_hdac_ext_bus_link_power_down_all(ebus);
-
- ret = skl_suspend_dsp(skl);
- if (ret < 0)
- return ret;
-
- snd_hdac_bus_stop_chip(bus);
- snd_hdac_bus_enter_link_reset(bus);
-
- return 0;
+ return _skl_suspend(ebus);
}
static int skl_runtime_resume(struct device *dev)
@@ -192,20 +277,10 @@ static int skl_runtime_resume(struct device *dev)
struct pci_dev *pci = to_pci_dev(dev);
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
struct hdac_bus *bus = ebus_to_hbus(ebus);
- struct skl *skl = ebus_to_skl(ebus);
- int status;
dev_dbg(bus->dev, "in %s\n", __func__);
- /* Read STATESTS before controller reset */
- status = snd_hdac_chip_readw(bus, STATESTS);
-
- skl_init_pci(skl);
- snd_hdac_bus_init_chip(bus, true);
- /* disable controller Wake Up event */
- snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0);
-
- return skl_resume_dsp(skl);
+ return _skl_resume(ebus);
}
#endif /* CONFIG_PM */
@@ -242,6 +317,43 @@ static int skl_free(struct hdac_ext_bus *ebus)
return 0;
}
+static int skl_machine_device_register(struct skl *skl, void *driver_data)
+{
+ struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
+ struct platform_device *pdev;
+ struct sst_acpi_mach *mach = driver_data;
+ int ret;
+
+ mach = sst_acpi_find_machine(mach);
+ if (mach == NULL) {
+ dev_err(bus->dev, "No matching machine driver found\n");
+ return -ENODEV;
+ }
+ skl->fw_name = mach->fw_filename;
+
+ pdev = platform_device_alloc(mach->drv_name, -1);
+ if (pdev == NULL) {
+ dev_err(bus->dev, "platform device alloc failed\n");
+ return -EIO;
+ }
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ dev_err(bus->dev, "failed to add machine device\n");
+ platform_device_put(pdev);
+ return -EIO;
+ }
+ skl->i2s_dev = pdev;
+
+ return 0;
+}
+
+static void skl_machine_device_unregister(struct skl *skl)
+{
+ if (skl->i2s_dev)
+ platform_device_unregister(skl->i2s_dev);
+}
+
static int skl_dmic_device_register(struct skl *skl)
{
struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
@@ -321,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);
}
}
}
@@ -431,12 +543,11 @@ 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) {
- dev_err(bus->dev, "no codecs found!\n");
- return -ENODEV;
+ dev_info(bus->dev, "no hda codecs found!\n");
}
return 0;
@@ -471,11 +582,18 @@ static int skl_probe(struct pci_dev *pci,
/* check if dsp is there */
if (ebus->ppcap) {
+ err = skl_machine_device_register(skl,
+ (void *)pci_id->driver_data);
+ if (err < 0)
+ goto out_free;
+
err = skl_init_dsp(skl);
if (err < 0) {
dev_dbg(bus->dev, "error failed to register dsp\n");
- goto out_free;
+ goto out_mach_free;
}
+ skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge;
+
}
if (ebus->mlcap)
snd_hdac_ext_bus_get_ml_capabilities(ebus);
@@ -509,6 +627,8 @@ out_dmic_free:
skl_dmic_device_unregister(skl);
out_dsp_free:
skl_free_dsp(skl);
+out_mach_free:
+ skl_machine_device_unregister(skl);
out_free:
skl->init_failed = 1;
skl_free(ebus);
@@ -529,15 +649,26 @@ static void skl_remove(struct pci_dev *pci)
pci_dev_put(pci);
skl_platform_unregister(&pci->dev);
skl_free_dsp(skl);
+ skl_machine_device_unregister(skl);
skl_dmic_device_unregister(skl);
skl_free(ebus);
dev_set_drvdata(&pci->dev, NULL);
}
+static struct sst_acpi_mach sst_skl_devdata[] = {
+ { "INT343A", "skl_alc286s_i2s", "intel/dsp_fw_release.bin", NULL, NULL, NULL },
+ { "INT343B", "skl_nau88l25_ssm4567_i2s", "intel/dsp_fw_release.bin",
+ NULL, NULL, NULL },
+ { "MX98357A", "skl_nau88l25_max98357a_i2s", "intel/dsp_fw_release.bin",
+ NULL, NULL, NULL },
+ {}
+};
+
/* PCI IDs */
static const struct pci_device_id skl_ids[] = {
/* Sunrise Point-LP */
- { PCI_DEVICE(0x8086, 0x9d70), 0},
+ { PCI_DEVICE(0x8086, 0x9d70),
+ .driver_data = (unsigned long)&sst_skl_devdata},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, skl_ids);