| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- // SPDX-License-Identifier: GPL-2.0-only
- //
- // Copyright(c) 2021-2022 Intel Corporation
- //
- // Authors: Cezary Rojewski <cezary.rojewski@intel.com>
- // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
- //
- #include <sound/hdaudio_ext.h>
- #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,
- u8 *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, u8 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;
- }
|