OSDN Git Service

ASoC: Intel: avs: Dynamic firmware resources management
authorCezary Rojewski <cezary.rojewski@intel.com>
Fri, 11 Mar 2022 15:35:40 +0000 (16:35 +0100)
committerMark Brown <broonie@kernel.org>
Fri, 11 Mar 2022 16:24:05 +0000 (16:24 +0000)
Wrap elementary DSP-core operations and resource control into more
complex handlers. This is done to reduce the number of invocations of
wrapped operations throughout the driver as order of operations matters -
most flows involve register manipulation and IPCs combined.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
Link: https://lore.kernel.org/r/20220311153544.136854-14-cezary.rojewski@intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/intel/avs/avs.h
sound/soc/intel/avs/dsp.c

index 08d5ebb..17ef8b5 100644 (file)
@@ -89,6 +89,7 @@ struct avs_dev {
        struct mutex modres_mutex;
        struct ida ppl_ida;
        struct list_head fw_list;
+       int *core_refs;         /* reference count per core */
 
        struct completion fw_ready;
 };
@@ -205,4 +206,13 @@ int avs_request_firmware(struct avs_dev *adev, const struct firmware **fw_p, con
 void avs_release_last_firmware(struct avs_dev *adev);
 void avs_release_firmwares(struct avs_dev *adev);
 
+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);
+void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u16 instance_id,
+                          u8 ppl_instance_id, u8 core_id);
+int avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
+                           bool lp, u16 attributes, u8 *instance_id);
+int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id);
+
 #endif /* __SOUND_SOC_INTEL_AVS_H */
index 63f5ce2..53925ea 100644 (file)
@@ -104,4 +104,173 @@ int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask)
        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) {
+               ret = avs_dsp_enable(adev, mask);
+               if (ret)
+                       goto err_enable_dsp;
+       }
+
+       return 0;
+
+err_enable_dsp:
+       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;
+       }
+
+       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;
+       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;
+
+       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:
+       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)
+{
+       /* 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);
+
+       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;
+}
+
 MODULE_LICENSE("GPL");