Skip to content

Commit

Permalink
ASoC: Intel: avs: Code loading over CLDMA
Browse files Browse the repository at this point in the history
With CLDMA transfer implemented, make use of it to shape firmware,
library and module loading routines for SKL and KBL platforms.

Signed-off-by: Amadeusz Sławiński <[email protected]>
Signed-off-by: Cezary Rojewski <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Mark Brown <[email protected]>
  • Loading branch information
crojewsk-intel authored and broonie committed Mar 11, 2022
1 parent 45864e4 commit 65794fe
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 0 deletions.
7 changes: 7 additions & 0 deletions sound/soc/intel/avs/avs.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ struct avs_dsp_ops {
#define avs_dsp_op(adev, op, ...) \
((adev)->spec->dsp_ops->op(adev, ## __VA_ARGS__))

#define AVS_PLATATTR_CLDMA BIT_ULL(0)

#define avs_platattr_test(adev, attr) \
((adev)->spec->attributes & AVS_PLATATTR_##attr)

Expand Down Expand Up @@ -232,4 +234,9 @@ void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable);
int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge);
int avs_dsp_first_boot_firmware(struct avs_dev *adev);

int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw);
int avs_cldma_load_library(struct avs_dev *adev, struct firmware *lib, u32 id);
int avs_cldma_transfer_modules(struct avs_dev *adev, bool load,
struct avs_module_entry *mods, u32 num_mods);

#endif /* __SOUND_SOC_INTEL_AVS_H */
159 changes: 159 additions & 0 deletions sound/soc/intel/avs/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,24 @@
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <sound/hdaudio_ext.h>
#include "avs.h"
#include "cldma.h"
#include "messages.h"
#include "registers.h"

#define AVS_ROM_STS_MASK 0xFF
#define AVS_ROM_INIT_DONE 0x1
#define SKL_ROM_BASEFW_ENTERED 0xF
#define AVS_ROM_INIT_POLLING_US 5
#define SKL_ROM_INIT_TIMEOUT_US 1000000

#define AVS_FW_INIT_POLLING_US 500
#define AVS_FW_INIT_TIMEOUT_US 3000000
#define AVS_FW_INIT_TIMEOUT_MS 3000

#define AVS_CLDMA_START_DELAY_MS 100

#define AVS_ROOT_DIR "intel/avs"
#define AVS_BASEFW_FILENAME "dsp_basefw.bin"
#define AVS_EXT_MANIFEST_MAGIC 0x31454124
Expand Down Expand Up @@ -111,6 +123,144 @@ static int avs_fw_manifest_strip_verify(struct avs_dev *adev, struct firmware *f
return 0;
}

int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw)
{
struct hda_cldma *cl = &code_loader;
unsigned int reg;
int ret;

ret = avs_dsp_op(adev, power, AVS_MAIN_CORE_MASK, true);
if (ret < 0)
return ret;

ret = avs_dsp_op(adev, reset, AVS_MAIN_CORE_MASK, false);
if (ret < 0)
return ret;

ret = hda_cldma_reset(cl);
if (ret < 0) {
dev_err(adev->dev, "cldma reset failed: %d\n", ret);
return ret;
}
hda_cldma_setup(cl);

ret = avs_dsp_op(adev, stall, AVS_MAIN_CORE_MASK, false);
if (ret < 0)
return ret;

reinit_completion(&adev->fw_ready);
avs_dsp_op(adev, int_control, true);

/* await ROM init */
ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg,
(reg & AVS_ROM_INIT_DONE) == AVS_ROM_INIT_DONE,
AVS_ROM_INIT_POLLING_US, SKL_ROM_INIT_TIMEOUT_US);
if (ret < 0) {
dev_err(adev->dev, "rom init timeout: %d\n", ret);
avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
return ret;
}

hda_cldma_set_data(cl, (void *)fw->data, fw->size);
/* transfer firmware */
hda_cldma_transfer(cl, 0);
ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg,
(reg & AVS_ROM_STS_MASK) == SKL_ROM_BASEFW_ENTERED,
AVS_FW_INIT_POLLING_US, AVS_FW_INIT_TIMEOUT_US);
hda_cldma_stop(cl);
if (ret < 0) {
dev_err(adev->dev, "transfer fw failed: %d\n", ret);
avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
return ret;
}

return 0;
}

int avs_cldma_load_library(struct avs_dev *adev, struct firmware *lib, u32 id)
{
struct hda_cldma *cl = &code_loader;
int ret;

hda_cldma_set_data(cl, (void *)lib->data, lib->size);
/* transfer modules manifest */
hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS));

/* DMA id ignored as there is only ever one code-loader DMA */
ret = avs_ipc_load_library(adev, 0, id);
hda_cldma_stop(cl);

if (ret) {
ret = AVS_IPC_RET(ret);
dev_err(adev->dev, "transfer lib %d failed: %d\n", id, ret);
}

return ret;
}

static int avs_cldma_load_module(struct avs_dev *adev, struct avs_module_entry *mentry)
{
struct hda_cldma *cl = &code_loader;
const struct firmware *mod;
char *mod_name;
int ret;

mod_name = kasprintf(GFP_KERNEL, "%s/%s/dsp_mod_%pUL.bin", AVS_ROOT_DIR,
adev->spec->name, mentry->uuid.b);
if (!mod_name)
return -ENOMEM;

ret = avs_request_firmware(adev, &mod, mod_name);
kfree(mod_name);
if (ret < 0)
return ret;

hda_cldma_set_data(cl, (void *)mod->data, mod->size);
hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS));
ret = avs_ipc_load_modules(adev, &mentry->module_id, 1);
hda_cldma_stop(cl);

if (ret) {
dev_err(adev->dev, "load module %d failed: %d\n", mentry->module_id, ret);
avs_release_last_firmware(adev);
return AVS_IPC_RET(ret);
}

return 0;
}

int avs_cldma_transfer_modules(struct avs_dev *adev, bool load,
struct avs_module_entry *mods, u32 num_mods)
{
u16 *mod_ids;
int ret, i;

/* Either load to DSP or unload them to free space. */
if (load) {
for (i = 0; i < num_mods; i++) {
ret = avs_cldma_load_module(adev, &mods[i]);
if (ret)
return ret;
}

return 0;
}

mod_ids = kcalloc(num_mods, sizeof(u16), GFP_KERNEL);
if (!mod_ids)
return -ENOMEM;

for (i = 0; i < num_mods; i++)
mod_ids[i] = mods[i].module_id;

ret = avs_ipc_unload_modules(adev, mod_ids, num_mods);
kfree(mod_ids);
if (ret)
return AVS_IPC_RET(ret);

return 0;
}

static int avs_dsp_load_basefw(struct avs_dev *adev)
{
const struct avs_fw_version *min_req;
Expand Down Expand Up @@ -195,6 +345,15 @@ int avs_dsp_first_boot_firmware(struct avs_dev *adev)
{
int ret, i;

if (avs_platattr_test(adev, CLDMA)) {
ret = hda_cldma_init(&code_loader, &adev->base.core,
adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE);
if (ret < 0) {
dev_err(adev->dev, "cldma init failed: %d\n", ret);
return ret;
}
}

ret = avs_dsp_boot_firmware(adev, true);
if (ret < 0) {
dev_err(adev->dev, "firmware boot failed: %d\n", ret);
Expand Down

0 comments on commit 65794fe

Please sign in to comment.