162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 462306a36Sopenharmony_ci// redistributing this file, you may do so under either license. 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> 962306a36Sopenharmony_ci// Ranjani Sridharan <ranjani.sridharan@linux.intel.com> 1062306a36Sopenharmony_ci// Rander Wang <rander.wang@intel.com> 1162306a36Sopenharmony_ci// Keyon Jie <yang.jie@linux.intel.com> 1262306a36Sopenharmony_ci// 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * Hardware interface for generic Intel audio DSP HDA IP 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <sound/sof/ipc4/header.h> 1962306a36Sopenharmony_ci#include <trace/events/sof_intel.h> 2062306a36Sopenharmony_ci#include "../ops.h" 2162306a36Sopenharmony_ci#include "hda.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void hda_dsp_ipc_host_done(struct snd_sof_dev *sdev) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci /* 2662306a36Sopenharmony_ci * tell DSP cmd is done - clear busy 2762306a36Sopenharmony_ci * interrupt and send reply msg to dsp 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, 3062306a36Sopenharmony_ci HDA_DSP_REG_HIPCT, 3162306a36Sopenharmony_ci HDA_DSP_REG_HIPCT_BUSY, 3262306a36Sopenharmony_ci HDA_DSP_REG_HIPCT_BUSY); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci /* unmask BUSY interrupt */ 3562306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 3662306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL, 3762306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL_BUSY, 3862306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL_BUSY); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void hda_dsp_ipc_dsp_done(struct snd_sof_dev *sdev) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci /* 4462306a36Sopenharmony_ci * set DONE bit - tell DSP we have received the reply msg 4562306a36Sopenharmony_ci * from DSP, and processed it, don't send more reply to host 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, 4862306a36Sopenharmony_ci HDA_DSP_REG_HIPCIE, 4962306a36Sopenharmony_ci HDA_DSP_REG_HIPCIE_DONE, 5062306a36Sopenharmony_ci HDA_DSP_REG_HIPCIE_DONE); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* unmask Done interrupt */ 5362306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 5462306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL, 5562306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL_DONE, 5662306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL_DONE); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ciint hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci /* send IPC message to DSP */ 6262306a36Sopenharmony_ci sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, 6362306a36Sopenharmony_ci msg->msg_size); 6462306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI, 6562306a36Sopenharmony_ci HDA_DSP_REG_HIPCI_BUSY); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic inline bool hda_dsp_ipc4_pm_msg(u32 primary) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci /* pm setting is only supported by module msg */ 7362306a36Sopenharmony_ci if (SOF_IPC4_MSG_IS_MODULE_MSG(primary) != SOF_IPC4_MODULE_MSG) 7462306a36Sopenharmony_ci return false; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_DX || 7762306a36Sopenharmony_ci SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_D0IX) 7862306a36Sopenharmony_ci return true; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return false; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_civoid hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev, 8462306a36Sopenharmony_ci struct snd_sof_ipc_msg *msg) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct sof_ipc4_msg *msg_data = msg->msg_data; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* Schedule a delayed work for d0i3 entry after sending non-pm ipc msg */ 8962306a36Sopenharmony_ci if (hda_dsp_ipc4_pm_msg(msg_data->primary)) 9062306a36Sopenharmony_ci return; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci mod_delayed_work(system_wq, &hdev->d0i3_work, 9362306a36Sopenharmony_ci msecs_to_jiffies(SOF_HDA_D0I3_WORK_DELAY_MS)); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ciint hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; 9962306a36Sopenharmony_ci struct sof_ipc4_msg *msg_data = msg->msg_data; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (hda_ipc4_tx_is_busy(sdev)) { 10262306a36Sopenharmony_ci hdev->delayed_ipc_tx_msg = msg; 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci hdev->delayed_ipc_tx_msg = NULL; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* send the message via mailbox */ 10962306a36Sopenharmony_ci if (msg_data->data_size) 11062306a36Sopenharmony_ci sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr, 11162306a36Sopenharmony_ci msg_data->data_size); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE, msg_data->extension); 11462306a36Sopenharmony_ci snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI, 11562306a36Sopenharmony_ci msg_data->primary | HDA_DSP_REG_HIPCI_BUSY); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci hda_dsp_ipc4_schedule_d0i3_work(hdev, msg); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_civoid hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct snd_sof_ipc_msg *msg = sdev->msg; 12562306a36Sopenharmony_ci struct sof_ipc_reply reply; 12662306a36Sopenharmony_ci struct sof_ipc_cmd_hdr *hdr; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* 12962306a36Sopenharmony_ci * Sometimes, there is unexpected reply ipc arriving. The reply 13062306a36Sopenharmony_ci * ipc belongs to none of the ipcs sent from driver. 13162306a36Sopenharmony_ci * In this case, the driver must ignore the ipc. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci if (!msg) { 13462306a36Sopenharmony_ci dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); 13562306a36Sopenharmony_ci return; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci hdr = msg->msg_data; 13962306a36Sopenharmony_ci if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE) || 14062306a36Sopenharmony_ci hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) { 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * memory windows are powered off before sending IPC reply, 14362306a36Sopenharmony_ci * so we can't read the mailbox for CTX_SAVE and PM_GATE 14462306a36Sopenharmony_ci * replies. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ci reply.error = 0; 14762306a36Sopenharmony_ci reply.hdr.cmd = SOF_IPC_GLB_REPLY; 14862306a36Sopenharmony_ci reply.hdr.size = sizeof(reply); 14962306a36Sopenharmony_ci memcpy(msg->reply_data, &reply, sizeof(reply)); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci msg->reply_error = 0; 15262306a36Sopenharmony_ci } else { 15362306a36Sopenharmony_ci snd_sof_ipc_get_reply(sdev); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ciirqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct sof_ipc4_msg notification_data = {{ 0 }}; 16062306a36Sopenharmony_ci struct snd_sof_dev *sdev = context; 16162306a36Sopenharmony_ci bool ack_received = false; 16262306a36Sopenharmony_ci bool ipc_irq = false; 16362306a36Sopenharmony_ci u32 hipcie, hipct; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE); 16662306a36Sopenharmony_ci hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (hipcie & HDA_DSP_REG_HIPCIE_DONE) { 16962306a36Sopenharmony_ci /* DSP received the message */ 17062306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL, 17162306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL_DONE, 0); 17262306a36Sopenharmony_ci hda_dsp_ipc_dsp_done(sdev); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ipc_irq = true; 17562306a36Sopenharmony_ci ack_received = true; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (hipct & HDA_DSP_REG_HIPCT_BUSY) { 17962306a36Sopenharmony_ci /* Message from DSP (reply or notification) */ 18062306a36Sopenharmony_ci u32 hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, 18162306a36Sopenharmony_ci HDA_DSP_REG_HIPCTE); 18262306a36Sopenharmony_ci u32 primary = hipct & HDA_DSP_REG_HIPCT_MSG_MASK; 18362306a36Sopenharmony_ci u32 extension = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* mask BUSY interrupt */ 18662306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL, 18762306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL_BUSY, 0); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (primary & SOF_IPC4_MSG_DIR_MASK) { 19062306a36Sopenharmony_ci /* Reply received */ 19162306a36Sopenharmony_ci if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) { 19262306a36Sopenharmony_ci struct sof_ipc4_msg *data = sdev->ipc->msg.reply_data; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci data->primary = primary; 19562306a36Sopenharmony_ci data->extension = extension; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci spin_lock_irq(&sdev->ipc_lock); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci snd_sof_ipc_get_reply(sdev); 20062306a36Sopenharmony_ci hda_dsp_ipc_host_done(sdev); 20162306a36Sopenharmony_ci snd_sof_ipc_reply(sdev, data->primary); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci spin_unlock_irq(&sdev->ipc_lock); 20462306a36Sopenharmony_ci } else { 20562306a36Sopenharmony_ci dev_dbg_ratelimited(sdev->dev, 20662306a36Sopenharmony_ci "IPC reply before FW_READY: %#x|%#x\n", 20762306a36Sopenharmony_ci primary, extension); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci } else { 21062306a36Sopenharmony_ci /* Notification received */ 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci notification_data.primary = primary; 21362306a36Sopenharmony_ci notification_data.extension = extension; 21462306a36Sopenharmony_ci sdev->ipc->msg.rx_data = ¬ification_data; 21562306a36Sopenharmony_ci snd_sof_ipc_msgs_rx(sdev); 21662306a36Sopenharmony_ci sdev->ipc->msg.rx_data = NULL; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* Let DSP know that we have finished processing the message */ 21962306a36Sopenharmony_ci hda_dsp_ipc_host_done(sdev); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ipc_irq = true; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (!ipc_irq) 22662306a36Sopenharmony_ci /* This interrupt is not shared so no need to return IRQ_NONE. */ 22762306a36Sopenharmony_ci dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n"); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (ack_received) { 23062306a36Sopenharmony_ci struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (hdev->delayed_ipc_tx_msg) 23362306a36Sopenharmony_ci hda_dsp_ipc4_send_msg(sdev, hdev->delayed_ipc_tx_msg); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return IRQ_HANDLED; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/* IPC handler thread */ 24062306a36Sopenharmony_ciirqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct snd_sof_dev *sdev = context; 24362306a36Sopenharmony_ci u32 hipci; 24462306a36Sopenharmony_ci u32 hipcie; 24562306a36Sopenharmony_ci u32 hipct; 24662306a36Sopenharmony_ci u32 hipcte; 24762306a36Sopenharmony_ci u32 msg; 24862306a36Sopenharmony_ci u32 msg_ext; 24962306a36Sopenharmony_ci bool ipc_irq = false; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* read IPC status */ 25262306a36Sopenharmony_ci hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, 25362306a36Sopenharmony_ci HDA_DSP_REG_HIPCIE); 25462306a36Sopenharmony_ci hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); 25562306a36Sopenharmony_ci hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI); 25662306a36Sopenharmony_ci hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCTE); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* is this a reply message from the DSP */ 25962306a36Sopenharmony_ci if (hipcie & HDA_DSP_REG_HIPCIE_DONE) { 26062306a36Sopenharmony_ci msg = hipci & HDA_DSP_REG_HIPCI_MSG_MASK; 26162306a36Sopenharmony_ci msg_ext = hipcie & HDA_DSP_REG_HIPCIE_MSG_MASK; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci trace_sof_intel_ipc_firmware_response(sdev, msg, msg_ext); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* mask Done interrupt */ 26662306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 26762306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL, 26862306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL_DONE, 0); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* 27162306a36Sopenharmony_ci * Make sure the interrupt thread cannot be preempted between 27262306a36Sopenharmony_ci * waking up the sender and re-enabling the interrupt. Also 27362306a36Sopenharmony_ci * protect against a theoretical race with sof_ipc_tx_message(): 27462306a36Sopenharmony_ci * if the DSP is fast enough to receive an IPC message, reply to 27562306a36Sopenharmony_ci * it, and the host interrupt processing calls this function on 27662306a36Sopenharmony_ci * a different core from the one, where the sending is taking 27762306a36Sopenharmony_ci * place, the message might not yet be marked as expecting a 27862306a36Sopenharmony_ci * reply. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) { 28162306a36Sopenharmony_ci spin_lock_irq(&sdev->ipc_lock); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* handle immediate reply from DSP core */ 28462306a36Sopenharmony_ci hda_dsp_ipc_get_reply(sdev); 28562306a36Sopenharmony_ci snd_sof_ipc_reply(sdev, msg); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* set the done bit */ 28862306a36Sopenharmony_ci hda_dsp_ipc_dsp_done(sdev); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci spin_unlock_irq(&sdev->ipc_lock); 29162306a36Sopenharmony_ci } else { 29262306a36Sopenharmony_ci dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_READY: %#x\n", 29362306a36Sopenharmony_ci msg); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci ipc_irq = true; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* is this a new message from DSP */ 30062306a36Sopenharmony_ci if (hipct & HDA_DSP_REG_HIPCT_BUSY) { 30162306a36Sopenharmony_ci msg = hipct & HDA_DSP_REG_HIPCT_MSG_MASK; 30262306a36Sopenharmony_ci msg_ext = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci trace_sof_intel_ipc_firmware_initiated(sdev, msg, msg_ext); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* mask BUSY interrupt */ 30762306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 30862306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL, 30962306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL_BUSY, 0); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* handle messages from DSP */ 31262306a36Sopenharmony_ci if ((hipct & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { 31362306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 31462306a36Sopenharmony_ci bool non_recoverable = true; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* 31762306a36Sopenharmony_ci * This is a PANIC message! 31862306a36Sopenharmony_ci * 31962306a36Sopenharmony_ci * If it is arriving during firmware boot and it is not 32062306a36Sopenharmony_ci * the last boot attempt then change the non_recoverable 32162306a36Sopenharmony_ci * to false as the DSP might be able to boot in the next 32262306a36Sopenharmony_ci * iteration(s) 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_ci if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS && 32562306a36Sopenharmony_ci hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS) 32662306a36Sopenharmony_ci non_recoverable = false; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext), 32962306a36Sopenharmony_ci non_recoverable); 33062306a36Sopenharmony_ci } else { 33162306a36Sopenharmony_ci /* normal message - process normally */ 33262306a36Sopenharmony_ci snd_sof_ipc_msgs_rx(sdev); 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci hda_dsp_ipc_host_done(sdev); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci ipc_irq = true; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (!ipc_irq) { 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * This interrupt is not shared so no need to return IRQ_NONE. 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_ci dev_dbg_ratelimited(sdev->dev, 34562306a36Sopenharmony_ci "nothing to do in IPC IRQ thread\n"); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return IRQ_HANDLED; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/* Check if an IPC IRQ occurred */ 35262306a36Sopenharmony_cibool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 35562306a36Sopenharmony_ci bool ret = false; 35662306a36Sopenharmony_ci u32 irq_status; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (sdev->dspless_mode_selected) 35962306a36Sopenharmony_ci return false; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* store status */ 36262306a36Sopenharmony_ci irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS); 36362306a36Sopenharmony_ci trace_sof_intel_hda_irq_ipc_check(sdev, irq_status); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* invalid message ? */ 36662306a36Sopenharmony_ci if (irq_status == 0xffffffff) 36762306a36Sopenharmony_ci goto out; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* IPC message ? */ 37062306a36Sopenharmony_ci if (irq_status & HDA_DSP_ADSPIS_IPC) 37162306a36Sopenharmony_ci ret = true; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* CLDMA message ? */ 37462306a36Sopenharmony_ci if (irq_status & HDA_DSP_ADSPIS_CL_DMA) { 37562306a36Sopenharmony_ci hda->code_loading = 0; 37662306a36Sopenharmony_ci wake_up(&hda->waitq); 37762306a36Sopenharmony_ci ret = false; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ciout: 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ciint hda_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci return HDA_DSP_MBOX_UPLINK_OFFSET; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ciint hda_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci return SRAM_WINDOW_OFFSET(id); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ciint hda_ipc_msg_data(struct snd_sof_dev *sdev, 39562306a36Sopenharmony_ci struct snd_sof_pcm_stream *sps, 39662306a36Sopenharmony_ci void *p, size_t sz) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci if (!sps || !sdev->stream_box.size) { 39962306a36Sopenharmony_ci sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); 40062306a36Sopenharmony_ci } else { 40162306a36Sopenharmony_ci struct snd_pcm_substream *substream = sps->substream; 40262306a36Sopenharmony_ci struct hdac_stream *hstream = substream->runtime->private_data; 40362306a36Sopenharmony_ci struct sof_intel_hda_stream *hda_stream; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci hda_stream = container_of(hstream, 40662306a36Sopenharmony_ci struct sof_intel_hda_stream, 40762306a36Sopenharmony_ci hext_stream.hstream); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* The stream might already be closed */ 41062306a36Sopenharmony_ci if (!hstream) 41162306a36Sopenharmony_ci return -ESTRPIPE; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci sof_mailbox_read(sdev, hda_stream->sof_intel_stream.posn_offset, p, sz); 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ciint hda_set_stream_data_offset(struct snd_sof_dev *sdev, 42062306a36Sopenharmony_ci struct snd_sof_pcm_stream *sps, 42162306a36Sopenharmony_ci size_t posn_offset) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct snd_pcm_substream *substream = sps->substream; 42462306a36Sopenharmony_ci struct hdac_stream *hstream = substream->runtime->private_data; 42562306a36Sopenharmony_ci struct sof_intel_hda_stream *hda_stream; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci hda_stream = container_of(hstream, struct sof_intel_hda_stream, 42862306a36Sopenharmony_ci hext_stream.hstream); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* check for unaligned offset or overflow */ 43162306a36Sopenharmony_ci if (posn_offset > sdev->stream_box.size || 43262306a36Sopenharmony_ci posn_offset % sizeof(struct sof_ipc_stream_posn) != 0) 43362306a36Sopenharmony_ci return -EINVAL; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci hda_stream->sof_intel_stream.posn_offset = sdev->stream_box.offset + posn_offset; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu", 43862306a36Sopenharmony_ci substream->stream, hda_stream->sof_intel_stream.posn_offset); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return 0; 44162306a36Sopenharmony_ci} 442