diff options
Diffstat (limited to 'sound/soc/sof')
-rw-r--r-- | sound/soc/sof/core.c | 88 | ||||
-rw-r--r-- | sound/soc/sof/intel/Kconfig | 20 | ||||
-rw-r--r-- | sound/soc/sof/intel/bdw.c | 7 | ||||
-rw-r--r-- | sound/soc/sof/intel/byt.c | 13 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda-codec.c | 19 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda-ctrl.c | 12 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda-dai.c | 4 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda-loader.c | 2 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda-stream.c | 45 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda.c | 32 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda.h | 7 | ||||
-rw-r--r-- | sound/soc/sof/ipc.c | 22 | ||||
-rw-r--r-- | sound/soc/sof/loader.c | 21 | ||||
-rw-r--r-- | sound/soc/sof/nocodec.c | 7 | ||||
-rw-r--r-- | sound/soc/sof/pm.c | 35 | ||||
-rw-r--r-- | sound/soc/sof/sof-priv.h | 11 | ||||
-rw-r--r-- | sound/soc/sof/topology.c | 19 | ||||
-rw-r--r-- | sound/soc/sof/trace.c | 7 |
18 files changed, 277 insertions, 94 deletions
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 5beda47cdf9f..74cd1989157b 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -288,6 +288,46 @@ static int sof_machine_check(struct snd_sof_dev *sdev) #endif } +/* + * FW Boot State Transition Diagram + * + * +-----------------------------------------------------------------------+ + * | | + * ------------------ ------------------ | + * | | | | | + * | BOOT_FAILED | | READY_FAILED |-------------------------+ | + * | | | | | | + * ------------------ ------------------ | | + * ^ ^ | | + * | | | | + * (FW Boot Timeout) (FW_READY FAIL) | | + * | | | | + * | | | | + * ------------------ | ------------------ | | + * | | | | | | | + * | IN_PROGRESS |---------------+------------->| COMPLETE | | | + * | | (FW Boot OK) (FW_READY OK) | | | | + * ------------------ ------------------ | | + * ^ | | | + * | | | | + * (FW Loading OK) (System Suspend/Runtime Suspend) + * | | | | + * | | | | + * ------------------ ------------------ | | | + * | | | |<-----+ | | + * | PREPARE | | NOT_STARTED |<---------------------+ | + * | | | |<---------------------------+ + * ------------------ ------------------ + * | ^ | ^ + * | | | | + * | +-----------------------+ | + * | (DSP Probe OK) | + * | | + * | | + * +------------------------------------+ + * (System Suspend/Runtime Suspend) + */ + static int sof_probe_continue(struct snd_sof_dev *sdev) { struct snd_sof_pdata *plat_data = sdev->pdata; @@ -303,6 +343,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) return ret; } + sdev->fw_state = SOF_FW_BOOT_PREPARE; + /* check machine info */ ret = sof_machine_check(sdev); if (ret < 0) { @@ -330,6 +372,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) /* init the IPC */ sdev->ipc = snd_sof_ipc_init(sdev); if (!sdev->ipc) { + ret = -ENOMEM; dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret); goto ipc_err; } @@ -342,7 +385,12 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto fw_load_err; } - /* boot the firmware */ + sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS; + + /* + * Boot the firmware. The FW boot status will be modified + * in snd_sof_run_firmware() depending on the outcome. + */ ret = snd_sof_run_firmware(sdev); if (ret < 0) { dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n", @@ -368,7 +416,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "error: failed to register DSP DAI driver %d\n", ret); - goto fw_run_err; + goto fw_trace_err; } drv_name = plat_data->machine->drv_name; @@ -382,7 +430,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) if (IS_ERR(plat_data->pdev_mach)) { ret = PTR_ERR(plat_data->pdev_mach); - goto fw_run_err; + goto fw_trace_err; } dev_dbg(sdev->dev, "created machine %s\n", @@ -393,7 +441,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) return 0; -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE) +fw_trace_err: + snd_sof_free_trace(sdev); fw_run_err: snd_sof_fw_unload(sdev); fw_load_err: @@ -402,21 +451,10 @@ ipc_err: snd_sof_free_debug(sdev); dbg_err: snd_sof_remove(sdev); -#else - - /* - * when the probe_continue is handled in a work queue, the - * probe does not fail so we don't release resources here. - * They will be released with an explicit call to - * snd_sof_device_remove() when the PCI/ACPI device is removed - */ -fw_run_err: -fw_load_err: -ipc_err: -dbg_err: - -#endif + /* all resources freed, update state to match */ + sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; + sdev->first_boot = true; return ret; } @@ -447,6 +485,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sdev->pdata = plat_data; sdev->first_boot = true; + sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; dev_set_drvdata(dev, sdev); /* check all mandatory ops */ @@ -494,10 +533,12 @@ int snd_sof_device_remove(struct device *dev) if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) cancel_work_sync(&sdev->probe_work); - snd_sof_fw_unload(sdev); - snd_sof_ipc_free(sdev); - snd_sof_free_debug(sdev); - snd_sof_free_trace(sdev); + if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { + snd_sof_fw_unload(sdev); + snd_sof_ipc_free(sdev); + snd_sof_free_debug(sdev); + snd_sof_free_trace(sdev); + } /* * Unregister machine driver. This will unbind the snd_card which @@ -513,7 +554,8 @@ int snd_sof_device_remove(struct device *dev) * scheduled on, when they are unloaded. Therefore, the DSP must be * removed only after the topology has been unloaded. */ - snd_sof_remove(sdev); + if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) + snd_sof_remove(sdev); /* release firmware */ release_firmware(pdata->fw); diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index b86b5f9783fd..3045a3a37889 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -72,10 +72,18 @@ config SND_SOC_SOF_BAYTRAIL config SND_SOC_SOF_BROADWELL_SUPPORT bool "SOF support for Broadwell" + depends on SND_SOC_INTEL_HASWELL=n help This adds support for Sound Open Firmware for Intel(R) platforms using the Broadwell processors. - Say Y if you have such a device. + This option is mutually exclusive with the Haswell/Broadwell legacy + driver. If you want to enable SOF on Broadwell you need to deselect + the legacy driver first. + SOF does fully support Broadwell yet, so this option is not + recommended for distros. At some point all legacy drivers will be + deprecated but not before all userspace firmware/topology/UCM files + are made available to downstream distros. + Say Y if you want to enable SOF on Broadwell If unsure select "N". config SND_SOC_SOF_BROADWELL @@ -209,6 +217,16 @@ config SND_SOC_SOF_HDA_AUDIO_CODEC Say Y if you want to enable HDAudio codecs with SOF. If unsure select "N". +config SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1 + bool "SOF enable DMI Link L1" + help + This option enables DMI L1 for both playback and capture + and disables known workarounds for specific HDaudio platforms. + Only use to look into power optimizations on platforms not + affected by DMI L1 issues. This option is not recommended. + Say Y if you want to enable DMI Link L1 + If unsure, select "N". + endif ## SND_SOC_SOF_HDA_COMMON config SND_SOC_SOF_HDA_LINK_BASELINE diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 70d524ef9bc0..0ca3c1b55eeb 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -37,6 +37,7 @@ #define MBOX_SIZE 0x1000 #define MBOX_DUMP_SIZE 0x30 #define EXCEPT_OFFSET 0x800 +#define EXCEPT_MAX_HDR_SIZE 0x400 /* DSP peripherals */ #define DMAC0_OFFSET 0xFE000 @@ -228,6 +229,11 @@ static void bdw_get_registers(struct snd_sof_dev *sdev, /* note: variable AR register array is not read */ /* then get panic info */ + if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) { + dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n", + xoops->arch_hdr.totalsize); + return; + } offset += xoops->arch_hdr.totalsize; sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info)); @@ -588,6 +594,7 @@ static int bdw_probe(struct snd_sof_dev *sdev) /* TODO: add offsets */ sdev->mmio_bar = BDW_DSP_BAR; sdev->mailbox_bar = BDW_DSP_BAR; + sdev->dsp_oops_offset = MBOX_OFFSET; /* PCI base */ mmio = platform_get_resource(pdev, IORESOURCE_MEM, diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 39d1ae01c45d..7c09a619f238 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -24,10 +24,12 @@ #define DRAM_OFFSET 0x100000 #define DRAM_SIZE (160 * 1024) #define SHIM_OFFSET 0x140000 -#define SHIM_SIZE 0x100 +#define SHIM_SIZE_BYT 0x100 +#define SHIM_SIZE_CHT 0x118 #define MBOX_OFFSET 0x144000 #define MBOX_SIZE 0x1000 #define EXCEPT_OFFSET 0x800 +#define EXCEPT_MAX_HDR_SIZE 0x400 /* DSP peripherals */ #define DMAC0_OFFSET 0x098000 @@ -74,7 +76,7 @@ static const struct snd_sof_debugfs_map byt_debugfs[] = { SOF_DEBUGFS_ACCESS_D0_ONLY}, {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE, SOF_DEBUGFS_ACCESS_D0_ONLY}, - {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE, + {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_BYT, SOF_DEBUGFS_ACCESS_ALWAYS}, }; @@ -101,7 +103,7 @@ static const struct snd_sof_debugfs_map cht_debugfs[] = { SOF_DEBUGFS_ACCESS_D0_ONLY}, {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE, SOF_DEBUGFS_ACCESS_D0_ONLY}, - {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE, + {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_CHT, SOF_DEBUGFS_ACCESS_ALWAYS}, }; @@ -273,6 +275,11 @@ static void byt_get_registers(struct snd_sof_dev *sdev, /* note: variable AR register array is not read */ /* then get panic info */ + if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) { + dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n", + xoops->arch_hdr.totalsize); + return; + } offset += xoops->arch_hdr.totalsize; sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info)); diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index 0d8437b080bf..2aa98b0de002 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -23,19 +23,18 @@ #define IDISP_VID_INTEL 0x80860000 /* load the legacy HDA codec driver */ -#ifdef MODULE -static void hda_codec_load_module(struct hda_codec *codec) +static int hda_codec_load_module(struct hda_codec *codec) { +#ifdef MODULE char alias[MODULE_NAME_LEN]; const char *module = alias; snd_hdac_codec_modalias(&codec->core, alias, sizeof(alias)); dev_dbg(&codec->core.dev, "loading codec module: %s\n", module); request_module(module); -} -#else -static void hda_codec_load_module(struct hda_codec *codec) {} #endif + return device_attach(hda_codec_dev(codec)); +} #endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */ @@ -76,10 +75,16 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address) /* use legacy bus only for HDA codecs, idisp uses ext bus */ if ((resp & 0xFFFF0000) != IDISP_VID_INTEL) { hdev->type = HDA_DEV_LEGACY; - hda_codec_load_module(&hda_priv->codec); + ret = hda_codec_load_module(&hda_priv->codec); + /* + * handle ret==0 (no driver bound) as an error, but pass + * other return codes without modification + */ + if (ret == 0) + ret = -ENOENT; } - return 0; + return ret; #else hdev = devm_kzalloc(sdev->dev, sizeof(*hdev), GFP_KERNEL); if (!hdev) diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index 07bc123112c9..5946c7e289d1 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -139,20 +139,16 @@ void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable) */ int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable) { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - struct hdac_bus *bus = sof_to_bus(sdev); -#endif u32 val; /* enable/disable audio dsp clock gating */ val = enable ? PCI_CGCTL_ADSPDCGE : 0; snd_sof_pci_update_bits(sdev, PCI_CGCTL, PCI_CGCTL_ADSPDCGE, val); -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - /* enable/disable L1 support */ - val = enable ? SOF_HDA_VS_EM2_L1SEN : 0; - snd_hdac_chip_updatel(bus, VS_EM2, SOF_HDA_VS_EM2_L1SEN, val); -#endif + /* enable/disable DMI Link L1 support */ + val = enable ? HDA_VS_INTEL_EM2_L1SEN : 0; + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2, + HDA_VS_INTEL_EM2_L1SEN, val); /* enable/disable audio dsp power gating */ val = enable ? 0 : PCI_PGCTL_ADSPPGD; diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index e1decf25aeac..2c748c1b361f 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -341,6 +341,10 @@ struct snd_soc_dai_driver skl_dai[] = { .ops = &hda_link_dai_ops, }, { + .name = "iDisp4 Pin", + .ops = &hda_link_dai_ops, +}, +{ .name = "Analog CPU DAI", .ops = &hda_link_dai_ops, }, diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 6427f0b3a2f1..356bb134ae93 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -44,6 +44,7 @@ static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, return -ENODEV; } hstream = &dsp_stream->hstream; + hstream->substream = NULL; /* allocate DMA buffer */ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, &pci->dev, size, dmab); @@ -277,7 +278,6 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) /* init for booting wait */ init_waitqueue_head(&sdev->boot_wait); - sdev->boot_complete = false; /* prepare DMA for code loader stream */ tag = cl_stream_prepare(sdev, 0x40, stripped_firmware.size, diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index c92006f89499..6b879702e202 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -177,6 +177,17 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction) direction == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture"); + /* + * Disable DMI Link L1 entry when capture stream is opened. + * Workaround to address a known issue with host DMA that results + * in xruns during pause/release in capture scenarios. + */ + if (!IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1)) + if (stream && direction == SNDRV_PCM_STREAM_CAPTURE) + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + HDA_VS_INTEL_EM2, + HDA_VS_INTEL_EM2_L1SEN, 0); + return stream; } @@ -185,23 +196,43 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) { struct hdac_bus *bus = sof_to_bus(sdev); struct hdac_stream *s; + bool active_capture_stream = false; + bool found = false; spin_lock_irq(&bus->reg_lock); - /* find used stream */ + /* + * close stream matching the stream tag + * and check if there are any open capture streams. + */ list_for_each_entry(s, &bus->stream_list, list) { - if (s->direction == direction && - s->opened && s->stream_tag == stream_tag) { + if (!s->opened) + continue; + + if (s->direction == direction && s->stream_tag == stream_tag) { s->opened = false; - spin_unlock_irq(&bus->reg_lock); - return 0; + found = true; + } else if (s->direction == SNDRV_PCM_STREAM_CAPTURE) { + active_capture_stream = true; } } spin_unlock_irq(&bus->reg_lock); - dev_dbg(sdev->dev, "stream_tag %d not opened!\n", stream_tag); - return -ENODEV; + /* Enable DMI L1 entry if there are no capture streams open */ + if (!IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1)) + if (!active_capture_stream) + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + HDA_VS_INTEL_EM2, + HDA_VS_INTEL_EM2_L1SEN, + HDA_VS_INTEL_EM2_L1SEN); + + if (!found) { + dev_dbg(sdev->dev, "stream_tag %d not opened!\n", stream_tag); + return -ENODEV; + } + + return 0; } int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index faf1a8ada091..35c9055ea439 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -32,6 +32,8 @@ /* platform specific devices */ #include "shim.h" +#define EXCEPT_MAX_HDR_SIZE 0x400 + /* * Debug */ @@ -116,6 +118,11 @@ static void hda_dsp_get_registers(struct snd_sof_dev *sdev, /* note: variable AR register array is not read */ /* then get panic info */ + if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) { + dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n", + xoops->arch_hdr.totalsize); + return; + } offset += xoops->arch_hdr.totalsize; sof_block_read(sdev, sdev->mmio_bar, offset, panic_info, sizeof(*panic_info)); @@ -144,7 +151,7 @@ void hda_dsp_dump_skl(struct snd_sof_dev *sdev, u32 flags) panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_ADSP_ERROR_CODE_SKL + 0x4); - if (sdev->boot_complete) { + if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) { hda_dsp_get_registers(sdev, &xoops, &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE); snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, @@ -171,7 +178,7 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) HDA_DSP_SRAM_REG_FW_STATUS); panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_TRACEP); - if (sdev->boot_complete) { + if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) { hda_dsp_get_registers(sdev, &xoops, &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE); snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, @@ -219,6 +226,7 @@ static int hda_init(struct snd_sof_dev *sdev) sof_hda_bus_init(bus, &pci->dev, ext_ops); bus->use_posbuf = 1; bus->bdl_pos_adj = 0; + bus->sync_write = 1; mutex_init(&hbus->prepare_mutex); hbus->pci = pci; @@ -293,10 +301,23 @@ static int hda_init_caps(struct snd_sof_dev *sdev) if (bus->ppcap) dev_dbg(sdev->dev, "PP capability, will probe DSP later.\n"); +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + /* init i915 and HDMI codecs */ + ret = hda_codec_i915_init(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: init i915 and HDMI codec failed\n"); + return ret; + } +#endif + + /* Init HDA controller after i915 init */ ret = hda_dsp_ctrl_init_chip(sdev, true); if (ret < 0) { dev_err(bus->dev, "error: init chip failed with ret: %d\n", ret); +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + hda_codec_i915_exit(sdev); +#endif return ret; } @@ -304,13 +325,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev) if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(bus); - /* init i915 and HDMI codecs */ - ret = hda_codec_i915_init(sdev); - if (ret < 0) { - dev_err(sdev->dev, "error: no HDMI audio devices found\n"); - return ret; - } - /* codec detection */ if (!bus->codec_mask) { dev_info(bus->dev, "no hda codecs found!\n"); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 92d45c43b4b1..54475ac93b4d 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -39,7 +39,6 @@ #define SOF_HDA_WAKESTS 0x0E #define SOF_HDA_WAKESTS_INT_MASK ((1 << 8) - 1) #define SOF_HDA_RIRBSTS 0x5d -#define SOF_HDA_VS_EM2_L1SEN BIT(13) /* SOF_HDA_GCTL register bist */ #define SOF_HDA_GCTL_RESET BIT(0) @@ -221,6 +220,10 @@ #define HDA_DSP_REG_HIPCIE (HDA_DSP_IPC_BASE + 0x0C) #define HDA_DSP_REG_HIPCCTL (HDA_DSP_IPC_BASE + 0x10) +/* Intel Vendor Specific Registers */ +#define HDA_VS_INTEL_EM2 0x1030 +#define HDA_VS_INTEL_EM2_L1SEN BIT(13) + /* HIPCI */ #define HDA_DSP_REG_HIPCI_BUSY BIT(31) #define HDA_DSP_REG_HIPCI_MSG_MASK 0x7FFFFFFF @@ -326,7 +329,7 @@ /* Number of DAIs */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) -#define SOF_SKL_NUM_DAIS 14 +#define SOF_SKL_NUM_DAIS 15 #else #define SOF_SKL_NUM_DAIS 8 #endif diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 666f7fe98693..b3bff871863b 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -344,19 +344,12 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) break; case SOF_IPC_FW_READY: /* check for FW boot completion */ - if (!sdev->boot_complete) { + if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) { err = sof_ops(sdev)->fw_ready(sdev, cmd); - if (err < 0) { - /* - * this indicates a mismatch in ABI - * between the driver and fw - */ - dev_err(sdev->dev, "error: ABI mismatch %d\n", - err); - } else { - /* firmware boot completed OK */ - sdev->boot_complete = true; - } + if (err < 0) + sdev->fw_state = SOF_FW_BOOT_READY_FAILED; + else + sdev->fw_state = SOF_FW_BOOT_COMPLETE; /* wake up firmware loader */ wake_up(&sdev->boot_wait); @@ -500,7 +493,7 @@ int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev, /* send IPC to the DSP */ err = sof_ipc_tx_message(sdev->ipc, - stream.hdr.cmd, &stream, sizeof(stream), &posn, + stream.hdr.cmd, &stream, sizeof(stream), posn, sizeof(*posn)); if (err < 0) { dev_err(sdev->dev, "error: failed to get stream %d position\n", @@ -830,6 +823,9 @@ void snd_sof_ipc_free(struct snd_sof_dev *sdev) { struct snd_sof_ipc *ipc = sdev->ipc; + if (!ipc) + return; + /* disable sending of ipc's */ mutex_lock(&ipc->tx_mutex); ipc->disable_ipc_tx = true; diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 628fae552442..4cd6e2934922 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -68,6 +68,8 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) ret = get_ext_windows(sdev, ext_hdr); break; default: + dev_warn(sdev->dev, "warning: unknown ext header type %d size 0x%x\n", + ext_hdr->type, ext_hdr->hdr.size); break; } @@ -335,7 +337,6 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) int init_core_mask; init_waitqueue_head(&sdev->boot_wait); - sdev->boot_complete = false; /* create fw_version debugfs to store boot version info */ if (sdev->first_boot) { @@ -367,19 +368,27 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) init_core_mask = ret; - /* now wait for the DSP to boot */ - ret = wait_event_timeout(sdev->boot_wait, sdev->boot_complete, + /* + * now wait for the DSP to boot. There are 3 possible outcomes: + * 1. Boot wait times out indicating FW boot failure. + * 2. FW boots successfully and fw_ready op succeeds. + * 3. FW boots but fw_ready op fails. + */ + ret = wait_event_timeout(sdev->boot_wait, + sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS, msecs_to_jiffies(sdev->boot_timeout)); if (ret == 0) { dev_err(sdev->dev, "error: firmware boot failure\n"); - /* after this point FW_READY msg should be ignored */ - sdev->boot_complete = true; snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX | SOF_DBG_TEXT | SOF_DBG_PCI); + sdev->fw_state = SOF_FW_BOOT_FAILED; return -EIO; } - dev_info(sdev->dev, "firmware boot complete\n"); + if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) + dev_info(sdev->dev, "firmware boot complete\n"); + else + return -EIO; /* FW boots but fw_ready op failed */ /* perform post fw run operations */ ret = snd_sof_dsp_post_fw_run(sdev); diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c index f84b4344dcc3..906cd6bdd54f 100644 --- a/sound/soc/sof/nocodec.c +++ b/sound/soc/sof/nocodec.c @@ -14,6 +14,7 @@ static struct snd_soc_card sof_nocodec_card = { .name = "nocodec", /* the sof- prefix is added by the core */ + .owner = THIS_MODULE }; static int sof_nocodec_bes_setup(struct device *dev, @@ -39,8 +40,10 @@ static int sof_nocodec_bes_setup(struct device *dev, links[i].platform_name = dev_name(dev); links[i].codec_dai_name = "snd-soc-dummy-dai"; links[i].codec_name = "snd-soc-dummy"; - links[i].dpcm_playback = 1; - links[i].dpcm_capture = 1; + if (ops->drv[i].playback.channels_min) + links[i].dpcm_playback = 1; + if (ops->drv[i].capture.channels_min) + links[i].dpcm_capture = 1; } card->dai_link = links; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 8ef1d51025d8..62a98ca18da9 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -257,7 +257,14 @@ static int sof_resume(struct device *dev, bool runtime_resume) int ret; /* do nothing if dsp resume callbacks are not set */ - if (!sof_ops(sdev)->resume || !sof_ops(sdev)->runtime_resume) + if (!runtime_resume && !sof_ops(sdev)->resume) + return 0; + + if (runtime_resume && !sof_ops(sdev)->runtime_resume) + return 0; + + /* DSP was never successfully started, nothing to resume */ + if (sdev->first_boot) return 0; /* @@ -274,6 +281,8 @@ static int sof_resume(struct device *dev, bool runtime_resume) return ret; } + sdev->fw_state = SOF_FW_BOOT_PREPARE; + /* load the firmware */ ret = snd_sof_load_firmware(sdev); if (ret < 0) { @@ -283,7 +292,12 @@ static int sof_resume(struct device *dev, bool runtime_resume) return ret; } - /* boot the firmware */ + sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS; + + /* + * Boot the firmware. The FW boot status will be modified + * in snd_sof_run_firmware() depending on the outcome. + */ ret = snd_sof_run_firmware(sdev); if (ret < 0) { dev_err(sdev->dev, @@ -326,9 +340,15 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) int ret; /* do nothing if dsp suspend callback is not set */ - if (!sof_ops(sdev)->suspend) + if (!runtime_suspend && !sof_ops(sdev)->suspend) return 0; + if (runtime_suspend && !sof_ops(sdev)->runtime_suspend) + return 0; + + if (sdev->fw_state != SOF_FW_BOOT_COMPLETE) + goto power_down; + /* release trace */ snd_sof_release_trace(sdev); @@ -350,6 +370,12 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) return ret; } +power_down: + + /* return if the DSP was not probed successfully */ + if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED) + return 0; + /* power down all DSP cores */ if (runtime_suspend) ret = snd_sof_dsp_runtime_suspend(sdev, 0); @@ -360,6 +386,9 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) "error: failed to power down DSP during suspend %d\n", ret); + /* reset FW state */ + sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; + return ret; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 1e85d6f9c5c3..05fde6f3cbc3 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -337,6 +337,15 @@ struct snd_sof_dai { struct list_head list; /* list in sdev dai list */ }; +enum snd_sof_fw_state { + SOF_FW_BOOT_NOT_STARTED = 0, + SOF_FW_BOOT_PREPARE, + SOF_FW_BOOT_IN_PROGRESS, + SOF_FW_BOOT_FAILED, + SOF_FW_BOOT_READY_FAILED, /* firmware booted but fw_ready op failed */ + SOF_FW_BOOT_COMPLETE, +}; + /* * SOF Device Level. */ @@ -353,7 +362,7 @@ struct snd_sof_dev { /* DSP firmware boot */ wait_queue_head_t boot_wait; - u32 boot_complete; + enum snd_sof_fw_state fw_state; u32 first_boot; /* work queue in case the probe is implemented in two steps */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index c88afa872a58..5fbb25f9ccb5 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -531,15 +531,16 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value; int max_size = sbe->max; - if (le32_to_cpu(control->priv.size) > max_size) { + /* init the get/put bytes data */ + scontrol->size = sizeof(struct sof_ipc_ctrl_data) + + le32_to_cpu(control->priv.size); + + if (scontrol->size > max_size) { dev_err(sdev->dev, "err: bytes data size %d exceeds max %d.\n", - control->priv.size, max_size); + scontrol->size, max_size); return -EINVAL; } - /* init the get/put bytes data */ - scontrol->size = sizeof(struct sof_ipc_ctrl_data) + - le32_to_cpu(control->priv.size); scontrol->control_data = kzalloc(max_size, GFP_KERNEL); cdata = scontrol->control_data; if (!scontrol->control_data) @@ -901,7 +902,9 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp, for (j = 0; j < count; j++) { /* match token type */ if (!(tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD || - tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT)) + tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT || + tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_BYTE || + tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_BOOL)) continue; /* match token id */ @@ -2717,6 +2720,10 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, if (!link->no_pcm) { link->nonatomic = true; + /* set trigger order */ + link->trigger[0] = SND_SOC_DPCM_TRIGGER_POST; + link->trigger[1] = SND_SOC_DPCM_TRIGGER_POST; + /* nothing more to do for FE dai links */ return 0; } diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index d588e4b70fad..39bafe319556 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -291,7 +291,10 @@ void snd_sof_free_trace(struct snd_sof_dev *sdev) { snd_sof_release_trace(sdev); - snd_dma_free_pages(&sdev->dmatb); - snd_dma_free_pages(&sdev->dmatp); + if (sdev->dma_trace_pages) { + snd_dma_free_pages(&sdev->dmatb); + snd_dma_free_pages(&sdev->dmatp); + sdev->dma_trace_pages = 0; + } } EXPORT_SYMBOL(snd_sof_free_trace); |