Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2//
3// This file is provided under a dual BSD/GPLv2 license. When using or
4// redistributing this file, you may do so under either license.
5//
6// Copyright(c) 2021 Advanced Micro Devices, Inc.
7//
8// Authors: Balakishore Pati <Balakishore.pati@amd.com>
9// Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
10
11/* ACP-specific SOF IPC code */
12
13#include <linux/module.h>
14#include "../ops.h"
15#include "acp.h"
16#include "acp-dsp-offset.h"
17
18void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes)
19{
20 memcpy_to_scratch(sdev, offset, message, bytes);
21}
22EXPORT_SYMBOL_NS(acp_mailbox_write, SND_SOC_SOF_AMD_COMMON);
23
24void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes)
25{
26 memcpy_from_scratch(sdev, offset, message, bytes);
27}
28EXPORT_SYMBOL_NS(acp_mailbox_read, SND_SOC_SOF_AMD_COMMON);
29
30static void acpbus_trigger_host_to_dsp_swintr(struct acp_dev_data *adata)
31{
32 struct snd_sof_dev *sdev = adata->dev;
33 u32 swintr_trigger;
34
35 swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW_INTR_TRIG);
36 swintr_trigger |= 0x01;
37 snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SW_INTR_TRIG, swintr_trigger);
38}
39
40static void acp_ipc_host_msg_set(struct snd_sof_dev *sdev)
41{
42 unsigned int host_msg = offsetof(struct scratch_ipc_conf, sof_host_msg_write);
43
44 snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_msg, 1);
45}
46
47static void acp_dsp_ipc_host_done(struct snd_sof_dev *sdev)
48{
49 unsigned int dsp_msg = offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
50
51 snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg, 0);
52}
53
54static void acp_dsp_ipc_dsp_done(struct snd_sof_dev *sdev)
55{
56 unsigned int dsp_ack = offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
57
58 snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack, 0);
59}
60
61int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
62{
63 struct acp_dev_data *adata = sdev->pdata->hw_pdata;
64 unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box);
65
66 acp_mailbox_write(sdev, offset, msg->msg_data, msg->msg_size);
67 acp_ipc_host_msg_set(sdev);
68
69 /* Trigger host to dsp interrupt for the msg */
70 acpbus_trigger_host_to_dsp_swintr(adata);
71 return 0;
72}
73EXPORT_SYMBOL_NS(acp_sof_ipc_send_msg, SND_SOC_SOF_AMD_COMMON);
74
75static void acp_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
76{
77 struct snd_sof_ipc_msg *msg = sdev->msg;
78 struct sof_ipc_reply reply;
79 struct sof_ipc_cmd_hdr *hdr;
80 unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box);
81 int ret = 0;
82
83 /*
84 * Sometimes, there is unexpected reply ipc arriving. The reply
85 * ipc belongs to none of the ipcs sent from driver.
86 * In this case, the driver must ignore the ipc.
87 */
88 if (!msg) {
89 dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
90 return;
91 }
92 hdr = msg->msg_data;
93 if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE) ||
94 hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {
95 /*
96 * memory windows are powered off before sending IPC reply,
97 * so we can't read the mailbox for CTX_SAVE and PM_GATE
98 * replies.
99 */
100 reply.error = 0;
101 reply.hdr.cmd = SOF_IPC_GLB_REPLY;
102 reply.hdr.size = sizeof(reply);
103 memcpy(msg->reply_data, &reply, sizeof(reply));
104 goto out;
105 }
106 /* get IPC reply from DSP in the mailbox */
107 acp_mailbox_read(sdev, offset, &reply, sizeof(reply));
108 if (reply.error < 0) {
109 memcpy(msg->reply_data, &reply, sizeof(reply));
110 ret = reply.error;
111 } else {
112 /* reply correct size ? */
113 if (reply.hdr.size != msg->reply_size &&
114 !(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) {
115 dev_err(sdev->dev, "reply expected %zu got %u bytes\n",
116 msg->reply_size, reply.hdr.size);
117 ret = -EINVAL;
118 }
119 /* read the message */
120 if (msg->reply_size > 0)
121 acp_mailbox_read(sdev, offset, msg->reply_data, msg->reply_size);
122 }
123out:
124 msg->reply_error = ret;
125}
126
127irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context)
128{
129 struct snd_sof_dev *sdev = context;
130 unsigned int dsp_msg_write = offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
131 unsigned int dsp_ack_write = offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
132 bool ipc_irq = false;
133 int dsp_msg, dsp_ack;
134
135 dsp_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write);
136 if (dsp_msg) {
137 snd_sof_ipc_msgs_rx(sdev);
138 acp_dsp_ipc_host_done(sdev);
139 ipc_irq = true;
140 }
141
142 dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write);
143 if (dsp_ack) {
144 spin_lock_irq(&sdev->ipc_lock);
145 /* handle immediate reply from DSP core */
146 acp_dsp_ipc_get_reply(sdev);
147 snd_sof_ipc_reply(sdev, 0);
148 /* set the done bit */
149 acp_dsp_ipc_dsp_done(sdev);
150 spin_unlock_irq(&sdev->ipc_lock);
151 ipc_irq = true;
152 }
153
154 if (!ipc_irq)
155 dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
156
157 return IRQ_HANDLED;
158}
159EXPORT_SYMBOL_NS(acp_sof_ipc_irq_thread, SND_SOC_SOF_AMD_COMMON);
160
161int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
162 void *p, size_t sz)
163{
164 unsigned int offset = offsetof(struct scratch_ipc_conf, sof_out_box);
165
166 if (!substream || !sdev->stream_box.size)
167 acp_mailbox_read(sdev, offset, p, sz);
168
169 return 0;
170}
171EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, SND_SOC_SOF_AMD_COMMON);
172
173int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
174 const struct sof_ipc_pcm_params_reply *reply)
175{
176 /* TODO: Implement stream hw params to validate stream offset */
177 return 0;
178}
179EXPORT_SYMBOL_NS(acp_sof_ipc_pcm_params, SND_SOC_SOF_AMD_COMMON);
180
181int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
182{
183 return ACP_SCRATCH_MEMORY_ADDRESS;
184}
185EXPORT_SYMBOL_NS(acp_sof_ipc_get_mailbox_offset, SND_SOC_SOF_AMD_COMMON);
186
187MODULE_DESCRIPTION("AMD ACP sof-ipc driver");