0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/irqreturn.h>
0009 #include "core.h"
0010 #include "messages.h"
0011 #include "registers.h"
0012 #include "trace.h"
0013
0014 #define CATPT_IPC_TIMEOUT_MS 300
0015
0016 void catpt_ipc_init(struct catpt_ipc *ipc, struct device *dev)
0017 {
0018 ipc->dev = dev;
0019 ipc->ready = false;
0020 ipc->default_timeout = CATPT_IPC_TIMEOUT_MS;
0021 init_completion(&ipc->done_completion);
0022 init_completion(&ipc->busy_completion);
0023 spin_lock_init(&ipc->lock);
0024 mutex_init(&ipc->mutex);
0025 }
0026
0027 static int catpt_ipc_arm(struct catpt_ipc *ipc, struct catpt_fw_ready *config)
0028 {
0029
0030
0031
0032
0033
0034 ipc->rx.data = devm_kzalloc(ipc->dev, config->outbox_size, GFP_KERNEL);
0035 if (!ipc->rx.data)
0036 return -ENOMEM;
0037
0038 memcpy(&ipc->config, config, sizeof(*config));
0039 ipc->ready = true;
0040
0041 return 0;
0042 }
0043
0044 static void catpt_ipc_msg_init(struct catpt_ipc *ipc,
0045 struct catpt_ipc_msg *reply)
0046 {
0047 lockdep_assert_held(&ipc->lock);
0048
0049 ipc->rx.header = 0;
0050 ipc->rx.size = reply ? reply->size : 0;
0051 reinit_completion(&ipc->done_completion);
0052 reinit_completion(&ipc->busy_completion);
0053 }
0054
0055 static void catpt_dsp_send_tx(struct catpt_dev *cdev,
0056 const struct catpt_ipc_msg *tx)
0057 {
0058 u32 header = tx->header | CATPT_IPCC_BUSY;
0059
0060 trace_catpt_ipc_request(header);
0061 trace_catpt_ipc_payload(tx->data, tx->size);
0062
0063 memcpy_toio(catpt_outbox_addr(cdev), tx->data, tx->size);
0064 catpt_writel_shim(cdev, IPCC, header);
0065 }
0066
0067 static int catpt_wait_msg_completion(struct catpt_dev *cdev, int timeout)
0068 {
0069 struct catpt_ipc *ipc = &cdev->ipc;
0070 int ret;
0071
0072 ret = wait_for_completion_timeout(&ipc->done_completion,
0073 msecs_to_jiffies(timeout));
0074 if (!ret)
0075 return -ETIMEDOUT;
0076 if (ipc->rx.rsp.status != CATPT_REPLY_PENDING)
0077 return 0;
0078
0079
0080 ret = wait_for_completion_timeout(&ipc->busy_completion,
0081 msecs_to_jiffies(timeout));
0082 return ret ? 0 : -ETIMEDOUT;
0083 }
0084
0085 static int catpt_dsp_do_send_msg(struct catpt_dev *cdev,
0086 struct catpt_ipc_msg request,
0087 struct catpt_ipc_msg *reply, int timeout)
0088 {
0089 struct catpt_ipc *ipc = &cdev->ipc;
0090 unsigned long flags;
0091 int ret;
0092
0093 if (!ipc->ready)
0094 return -EPERM;
0095 if (request.size > ipc->config.outbox_size ||
0096 (reply && reply->size > ipc->config.outbox_size))
0097 return -EINVAL;
0098
0099 spin_lock_irqsave(&ipc->lock, flags);
0100 catpt_ipc_msg_init(ipc, reply);
0101 catpt_dsp_send_tx(cdev, &request);
0102 spin_unlock_irqrestore(&ipc->lock, flags);
0103
0104 ret = catpt_wait_msg_completion(cdev, timeout);
0105 if (ret) {
0106 dev_crit(cdev->dev, "communication severed: %d, rebooting dsp..\n",
0107 ret);
0108 ipc->ready = false;
0109
0110 return ret;
0111 }
0112
0113 ret = ipc->rx.rsp.status;
0114 if (reply) {
0115 reply->header = ipc->rx.header;
0116
0117 if (!ret && reply->data)
0118 memcpy(reply->data, ipc->rx.data, reply->size);
0119 }
0120
0121 return ret;
0122 }
0123
0124 int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev,
0125 struct catpt_ipc_msg request,
0126 struct catpt_ipc_msg *reply, int timeout)
0127 {
0128 struct catpt_ipc *ipc = &cdev->ipc;
0129 int ret;
0130
0131 mutex_lock(&ipc->mutex);
0132 ret = catpt_dsp_do_send_msg(cdev, request, reply, timeout);
0133 mutex_unlock(&ipc->mutex);
0134
0135 return ret;
0136 }
0137
0138 int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request,
0139 struct catpt_ipc_msg *reply)
0140 {
0141 return catpt_dsp_send_msg_timeout(cdev, request, reply,
0142 cdev->ipc.default_timeout);
0143 }
0144
0145 static void
0146 catpt_dsp_notify_stream(struct catpt_dev *cdev, union catpt_notify_msg msg)
0147 {
0148 struct catpt_stream_runtime *stream;
0149 struct catpt_notify_position pos;
0150 struct catpt_notify_glitch glitch;
0151
0152 stream = catpt_stream_find(cdev, msg.stream_hw_id);
0153 if (!stream) {
0154 dev_warn(cdev->dev, "notify %d for non-existent stream %d\n",
0155 msg.notify_reason, msg.stream_hw_id);
0156 return;
0157 }
0158
0159 switch (msg.notify_reason) {
0160 case CATPT_NOTIFY_POSITION_CHANGED:
0161 memcpy_fromio(&pos, catpt_inbox_addr(cdev), sizeof(pos));
0162 trace_catpt_ipc_payload((u8 *)&pos, sizeof(pos));
0163
0164 catpt_stream_update_position(cdev, stream, &pos);
0165 break;
0166
0167 case CATPT_NOTIFY_GLITCH_OCCURRED:
0168 memcpy_fromio(&glitch, catpt_inbox_addr(cdev), sizeof(glitch));
0169 trace_catpt_ipc_payload((u8 *)&glitch, sizeof(glitch));
0170
0171 dev_warn(cdev->dev, "glitch %d at pos: 0x%08llx, wp: 0x%08x\n",
0172 glitch.type, glitch.presentation_pos,
0173 glitch.write_pos);
0174 break;
0175
0176 default:
0177 dev_warn(cdev->dev, "unknown notification: %d received\n",
0178 msg.notify_reason);
0179 break;
0180 }
0181 }
0182
0183 static void catpt_dsp_copy_rx(struct catpt_dev *cdev, u32 header)
0184 {
0185 struct catpt_ipc *ipc = &cdev->ipc;
0186
0187 ipc->rx.header = header;
0188 if (ipc->rx.rsp.status != CATPT_REPLY_SUCCESS)
0189 return;
0190
0191 memcpy_fromio(ipc->rx.data, catpt_outbox_addr(cdev), ipc->rx.size);
0192 trace_catpt_ipc_payload(ipc->rx.data, ipc->rx.size);
0193 }
0194
0195 static void catpt_dsp_process_response(struct catpt_dev *cdev, u32 header)
0196 {
0197 union catpt_notify_msg msg = CATPT_MSG(header);
0198 struct catpt_ipc *ipc = &cdev->ipc;
0199
0200 if (msg.fw_ready) {
0201 struct catpt_fw_ready config;
0202
0203 u32 off = msg.mailbox_address << 3;
0204
0205 memcpy_fromio(&config, cdev->lpe_ba + off, sizeof(config));
0206 trace_catpt_ipc_payload((u8 *)&config, sizeof(config));
0207
0208 catpt_ipc_arm(ipc, &config);
0209 complete(&cdev->fw_ready);
0210 return;
0211 }
0212
0213 switch (msg.global_msg_type) {
0214 case CATPT_GLB_REQUEST_CORE_DUMP:
0215 dev_err(cdev->dev, "ADSP device coredump received\n");
0216 ipc->ready = false;
0217 catpt_coredump(cdev);
0218
0219 break;
0220
0221 case CATPT_GLB_STREAM_MESSAGE:
0222 switch (msg.stream_msg_type) {
0223 case CATPT_STRM_NOTIFICATION:
0224 catpt_dsp_notify_stream(cdev, msg);
0225 break;
0226 default:
0227 catpt_dsp_copy_rx(cdev, header);
0228
0229 complete(&ipc->busy_completion);
0230 break;
0231 }
0232 break;
0233
0234 default:
0235 dev_warn(cdev->dev, "unknown response: %d received\n",
0236 msg.global_msg_type);
0237 break;
0238 }
0239 }
0240
0241 irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id)
0242 {
0243 struct catpt_dev *cdev = dev_id;
0244 u32 ipcd;
0245
0246 ipcd = catpt_readl_shim(cdev, IPCD);
0247 trace_catpt_ipc_notify(ipcd);
0248
0249
0250 if (!(ipcd & CATPT_IPCD_BUSY))
0251 return IRQ_NONE;
0252
0253 catpt_dsp_process_response(cdev, ipcd);
0254
0255
0256 catpt_updatel_shim(cdev, IPCD, CATPT_IPCD_BUSY | CATPT_IPCD_DONE,
0257 CATPT_IPCD_DONE);
0258
0259 catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB, 0);
0260
0261 return IRQ_HANDLED;
0262 }
0263
0264 irqreturn_t catpt_dsp_irq_handler(int irq, void *dev_id)
0265 {
0266 struct catpt_dev *cdev = dev_id;
0267 irqreturn_t ret = IRQ_NONE;
0268 u32 isc, ipcc;
0269
0270 isc = catpt_readl_shim(cdev, ISC);
0271 trace_catpt_irq(isc);
0272
0273
0274 if (isc & CATPT_ISC_IPCCD) {
0275
0276 catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCCD, CATPT_IMC_IPCCD);
0277
0278 ipcc = catpt_readl_shim(cdev, IPCC);
0279 trace_catpt_ipc_reply(ipcc);
0280 catpt_dsp_copy_rx(cdev, ipcc);
0281 complete(&cdev->ipc.done_completion);
0282
0283
0284 catpt_updatel_shim(cdev, IPCC, CATPT_IPCC_DONE, 0);
0285
0286 catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCCD, 0);
0287 ret = IRQ_HANDLED;
0288 }
0289
0290
0291 if (isc & CATPT_ISC_IPCDB) {
0292
0293 catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB, CATPT_IMC_IPCDB);
0294 ret = IRQ_WAKE_THREAD;
0295 }
0296
0297 return ret;
0298 }