diff options
Diffstat (limited to 'sound/soc/sof/ipc.c')
-rw-r--r-- | sound/soc/sof/ipc.c | 217 |
1 files changed, 156 insertions, 61 deletions
diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index c2d07b783f60..e6c53c6c470e 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -18,7 +18,7 @@ #include "sof-audio.h" #include "ops.h" -static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id); +static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type); static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd); /* @@ -192,6 +192,29 @@ static void ipc_log_header(struct device *dev, u8 *text, u32 cmd) str2 = "unknown type"; break; } break; + case SOF_IPC_GLB_PROBE: + str = "GLB_PROBE"; + switch (type) { + case SOF_IPC_PROBE_INIT: + str2 = "INIT"; break; + case SOF_IPC_PROBE_DEINIT: + str2 = "DEINIT"; break; + case SOF_IPC_PROBE_DMA_ADD: + str2 = "DMA_ADD"; break; + case SOF_IPC_PROBE_DMA_INFO: + str2 = "DMA_INFO"; break; + case SOF_IPC_PROBE_DMA_REMOVE: + str2 = "DMA_REMOVE"; break; + case SOF_IPC_PROBE_POINT_ADD: + str2 = "POINT_ADD"; break; + case SOF_IPC_PROBE_POINT_INFO: + str2 = "POINT_INFO"; break; + case SOF_IPC_PROBE_POINT_REMOVE: + str2 = "POINT_REMOVE"; break; + default: + str2 = "unknown type"; break; + } + break; default: str = "unknown GLB command"; break; } @@ -226,15 +249,17 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, msecs_to_jiffies(sdev->ipc_timeout)); if (ret == 0) { - dev_err(sdev->dev, "error: ipc timed out for 0x%x size %d\n", - hdr->cmd, hdr->size); + dev_err(sdev->dev, + "ipc tx timed out for %#x (msg/reply size: %d/%zu)\n", + hdr->cmd, hdr->size, msg->reply_size); snd_sof_handle_fw_exception(ipc->sdev); ret = -ETIMEDOUT; } else { ret = msg->reply_error; if (ret < 0) { - dev_err(sdev->dev, "error: ipc error for 0x%x size %zu\n", - hdr->cmd, msg->reply_size); + dev_err(sdev->dev, + "ipc tx error for %#x (msg/reply size: %d/%zu): %d\n", + hdr->cmd, hdr->size, msg->reply_size, ret); } else { ipc_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd); if (msg->reply_size) @@ -242,6 +267,12 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, memcpy(reply_data, msg->reply_data, msg->reply_size); } + + /* re-enable dumps after successful IPC tx */ + if (sdev->ipc_dump_printed) { + sdev->dbg_dump_printed = false; + sdev->ipc_dump_printed = false; + } } return ret; @@ -286,7 +317,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, spin_unlock_irq(&sdev->ipc_lock); - if (ret < 0) { + if (ret) { dev_err_ratelimited(sdev->dev, "error: ipc tx failed with error %d\n", ret); @@ -296,10 +327,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, ipc_log_header(sdev->dev, "ipc tx", msg->header); /* now wait for completion */ - if (!ret) - ret = tx_wait_done(ipc, msg, reply_data); - - return ret; + return tx_wait_done(ipc, msg, reply_data); } /* send IPC message from host to DSP */ @@ -369,15 +397,52 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) } EXPORT_SYMBOL(snd_sof_ipc_reply); +static void ipc_comp_notification(struct snd_sof_dev *sdev, + struct sof_ipc_cmd_hdr *hdr) +{ + u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; + struct sof_ipc_ctrl_data *cdata; + int ret; + + switch (msg_type) { + case SOF_IPC_COMP_GET_VALUE: + case SOF_IPC_COMP_GET_DATA: + cdata = kmalloc(hdr->size, GFP_KERNEL); + if (!cdata) + return; + + /* read back full message */ + ret = snd_sof_ipc_msg_data(sdev, NULL, cdata, hdr->size); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to read component event: %d\n", ret); + goto err; + } + break; + default: + dev_err(sdev->dev, "error: unhandled component message %#x\n", msg_type); + return; + } + + snd_sof_control_notify(sdev, cdata); + +err: + kfree(cdata); +} + /* DSP firmware has sent host a message */ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) { struct sof_ipc_cmd_hdr hdr; u32 cmd, type; - int err = 0; + int err; /* read back header */ - snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr)); + err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr)); + if (err < 0) { + dev_warn(sdev->dev, "failed to read IPC header: %d\n", err); + return; + } ipc_log_header(sdev->dev, "ipc rx", hdr.cmd); cmd = hdr.cmd & SOF_GLB_TYPE_MASK; @@ -393,9 +458,9 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) { err = sof_ops(sdev)->fw_ready(sdev, cmd); if (err < 0) - sdev->fw_state = SOF_FW_BOOT_READY_FAILED; + sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED); else - sdev->fw_state = SOF_FW_BOOT_COMPLETE; + sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); /* wake up firmware loader */ wake_up(&sdev->boot_wait); @@ -404,7 +469,9 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) case SOF_IPC_GLB_COMPOUND: case SOF_IPC_GLB_TPLG_MSG: case SOF_IPC_GLB_PM_MSG: + break; case SOF_IPC_GLB_COMP_MSG: + ipc_comp_notification(sdev, &hdr); break; case SOF_IPC_GLB_STREAM_MSG: /* need to pass msg id into the function */ @@ -426,19 +493,22 @@ EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); * IPC trace mechanism. */ -static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id) +static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type) { struct sof_ipc_dma_trace_posn posn; + int ret; - switch (msg_id) { + switch (msg_type) { case SOF_IPC_TRACE_DMA_POSITION: /* read back full message */ - snd_sof_ipc_msg_data(sdev, NULL, &posn, sizeof(posn)); - snd_sof_trace_update_pos(sdev, &posn); + ret = snd_sof_ipc_msg_data(sdev, NULL, &posn, sizeof(posn)); + if (ret < 0) + dev_warn(sdev->dev, "failed to read trace position: %d\n", ret); + else + snd_sof_trace_update_pos(sdev, &posn); break; default: - dev_err(sdev->dev, "error: unhandled trace message %x\n", - msg_id); + dev_err(sdev->dev, "error: unhandled trace message %#x\n", msg_type); break; } } @@ -453,7 +523,7 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) struct snd_sof_pcm_stream *stream; struct sof_ipc_stream_posn posn; struct snd_sof_pcm *spcm; - int direction; + int direction, ret; spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); if (!spcm) { @@ -464,15 +534,21 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) } stream = &spcm->stream[direction]; - snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); + ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); + if (ret < 0) { + dev_warn(sdev->dev, "failed to read stream position: %d\n", ret); + return; + } dev_vdbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n", posn.host_posn, posn.dai_posn, posn.wallclock); memcpy(&stream->posn, &posn, sizeof(posn)); - /* only inform ALSA for period_wakeup mode */ - if (!stream->substream->runtime->no_period_wakeup) + if (spcm->pcm.compress) + snd_sof_compr_fragment_elapsed(stream->cstream); + else if (!stream->substream->runtime->no_period_wakeup) + /* only inform ALSA for period_wakeup mode */ snd_sof_pcm_period_elapsed(stream->substream); } @@ -483,7 +559,7 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) struct snd_sof_pcm_stream *stream; struct sof_ipc_stream_posn posn; struct snd_sof_pcm *spcm; - int direction; + int direction, ret; spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); if (!spcm) { @@ -493,7 +569,11 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) } stream = &spcm->stream[direction]; - snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); + ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); + if (ret < 0) { + dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret); + return; + } dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n", posn.host_posn, posn.xrun_comp_id, posn.xrun_size); @@ -520,7 +600,7 @@ static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd) ipc_xrun(sdev, msg_id); break; default: - dev_err(sdev->dev, "error: unhandled stream message %x\n", + dev_err(sdev->dev, "error: unhandled stream message %#x\n", msg_id); break; } @@ -672,24 +752,50 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, struct sof_ipc_fw_ready *ready = &sdev->fw_ready; struct sof_ipc_fw_version *v = &ready->version; struct sof_ipc_ctrl_data_params sparams; + struct snd_sof_widget *swidget; + bool widget_found = false; size_t send_bytes; int err; + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->comp_id == scontrol->comp_id) { + widget_found = true; + break; + } + } + + if (!widget_found) { + dev_err(sdev->dev, "error: can't find widget with id %d\n", scontrol->comp_id); + return -EINVAL; + } + + /* + * Volatile controls should always be part of static pipelines and the widget use_count + * would always be > 0 in this case. For the others, just return the cached value if the + * widget is not set up. + */ + if (!swidget->use_count) + return 0; + /* read or write firmware volume */ if (scontrol->readback_offset != 0) { /* write/read value header via mmaped region */ send_bytes = sizeof(struct sof_ipc_ctrl_value_chan) * cdata->num_elems; if (send) - snd_sof_dsp_block_write(sdev, sdev->mmio_bar, - scontrol->readback_offset, - cdata->chanv, send_bytes); + err = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM, + scontrol->readback_offset, + cdata->chanv, send_bytes); else - snd_sof_dsp_block_read(sdev, sdev->mmio_bar, - scontrol->readback_offset, - cdata->chanv, send_bytes); - return 0; + err = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_IRAM, + scontrol->readback_offset, + cdata->chanv, send_bytes); + + if (err) + dev_err_once(sdev->dev, "error: %s TYPE_IRAM failed\n", + send ? "write to" : "read from"); + return err; } cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; @@ -762,22 +868,6 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, } EXPORT_SYMBOL(snd_sof_ipc_set_get_comp_data); -/* - * IPC layer enumeration. - */ - -int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox, - size_t dspbox_size, u32 hostbox, - size_t hostbox_size) -{ - sdev->dsp_box.offset = dspbox; - sdev->dsp_box.size = dspbox_size; - sdev->host_box.offset = hostbox; - sdev->host_box.size = hostbox_size; - return 0; -} -EXPORT_SYMBOL(snd_sof_dsp_mailbox_init); - int snd_sof_ipc_valid(struct snd_sof_dev *sdev) { struct sof_ipc_fw_ready *ready = &sdev->fw_ready; @@ -829,6 +919,22 @@ int snd_sof_ipc_valid(struct snd_sof_dev *sdev) } EXPORT_SYMBOL(snd_sof_ipc_valid); +int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc_msg *msg; + + msg = &sdev->ipc->msg; + msg->msg_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + if (!msg->msg_data) + return -ENOMEM; + + msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + if (!msg->reply_data) + return -ENOMEM; + + return 0; +} + struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) { struct snd_sof_ipc *ipc; @@ -845,17 +951,6 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) /* indicate that we aren't sending a message ATM */ msg->ipc_complete = true; - /* pre-allocate message data */ - msg->msg_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, - GFP_KERNEL); - if (!msg->msg_data) - return NULL; - - msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, - GFP_KERNEL); - if (!msg->reply_data) - return NULL; - init_waitqueue_head(&msg->waitq); return ipc; |