// SPDX-License-Identifier: GPL-2.0-only // // Copyright(c) 2021-2022 Intel Corporation. All rights reserved. // // Authors: Cezary Rojewski // Amadeusz Slawinski // #include #include "avs.h" #include "registers.h" #include "trace.h" #define AVS_ADSPCS_INTERVAL_US 500 #define AVS_ADSPCS_TIMEOUT_US 50000 #define AVS_ADSPCS_DELAY_US 1000 int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power) { u32 value, mask, reg; int ret; value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS); trace_avs_dsp_core_op(value, core_mask, "power", power); mask = AVS_ADSPCS_SPA_MASK(core_mask); value = power ? mask : 0; snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value); /* Delay the polling to avoid false positives. */ usleep_range(AVS_ADSPCS_DELAY_US, 2 * AVS_ADSPCS_DELAY_US); mask = AVS_ADSPCS_CPA_MASK(core_mask); value = power ? mask : 0; ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS, reg, (reg & mask) == value, AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US); if (ret) dev_err(adev->dev, "core_mask %d power %s failed: %d\n", core_mask, power ? "on" : "off", ret); return ret; } int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset) { u32 value, mask, reg; int ret; value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS); trace_avs_dsp_core_op(value, core_mask, "reset", reset); mask = AVS_ADSPCS_CRST_MASK(core_mask); value = reset ? mask : 0; snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value); ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS, reg, (reg & mask) == value, AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US); if (ret) dev_err(adev->dev, "core_mask %d %s reset failed: %d\n", core_mask, reset ? "enter" : "exit", ret); return ret; } int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall) { u32 value, mask, reg; int ret; value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS); trace_avs_dsp_core_op(value, core_mask, "stall", stall); mask = AVS_ADSPCS_CSTALL_MASK(core_mask); value = stall ? mask : 0; snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value); ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS, reg, (reg & mask) == value, AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US); if (ret) { dev_err(adev->dev, "core_mask %d %sstall failed: %d\n", core_mask, stall ? "" : "un", ret); return ret; } /* Give HW time to propagate the change. */ usleep_range(AVS_ADSPCS_DELAY_US, 2 * AVS_ADSPCS_DELAY_US); return 0; } int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask) { int ret; ret = avs_dsp_op(adev, power, core_mask, true); if (ret) return ret; ret = avs_dsp_op(adev, reset, core_mask, false); if (ret) return ret; return avs_dsp_op(adev, stall, core_mask, false); } int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask) { /* No error checks to allow for complete DSP shutdown. */ avs_dsp_op(adev, stall, core_mask, true); avs_dsp_op(adev, reset, core_mask, true); return avs_dsp_op(adev, power, core_mask, false); } static int avs_dsp_enable(struct avs_dev *adev, u32 core_mask) { u32 mask; int ret; ret = avs_dsp_core_enable(adev, core_mask); if (ret < 0) return ret; mask = core_mask & ~AVS_MAIN_CORE_MASK; if (!mask) /* * without main core, fw is dead anyway * so setting D0 for it is futile. */ return 0; ret = avs_ipc_set_dx(adev, mask, true); return AVS_IPC_RET(ret); } static int avs_dsp_disable(struct avs_dev *adev, u32 core_mask) { int ret; ret = avs_ipc_set_dx(adev, core_mask, false); if (ret) return AVS_IPC_RET(ret); return avs_dsp_core_disable(adev, core_mask); } static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id) { u32 mask; int ret; mask = BIT_MASK(core_id); if (mask == AVS_MAIN_CORE_MASK) /* nothing to do for main core */ return 0; if (core_id >= adev->hw_cfg.dsp_cores) { ret = -EINVAL; goto err; } adev->core_refs[core_id]++; if (adev->core_refs[core_id] == 1) { /* * No cores other than main-core can be running for DSP * to achieve d0ix. Conscious SET_D0IX IPC failure is permitted, * simply d0ix power state will no longer be attempted. */ ret = avs_dsp_disable_d0ix(adev); if (ret && ret != -AVS_EIPC) goto err_disable_d0ix; ret = avs_dsp_enable(adev, mask); if (ret) goto err_enable_dsp; } return 0; err_enable_dsp: avs_dsp_enable_d0ix(adev); err_disable_d0ix: adev->core_refs[core_id]--; err: dev_err(adev->dev, "get core %d failed: %d\n", core_id, ret); return ret; } static int avs_dsp_put_core(struct avs_dev *adev, u32 core_id) { u32 mask; int ret; mask = BIT_MASK(core_id); if (mask == AVS_MAIN_CORE_MASK) /* nothing to do for main core */ return 0; if (core_id >= adev->hw_cfg.dsp_cores) { ret = -EINVAL; goto err; } adev->core_refs[core_id]--; if (!adev->core_refs[core_id]) { ret = avs_dsp_disable(adev, mask); if (ret) goto err; /* Match disable_d0ix in avs_dsp_get_core(). */ avs_dsp_enable_d0ix(adev); } return 0; err: dev_err(adev->dev, "put core %d failed: %d\n", core_id, ret); return ret; } int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id, u8 core_id, u8 domain, void *param, u32 param_size, u16 *instance_id) { struct avs_module_entry mentry; bool was_loaded = false; int ret, id; id = avs_module_id_alloc(adev, module_id); if (id < 0) return id; ret = avs_get_module_id_entry(adev, module_id, &mentry); if (ret) goto err_mod_entry; ret = avs_dsp_get_core(adev, core_id); if (ret) goto err_mod_entry; /* Load code into memory if this is the first instance. */ if (!id && !avs_module_entry_is_loaded(&mentry)) { ret = avs_dsp_op(adev, transfer_mods, true, &mentry, 1); if (ret) { dev_err(adev->dev, "load modules failed: %d\n", ret); goto err_mod_entry; } was_loaded = true; } ret = avs_ipc_init_instance(adev, module_id, id, ppl_instance_id, core_id, domain, param, param_size); if (ret) { ret = AVS_IPC_RET(ret); goto err_ipc; } *instance_id = id; return 0; err_ipc: if (was_loaded) avs_dsp_op(adev, transfer_mods, false, &mentry, 1); avs_dsp_put_core(adev, core_id); err_mod_entry: avs_module_id_free(adev, module_id, id); return ret; } void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u16 instance_id, u8 ppl_instance_id, u8 core_id) { struct avs_module_entry mentry; int ret; /* Modules not owned by any pipeline need to be freed explicitly. */ if (ppl_instance_id == INVALID_PIPELINE_ID) avs_ipc_delete_instance(adev, module_id, instance_id); avs_module_id_free(adev, module_id, instance_id); ret = avs_get_module_id_entry(adev, module_id, &mentry); /* Unload occupied memory if this was the last instance. */ if (!ret && mentry.type.load_type == AVS_MODULE_LOAD_TYPE_LOADABLE) { if (avs_is_module_ida_empty(adev, module_id)) { ret = avs_dsp_op(adev, transfer_mods, false, &mentry, 1); if (ret) dev_err(adev->dev, "unload modules failed: %d\n", ret); } } avs_dsp_put_core(adev, core_id); } int avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority, bool lp, u16 attributes, u8 *instance_id) { struct avs_fw_cfg *fw_cfg = &adev->fw_cfg; int ret, id; id = ida_alloc_max(&adev->ppl_ida, fw_cfg->max_ppl_count - 1, GFP_KERNEL); if (id < 0) return id; ret = avs_ipc_create_pipeline(adev, req_size, priority, id, lp, attributes); if (ret) { ida_free(&adev->ppl_ida, id); return AVS_IPC_RET(ret); } *instance_id = id; return 0; } int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id) { int ret; ret = avs_ipc_delete_pipeline(adev, instance_id); if (ret) ret = AVS_IPC_RET(ret); ida_free(&adev->ppl_ida, instance_id); return ret; }