brintos

brintos / linux-shallow public Read only

0
0
Text · 7.8 KiB · 7b47e52 Raw
331 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// Copyright(c) 2021-2022 Intel Corporation4//5// Authors: Cezary Rojewski <cezary.rojewski@intel.com>6//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>7//8 9#include <sound/hdaudio_ext.h>10#include "avs.h"11#include "registers.h"12#include "trace.h"13 14#define AVS_ADSPCS_INTERVAL_US		50015#define AVS_ADSPCS_TIMEOUT_US		5000016#define AVS_ADSPCS_DELAY_US		100017 18int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)19{20	u32 value, mask, reg;21	int ret;22 23	value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);24	trace_avs_dsp_core_op(value, core_mask, "power", power);25 26	mask = AVS_ADSPCS_SPA_MASK(core_mask);27	value = power ? mask : 0;28 29	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);30	/* Delay the polling to avoid false positives. */31	usleep_range(AVS_ADSPCS_DELAY_US, 2 * AVS_ADSPCS_DELAY_US);32 33	mask = AVS_ADSPCS_CPA_MASK(core_mask);34	value = power ? mask : 0;35 36	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,37				       reg, (reg & mask) == value,38				       AVS_ADSPCS_INTERVAL_US,39				       AVS_ADSPCS_TIMEOUT_US);40	if (ret)41		dev_err(adev->dev, "core_mask %d power %s failed: %d\n",42			core_mask, power ? "on" : "off", ret);43 44	return ret;45}46 47int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)48{49	u32 value, mask, reg;50	int ret;51 52	value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);53	trace_avs_dsp_core_op(value, core_mask, "reset", reset);54 55	mask = AVS_ADSPCS_CRST_MASK(core_mask);56	value = reset ? mask : 0;57 58	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);59 60	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,61				       reg, (reg & mask) == value,62				       AVS_ADSPCS_INTERVAL_US,63				       AVS_ADSPCS_TIMEOUT_US);64	if (ret)65		dev_err(adev->dev, "core_mask %d %s reset failed: %d\n",66			core_mask, reset ? "enter" : "exit", ret);67 68	return ret;69}70 71int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)72{73	u32 value, mask, reg;74	int ret;75 76	value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);77	trace_avs_dsp_core_op(value, core_mask, "stall", stall);78 79	mask = AVS_ADSPCS_CSTALL_MASK(core_mask);80	value = stall ? mask : 0;81 82	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);83 84	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,85				       reg, (reg & mask) == value,86				       AVS_ADSPCS_INTERVAL_US,87				       AVS_ADSPCS_TIMEOUT_US);88	if (ret) {89		dev_err(adev->dev, "core_mask %d %sstall failed: %d\n",90			core_mask, stall ? "" : "un", ret);91		return ret;92	}93 94	/* Give HW time to propagate the change. */95	usleep_range(AVS_ADSPCS_DELAY_US, 2 * AVS_ADSPCS_DELAY_US);96	return 0;97}98 99int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask)100{101	int ret;102 103	ret = avs_dsp_op(adev, power, core_mask, true);104	if (ret)105		return ret;106 107	ret = avs_dsp_op(adev, reset, core_mask, false);108	if (ret)109		return ret;110 111	return avs_dsp_op(adev, stall, core_mask, false);112}113 114int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask)115{116	/* No error checks to allow for complete DSP shutdown. */117	avs_dsp_op(adev, stall, core_mask, true);118	avs_dsp_op(adev, reset, core_mask, true);119 120	return avs_dsp_op(adev, power, core_mask, false);121}122 123static int avs_dsp_enable(struct avs_dev *adev, u32 core_mask)124{125	u32 mask;126	int ret;127 128	ret = avs_dsp_core_enable(adev, core_mask);129	if (ret < 0)130		return ret;131 132	mask = core_mask & ~AVS_MAIN_CORE_MASK;133	if (!mask)134		/*135		 * without main core, fw is dead anyway136		 * so setting D0 for it is futile.137		 */138		return 0;139 140	ret = avs_ipc_set_dx(adev, mask, true);141	return AVS_IPC_RET(ret);142}143 144static int avs_dsp_disable(struct avs_dev *adev, u32 core_mask)145{146	int ret;147 148	ret = avs_ipc_set_dx(adev, core_mask, false);149	if (ret)150		return AVS_IPC_RET(ret);151 152	return avs_dsp_core_disable(adev, core_mask);153}154 155static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)156{157	u32 mask;158	int ret;159 160	mask = BIT_MASK(core_id);161	if (mask == AVS_MAIN_CORE_MASK)162		/* nothing to do for main core */163		return 0;164	if (core_id >= adev->hw_cfg.dsp_cores) {165		ret = -EINVAL;166		goto err;167	}168 169	adev->core_refs[core_id]++;170	if (adev->core_refs[core_id] == 1) {171		/*172		 * No cores other than main-core can be running for DSP173		 * to achieve d0ix. Conscious SET_D0IX IPC failure is permitted,174		 * simply d0ix power state will no longer be attempted.175		 */176		ret = avs_dsp_disable_d0ix(adev);177		if (ret && ret != -AVS_EIPC)178			goto err_disable_d0ix;179 180		ret = avs_dsp_enable(adev, mask);181		if (ret)182			goto err_enable_dsp;183	}184 185	return 0;186 187err_enable_dsp:188	avs_dsp_enable_d0ix(adev);189err_disable_d0ix:190	adev->core_refs[core_id]--;191err:192	dev_err(adev->dev, "get core %d failed: %d\n", core_id, ret);193	return ret;194}195 196static int avs_dsp_put_core(struct avs_dev *adev, u32 core_id)197{198	u32 mask;199	int ret;200 201	mask = BIT_MASK(core_id);202	if (mask == AVS_MAIN_CORE_MASK)203		/* nothing to do for main core */204		return 0;205	if (core_id >= adev->hw_cfg.dsp_cores) {206		ret = -EINVAL;207		goto err;208	}209 210	adev->core_refs[core_id]--;211	if (!adev->core_refs[core_id]) {212		ret = avs_dsp_disable(adev, mask);213		if (ret)214			goto err;215 216		/* Match disable_d0ix in avs_dsp_get_core(). */217		avs_dsp_enable_d0ix(adev);218	}219 220	return 0;221err:222	dev_err(adev->dev, "put core %d failed: %d\n", core_id, ret);223	return ret;224}225 226int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id,227			u8 core_id, u8 domain, void *param, u32 param_size,228			u8 *instance_id)229{230	struct avs_module_entry mentry;231	bool was_loaded = false;232	int ret, id;233 234	id = avs_module_id_alloc(adev, module_id);235	if (id < 0)236		return id;237 238	ret = avs_get_module_id_entry(adev, module_id, &mentry);239	if (ret)240		goto err_mod_entry;241 242	ret = avs_dsp_get_core(adev, core_id);243	if (ret)244		goto err_mod_entry;245 246	/* Load code into memory if this is the first instance. */247	if (!id && !avs_module_entry_is_loaded(&mentry)) {248		ret = avs_dsp_op(adev, transfer_mods, true, &mentry, 1);249		if (ret) {250			dev_err(adev->dev, "load modules failed: %d\n", ret);251			goto err_mod_entry;252		}253		was_loaded = true;254	}255 256	ret = avs_ipc_init_instance(adev, module_id, id, ppl_instance_id,257				    core_id, domain, param, param_size);258	if (ret) {259		ret = AVS_IPC_RET(ret);260		goto err_ipc;261	}262 263	*instance_id = id;264	return 0;265 266err_ipc:267	if (was_loaded)268		avs_dsp_op(adev, transfer_mods, false, &mentry, 1);269	avs_dsp_put_core(adev, core_id);270err_mod_entry:271	avs_module_id_free(adev, module_id, id);272	return ret;273}274 275void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u8 instance_id,276			   u8 ppl_instance_id, u8 core_id)277{278	struct avs_module_entry mentry;279	int ret;280 281	/* Modules not owned by any pipeline need to be freed explicitly. */282	if (ppl_instance_id == INVALID_PIPELINE_ID)283		avs_ipc_delete_instance(adev, module_id, instance_id);284 285	avs_module_id_free(adev, module_id, instance_id);286 287	ret = avs_get_module_id_entry(adev, module_id, &mentry);288	/* Unload occupied memory if this was the last instance. */289	if (!ret && mentry.type.load_type == AVS_MODULE_LOAD_TYPE_LOADABLE) {290		if (avs_is_module_ida_empty(adev, module_id)) {291			ret = avs_dsp_op(adev, transfer_mods, false, &mentry, 1);292			if (ret)293				dev_err(adev->dev, "unload modules failed: %d\n", ret);294		}295	}296 297	avs_dsp_put_core(adev, core_id);298}299 300int avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,301			    bool lp, u16 attributes, u8 *instance_id)302{303	struct avs_fw_cfg *fw_cfg = &adev->fw_cfg;304	int ret, id;305 306	id = ida_alloc_max(&adev->ppl_ida, fw_cfg->max_ppl_count - 1, GFP_KERNEL);307	if (id < 0)308		return id;309 310	ret = avs_ipc_create_pipeline(adev, req_size, priority, id, lp, attributes);311	if (ret) {312		ida_free(&adev->ppl_ida, id);313		return AVS_IPC_RET(ret);314	}315 316	*instance_id = id;317	return 0;318}319 320int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id)321{322	int ret;323 324	ret = avs_ipc_delete_pipeline(adev, instance_id);325	if (ret)326		ret = AVS_IPC_RET(ret);327 328	ida_free(&adev->ppl_ida, instance_id);329	return ret;330}331