blob: b0e5315a566c98f0130faf1ecd396c59dce6b7df [file] [log] [blame]
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301/*
2 * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/slab.h>
16#include <linux/stringify.h>
17#include <linux/of.h>
18#include <linux/debugfs.h>
19#include <linux/component.h>
20#include <linux/dma-mapping.h>
21#include <soc/qcom/ramdump.h>
22#include <sound/wcd-dsp-mgr.h>
23#include "wcd-dsp-utils.h"
24
25/* Forward declarations */
26static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type);
27
28/* Component related macros */
Asish Bhattacharya84f7f732017-07-25 16:29:27 +053029#define WDSP_GET_COMPONENT(wdsp, x) ((x >= WDSP_CMPNT_TYPE_MAX || x < 0) ? \
30 NULL : (&(wdsp->cmpnts[x])))
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053031#define WDSP_GET_CMPNT_TYPE_STR(x) wdsp_get_cmpnt_type_string(x)
32
33/*
34 * These #defines indicate the bit number in status field
35 * for each of the status. If bit is set, it indicates
36 * the status as done, else if bit is not set, it indicates
37 * the status is either failed or not done.
38 */
39#define WDSP_STATUS_INITIALIZED BIT(0)
40#define WDSP_STATUS_CODE_DLOADED BIT(1)
41#define WDSP_STATUS_DATA_DLOADED BIT(2)
42#define WDSP_STATUS_BOOTED BIT(3)
43
44/* Helper macros for printing wdsp messages */
45#define WDSP_ERR(wdsp, fmt, ...) \
46 dev_err(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
47#define WDSP_DBG(wdsp, fmt, ...) \
48 dev_dbg(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
49
50/* Helper macros for locking */
51#define WDSP_MGR_MUTEX_LOCK(wdsp, lock) \
52{ \
53 WDSP_DBG(wdsp, "mutex_lock(%s)", \
54 __stringify_1(lock)); \
55 mutex_lock(&lock); \
56}
57
58#define WDSP_MGR_MUTEX_UNLOCK(wdsp, lock) \
59{ \
60 WDSP_DBG(wdsp, "mutex_unlock(%s)", \
61 __stringify_1(lock)); \
62 mutex_unlock(&lock); \
63}
64
65/* Helper macros for using status mask */
66#define WDSP_SET_STATUS(wdsp, state) \
67{ \
68 wdsp->status |= state; \
69 WDSP_DBG(wdsp, "set 0x%lx, new_state = 0x%x", \
70 state, wdsp->status); \
71}
72
73#define WDSP_CLEAR_STATUS(wdsp, state) \
74{ \
75 wdsp->status &= (~state); \
76 WDSP_DBG(wdsp, "clear 0x%lx, new_state = 0x%x", \
77 state, wdsp->status); \
78}
79
80#define WDSP_STATUS_IS_SET(wdsp, state) (wdsp->status & state)
81
82/* SSR relate status macros */
83#define WDSP_SSR_STATUS_WDSP_READY BIT(0)
84#define WDSP_SSR_STATUS_CDC_READY BIT(1)
85#define WDSP_SSR_STATUS_READY \
86 (WDSP_SSR_STATUS_WDSP_READY | WDSP_SSR_STATUS_CDC_READY)
87#define WDSP_SSR_READY_WAIT_TIMEOUT (10 * HZ)
88
89enum wdsp_ssr_type {
90
91 /* Init value, indicates there is no SSR in progress */
92 WDSP_SSR_TYPE_NO_SSR = 0,
93
94 /*
95 * Indicates WDSP crashed. The manager driver internally
96 * decides when to perform WDSP restart based on the
97 * users of wdsp. Hence there is no explicit WDSP_UP.
98 */
99 WDSP_SSR_TYPE_WDSP_DOWN,
100
101 /* Indicates codec hardware is down */
102 WDSP_SSR_TYPE_CDC_DOWN,
103
104 /* Indicates codec hardware is up, trigger to restart WDSP */
105 WDSP_SSR_TYPE_CDC_UP,
106};
107
108struct wdsp_cmpnt {
109
110 /* OF node of the phandle */
111 struct device_node *np;
112
113 /*
114 * Child component's dev_name, should be set in DT for the child's
115 * phandle if child's dev->of_node does not match the phandle->of_node
116 */
117 const char *cdev_name;
118
119 /* Child component's device node */
120 struct device *cdev;
121
122 /* Private data that component may want back on callbacks */
123 void *priv_data;
124
125 /* Child ops */
126 struct wdsp_cmpnt_ops *ops;
127};
128
129struct wdsp_ramdump_data {
130
131 /* Ramdump device */
132 void *rd_dev;
133
134 /* DMA address of the dump */
135 dma_addr_t rd_addr;
136
137 /* Virtual address of the dump */
138 void *rd_v_addr;
139
140 /* Data provided through error interrupt */
141 struct wdsp_err_signal_arg err_data;
142};
143
144struct wdsp_mgr_priv {
145
146 /* Manager driver's struct device pointer */
147 struct device *mdev;
148
149 /* Match struct for component framework */
150 struct component_match *match;
151
152 /* Manager's ops/function callbacks */
153 struct wdsp_mgr_ops *ops;
154
155 /* Array to store information for all expected components */
156 struct wdsp_cmpnt cmpnts[WDSP_CMPNT_TYPE_MAX];
157
158 /* The filename of image to be downloaded */
159 const char *img_fname;
160
161 /* Keeps track of current state of manager driver */
162 u32 status;
163
164 /* Work to load the firmware image after component binding */
165 struct work_struct load_fw_work;
166
167 /* List of segments in image to be downloaded */
168 struct list_head *seg_list;
169
170 /* Base address of the image in memory */
171 u32 base_addr;
172
173 /* Instances using dsp */
174 int dsp_users;
175
176 /* Lock for serializing ops called by components */
177 struct mutex api_mutex;
178
179 struct wdsp_ramdump_data dump_data;
180
181 /* SSR related */
182 enum wdsp_ssr_type ssr_type;
183 struct mutex ssr_mutex;
184 struct work_struct ssr_work;
185 u16 ready_status;
186 struct completion ready_compl;
187
188 /* Debugfs related */
189 struct dentry *entry;
190 bool panic_on_error;
191};
192
193static char *wdsp_get_ssr_type_string(enum wdsp_ssr_type type)
194{
195 switch (type) {
196 case WDSP_SSR_TYPE_NO_SSR:
197 return "NO_SSR";
198 case WDSP_SSR_TYPE_WDSP_DOWN:
199 return "WDSP_DOWN";
200 case WDSP_SSR_TYPE_CDC_DOWN:
201 return "CDC_DOWN";
202 case WDSP_SSR_TYPE_CDC_UP:
203 return "CDC_UP";
204 default:
205 pr_err("%s: Invalid ssr_type %d\n",
206 __func__, type);
207 return "Invalid";
208 }
209}
210
211static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type)
212{
213 switch (type) {
214 case WDSP_CMPNT_CONTROL:
215 return "control";
216 case WDSP_CMPNT_IPC:
217 return "ipc";
218 case WDSP_CMPNT_TRANSPORT:
219 return "transport";
220 default:
221 pr_err("%s: Invalid component type %d\n",
222 __func__, type);
223 return "Invalid";
224 }
225}
226
227static void __wdsp_clr_ready_locked(struct wdsp_mgr_priv *wdsp,
228 u16 value)
229{
230 wdsp->ready_status &= ~(value);
231 WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status);
232}
233
234static void __wdsp_set_ready_locked(struct wdsp_mgr_priv *wdsp,
235 u16 value, bool mark_complete)
236{
237 wdsp->ready_status |= value;
238 WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status);
239
240 if (mark_complete &&
241 wdsp->ready_status == WDSP_SSR_STATUS_READY) {
242 WDSP_DBG(wdsp, "marking ready completion");
243 complete(&wdsp->ready_compl);
244 }
245}
246
247static void wdsp_broadcast_event_upseq(struct wdsp_mgr_priv *wdsp,
248 enum wdsp_event_type event,
249 void *data)
250{
251 struct wdsp_cmpnt *cmpnt;
252 int i;
253
254 for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) {
255 cmpnt = WDSP_GET_COMPONENT(wdsp, i);
256 if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler)
257 cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data,
258 event, data);
259 }
260}
261
262static void wdsp_broadcast_event_downseq(struct wdsp_mgr_priv *wdsp,
263 enum wdsp_event_type event,
264 void *data)
265{
266 struct wdsp_cmpnt *cmpnt;
267 int i;
268
269 for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) {
270 cmpnt = WDSP_GET_COMPONENT(wdsp, i);
271 if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler)
272 cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data,
273 event, data);
274 }
275}
276
277static int wdsp_unicast_event(struct wdsp_mgr_priv *wdsp,
278 enum wdsp_cmpnt_type type,
279 enum wdsp_event_type event,
280 void *data)
281{
282 struct wdsp_cmpnt *cmpnt;
283 int ret;
284
285 cmpnt = WDSP_GET_COMPONENT(wdsp, type);
286 if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) {
287 ret = cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data,
288 event, data);
289 } else {
290 WDSP_ERR(wdsp, "not valid event_handler for %s",
291 WDSP_GET_CMPNT_TYPE_STR(type));
292 ret = -EINVAL;
293 }
294
295 return ret;
296}
297
298static void wdsp_deinit_components(struct wdsp_mgr_priv *wdsp)
299{
300 struct wdsp_cmpnt *cmpnt;
301 int i;
302
303 for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) {
304 cmpnt = WDSP_GET_COMPONENT(wdsp, i);
305 if (cmpnt && cmpnt->ops && cmpnt->ops->deinit)
306 cmpnt->ops->deinit(cmpnt->cdev, cmpnt->priv_data);
307 }
308}
309
310static int wdsp_init_components(struct wdsp_mgr_priv *wdsp)
311{
312 struct wdsp_cmpnt *cmpnt;
313 int fail_idx = WDSP_CMPNT_TYPE_MAX;
314 int i, ret = 0;
315
316 for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) {
317
318 cmpnt = WDSP_GET_COMPONENT(wdsp, i);
319
320 /* Init is allowed to be NULL */
321 if (!cmpnt->ops || !cmpnt->ops->init)
322 continue;
323 ret = cmpnt->ops->init(cmpnt->cdev, cmpnt->priv_data);
324 if (ret) {
325 WDSP_ERR(wdsp, "Init failed (%d) for component %s",
326 ret, WDSP_GET_CMPNT_TYPE_STR(i));
327 fail_idx = i;
328 break;
329 }
330 }
331
332 if (fail_idx < WDSP_CMPNT_TYPE_MAX) {
333 /* Undo init for already initialized components */
334 for (i = fail_idx - 1; i >= 0; i--) {
335 struct wdsp_cmpnt *cmpnt = WDSP_GET_COMPONENT(wdsp, i);
336
337 if (cmpnt->ops && cmpnt->ops->deinit)
338 cmpnt->ops->deinit(cmpnt->cdev,
339 cmpnt->priv_data);
340 }
341 } else {
342 wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_INIT, NULL);
343 }
344
345 return ret;
346}
347
348static int wdsp_load_each_segment(struct wdsp_mgr_priv *wdsp,
349 struct wdsp_img_segment *seg)
350{
351 struct wdsp_img_section img_section;
352 int ret;
353
354 WDSP_DBG(wdsp,
355 "base_addr 0x%x, split_fname %s, load_addr 0x%x, size 0x%zx",
356 wdsp->base_addr, seg->split_fname, seg->load_addr, seg->size);
357
358 if (seg->load_addr < wdsp->base_addr) {
359 WDSP_ERR(wdsp, "Invalid addr 0x%x, base_addr = 0x%x",
360 seg->load_addr, wdsp->base_addr);
361 return -EINVAL;
362 }
363
364 img_section.addr = seg->load_addr - wdsp->base_addr;
365 img_section.size = seg->size;
366 img_section.data = seg->data;
367
368 ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT,
369 WDSP_EVENT_DLOAD_SECTION,
370 &img_section);
371 if (ret < 0)
372 WDSP_ERR(wdsp,
373 "Failed, err = %d for base_addr = 0x%x split_fname = %s, load_addr = 0x%x, size = 0x%zx",
374 ret, wdsp->base_addr, seg->split_fname,
375 seg->load_addr, seg->size);
376 return ret;
377}
378
379static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp,
380 unsigned int type)
381{
382 struct wdsp_cmpnt *ctl;
383 struct wdsp_img_segment *seg = NULL;
384 enum wdsp_event_type pre, post;
385 long status;
386 int ret;
387
388 ctl = WDSP_GET_COMPONENT(wdsp, WDSP_CMPNT_CONTROL);
389
390 if (type == WDSP_ELF_FLAG_RE) {
391 pre = WDSP_EVENT_PRE_DLOAD_CODE;
392 post = WDSP_EVENT_POST_DLOAD_CODE;
393 status = WDSP_STATUS_CODE_DLOADED;
394 } else if (type == WDSP_ELF_FLAG_WRITE) {
395 pre = WDSP_EVENT_PRE_DLOAD_DATA;
396 post = WDSP_EVENT_POST_DLOAD_DATA;
397 status = WDSP_STATUS_DATA_DLOADED;
398 } else {
399 WDSP_ERR(wdsp, "Invalid type %u", type);
400 return -EINVAL;
401 }
402
403 ret = wdsp_get_segment_list(ctl->cdev, wdsp->img_fname,
404 type, wdsp->seg_list, &wdsp->base_addr);
405 if (ret < 0 ||
406 list_empty(wdsp->seg_list)) {
407 WDSP_ERR(wdsp, "Error %d to get image segments for type %d",
408 ret, type);
409 wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED,
410 NULL);
411 goto done;
412 }
413
414 /* Notify all components that image is about to be downloaded */
415 wdsp_broadcast_event_upseq(wdsp, pre, NULL);
416
417 /* Go through the list of segments and download one by one */
418 list_for_each_entry(seg, wdsp->seg_list, list) {
419 ret = wdsp_load_each_segment(wdsp, seg);
Laxminath Kasam38070be2017-08-17 18:21:59 +0530420 if (ret)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530421 goto dload_error;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530422 }
423
Laxminath Kasam38070be2017-08-17 18:21:59 +0530424 /* Flush the list before setting status and notifying components */
425 wdsp_flush_segment_list(wdsp->seg_list);
426
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530427 WDSP_SET_STATUS(wdsp, status);
428
429 /* Notify all components that image is downloaded */
430 wdsp_broadcast_event_downseq(wdsp, post, NULL);
Laxminath Kasam38070be2017-08-17 18:21:59 +0530431done:
432 return ret;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530433
434dload_error:
435 wdsp_flush_segment_list(wdsp->seg_list);
Laxminath Kasam38070be2017-08-17 18:21:59 +0530436 wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED, NULL);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530437 return ret;
438}
439
440static int wdsp_init_and_dload_code_sections(struct wdsp_mgr_priv *wdsp)
441{
442 int ret;
443 bool is_initialized;
444
445 is_initialized = WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_INITIALIZED);
446
447 if (!is_initialized) {
448 /* Components are not initialized yet, initialize them */
449 ret = wdsp_init_components(wdsp);
450 if (ret < 0) {
451 WDSP_ERR(wdsp, "INIT failed, err = %d", ret);
452 goto done;
453 }
454 WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
455 }
456
457 /* Download the read-execute sections of image */
458 ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_RE);
459 if (ret < 0) {
460 WDSP_ERR(wdsp, "Error %d to download code sections", ret);
461 goto done;
462 }
463done:
464 return ret;
465}
466
467static void wdsp_load_fw_image(struct work_struct *work)
468{
469 struct wdsp_mgr_priv *wdsp;
470 int ret;
471
472 wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work);
473 if (!wdsp) {
474 pr_err("%s: Invalid private_data\n", __func__);
475 return;
476 }
477
478 ret = wdsp_init_and_dload_code_sections(wdsp);
479 if (ret < 0)
480 WDSP_ERR(wdsp, "dload code sections failed, err = %d", ret);
481}
482
483static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp)
484{
485 int ret;
486
487 /* Make sure wdsp is in good state */
488 if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_CODE_DLOADED)) {
489 WDSP_ERR(wdsp, "WDSP in invalid state 0x%x", wdsp->status);
Laxminath Kasam38070be2017-08-17 18:21:59 +0530490 return -EINVAL;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530491 }
492
Laxminath Kasam38070be2017-08-17 18:21:59 +0530493 /*
494 * Acquire SSR mutex lock to make sure enablement of DSP
495 * does not race with SSR handling.
496 */
497 WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530498 /* Download the read-write sections of image */
499 ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_WRITE);
500 if (ret < 0) {
501 WDSP_ERR(wdsp, "Data section download failed, err = %d", ret);
502 goto done;
503 }
504
505 wdsp_broadcast_event_upseq(wdsp, WDSP_EVENT_PRE_BOOTUP, NULL);
506
507 ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
508 WDSP_EVENT_DO_BOOT, NULL);
509 if (ret < 0) {
510 WDSP_ERR(wdsp, "Failed to boot dsp, err = %d", ret);
511 WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
512 goto done;
513 }
514
515 wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_BOOTUP, NULL);
516 WDSP_SET_STATUS(wdsp, WDSP_STATUS_BOOTED);
517done:
Laxminath Kasam38070be2017-08-17 18:21:59 +0530518 WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530519 return ret;
520}
521
522static int wdsp_disable_dsp(struct wdsp_mgr_priv *wdsp)
523{
524 int ret;
525
526 WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
527
528 /*
529 * If Disable happened while SSR is in progress, then set the SSR
530 * ready status indicating WDSP is now ready. Ignore the disable
531 * event here and let the SSR handler go through shutdown.
532 */
533 if (wdsp->ssr_type != WDSP_SSR_TYPE_NO_SSR) {
534 __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY, true);
535 WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
536 return 0;
537 }
538
539 WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
540
541 /* Make sure wdsp is in good state */
542 if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
543 WDSP_ERR(wdsp, "wdsp in invalid state 0x%x", wdsp->status);
544 ret = -EINVAL;
545 goto done;
546 }
547
548 wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN, NULL);
549 ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
550 WDSP_EVENT_DO_SHUTDOWN, NULL);
551 if (ret < 0) {
552 WDSP_ERR(wdsp, "Failed to shutdown dsp, err = %d", ret);
553 goto done;
554 }
555
556 wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, NULL);
557 WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED);
558
559 /* Data sections are to be downloaded per boot */
560 WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
561done:
562 return ret;
563}
564
565static int wdsp_register_cmpnt_ops(struct device *wdsp_dev,
566 struct device *cdev,
567 void *priv_data,
568 struct wdsp_cmpnt_ops *ops)
569{
570 struct wdsp_mgr_priv *wdsp;
571 struct wdsp_cmpnt *cmpnt;
572 int i, ret;
573
574 if (!wdsp_dev || !cdev || !ops)
575 return -EINVAL;
576
577 wdsp = dev_get_drvdata(wdsp_dev);
578
579 WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex);
580
581 for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) {
582 cmpnt = WDSP_GET_COMPONENT(wdsp, i);
583 if ((cdev->of_node && cdev->of_node == cmpnt->np) ||
584 (cmpnt->cdev_name &&
585 !strcmp(dev_name(cdev), cmpnt->cdev_name))) {
586 break;
587 }
588 }
589
590 if (i == WDSP_CMPNT_TYPE_MAX) {
591 WDSP_ERR(wdsp, "Failed to register component dev %s",
592 dev_name(cdev));
593 ret = -EINVAL;
594 goto done;
595 }
596
597 cmpnt->cdev = cdev;
598 cmpnt->ops = ops;
599 cmpnt->priv_data = priv_data;
600done:
601 WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex);
602 return 0;
603}
604
605static struct device *wdsp_get_dev_for_cmpnt(struct device *wdsp_dev,
606 enum wdsp_cmpnt_type type)
607{
608 struct wdsp_mgr_priv *wdsp;
609 struct wdsp_cmpnt *cmpnt;
610
611 if (!wdsp_dev || type >= WDSP_CMPNT_TYPE_MAX)
612 return NULL;
613
614 wdsp = dev_get_drvdata(wdsp_dev);
615 cmpnt = WDSP_GET_COMPONENT(wdsp, type);
616
617 return cmpnt->cdev;
618}
619
620static int wdsp_get_devops_for_cmpnt(struct device *wdsp_dev,
621 enum wdsp_cmpnt_type type,
622 void *data)
623{
624 struct wdsp_mgr_priv *wdsp;
625 int ret = 0;
626
627 if (!wdsp_dev || type >= WDSP_CMPNT_TYPE_MAX)
628 return -EINVAL;
629
630 wdsp = dev_get_drvdata(wdsp_dev);
631 ret = wdsp_unicast_event(wdsp, type,
632 WDSP_EVENT_GET_DEVOPS, data);
633 if (ret)
634 WDSP_ERR(wdsp, "get_dev_ops failed for cmpnt type %d",
635 type);
636 return ret;
637}
638
639static void wdsp_collect_ramdumps(struct wdsp_mgr_priv *wdsp)
640{
641 struct wdsp_img_section img_section;
642 struct wdsp_err_signal_arg *data = &wdsp->dump_data.err_data;
643 struct ramdump_segment rd_seg;
644 int ret = 0;
645
646 if (wdsp->ssr_type != WDSP_SSR_TYPE_WDSP_DOWN ||
647 !data->mem_dumps_enabled) {
648 WDSP_DBG(wdsp, "cannot dump memory, ssr_type %s, dumps %s",
649 wdsp_get_ssr_type_string(wdsp->ssr_type),
650 !(data->mem_dumps_enabled) ? "disabled" : "enabled");
651 goto done;
652 }
653
654 if (data->dump_size == 0 ||
655 data->remote_start_addr < wdsp->base_addr) {
656 WDSP_ERR(wdsp, "Invalid start addr 0x%x or dump_size 0x%zx",
657 data->remote_start_addr, data->dump_size);
658 goto done;
659 }
660
661 if (!wdsp->dump_data.rd_dev) {
662 WDSP_ERR(wdsp, "Ramdump device is not setup");
663 goto done;
664 }
665
666 WDSP_DBG(wdsp, "base_addr 0x%x, dump_start_addr 0x%x, dump_size 0x%zx",
667 wdsp->base_addr, data->remote_start_addr, data->dump_size);
668
669 /* Allocate memory for dumps */
670 wdsp->dump_data.rd_v_addr = dma_alloc_coherent(wdsp->mdev,
671 data->dump_size,
672 &wdsp->dump_data.rd_addr,
673 GFP_KERNEL);
674 if (!wdsp->dump_data.rd_v_addr)
675 goto done;
676
677 img_section.addr = data->remote_start_addr - wdsp->base_addr;
678 img_section.size = data->dump_size;
679 img_section.data = wdsp->dump_data.rd_v_addr;
680
681 ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT,
682 WDSP_EVENT_READ_SECTION,
683 &img_section);
684 if (ret < 0) {
685 WDSP_ERR(wdsp, "Failed to read dumps, size 0x%zx at addr 0x%x",
686 img_section.size, img_section.addr);
687 goto err_read_dumps;
688 }
689
690 /*
691 * If panic_on_error flag is explicitly set through the debugfs,
692 * then cause a BUG here to aid debugging.
693 */
694 BUG_ON(wdsp->panic_on_error);
695
696 rd_seg.address = (unsigned long) wdsp->dump_data.rd_v_addr;
697 rd_seg.size = img_section.size;
698 rd_seg.v_address = wdsp->dump_data.rd_v_addr;
699
700 ret = do_ramdump(wdsp->dump_data.rd_dev, &rd_seg, 1);
701 if (ret < 0)
702 WDSP_ERR(wdsp, "do_ramdump failed with error %d", ret);
703
704err_read_dumps:
705 dma_free_coherent(wdsp->mdev, data->dump_size,
706 wdsp->dump_data.rd_v_addr, wdsp->dump_data.rd_addr);
707done:
708 return;
709}
710
711static void wdsp_ssr_work_fn(struct work_struct *work)
712{
713 struct wdsp_mgr_priv *wdsp;
714 int ret;
715
716 wdsp = container_of(work, struct wdsp_mgr_priv, ssr_work);
717 if (!wdsp) {
718 pr_err("%s: Invalid private_data\n", __func__);
719 return;
720 }
721
722 WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
723
724 /* Issue ramdumps and shutdown only if DSP is currently booted */
725 if (WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
726 wdsp_collect_ramdumps(wdsp);
727 ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
728 WDSP_EVENT_DO_SHUTDOWN, NULL);
729 if (ret < 0)
730 WDSP_ERR(wdsp, "Failed WDSP shutdown, err = %d", ret);
731
732 wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN,
733 NULL);
734 WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED);
735 }
736
737 WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
738 ret = wait_for_completion_timeout(&wdsp->ready_compl,
739 WDSP_SSR_READY_WAIT_TIMEOUT);
740 WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
741 if (ret == 0) {
742 WDSP_ERR(wdsp, "wait_for_ready timed out, status = 0x%x",
743 wdsp->ready_status);
744 goto done;
745 }
746
747 /* Data sections are to downloaded per WDSP boot */
748 WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
749
750 /*
751 * Even though code section could possible be retained on DSP
752 * crash, go ahead and still re-download just to avoid any
753 * memory corruption from previous crash.
754 */
755 WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED);
756
757 /* If codec restarted, then all components must be re-initialized */
758 if (wdsp->ssr_type == WDSP_SSR_TYPE_CDC_UP) {
759 wdsp_deinit_components(wdsp);
760 WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
761 }
762
763 ret = wdsp_init_and_dload_code_sections(wdsp);
764 if (ret < 0) {
765 WDSP_ERR(wdsp, "Failed to dload code sections err = %d",
766 ret);
767 goto done;
768 }
769
770 /* SSR handling is finished, mark SSR type as NO_SSR */
771 wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR;
772done:
773 WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
774}
775
776static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg,
777 enum wdsp_ssr_type ssr_type)
778{
779 enum wdsp_ssr_type current_ssr_type;
780 struct wdsp_err_signal_arg *err_data;
781
782 WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
783
784 current_ssr_type = wdsp->ssr_type;
785 WDSP_DBG(wdsp, "Current ssr_type %s, handling ssr_type %s",
786 wdsp_get_ssr_type_string(current_ssr_type),
787 wdsp_get_ssr_type_string(ssr_type));
788 wdsp->ssr_type = ssr_type;
789
790 if (arg) {
791 err_data = (struct wdsp_err_signal_arg *) arg;
792 memcpy(&wdsp->dump_data.err_data, err_data,
793 sizeof(*err_data));
794 } else {
795 memset(&wdsp->dump_data.err_data, 0,
796 sizeof(wdsp->dump_data.err_data));
797 }
798
799 switch (ssr_type) {
800
801 case WDSP_SSR_TYPE_WDSP_DOWN:
802 __wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY);
803 wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN,
804 NULL);
Meng Wangff817b02018-07-25 16:31:40 +0800805 reinit_completion(&wdsp->ready_compl);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530806 schedule_work(&wdsp->ssr_work);
807 break;
808
809 case WDSP_SSR_TYPE_CDC_DOWN:
810 __wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY);
811 /*
812 * If DSP is booted when CDC_DOWN is received, it needs
813 * to be shutdown.
814 */
815 if (WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
816 __wdsp_clr_ready_locked(wdsp,
817 WDSP_SSR_STATUS_WDSP_READY);
818 wdsp_broadcast_event_downseq(wdsp,
819 WDSP_EVENT_PRE_SHUTDOWN,
820 NULL);
821 }
Meng Wangff817b02018-07-25 16:31:40 +0800822 reinit_completion(&wdsp->ready_compl);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530823 schedule_work(&wdsp->ssr_work);
824 break;
825
826 case WDSP_SSR_TYPE_CDC_UP:
827 __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY, true);
828 break;
829
830 default:
831 WDSP_ERR(wdsp, "undefined ssr_type %d\n", ssr_type);
832 /* Revert back the ssr_type for undefined events */
833 wdsp->ssr_type = current_ssr_type;
834 break;
835 }
836
837 WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
838
839 return 0;
840}
841
Walter Yang83b05842017-11-17 15:47:57 +0800842#ifdef CONFIG_DEBUG_FS
Xiaojun Sang86e0d632018-01-22 15:25:26 +0800843static int __wdsp_dbg_dump_locked(struct wdsp_mgr_priv *wdsp, void *arg)
Walter Yang83b05842017-11-17 15:47:57 +0800844{
845 struct wdsp_err_signal_arg *err_data;
846 int ret = 0;
847
Walter Yang83b05842017-11-17 15:47:57 +0800848 /* If there is no SSR, set the SSR type to collect ramdumps */
849 if (wdsp->ssr_type == WDSP_SSR_TYPE_NO_SSR) {
850 wdsp->ssr_type = WDSP_SSR_TYPE_WDSP_DOWN;
851 } else {
852 WDSP_DBG(wdsp, "SSR handling is running, skip debug ramdump");
853 ret = 0;
Walter Yang83b05842017-11-17 15:47:57 +0800854 goto done;
855 }
Xiaojun Sang86e0d632018-01-22 15:25:26 +0800856
Walter Yang83b05842017-11-17 15:47:57 +0800857 if (arg) {
858 err_data = (struct wdsp_err_signal_arg *) arg;
859 memcpy(&wdsp->dump_data.err_data, err_data,
860 sizeof(*err_data));
861 } else {
862 WDSP_DBG(wdsp, "Invalid input, arg is NULL");
863 ret = -EINVAL;
Walter Yang83b05842017-11-17 15:47:57 +0800864 goto done;
865 }
866 wdsp_collect_ramdumps(wdsp);
867 wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR;
Walter Yang83b05842017-11-17 15:47:57 +0800868done:
869 return ret;
870}
Xiaojun Sang86e0d632018-01-22 15:25:26 +0800871static int wdsp_debug_dump_handler(struct wdsp_mgr_priv *wdsp, void *arg)
872{
873 int ret = 0;
874
875 WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
876 ret = __wdsp_dbg_dump_locked(wdsp, arg);
877 WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
878
879 return ret;
880}
Walter Yang83b05842017-11-17 15:47:57 +0800881#else
Xiaojun Sang86e0d632018-01-22 15:25:26 +0800882static int __wdsp_dbg_dump_locked(struct wdsp_mgr_priv *wdsp, void *arg)
883{
884 return 0;
885}
886
Walter Yang83b05842017-11-17 15:47:57 +0800887static int wdsp_debug_dump_handler(struct wdsp_mgr_priv *wdsp, void *arg)
888{
889 return 0;
890}
891#endif
892
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530893static int wdsp_signal_handler(struct device *wdsp_dev,
894 enum wdsp_signal signal, void *arg)
895{
896 struct wdsp_mgr_priv *wdsp;
897 int ret;
898
899 if (!wdsp_dev)
900 return -EINVAL;
901
902 wdsp = dev_get_drvdata(wdsp_dev);
Xiaojun Sang86e0d632018-01-22 15:25:26 +0800903
904#ifdef CONFIG_DEBUG_FS
905 if (signal != WDSP_DEBUG_DUMP_INTERNAL)
906 WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex);
907#else
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530908 WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex);
Xiaojun Sang86e0d632018-01-22 15:25:26 +0800909#endif
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530910
911 WDSP_DBG(wdsp, "Raised signal %d", signal);
912
913 switch (signal) {
914 case WDSP_IPC1_INTR:
915 ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC,
916 WDSP_EVENT_IPC1_INTR, NULL);
917 break;
918 case WDSP_ERR_INTR:
919 ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_WDSP_DOWN);
920 break;
921 case WDSP_CDC_DOWN_SIGNAL:
922 ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_DOWN);
923 break;
924 case WDSP_CDC_UP_SIGNAL:
925 ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_UP);
926 break;
Walter Yang83b05842017-11-17 15:47:57 +0800927 case WDSP_DEBUG_DUMP:
928 ret = wdsp_debug_dump_handler(wdsp, arg);
929 break;
Xiaojun Sang86e0d632018-01-22 15:25:26 +0800930 case WDSP_DEBUG_DUMP_INTERNAL:
931 ret = __wdsp_dbg_dump_locked(wdsp, arg);
932 break;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530933 default:
934 ret = -EINVAL;
935 break;
936 }
937
938 if (ret < 0)
939 WDSP_ERR(wdsp, "handling signal %d failed with error %d",
940 signal, ret);
Xiaojun Sang86e0d632018-01-22 15:25:26 +0800941
942#ifdef CONFIG_DEBUG_FS
943 if (signal != WDSP_DEBUG_DUMP_INTERNAL)
944 WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex);
945#else
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530946 WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex);
Xiaojun Sang86e0d632018-01-22 15:25:26 +0800947#endif
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530948
949 return ret;
950}
951
952static int wdsp_vote_for_dsp(struct device *wdsp_dev,
953 bool vote)
954{
955 struct wdsp_mgr_priv *wdsp;
956 int ret = 0;
957
958 if (!wdsp_dev)
959 return -EINVAL;
960
961 wdsp = dev_get_drvdata(wdsp_dev);
962
963 WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex);
964 WDSP_DBG(wdsp, "request %s, current users = %d",
965 vote ? "enable" : "disable", wdsp->dsp_users);
966
967 if (vote) {
968 wdsp->dsp_users++;
969 if (wdsp->dsp_users == 1)
970 ret = wdsp_enable_dsp(wdsp);
971 } else {
972 if (wdsp->dsp_users == 0)
973 goto done;
974
975 wdsp->dsp_users--;
976 if (wdsp->dsp_users == 0)
977 ret = wdsp_disable_dsp(wdsp);
978 }
979
980 if (ret < 0)
981 WDSP_DBG(wdsp, "wdsp %s failed, err = %d",
982 vote ? "enable" : "disable", ret);
983
984done:
985 WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex);
986 return ret;
987}
988
989static int wdsp_suspend(struct device *wdsp_dev)
990{
991 struct wdsp_mgr_priv *wdsp;
992 int rc = 0, i;
993
994 if (!wdsp_dev) {
995 pr_err("%s: Invalid handle to device\n", __func__);
996 return -EINVAL;
997 }
998
999 wdsp = dev_get_drvdata(wdsp_dev);
1000
1001 for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) {
1002 rc = wdsp_unicast_event(wdsp, i, WDSP_EVENT_SUSPEND, NULL);
1003 if (rc < 0) {
1004 WDSP_ERR(wdsp, "component %s failed to suspend\n",
1005 WDSP_GET_CMPNT_TYPE_STR(i));
1006 break;
1007 }
1008 }
1009
1010 return rc;
1011}
1012
1013static int wdsp_resume(struct device *wdsp_dev)
1014{
1015 struct wdsp_mgr_priv *wdsp;
1016 int rc = 0, i;
1017
1018 if (!wdsp_dev) {
1019 pr_err("%s: Invalid handle to device\n", __func__);
1020 return -EINVAL;
1021 }
1022
1023 wdsp = dev_get_drvdata(wdsp_dev);
1024
1025 for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) {
1026 rc = wdsp_unicast_event(wdsp, i, WDSP_EVENT_RESUME, NULL);
1027 if (rc < 0) {
1028 WDSP_ERR(wdsp, "component %s failed to resume\n",
1029 WDSP_GET_CMPNT_TYPE_STR(i));
1030 break;
1031 }
1032 }
1033
1034 return rc;
1035}
1036
1037static struct wdsp_mgr_ops wdsp_ops = {
1038 .register_cmpnt_ops = wdsp_register_cmpnt_ops,
1039 .get_dev_for_cmpnt = wdsp_get_dev_for_cmpnt,
1040 .get_devops_for_cmpnt = wdsp_get_devops_for_cmpnt,
1041 .signal_handler = wdsp_signal_handler,
1042 .vote_for_dsp = wdsp_vote_for_dsp,
1043 .suspend = wdsp_suspend,
1044 .resume = wdsp_resume,
1045};
1046
1047static int wdsp_mgr_compare_of(struct device *dev, void *data)
1048{
1049 struct wdsp_cmpnt *cmpnt = data;
1050
1051 /*
1052 * First try to match based on of_node, if of_node is not
1053 * present, try to match on the dev_name
1054 */
1055 return ((dev->of_node && dev->of_node == cmpnt->np) ||
1056 (cmpnt->cdev_name &&
1057 !strcmp(dev_name(dev), cmpnt->cdev_name)));
1058}
1059
1060static void wdsp_mgr_debugfs_init(struct wdsp_mgr_priv *wdsp)
1061{
1062 wdsp->entry = debugfs_create_dir("wdsp_mgr", NULL);
1063 if (IS_ERR_OR_NULL(wdsp->entry))
1064 return;
1065
1066 debugfs_create_bool("panic_on_error", 0644,
1067 wdsp->entry, &wdsp->panic_on_error);
1068}
1069
1070static void wdsp_mgr_debugfs_remove(struct wdsp_mgr_priv *wdsp)
1071{
1072 debugfs_remove_recursive(wdsp->entry);
1073 wdsp->entry = NULL;
1074}
1075
1076static int wdsp_mgr_bind(struct device *dev)
1077{
1078 struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev);
1079 struct wdsp_cmpnt *cmpnt;
1080 int ret, idx;
1081
1082 wdsp->ops = &wdsp_ops;
1083
1084 /* Setup ramdump device */
1085 wdsp->dump_data.rd_dev = create_ramdump_device("wdsp", dev);
1086 if (!wdsp->dump_data.rd_dev)
1087 dev_info(dev, "%s: create_ramdump_device failed\n", __func__);
1088
1089 ret = component_bind_all(dev, wdsp->ops);
1090 if (ret < 0)
1091 WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret);
1092
1093 /* Make sure all components registered ops */
1094 for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) {
1095 cmpnt = WDSP_GET_COMPONENT(wdsp, idx);
1096 if (!cmpnt->cdev || !cmpnt->ops) {
1097 WDSP_ERR(wdsp, "%s did not register ops\n",
1098 WDSP_GET_CMPNT_TYPE_STR(idx));
1099 ret = -EINVAL;
1100 component_unbind_all(dev, wdsp->ops);
1101 break;
1102 }
1103 }
1104
1105 wdsp_mgr_debugfs_init(wdsp);
1106
1107 /* Schedule the work to download image if binding was successful. */
1108 if (!ret)
1109 schedule_work(&wdsp->load_fw_work);
1110
1111 return ret;
1112}
1113
1114static void wdsp_mgr_unbind(struct device *dev)
1115{
1116 struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev);
1117 struct wdsp_cmpnt *cmpnt;
1118 int idx;
1119
1120 component_unbind_all(dev, wdsp->ops);
1121
1122 wdsp_mgr_debugfs_remove(wdsp);
1123
1124 if (wdsp->dump_data.rd_dev) {
1125 destroy_ramdump_device(wdsp->dump_data.rd_dev);
1126 wdsp->dump_data.rd_dev = NULL;
1127 }
1128
1129 /* Clear all status bits */
1130 wdsp->status = 0x00;
1131
1132 /* clean up the components */
1133 for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) {
1134 cmpnt = WDSP_GET_COMPONENT(wdsp, idx);
1135 cmpnt->cdev = NULL;
1136 cmpnt->ops = NULL;
1137 cmpnt->priv_data = NULL;
1138 }
1139}
1140
1141static const struct component_master_ops wdsp_master_ops = {
1142 .bind = wdsp_mgr_bind,
1143 .unbind = wdsp_mgr_unbind,
1144};
1145
1146static void *wdsp_mgr_parse_phandle(struct wdsp_mgr_priv *wdsp,
1147 int index)
1148{
1149 struct device *mdev = wdsp->mdev;
1150 struct device_node *np;
1151 struct wdsp_cmpnt *cmpnt = NULL;
1152 struct of_phandle_args pargs;
1153 u32 value;
1154 int ret;
1155
1156 ret = of_parse_phandle_with_fixed_args(mdev->of_node,
1157 "qcom,wdsp-components", 1,
1158 index, &pargs);
1159 if (ret) {
1160 WDSP_ERR(wdsp, "parse_phandle at index %d failed %d",
1161 index, ret);
1162 return NULL;
1163 }
1164
1165 np = pargs.np;
1166 value = pargs.args[0];
1167
1168 if (value >= WDSP_CMPNT_TYPE_MAX) {
1169 WDSP_ERR(wdsp, "invalid phandle_arg to of_node %s", np->name);
1170 goto done;
1171 }
1172
1173 cmpnt = WDSP_GET_COMPONENT(wdsp, value);
1174 if (cmpnt->np || cmpnt->cdev_name) {
1175 WDSP_ERR(wdsp, "cmpnt %d already added", value);
1176 cmpnt = NULL;
1177 goto done;
1178 }
1179
1180 cmpnt->np = np;
1181 of_property_read_string(np, "qcom,wdsp-cmpnt-dev-name",
1182 &cmpnt->cdev_name);
1183done:
1184 of_node_put(np);
1185 return cmpnt;
1186}
1187
1188static int wdsp_mgr_parse_dt_entries(struct wdsp_mgr_priv *wdsp)
1189{
1190 struct device *dev = wdsp->mdev;
1191 void *match_data;
1192 int ph_idx, ret;
1193
1194 ret = of_property_read_string(dev->of_node, "qcom,img-filename",
1195 &wdsp->img_fname);
1196 if (ret < 0) {
1197 WDSP_ERR(wdsp, "Reading property %s failed, error = %d",
1198 "qcom,img-filename", ret);
1199 return ret;
1200 }
1201
1202 ret = of_count_phandle_with_args(dev->of_node,
1203 "qcom,wdsp-components",
1204 NULL);
1205 if (ret == -ENOENT) {
1206 WDSP_ERR(wdsp, "Property %s not defined in DT",
1207 "qcom,wdsp-components");
1208 goto done;
1209 } else if (ret != WDSP_CMPNT_TYPE_MAX * 2) {
1210 WDSP_ERR(wdsp, "Invalid phandle + arg count %d, expected %d",
1211 ret, WDSP_CMPNT_TYPE_MAX * 2);
1212 ret = -EINVAL;
1213 goto done;
1214 }
1215
1216 ret = 0;
1217
1218 for (ph_idx = 0; ph_idx < WDSP_CMPNT_TYPE_MAX; ph_idx++) {
1219
1220 match_data = wdsp_mgr_parse_phandle(wdsp, ph_idx);
1221 if (!match_data) {
1222 WDSP_ERR(wdsp, "component not found at idx %d", ph_idx);
1223 ret = -EINVAL;
1224 goto done;
1225 }
1226
1227 component_match_add(dev, &wdsp->match,
1228 wdsp_mgr_compare_of, match_data);
1229 }
1230
1231done:
1232 return ret;
1233}
1234
1235static int wdsp_mgr_probe(struct platform_device *pdev)
1236{
1237 struct wdsp_mgr_priv *wdsp;
1238 struct device *mdev = &pdev->dev;
1239 int ret;
1240
1241 wdsp = devm_kzalloc(mdev, sizeof(*wdsp), GFP_KERNEL);
1242 if (!wdsp)
1243 return -ENOMEM;
1244 wdsp->mdev = mdev;
1245 wdsp->seg_list = devm_kzalloc(mdev, sizeof(struct list_head),
1246 GFP_KERNEL);
1247 if (!wdsp->seg_list) {
1248 devm_kfree(mdev, wdsp);
1249 return -ENOMEM;
1250 }
1251
1252 ret = wdsp_mgr_parse_dt_entries(wdsp);
1253 if (ret)
1254 goto err_dt_parse;
1255
1256 INIT_WORK(&wdsp->load_fw_work, wdsp_load_fw_image);
1257 INIT_LIST_HEAD(wdsp->seg_list);
1258 mutex_init(&wdsp->api_mutex);
1259 mutex_init(&wdsp->ssr_mutex);
1260 wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR;
1261 wdsp->ready_status = WDSP_SSR_STATUS_READY;
1262 INIT_WORK(&wdsp->ssr_work, wdsp_ssr_work_fn);
1263 init_completion(&wdsp->ready_compl);
1264 arch_setup_dma_ops(wdsp->mdev, 0, 0, NULL, 0);
1265 dev_set_drvdata(mdev, wdsp);
1266
1267 ret = component_master_add_with_match(mdev, &wdsp_master_ops,
1268 wdsp->match);
1269 if (ret < 0) {
1270 WDSP_ERR(wdsp, "Failed to add master, err = %d", ret);
1271 goto err_master_add;
1272 }
1273
1274 return 0;
1275
1276err_master_add:
1277 mutex_destroy(&wdsp->api_mutex);
1278 mutex_destroy(&wdsp->ssr_mutex);
1279err_dt_parse:
1280 devm_kfree(mdev, wdsp->seg_list);
1281 devm_kfree(mdev, wdsp);
1282 dev_set_drvdata(mdev, NULL);
1283
1284 return ret;
1285}
1286
1287static int wdsp_mgr_remove(struct platform_device *pdev)
1288{
1289 struct device *mdev = &pdev->dev;
1290 struct wdsp_mgr_priv *wdsp = dev_get_drvdata(mdev);
1291
1292 component_master_del(mdev, &wdsp_master_ops);
1293
1294 mutex_destroy(&wdsp->api_mutex);
1295 mutex_destroy(&wdsp->ssr_mutex);
1296 devm_kfree(mdev, wdsp->seg_list);
1297 devm_kfree(mdev, wdsp);
1298 dev_set_drvdata(mdev, NULL);
1299
1300 return 0;
1301};
1302
1303static const struct of_device_id wdsp_mgr_dt_match[] = {
1304 {.compatible = "qcom,wcd-dsp-mgr" },
1305 { }
1306};
1307
1308static struct platform_driver wdsp_mgr_driver = {
1309 .driver = {
1310 .name = "wcd-dsp-mgr",
1311 .owner = THIS_MODULE,
1312 .of_match_table = of_match_ptr(wdsp_mgr_dt_match),
1313 },
1314 .probe = wdsp_mgr_probe,
1315 .remove = wdsp_mgr_remove,
1316};
1317
1318int wcd_dsp_mgr_init(void)
1319{
1320 return platform_driver_register(&wdsp_mgr_driver);
1321}
1322
1323void wcd_dsp_mgr_exit(void)
1324{
1325 platform_driver_unregister(&wdsp_mgr_driver);
1326}
1327
1328MODULE_DESCRIPTION("WCD DSP manager driver");
1329MODULE_DEVICE_TABLE(of, wdsp_mgr_dt_match);
1330MODULE_LICENSE("GPL v2");