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