ipc.c (c16c8bfa09d5f318c1bd65698d058d3739970c24) ipc.c (2f1f570cd730c81807ae143a83766068dd82d577)
1// SPDX-License-Identifier: GPL-2.0-only
2//
3// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
4//
5// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
6// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
7//
8
9#include <linux/slab.h>
10#include <sound/hdaudio_ext.h>
11#include "avs.h"
12#include "messages.h"
13#include "registers.h"
14
15#define AVS_IPC_TIMEOUT_MS 300
16
1// SPDX-License-Identifier: GPL-2.0-only
2//
3// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
4//
5// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
6// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
7//
8
9#include <linux/slab.h>
10#include <sound/hdaudio_ext.h>
11#include "avs.h"
12#include "messages.h"
13#include "registers.h"
14
15#define AVS_IPC_TIMEOUT_MS 300
16
17static void avs_dsp_recovery(struct avs_dev *adev)
18{
19 struct avs_soc_component *acomp;
20 unsigned int core_mask;
21 int ret;
22
23 mutex_lock(&adev->comp_list_mutex);
24 /* disconnect all running streams */
25 list_for_each_entry(acomp, &adev->comp_list, node) {
26 struct snd_soc_pcm_runtime *rtd;
27 struct snd_soc_card *card;
28
29 card = acomp->base.card;
30 if (!card)
31 continue;
32
33 for_each_card_rtds(card, rtd) {
34 struct snd_pcm *pcm;
35 int dir;
36
37 pcm = rtd->pcm;
38 if (!pcm || rtd->dai_link->no_pcm)
39 continue;
40
41 for_each_pcm_streams(dir) {
42 struct snd_pcm_substream *substream;
43
44 substream = pcm->streams[dir].substream;
45 if (!substream || !substream->runtime)
46 continue;
47
48 snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
49 }
50 }
51 }
52 mutex_unlock(&adev->comp_list_mutex);
53
54 /* forcibly shutdown all cores */
55 core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0);
56 avs_dsp_core_disable(adev, core_mask);
57
58 /* attempt dsp reboot */
59 ret = avs_dsp_boot_firmware(adev, true);
60 if (ret < 0)
61 dev_err(adev->dev, "dsp reboot failed: %d\n", ret);
62
63 pm_runtime_mark_last_busy(adev->dev);
64 pm_runtime_enable(adev->dev);
65 pm_request_autosuspend(adev->dev);
66
67 atomic_set(&adev->ipc->recovering, 0);
68}
69
70static void avs_dsp_recovery_work(struct work_struct *work)
71{
72 struct avs_ipc *ipc = container_of(work, struct avs_ipc, recovery_work);
73
74 avs_dsp_recovery(to_avs_dev(ipc->dev));
75}
76
77static void avs_dsp_exception_caught(struct avs_dev *adev, union avs_notify_msg *msg)
78{
79 struct avs_ipc *ipc = adev->ipc;
80
81 /* Account for the double-exception case. */
82 ipc->ready = false;
83
84 if (!atomic_add_unless(&ipc->recovering, 1, 1)) {
85 dev_err(adev->dev, "dsp recovery is already in progress\n");
86 return;
87 }
88
89 dev_crit(adev->dev, "communication severed, rebooting dsp..\n");
90
91 /* Re-enabled on recovery completion. */
92 pm_runtime_disable(adev->dev);
93
94 /* Process received notification. */
95 avs_dsp_op(adev, coredump, msg);
96
97 schedule_work(&ipc->recovery_work);
98}
99
17static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header)
18{
19 struct avs_ipc *ipc = adev->ipc;
20 union avs_reply_msg msg = AVS_MSG(header);
21
22 ipc->rx.header = header;
23 /* Abort copying payload if request processing was unsuccessful. */
24 if (!msg.status) {

--- 27 unchanged lines hidden (view full) ---

52 case AVS_NOTIFY_PHRASE_DETECTED:
53 data_size = sizeof(struct avs_notify_voice_data);
54 break;
55
56 case AVS_NOTIFY_RESOURCE_EVENT:
57 data_size = sizeof(struct avs_notify_res_data);
58 break;
59
100static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header)
101{
102 struct avs_ipc *ipc = adev->ipc;
103 union avs_reply_msg msg = AVS_MSG(header);
104
105 ipc->rx.header = header;
106 /* Abort copying payload if request processing was unsuccessful. */
107 if (!msg.status) {

--- 27 unchanged lines hidden (view full) ---

135 case AVS_NOTIFY_PHRASE_DETECTED:
136 data_size = sizeof(struct avs_notify_voice_data);
137 break;
138
139 case AVS_NOTIFY_RESOURCE_EVENT:
140 data_size = sizeof(struct avs_notify_res_data);
141 break;
142
143 case AVS_NOTIFY_EXCEPTION_CAUGHT:
144 break;
145
60 case AVS_NOTIFY_MODULE_EVENT:
61 /* To know the total payload size, header needs to be read first. */
62 memcpy_fromio(&mod_data, avs_uplink_addr(adev), sizeof(mod_data));
63 data_size = sizeof(mod_data) + mod_data.data_size;
64 break;
65
66 default:
67 dev_info(adev->dev, "unknown notification: 0x%08x\n", msg.primary);

--- 11 unchanged lines hidden (view full) ---

79 /* Perform notification-specific operations. */
80 switch (msg.notify_msg_type) {
81 case AVS_NOTIFY_FW_READY:
82 dev_dbg(adev->dev, "FW READY 0x%08x\n", msg.primary);
83 adev->ipc->ready = true;
84 complete(&adev->fw_ready);
85 break;
86
146 case AVS_NOTIFY_MODULE_EVENT:
147 /* To know the total payload size, header needs to be read first. */
148 memcpy_fromio(&mod_data, avs_uplink_addr(adev), sizeof(mod_data));
149 data_size = sizeof(mod_data) + mod_data.data_size;
150 break;
151
152 default:
153 dev_info(adev->dev, "unknown notification: 0x%08x\n", msg.primary);

--- 11 unchanged lines hidden (view full) ---

165 /* Perform notification-specific operations. */
166 switch (msg.notify_msg_type) {
167 case AVS_NOTIFY_FW_READY:
168 dev_dbg(adev->dev, "FW READY 0x%08x\n", msg.primary);
169 adev->ipc->ready = true;
170 complete(&adev->fw_ready);
171 break;
172
173 case AVS_NOTIFY_EXCEPTION_CAUGHT:
174 avs_dsp_exception_caught(adev, &msg);
175 break;
176
87 default:
88 break;
89 }
90
91 kfree(data);
92}
93
94void avs_dsp_process_response(struct avs_dev *adev, u64 header)

--- 178 unchanged lines hidden (view full) ---

273 spin_lock(&ipc->rx_lock);
274 avs_ipc_msg_init(ipc, reply);
275 avs_dsp_send_tx(adev, request);
276 spin_unlock(&ipc->rx_lock);
277
278 ret = avs_ipc_wait_busy_completion(ipc, timeout);
279 if (ret) {
280 if (ret == -ETIMEDOUT) {
177 default:
178 break;
179 }
180
181 kfree(data);
182}
183
184void avs_dsp_process_response(struct avs_dev *adev, u64 header)

--- 178 unchanged lines hidden (view full) ---

363 spin_lock(&ipc->rx_lock);
364 avs_ipc_msg_init(ipc, reply);
365 avs_dsp_send_tx(adev, request);
366 spin_unlock(&ipc->rx_lock);
367
368 ret = avs_ipc_wait_busy_completion(ipc, timeout);
369 if (ret) {
370 if (ret == -ETIMEDOUT) {
281 dev_crit(adev->dev, "communication severed: %d, rebooting dsp..\n", ret);
371 union avs_notify_msg msg = AVS_NOTIFICATION(EXCEPTION_CAUGHT);
282
372
283 avs_ipc_block(ipc);
373 /* Same treatment as on exception, just stack_dump=0. */
374 avs_dsp_exception_caught(adev, &msg);
284 }
285 goto exit;
286 }
287
288 ret = ipc->rx.rsp.status;
289 if (reply) {
290 reply->header = ipc->rx.header;
291 if (reply->data && ipc->rx.size)

--- 71 unchanged lines hidden (view full) ---

363{
364 ipc->rx.data = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL);
365 if (!ipc->rx.data)
366 return -ENOMEM;
367
368 ipc->dev = dev;
369 ipc->ready = false;
370 ipc->default_timeout_ms = AVS_IPC_TIMEOUT_MS;
375 }
376 goto exit;
377 }
378
379 ret = ipc->rx.rsp.status;
380 if (reply) {
381 reply->header = ipc->rx.header;
382 if (reply->data && ipc->rx.size)

--- 71 unchanged lines hidden (view full) ---

454{
455 ipc->rx.data = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL);
456 if (!ipc->rx.data)
457 return -ENOMEM;
458
459 ipc->dev = dev;
460 ipc->ready = false;
461 ipc->default_timeout_ms = AVS_IPC_TIMEOUT_MS;
462 INIT_WORK(&ipc->recovery_work, avs_dsp_recovery_work);
371 init_completion(&ipc->done_completion);
372 init_completion(&ipc->busy_completion);
373 spin_lock_init(&ipc->rx_lock);
374 mutex_init(&ipc->msg_mutex);
375
376 return 0;
377}
378
379void avs_ipc_block(struct avs_ipc *ipc)
380{
381 ipc->ready = false;
463 init_completion(&ipc->done_completion);
464 init_completion(&ipc->busy_completion);
465 spin_lock_init(&ipc->rx_lock);
466 mutex_init(&ipc->msg_mutex);
467
468 return 0;
469}
470
471void avs_ipc_block(struct avs_ipc *ipc)
472{
473 ipc->ready = false;
474 cancel_work_sync(&ipc->recovery_work);
382}
475}