brintos

brintos / linux-shallow public Read only

0
0
Text · 9.8 KiB · 8e3bcf6 Raw
390 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)2//3// This file is provided under a dual BSD/GPLv2 license.  When using or4// redistributing this file, you may do so under either license.5//6// Copyright(c) 2018 Intel Corporation7//8// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>9//10 11#include "ops.h"12#include "sof-priv.h"13#include "sof-audio.h"14 15/*16 * Helper function to determine the target DSP state during17 * system suspend. This function only cares about the device18 * D-states. Platform-specific substates, if any, should be19 * handled by the platform-specific parts.20 */21static u32 snd_sof_dsp_power_target(struct snd_sof_dev *sdev)22{23	u32 target_dsp_state;24 25	switch (sdev->system_suspend_target) {26	case SOF_SUSPEND_S5:27	case SOF_SUSPEND_S4:28		/* DSP should be in D3 if the system is suspending to S3+ */29	case SOF_SUSPEND_S3:30		/* DSP should be in D3 if the system is suspending to S3 */31		target_dsp_state = SOF_DSP_PM_D3;32		break;33	case SOF_SUSPEND_S0IX:34		/*35		 * Currently, the only criterion for retaining the DSP in D036		 * is that there are streams that ignored the suspend trigger.37		 * Additional criteria such Soundwire clock-stop mode and38		 * device suspend latency considerations will be added later.39		 */40		if (snd_sof_stream_suspend_ignored(sdev))41			target_dsp_state = SOF_DSP_PM_D0;42		else43			target_dsp_state = SOF_DSP_PM_D3;44		break;45	default:46		/* This case would be during runtime suspend */47		target_dsp_state = SOF_DSP_PM_D3;48		break;49	}50 51	return target_dsp_state;52}53 54#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)55static void sof_cache_debugfs(struct snd_sof_dev *sdev)56{57	struct snd_sof_dfsentry *dfse;58 59	list_for_each_entry(dfse, &sdev->dfsentry_list, list) {60 61		/* nothing to do if debugfs buffer is not IO mem */62		if (dfse->type == SOF_DFSENTRY_TYPE_BUF)63			continue;64 65		/* cache memory that is only accessible in D0 */66		if (dfse->access_type == SOF_DEBUGFS_ACCESS_D0_ONLY)67			memcpy_fromio(dfse->cache_buf, dfse->io_mem,68				      dfse->size);69	}70}71#endif72 73static int sof_resume(struct device *dev, bool runtime_resume)74{75	struct snd_sof_dev *sdev = dev_get_drvdata(dev);76	const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);77	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);78	u32 old_state = sdev->dsp_power_state.state;79	int ret;80 81	/* do nothing if dsp resume callbacks are not set */82	if (!runtime_resume && !sof_ops(sdev)->resume)83		return 0;84 85	if (runtime_resume && !sof_ops(sdev)->runtime_resume)86		return 0;87 88	/* DSP was never successfully started, nothing to resume */89	if (sdev->first_boot)90		return 0;91 92	/*93	 * if the runtime_resume flag is set, call the runtime_resume routine94	 * or else call the system resume routine95	 */96	if (runtime_resume)97		ret = snd_sof_dsp_runtime_resume(sdev);98	else99		ret = snd_sof_dsp_resume(sdev);100	if (ret < 0) {101		dev_err(sdev->dev,102			"error: failed to power up DSP after resume\n");103		return ret;104	}105 106	if (sdev->dspless_mode_selected) {107		sof_set_fw_state(sdev, SOF_DSPLESS_MODE);108		return 0;109	}110 111	/*112	 * Nothing further to be done for platforms that support the low power113	 * D0 substate. Resume trace and return when resuming from114	 * low-power D0 substate115	 */116	if (!runtime_resume && sof_ops(sdev)->set_power_state &&117	    old_state == SOF_DSP_PM_D0) {118		ret = sof_fw_trace_resume(sdev);119		if (ret < 0)120			/* non fatal */121			dev_warn(sdev->dev,122				 "failed to enable trace after resume %d\n", ret);123		return 0;124	}125 126	sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);127 128	/* load the firmware */129	ret = snd_sof_load_firmware(sdev);130	if (ret < 0) {131		dev_err(sdev->dev,132			"error: failed to load DSP firmware after resume %d\n",133			ret);134		sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);135		return ret;136	}137 138	sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS);139 140	/*141	 * Boot the firmware. The FW boot status will be modified142	 * in snd_sof_run_firmware() depending on the outcome.143	 */144	ret = snd_sof_run_firmware(sdev);145	if (ret < 0) {146		dev_err(sdev->dev,147			"error: failed to boot DSP firmware after resume %d\n",148			ret);149		sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);150		return ret;151	}152 153	/* resume DMA trace */154	ret = sof_fw_trace_resume(sdev);155	if (ret < 0) {156		/* non fatal */157		dev_warn(sdev->dev,158			 "warning: failed to init trace after resume %d\n",159			 ret);160	}161 162	/* restore pipelines */163	if (tplg_ops && tplg_ops->set_up_all_pipelines) {164		ret = tplg_ops->set_up_all_pipelines(sdev, false);165		if (ret < 0) {166			dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret);167			goto setup_fail;168		}169	}170 171	/* Notify clients not managed by pm framework about core resume */172	sof_resume_clients(sdev);173 174	/* notify DSP of system resume */175	if (pm_ops && pm_ops->ctx_restore) {176		ret = pm_ops->ctx_restore(sdev);177		if (ret < 0)178			dev_err(sdev->dev, "ctx_restore IPC error during resume: %d\n", ret);179	}180 181setup_fail:182#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)183	if (ret < 0) {184		/*185		 * Debugfs cannot be read in runtime suspend, so cache186		 * the contents upon failure. This allows to capture187		 * possible DSP coredump information.188		 */189		sof_cache_debugfs(sdev);190	}191#endif192 193	return ret;194}195 196static int sof_suspend(struct device *dev, bool runtime_suspend)197{198	struct snd_sof_dev *sdev = dev_get_drvdata(dev);199	const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);200	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);201	pm_message_t pm_state;202	u32 target_state = snd_sof_dsp_power_target(sdev);203	u32 old_state = sdev->dsp_power_state.state;204	int ret;205 206	/* do nothing if dsp suspend callback is not set */207	if (!runtime_suspend && !sof_ops(sdev)->suspend)208		return 0;209 210	if (runtime_suspend && !sof_ops(sdev)->runtime_suspend)211		return 0;212 213	/* we need to tear down pipelines only if the DSP hardware is214	 * active, which happens for PCI devices. if the device is215	 * suspended, it is brought back to full power and then216	 * suspended again217	 */218	if (tplg_ops && tplg_ops->tear_down_all_pipelines && (old_state == SOF_DSP_PM_D0))219		tplg_ops->tear_down_all_pipelines(sdev, false);220 221	if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)222		goto suspend;223 224	/* prepare for streams to be resumed properly upon resume */225	if (!runtime_suspend) {226		ret = snd_sof_dsp_hw_params_upon_resume(sdev);227		if (ret < 0) {228			dev_err(sdev->dev,229				"error: setting hw_params flag during suspend %d\n",230				ret);231			return ret;232		}233	}234 235	pm_state.event = target_state;236 237	/* suspend DMA trace */238	sof_fw_trace_suspend(sdev, pm_state);239 240	/* Notify clients not managed by pm framework about core suspend */241	sof_suspend_clients(sdev, pm_state);242 243	/* Skip to platform-specific suspend if DSP is entering D0 */244	if (target_state == SOF_DSP_PM_D0)245		goto suspend;246 247#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)248	/* cache debugfs contents during runtime suspend */249	if (runtime_suspend)250		sof_cache_debugfs(sdev);251#endif252	/* notify DSP of upcoming power down */253	if (pm_ops && pm_ops->ctx_save) {254		ret = pm_ops->ctx_save(sdev);255		if (ret == -EBUSY || ret == -EAGAIN) {256			/*257			 * runtime PM has logic to handle -EBUSY/-EAGAIN so258			 * pass these errors up259			 */260			dev_err(sdev->dev, "ctx_save IPC error during suspend: %d\n", ret);261			return ret;262		} else if (ret < 0) {263			/* FW in unexpected state, continue to power down */264			dev_warn(sdev->dev, "ctx_save IPC error: %d, proceeding with suspend\n",265				 ret);266		}267	}268 269suspend:270 271	/* return if the DSP was not probed successfully */272	if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED)273		return 0;274 275	/* platform-specific suspend */276	if (runtime_suspend)277		ret = snd_sof_dsp_runtime_suspend(sdev);278	else279		ret = snd_sof_dsp_suspend(sdev, target_state);280	if (ret < 0)281		dev_err(sdev->dev,282			"error: failed to power down DSP during suspend %d\n",283			ret);284 285	/* Do not reset FW state if DSP is in D0 */286	if (target_state == SOF_DSP_PM_D0)287		return ret;288 289	/* reset FW state */290	sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);291	sdev->enabled_cores_mask = 0;292 293	return ret;294}295 296int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev)297{298	const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);299 300	/* Notify DSP of upcoming power down */301	if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save)302		return pm_ops->ctx_save(sdev);303 304	return 0;305}306 307int snd_sof_runtime_suspend(struct device *dev)308{309	return sof_suspend(dev, true);310}311EXPORT_SYMBOL(snd_sof_runtime_suspend);312 313int snd_sof_runtime_idle(struct device *dev)314{315	struct snd_sof_dev *sdev = dev_get_drvdata(dev);316 317	return snd_sof_dsp_runtime_idle(sdev);318}319EXPORT_SYMBOL(snd_sof_runtime_idle);320 321int snd_sof_runtime_resume(struct device *dev)322{323	return sof_resume(dev, true);324}325EXPORT_SYMBOL(snd_sof_runtime_resume);326 327int snd_sof_resume(struct device *dev)328{329	return sof_resume(dev, false);330}331EXPORT_SYMBOL(snd_sof_resume);332 333int snd_sof_suspend(struct device *dev)334{335	return sof_suspend(dev, false);336}337EXPORT_SYMBOL(snd_sof_suspend);338 339int snd_sof_prepare(struct device *dev)340{341	struct snd_sof_dev *sdev = dev_get_drvdata(dev);342	const struct sof_dev_desc *desc = sdev->pdata->desc;343 344	/* will suspend to S3 by default */345	sdev->system_suspend_target = SOF_SUSPEND_S3;346 347	/*348	 * if the firmware is crashed or boot failed then we try to aim for S3349	 * to reboot the firmware350	 */351	if (sdev->fw_state == SOF_FW_CRASHED ||352	    sdev->fw_state == SOF_FW_BOOT_FAILED)353		return 0;354 355	if (!desc->use_acpi_target_states)356		return 0;357 358#if defined(CONFIG_ACPI)359	switch (acpi_target_system_state()) {360	case ACPI_STATE_S0:361		sdev->system_suspend_target = SOF_SUSPEND_S0IX;362		break;363	case ACPI_STATE_S1:364	case ACPI_STATE_S2:365	case ACPI_STATE_S3:366		sdev->system_suspend_target = SOF_SUSPEND_S3;367		break;368	case ACPI_STATE_S4:369		sdev->system_suspend_target = SOF_SUSPEND_S4;370		break;371	case ACPI_STATE_S5:372		sdev->system_suspend_target = SOF_SUSPEND_S5;373		break;374	default:375		break;376	}377#endif378 379	return 0;380}381EXPORT_SYMBOL(snd_sof_prepare);382 383void snd_sof_complete(struct device *dev)384{385	struct snd_sof_dev *sdev = dev_get_drvdata(dev);386 387	sdev->system_suspend_target = SOF_SUSPEND_NONE;388}389EXPORT_SYMBOL(snd_sof_complete);390