162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
662306a36Sopenharmony_ci//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
762306a36Sopenharmony_ci//
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <sound/hdaudio_ext.h>
1062306a36Sopenharmony_ci#include "avs.h"
1162306a36Sopenharmony_ci#include "registers.h"
1262306a36Sopenharmony_ci#include "trace.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define AVS_ADSPCS_INTERVAL_US		500
1562306a36Sopenharmony_ci#define AVS_ADSPCS_TIMEOUT_US		50000
1662306a36Sopenharmony_ci#define AVS_ADSPCS_DELAY_US		1000
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ciint avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	u32 value, mask, reg;
2162306a36Sopenharmony_ci	int ret;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
2462306a36Sopenharmony_ci	trace_avs_dsp_core_op(value, core_mask, "power", power);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	mask = AVS_ADSPCS_SPA_MASK(core_mask);
2762306a36Sopenharmony_ci	value = power ? mask : 0;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
3062306a36Sopenharmony_ci	/* Delay the polling to avoid false positives. */
3162306a36Sopenharmony_ci	usleep_range(AVS_ADSPCS_DELAY_US, 2 * AVS_ADSPCS_DELAY_US);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	mask = AVS_ADSPCS_CPA_MASK(core_mask);
3462306a36Sopenharmony_ci	value = power ? mask : 0;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
3762306a36Sopenharmony_ci				       reg, (reg & mask) == value,
3862306a36Sopenharmony_ci				       AVS_ADSPCS_INTERVAL_US,
3962306a36Sopenharmony_ci				       AVS_ADSPCS_TIMEOUT_US);
4062306a36Sopenharmony_ci	if (ret)
4162306a36Sopenharmony_ci		dev_err(adev->dev, "core_mask %d power %s failed: %d\n",
4262306a36Sopenharmony_ci			core_mask, power ? "on" : "off", ret);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return ret;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ciint avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	u32 value, mask, reg;
5062306a36Sopenharmony_ci	int ret;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
5362306a36Sopenharmony_ci	trace_avs_dsp_core_op(value, core_mask, "reset", reset);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	mask = AVS_ADSPCS_CRST_MASK(core_mask);
5662306a36Sopenharmony_ci	value = reset ? mask : 0;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
6162306a36Sopenharmony_ci				       reg, (reg & mask) == value,
6262306a36Sopenharmony_ci				       AVS_ADSPCS_INTERVAL_US,
6362306a36Sopenharmony_ci				       AVS_ADSPCS_TIMEOUT_US);
6462306a36Sopenharmony_ci	if (ret)
6562306a36Sopenharmony_ci		dev_err(adev->dev, "core_mask %d %s reset failed: %d\n",
6662306a36Sopenharmony_ci			core_mask, reset ? "enter" : "exit", ret);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return ret;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ciint avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	u32 value, mask, reg;
7462306a36Sopenharmony_ci	int ret;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
7762306a36Sopenharmony_ci	trace_avs_dsp_core_op(value, core_mask, "stall", stall);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	mask = AVS_ADSPCS_CSTALL_MASK(core_mask);
8062306a36Sopenharmony_ci	value = stall ? mask : 0;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
8562306a36Sopenharmony_ci				       reg, (reg & mask) == value,
8662306a36Sopenharmony_ci				       AVS_ADSPCS_INTERVAL_US,
8762306a36Sopenharmony_ci				       AVS_ADSPCS_TIMEOUT_US);
8862306a36Sopenharmony_ci	if (ret) {
8962306a36Sopenharmony_ci		dev_err(adev->dev, "core_mask %d %sstall failed: %d\n",
9062306a36Sopenharmony_ci			core_mask, stall ? "" : "un", ret);
9162306a36Sopenharmony_ci		return ret;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Give HW time to propagate the change. */
9562306a36Sopenharmony_ci	usleep_range(AVS_ADSPCS_DELAY_US, 2 * AVS_ADSPCS_DELAY_US);
9662306a36Sopenharmony_ci	return 0;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ciint avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	int ret;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	ret = avs_dsp_op(adev, power, core_mask, true);
10462306a36Sopenharmony_ci	if (ret)
10562306a36Sopenharmony_ci		return ret;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	ret = avs_dsp_op(adev, reset, core_mask, false);
10862306a36Sopenharmony_ci	if (ret)
10962306a36Sopenharmony_ci		return ret;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return avs_dsp_op(adev, stall, core_mask, false);
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ciint avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	/* No error checks to allow for complete DSP shutdown. */
11762306a36Sopenharmony_ci	avs_dsp_op(adev, stall, core_mask, true);
11862306a36Sopenharmony_ci	avs_dsp_op(adev, reset, core_mask, true);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return avs_dsp_op(adev, power, core_mask, false);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int avs_dsp_enable(struct avs_dev *adev, u32 core_mask)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	u32 mask;
12662306a36Sopenharmony_ci	int ret;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	ret = avs_dsp_core_enable(adev, core_mask);
12962306a36Sopenharmony_ci	if (ret < 0)
13062306a36Sopenharmony_ci		return ret;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	mask = core_mask & ~AVS_MAIN_CORE_MASK;
13362306a36Sopenharmony_ci	if (!mask)
13462306a36Sopenharmony_ci		/*
13562306a36Sopenharmony_ci		 * without main core, fw is dead anyway
13662306a36Sopenharmony_ci		 * so setting D0 for it is futile.
13762306a36Sopenharmony_ci		 */
13862306a36Sopenharmony_ci		return 0;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	ret = avs_ipc_set_dx(adev, mask, true);
14162306a36Sopenharmony_ci	return AVS_IPC_RET(ret);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic int avs_dsp_disable(struct avs_dev *adev, u32 core_mask)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	int ret;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	ret = avs_ipc_set_dx(adev, core_mask, false);
14962306a36Sopenharmony_ci	if (ret)
15062306a36Sopenharmony_ci		return AVS_IPC_RET(ret);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return avs_dsp_core_disable(adev, core_mask);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	u32 mask;
15862306a36Sopenharmony_ci	int ret;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	mask = BIT_MASK(core_id);
16162306a36Sopenharmony_ci	if (mask == AVS_MAIN_CORE_MASK)
16262306a36Sopenharmony_ci		/* nothing to do for main core */
16362306a36Sopenharmony_ci		return 0;
16462306a36Sopenharmony_ci	if (core_id >= adev->hw_cfg.dsp_cores) {
16562306a36Sopenharmony_ci		ret = -EINVAL;
16662306a36Sopenharmony_ci		goto err;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	adev->core_refs[core_id]++;
17062306a36Sopenharmony_ci	if (adev->core_refs[core_id] == 1) {
17162306a36Sopenharmony_ci		/*
17262306a36Sopenharmony_ci		 * No cores other than main-core can be running for DSP
17362306a36Sopenharmony_ci		 * to achieve d0ix. Conscious SET_D0IX IPC failure is permitted,
17462306a36Sopenharmony_ci		 * simply d0ix power state will no longer be attempted.
17562306a36Sopenharmony_ci		 */
17662306a36Sopenharmony_ci		ret = avs_dsp_disable_d0ix(adev);
17762306a36Sopenharmony_ci		if (ret && ret != -AVS_EIPC)
17862306a36Sopenharmony_ci			goto err_disable_d0ix;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci		ret = avs_dsp_enable(adev, mask);
18162306a36Sopenharmony_ci		if (ret)
18262306a36Sopenharmony_ci			goto err_enable_dsp;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return 0;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cierr_enable_dsp:
18862306a36Sopenharmony_ci	avs_dsp_enable_d0ix(adev);
18962306a36Sopenharmony_cierr_disable_d0ix:
19062306a36Sopenharmony_ci	adev->core_refs[core_id]--;
19162306a36Sopenharmony_cierr:
19262306a36Sopenharmony_ci	dev_err(adev->dev, "get core %d failed: %d\n", core_id, ret);
19362306a36Sopenharmony_ci	return ret;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic int avs_dsp_put_core(struct avs_dev *adev, u32 core_id)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	u32 mask;
19962306a36Sopenharmony_ci	int ret;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	mask = BIT_MASK(core_id);
20262306a36Sopenharmony_ci	if (mask == AVS_MAIN_CORE_MASK)
20362306a36Sopenharmony_ci		/* nothing to do for main core */
20462306a36Sopenharmony_ci		return 0;
20562306a36Sopenharmony_ci	if (core_id >= adev->hw_cfg.dsp_cores) {
20662306a36Sopenharmony_ci		ret = -EINVAL;
20762306a36Sopenharmony_ci		goto err;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	adev->core_refs[core_id]--;
21162306a36Sopenharmony_ci	if (!adev->core_refs[core_id]) {
21262306a36Sopenharmony_ci		ret = avs_dsp_disable(adev, mask);
21362306a36Sopenharmony_ci		if (ret)
21462306a36Sopenharmony_ci			goto err;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		/* Match disable_d0ix in avs_dsp_get_core(). */
21762306a36Sopenharmony_ci		avs_dsp_enable_d0ix(adev);
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return 0;
22162306a36Sopenharmony_cierr:
22262306a36Sopenharmony_ci	dev_err(adev->dev, "put core %d failed: %d\n", core_id, ret);
22362306a36Sopenharmony_ci	return ret;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ciint avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id,
22762306a36Sopenharmony_ci			u8 core_id, u8 domain, void *param, u32 param_size,
22862306a36Sopenharmony_ci			u8 *instance_id)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct avs_module_entry mentry;
23162306a36Sopenharmony_ci	bool was_loaded = false;
23262306a36Sopenharmony_ci	int ret, id;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	id = avs_module_id_alloc(adev, module_id);
23562306a36Sopenharmony_ci	if (id < 0)
23662306a36Sopenharmony_ci		return id;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	ret = avs_get_module_id_entry(adev, module_id, &mentry);
23962306a36Sopenharmony_ci	if (ret)
24062306a36Sopenharmony_ci		goto err_mod_entry;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	ret = avs_dsp_get_core(adev, core_id);
24362306a36Sopenharmony_ci	if (ret)
24462306a36Sopenharmony_ci		goto err_mod_entry;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* Load code into memory if this is the first instance. */
24762306a36Sopenharmony_ci	if (!id && !avs_module_entry_is_loaded(&mentry)) {
24862306a36Sopenharmony_ci		ret = avs_dsp_op(adev, transfer_mods, true, &mentry, 1);
24962306a36Sopenharmony_ci		if (ret) {
25062306a36Sopenharmony_ci			dev_err(adev->dev, "load modules failed: %d\n", ret);
25162306a36Sopenharmony_ci			goto err_mod_entry;
25262306a36Sopenharmony_ci		}
25362306a36Sopenharmony_ci		was_loaded = true;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	ret = avs_ipc_init_instance(adev, module_id, id, ppl_instance_id,
25762306a36Sopenharmony_ci				    core_id, domain, param, param_size);
25862306a36Sopenharmony_ci	if (ret) {
25962306a36Sopenharmony_ci		ret = AVS_IPC_RET(ret);
26062306a36Sopenharmony_ci		goto err_ipc;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	*instance_id = id;
26462306a36Sopenharmony_ci	return 0;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cierr_ipc:
26762306a36Sopenharmony_ci	if (was_loaded)
26862306a36Sopenharmony_ci		avs_dsp_op(adev, transfer_mods, false, &mentry, 1);
26962306a36Sopenharmony_ci	avs_dsp_put_core(adev, core_id);
27062306a36Sopenharmony_cierr_mod_entry:
27162306a36Sopenharmony_ci	avs_module_id_free(adev, module_id, id);
27262306a36Sopenharmony_ci	return ret;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_civoid avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u8 instance_id,
27662306a36Sopenharmony_ci			   u8 ppl_instance_id, u8 core_id)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct avs_module_entry mentry;
27962306a36Sopenharmony_ci	int ret;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* Modules not owned by any pipeline need to be freed explicitly. */
28262306a36Sopenharmony_ci	if (ppl_instance_id == INVALID_PIPELINE_ID)
28362306a36Sopenharmony_ci		avs_ipc_delete_instance(adev, module_id, instance_id);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	avs_module_id_free(adev, module_id, instance_id);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	ret = avs_get_module_id_entry(adev, module_id, &mentry);
28862306a36Sopenharmony_ci	/* Unload occupied memory if this was the last instance. */
28962306a36Sopenharmony_ci	if (!ret && mentry.type.load_type == AVS_MODULE_LOAD_TYPE_LOADABLE) {
29062306a36Sopenharmony_ci		if (avs_is_module_ida_empty(adev, module_id)) {
29162306a36Sopenharmony_ci			ret = avs_dsp_op(adev, transfer_mods, false, &mentry, 1);
29262306a36Sopenharmony_ci			if (ret)
29362306a36Sopenharmony_ci				dev_err(adev->dev, "unload modules failed: %d\n", ret);
29462306a36Sopenharmony_ci		}
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	avs_dsp_put_core(adev, core_id);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ciint avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
30162306a36Sopenharmony_ci			    bool lp, u16 attributes, u8 *instance_id)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct avs_fw_cfg *fw_cfg = &adev->fw_cfg;
30462306a36Sopenharmony_ci	int ret, id;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	id = ida_alloc_max(&adev->ppl_ida, fw_cfg->max_ppl_count - 1, GFP_KERNEL);
30762306a36Sopenharmony_ci	if (id < 0)
30862306a36Sopenharmony_ci		return id;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	ret = avs_ipc_create_pipeline(adev, req_size, priority, id, lp, attributes);
31162306a36Sopenharmony_ci	if (ret) {
31262306a36Sopenharmony_ci		ida_free(&adev->ppl_ida, id);
31362306a36Sopenharmony_ci		return AVS_IPC_RET(ret);
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	*instance_id = id;
31762306a36Sopenharmony_ci	return 0;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ciint avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	int ret;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	ret = avs_ipc_delete_pipeline(adev, instance_id);
32562306a36Sopenharmony_ci	if (ret)
32662306a36Sopenharmony_ci		ret = AVS_IPC_RET(ret);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	ida_free(&adev->ppl_ida, instance_id);
32962306a36Sopenharmony_ci	return ret;
33062306a36Sopenharmony_ci}
331