ASoC: SOF: ipc3: Add local implementation for handling fw_ready message

The handling of fw_ready is IPC3 specific, move the needed code from the
loader.c to ipc3.c and stop using the sof_ops(sdev)->fw_ready() callback.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Ajit Pandey <ajitkumar.pandey@amd.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Link: https://lore.kernel.org/r/20220421080735.31698-2-peter.ujfalusi@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Peter Ujfalusi 2022-04-21 11:07:30 +03:00 committed by Mark Brown
parent 57ebd5d568
commit b641fb7e01
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
1 changed files with 305 additions and 1 deletions

View File

@ -475,6 +475,310 @@ static int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t da
return ret;
}
static int sof_ipc3_get_ext_windows(struct snd_sof_dev *sdev,
const struct sof_ipc_ext_data_hdr *ext_hdr)
{
const struct sof_ipc_window *w =
container_of(ext_hdr, struct sof_ipc_window, ext_hdr);
if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS)
return -EINVAL;
if (sdev->info_window) {
if (memcmp(sdev->info_window, w, ext_hdr->hdr.size)) {
dev_err(sdev->dev, "mismatch between window descriptor from extended manifest and mailbox");
return -EINVAL;
}
return 0;
}
/* keep a local copy of the data */
sdev->info_window = devm_kmemdup(sdev->dev, w, ext_hdr->hdr.size, GFP_KERNEL);
if (!sdev->info_window)
return -ENOMEM;
return 0;
}
static int sof_ipc3_get_cc_info(struct snd_sof_dev *sdev,
const struct sof_ipc_ext_data_hdr *ext_hdr)
{
int ret;
const struct sof_ipc_cc_version *cc =
container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr);
if (sdev->cc_version) {
if (memcmp(sdev->cc_version, cc, cc->ext_hdr.hdr.size)) {
dev_err(sdev->dev,
"Receive diverged cc_version descriptions");
return -EINVAL;
}
return 0;
}
dev_dbg(sdev->dev,
"Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n",
cc->name, cc->major, cc->minor, cc->micro, cc->desc, cc->optim);
/* create read-only cc_version debugfs to store compiler version info */
/* use local copy of the cc_version to prevent data corruption */
if (sdev->first_boot) {
sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size,
GFP_KERNEL);
if (!sdev->cc_version)
return -ENOMEM;
memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size);
ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version,
cc->ext_hdr.hdr.size,
"cc_version", 0444);
/* errors are only due to memory allocation, not debugfs */
if (ret < 0) {
dev_err(sdev->dev, "snd_sof_debugfs_buf_item failed\n");
return ret;
}
}
return 0;
}
/* parse the extended FW boot data structures from FW boot message */
static int ipc3_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset)
{
struct sof_ipc_ext_data_hdr *ext_hdr;
void *ext_data;
int ret = 0;
ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!ext_data)
return -ENOMEM;
/* get first header */
snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data,
sizeof(*ext_hdr));
ext_hdr = ext_data;
while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) {
/* read in ext structure */
snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM,
offset + sizeof(*ext_hdr),
(void *)((u8 *)ext_data + sizeof(*ext_hdr)),
ext_hdr->hdr.size - sizeof(*ext_hdr));
dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n",
ext_hdr->type, ext_hdr->hdr.size);
/* process structure data */
switch (ext_hdr->type) {
case SOF_IPC_EXT_WINDOW:
ret = sof_ipc3_get_ext_windows(sdev, ext_hdr);
break;
case SOF_IPC_EXT_CC_INFO:
ret = sof_ipc3_get_cc_info(sdev, ext_hdr);
break;
case SOF_IPC_EXT_UNUSED:
case SOF_IPC_EXT_PROBE_INFO:
case SOF_IPC_EXT_USER_ABI_INFO:
/* They are supported but we don't do anything here */
break;
default:
dev_info(sdev->dev, "unknown ext header type %d size 0x%x\n",
ext_hdr->type, ext_hdr->hdr.size);
ret = 0;
break;
}
if (ret < 0) {
dev_err(sdev->dev, "Failed to parse ext data type %d\n",
ext_hdr->type);
break;
}
/* move to next header */
offset += ext_hdr->hdr.size;
snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data,
sizeof(*ext_hdr));
ext_hdr = ext_data;
}
kfree(ext_data);
return ret;
}
static void ipc3_get_windows(struct snd_sof_dev *sdev)
{
struct sof_ipc_window_elem *elem;
u32 outbox_offset = 0;
u32 stream_offset = 0;
u32 inbox_offset = 0;
u32 outbox_size = 0;
u32 stream_size = 0;
u32 inbox_size = 0;
u32 debug_size = 0;
u32 debug_offset = 0;
int window_offset;
int i;
if (!sdev->info_window) {
dev_err(sdev->dev, "%s: No window info present\n", __func__);
return;
}
for (i = 0; i < sdev->info_window->num_windows; i++) {
elem = &sdev->info_window->window[i];
window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id);
if (window_offset < 0) {
dev_warn(sdev->dev, "No offset for window %d\n", elem->id);
continue;
}
switch (elem->type) {
case SOF_IPC_REGION_UPBOX:
inbox_offset = window_offset + elem->offset;
inbox_size = elem->size;
snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
inbox_offset,
elem->size, "inbox",
SOF_DEBUGFS_ACCESS_D0_ONLY);
break;
case SOF_IPC_REGION_DOWNBOX:
outbox_offset = window_offset + elem->offset;
outbox_size = elem->size;
snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
outbox_offset,
elem->size, "outbox",
SOF_DEBUGFS_ACCESS_D0_ONLY);
break;
case SOF_IPC_REGION_TRACE:
snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
window_offset + elem->offset,
elem->size, "etrace",
SOF_DEBUGFS_ACCESS_D0_ONLY);
break;
case SOF_IPC_REGION_DEBUG:
debug_offset = window_offset + elem->offset;
debug_size = elem->size;
snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
window_offset + elem->offset,
elem->size, "debug",
SOF_DEBUGFS_ACCESS_D0_ONLY);
break;
case SOF_IPC_REGION_STREAM:
stream_offset = window_offset + elem->offset;
stream_size = elem->size;
snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
stream_offset,
elem->size, "stream",
SOF_DEBUGFS_ACCESS_D0_ONLY);
break;
case SOF_IPC_REGION_REGS:
snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
window_offset + elem->offset,
elem->size, "regs",
SOF_DEBUGFS_ACCESS_D0_ONLY);
break;
case SOF_IPC_REGION_EXCEPTION:
sdev->dsp_oops_offset = window_offset + elem->offset;
snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,
window_offset + elem->offset,
elem->size, "exception",
SOF_DEBUGFS_ACCESS_D0_ONLY);
break;
default:
dev_err(sdev->dev, "%s: Illegal window info: %u\n",
__func__, elem->type);
return;
}
}
if (outbox_size == 0 || inbox_size == 0) {
dev_err(sdev->dev, "%s: Illegal mailbox window\n", __func__);
return;
}
sdev->dsp_box.offset = inbox_offset;
sdev->dsp_box.size = inbox_size;
sdev->host_box.offset = outbox_offset;
sdev->host_box.size = outbox_size;
sdev->stream_box.offset = stream_offset;
sdev->stream_box.size = stream_size;
sdev->debug_box.offset = debug_offset;
sdev->debug_box.size = debug_size;
dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n",
inbox_offset, inbox_size);
dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n",
outbox_offset, outbox_size);
dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n",
stream_offset, stream_size);
dev_dbg(sdev->dev, " debug region 0x%x - size 0x%x\n",
debug_offset, debug_size);
}
static int ipc3_init_reply_data_buffer(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = &sdev->ipc->msg;
msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
if (!msg->reply_data)
return -ENOMEM;
sdev->ipc->max_payload_size = SOF_IPC_MSG_MAX_SIZE;
return 0;
}
static int ipc3_fw_ready(struct snd_sof_dev *sdev, u32 cmd)
{
struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready;
int offset;
int ret;
/* mailbox must be on 4k boundary */
offset = snd_sof_dsp_get_mailbox_offset(sdev);
if (offset < 0) {
dev_err(sdev->dev, "%s: no mailbox offset\n", __func__);
return offset;
}
dev_dbg(sdev->dev, "DSP is ready 0x%8.8x offset 0x%x\n", cmd, offset);
/* no need to re-check version/ABI for subsequent boots */
if (!sdev->first_boot)
return 0;
/*
* copy data from the DSP FW ready offset
* Subsequent error handling is not needed for BLK_TYPE_SRAM
*/
ret = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, fw_ready,
sizeof(*fw_ready));
if (ret) {
dev_err(sdev->dev,
"Unable to read fw_ready, read from TYPE_SRAM failed\n");
return ret;
}
/* make sure ABI version is compatible */
ret = snd_sof_ipc_valid(sdev);
if (ret < 0)
return ret;
/* now check for extended data */
ipc3_fw_parse_ext_data(sdev, offset + sizeof(struct sof_ipc_fw_ready));
ipc3_get_windows(sdev);
return ipc3_init_reply_data_buffer(sdev);
}
/* IPC stream position. */
static void ipc3_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id)
{
@ -633,7 +937,7 @@ static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev)
case SOF_IPC_FW_READY:
/* check for FW boot completion */
if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) {
err = sof_ops(sdev)->fw_ready(sdev, cmd);
err = ipc3_fw_ready(sdev, cmd);
if (err < 0)
sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED);
else