1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * cs_dsp.c -- Cirrus Logic DSP firmware support |
4 | * |
5 | * Based on sound/soc/codecs/wm_adsp.c |
6 | * |
7 | * Copyright 2012 Wolfson Microelectronics plc |
8 | * Copyright (C) 2015-2021 Cirrus Logic, Inc. and |
9 | * Cirrus Logic International Semiconductor Ltd. |
10 | */ |
11 | |
12 | #include <linux/ctype.h> |
13 | #include <linux/debugfs.h> |
14 | #include <linux/delay.h> |
15 | #include <linux/module.h> |
16 | #include <linux/moduleparam.h> |
17 | #include <linux/seq_file.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/vmalloc.h> |
20 | |
21 | #include <linux/firmware/cirrus/cs_dsp.h> |
22 | #include <linux/firmware/cirrus/wmfw.h> |
23 | |
24 | #define cs_dsp_err(_dsp, fmt, ...) \ |
25 | dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) |
26 | #define cs_dsp_warn(_dsp, fmt, ...) \ |
27 | dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) |
28 | #define cs_dsp_info(_dsp, fmt, ...) \ |
29 | dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) |
30 | #define cs_dsp_dbg(_dsp, fmt, ...) \ |
31 | dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) |
32 | |
33 | #define ADSP1_CONTROL_1 0x00 |
34 | #define ADSP1_CONTROL_2 0x02 |
35 | #define ADSP1_CONTROL_3 0x03 |
36 | #define ADSP1_CONTROL_4 0x04 |
37 | #define ADSP1_CONTROL_5 0x06 |
38 | #define ADSP1_CONTROL_6 0x07 |
39 | #define ADSP1_CONTROL_7 0x08 |
40 | #define ADSP1_CONTROL_8 0x09 |
41 | #define ADSP1_CONTROL_9 0x0A |
42 | #define ADSP1_CONTROL_10 0x0B |
43 | #define ADSP1_CONTROL_11 0x0C |
44 | #define ADSP1_CONTROL_12 0x0D |
45 | #define ADSP1_CONTROL_13 0x0F |
46 | #define ADSP1_CONTROL_14 0x10 |
47 | #define ADSP1_CONTROL_15 0x11 |
48 | #define ADSP1_CONTROL_16 0x12 |
49 | #define ADSP1_CONTROL_17 0x13 |
50 | #define ADSP1_CONTROL_18 0x14 |
51 | #define ADSP1_CONTROL_19 0x16 |
52 | #define ADSP1_CONTROL_20 0x17 |
53 | #define ADSP1_CONTROL_21 0x18 |
54 | #define ADSP1_CONTROL_22 0x1A |
55 | #define ADSP1_CONTROL_23 0x1B |
56 | #define ADSP1_CONTROL_24 0x1C |
57 | #define ADSP1_CONTROL_25 0x1E |
58 | #define ADSP1_CONTROL_26 0x20 |
59 | #define ADSP1_CONTROL_27 0x21 |
60 | #define ADSP1_CONTROL_28 0x22 |
61 | #define ADSP1_CONTROL_29 0x23 |
62 | #define ADSP1_CONTROL_30 0x24 |
63 | #define ADSP1_CONTROL_31 0x26 |
64 | |
65 | /* |
66 | * ADSP1 Control 19 |
67 | */ |
68 | #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ |
69 | #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ |
70 | #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ |
71 | |
72 | /* |
73 | * ADSP1 Control 30 |
74 | */ |
75 | #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ |
76 | #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ |
77 | #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ |
78 | #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ |
79 | #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ |
80 | #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ |
81 | #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ |
82 | #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ |
83 | #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ |
84 | #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ |
85 | #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ |
86 | #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ |
87 | #define ADSP1_START 0x0001 /* DSP1_START */ |
88 | #define ADSP1_START_MASK 0x0001 /* DSP1_START */ |
89 | #define ADSP1_START_SHIFT 0 /* DSP1_START */ |
90 | #define ADSP1_START_WIDTH 1 /* DSP1_START */ |
91 | |
92 | /* |
93 | * ADSP1 Control 31 |
94 | */ |
95 | #define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ |
96 | #define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ |
97 | #define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ |
98 | |
99 | #define ADSP2_CONTROL 0x0 |
100 | #define ADSP2_CLOCKING 0x1 |
101 | #define ADSP2V2_CLOCKING 0x2 |
102 | #define ADSP2_STATUS1 0x4 |
103 | #define ADSP2_WDMA_CONFIG_1 0x30 |
104 | #define ADSP2_WDMA_CONFIG_2 0x31 |
105 | #define ADSP2V2_WDMA_CONFIG_2 0x32 |
106 | #define ADSP2_RDMA_CONFIG_1 0x34 |
107 | |
108 | #define ADSP2_SCRATCH0 0x40 |
109 | #define ADSP2_SCRATCH1 0x41 |
110 | #define ADSP2_SCRATCH2 0x42 |
111 | #define ADSP2_SCRATCH3 0x43 |
112 | |
113 | #define ADSP2V2_SCRATCH0_1 0x40 |
114 | #define ADSP2V2_SCRATCH2_3 0x42 |
115 | |
116 | /* |
117 | * ADSP2 Control |
118 | */ |
119 | #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ |
120 | #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ |
121 | #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ |
122 | #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ |
123 | #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ |
124 | #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ |
125 | #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ |
126 | #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ |
127 | #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ |
128 | #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ |
129 | #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ |
130 | #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ |
131 | #define ADSP2_START 0x0001 /* DSP1_START */ |
132 | #define ADSP2_START_MASK 0x0001 /* DSP1_START */ |
133 | #define ADSP2_START_SHIFT 0 /* DSP1_START */ |
134 | #define ADSP2_START_WIDTH 1 /* DSP1_START */ |
135 | |
136 | /* |
137 | * ADSP2 clocking |
138 | */ |
139 | #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ |
140 | #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ |
141 | #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ |
142 | |
143 | /* |
144 | * ADSP2V2 clocking |
145 | */ |
146 | #define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */ |
147 | #define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */ |
148 | #define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ |
149 | |
150 | #define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */ |
151 | #define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */ |
152 | #define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */ |
153 | |
154 | /* |
155 | * ADSP2 Status 1 |
156 | */ |
157 | #define ADSP2_RAM_RDY 0x0001 |
158 | #define ADSP2_RAM_RDY_MASK 0x0001 |
159 | #define ADSP2_RAM_RDY_SHIFT 0 |
160 | #define ADSP2_RAM_RDY_WIDTH 1 |
161 | |
162 | /* |
163 | * ADSP2 Lock support |
164 | */ |
165 | #define ADSP2_LOCK_CODE_0 0x5555 |
166 | #define ADSP2_LOCK_CODE_1 0xAAAA |
167 | |
168 | #define ADSP2_WATCHDOG 0x0A |
169 | #define ADSP2_BUS_ERR_ADDR 0x52 |
170 | #define ADSP2_REGION_LOCK_STATUS 0x64 |
171 | #define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66 |
172 | #define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68 |
173 | #define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A |
174 | #define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C |
175 | #define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E |
176 | #define ADSP2_LOCK_REGION_CTRL 0x7A |
177 | #define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C |
178 | |
179 | #define ADSP2_REGION_LOCK_ERR_MASK 0x8000 |
180 | #define ADSP2_ADDR_ERR_MASK 0x4000 |
181 | #define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000 |
182 | #define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002 |
183 | #define ADSP2_CTRL_ERR_EINT 0x0001 |
184 | |
185 | #define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF |
186 | #define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF |
187 | #define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000 |
188 | #define ADSP2_PMEM_ERR_ADDR_SHIFT 16 |
189 | #define ADSP2_WDT_ENA_MASK 0xFFFFFFFD |
190 | |
191 | #define ADSP2_LOCK_REGION_SHIFT 16 |
192 | |
193 | /* |
194 | * Event control messages |
195 | */ |
196 | #define CS_DSP_FW_EVENT_SHUTDOWN 0x000001 |
197 | |
198 | /* |
199 | * HALO system info |
200 | */ |
201 | #define HALO_AHBM_WINDOW_DEBUG_0 0x02040 |
202 | #define HALO_AHBM_WINDOW_DEBUG_1 0x02044 |
203 | |
204 | /* |
205 | * HALO core |
206 | */ |
207 | #define HALO_SCRATCH1 0x005c0 |
208 | #define HALO_SCRATCH2 0x005c8 |
209 | #define HALO_SCRATCH3 0x005d0 |
210 | #define HALO_SCRATCH4 0x005d8 |
211 | #define HALO_CCM_CORE_CONTROL 0x41000 |
212 | #define HALO_CORE_SOFT_RESET 0x00010 |
213 | #define HALO_WDT_CONTROL 0x47000 |
214 | |
215 | /* |
216 | * HALO MPU banks |
217 | */ |
218 | #define HALO_MPU_XMEM_ACCESS_0 0x43000 |
219 | #define HALO_MPU_YMEM_ACCESS_0 0x43004 |
220 | #define HALO_MPU_WINDOW_ACCESS_0 0x43008 |
221 | #define HALO_MPU_XREG_ACCESS_0 0x4300C |
222 | #define HALO_MPU_YREG_ACCESS_0 0x43014 |
223 | #define HALO_MPU_XMEM_ACCESS_1 0x43018 |
224 | #define HALO_MPU_YMEM_ACCESS_1 0x4301C |
225 | #define HALO_MPU_WINDOW_ACCESS_1 0x43020 |
226 | #define HALO_MPU_XREG_ACCESS_1 0x43024 |
227 | #define HALO_MPU_YREG_ACCESS_1 0x4302C |
228 | #define HALO_MPU_XMEM_ACCESS_2 0x43030 |
229 | #define HALO_MPU_YMEM_ACCESS_2 0x43034 |
230 | #define HALO_MPU_WINDOW_ACCESS_2 0x43038 |
231 | #define HALO_MPU_XREG_ACCESS_2 0x4303C |
232 | #define HALO_MPU_YREG_ACCESS_2 0x43044 |
233 | #define HALO_MPU_XMEM_ACCESS_3 0x43048 |
234 | #define HALO_MPU_YMEM_ACCESS_3 0x4304C |
235 | #define HALO_MPU_WINDOW_ACCESS_3 0x43050 |
236 | #define HALO_MPU_XREG_ACCESS_3 0x43054 |
237 | #define HALO_MPU_YREG_ACCESS_3 0x4305C |
238 | #define HALO_MPU_XM_VIO_ADDR 0x43100 |
239 | #define HALO_MPU_XM_VIO_STATUS 0x43104 |
240 | #define HALO_MPU_YM_VIO_ADDR 0x43108 |
241 | #define HALO_MPU_YM_VIO_STATUS 0x4310C |
242 | #define HALO_MPU_PM_VIO_ADDR 0x43110 |
243 | #define HALO_MPU_PM_VIO_STATUS 0x43114 |
244 | #define HALO_MPU_LOCK_CONFIG 0x43140 |
245 | |
246 | /* |
247 | * HALO_AHBM_WINDOW_DEBUG_1 |
248 | */ |
249 | #define HALO_AHBM_CORE_ERR_ADDR_MASK 0x0fffff00 |
250 | #define HALO_AHBM_CORE_ERR_ADDR_SHIFT 8 |
251 | #define HALO_AHBM_FLAGS_ERR_MASK 0x000000ff |
252 | |
253 | /* |
254 | * HALO_CCM_CORE_CONTROL |
255 | */ |
256 | #define HALO_CORE_RESET 0x00000200 |
257 | #define HALO_CORE_EN 0x00000001 |
258 | |
259 | /* |
260 | * HALO_CORE_SOFT_RESET |
261 | */ |
262 | #define HALO_CORE_SOFT_RESET_MASK 0x00000001 |
263 | |
264 | /* |
265 | * HALO_WDT_CONTROL |
266 | */ |
267 | #define HALO_WDT_EN_MASK 0x00000001 |
268 | |
269 | /* |
270 | * HALO_MPU_?M_VIO_STATUS |
271 | */ |
272 | #define HALO_MPU_VIO_STS_MASK 0x007e0000 |
273 | #define HALO_MPU_VIO_STS_SHIFT 17 |
274 | #define HALO_MPU_VIO_ERR_WR_MASK 0x00008000 |
275 | #define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff |
276 | #define HALO_MPU_VIO_ERR_SRC_SHIFT 0 |
277 | |
278 | struct cs_dsp_ops { |
279 | bool (*validate_version)(struct cs_dsp *dsp, unsigned int version); |
280 | unsigned int (*parse_sizes)(struct cs_dsp *dsp, |
281 | const char * const file, |
282 | unsigned int pos, |
283 | const struct firmware *firmware); |
284 | int (*setup_algs)(struct cs_dsp *dsp); |
285 | unsigned int (*region_to_reg)(struct cs_dsp_region const *mem, |
286 | unsigned int offset); |
287 | |
288 | void (*show_fw_status)(struct cs_dsp *dsp); |
289 | void (*stop_watchdog)(struct cs_dsp *dsp); |
290 | |
291 | int (*enable_memory)(struct cs_dsp *dsp); |
292 | void (*disable_memory)(struct cs_dsp *dsp); |
293 | int (*lock_memory)(struct cs_dsp *dsp, unsigned int lock_regions); |
294 | |
295 | int (*enable_core)(struct cs_dsp *dsp); |
296 | void (*disable_core)(struct cs_dsp *dsp); |
297 | |
298 | int (*start_core)(struct cs_dsp *dsp); |
299 | void (*stop_core)(struct cs_dsp *dsp); |
300 | }; |
301 | |
302 | static const struct cs_dsp_ops cs_dsp_adsp1_ops; |
303 | static const struct cs_dsp_ops cs_dsp_adsp2_ops[]; |
304 | static const struct cs_dsp_ops cs_dsp_halo_ops; |
305 | static const struct cs_dsp_ops cs_dsp_halo_ao_ops; |
306 | |
307 | struct cs_dsp_buf { |
308 | struct list_head list; |
309 | void *buf; |
310 | }; |
311 | |
312 | static struct cs_dsp_buf *cs_dsp_buf_alloc(const void *src, size_t len, |
313 | struct list_head *list) |
314 | { |
315 | struct cs_dsp_buf *buf = kzalloc(size: sizeof(*buf), GFP_KERNEL); |
316 | |
317 | if (buf == NULL) |
318 | return NULL; |
319 | |
320 | buf->buf = vmalloc(size: len); |
321 | if (!buf->buf) { |
322 | kfree(objp: buf); |
323 | return NULL; |
324 | } |
325 | memcpy(buf->buf, src, len); |
326 | |
327 | if (list) |
328 | list_add_tail(new: &buf->list, head: list); |
329 | |
330 | return buf; |
331 | } |
332 | |
333 | static void cs_dsp_buf_free(struct list_head *list) |
334 | { |
335 | while (!list_empty(head: list)) { |
336 | struct cs_dsp_buf *buf = list_first_entry(list, |
337 | struct cs_dsp_buf, |
338 | list); |
339 | list_del(entry: &buf->list); |
340 | vfree(addr: buf->buf); |
341 | kfree(objp: buf); |
342 | } |
343 | } |
344 | |
345 | /** |
346 | * cs_dsp_mem_region_name() - Return a name string for a memory type |
347 | * @type: the memory type to match |
348 | * |
349 | * Return: A const string identifying the memory region. |
350 | */ |
351 | const char *cs_dsp_mem_region_name(unsigned int type) |
352 | { |
353 | switch (type) { |
354 | case WMFW_ADSP1_PM: |
355 | return "PM" ; |
356 | case WMFW_HALO_PM_PACKED: |
357 | return "PM_PACKED" ; |
358 | case WMFW_ADSP1_DM: |
359 | return "DM" ; |
360 | case WMFW_ADSP2_XM: |
361 | return "XM" ; |
362 | case WMFW_HALO_XM_PACKED: |
363 | return "XM_PACKED" ; |
364 | case WMFW_ADSP2_YM: |
365 | return "YM" ; |
366 | case WMFW_HALO_YM_PACKED: |
367 | return "YM_PACKED" ; |
368 | case WMFW_ADSP1_ZM: |
369 | return "ZM" ; |
370 | default: |
371 | return NULL; |
372 | } |
373 | } |
374 | EXPORT_SYMBOL_NS_GPL(cs_dsp_mem_region_name, FW_CS_DSP); |
375 | |
376 | #ifdef CONFIG_DEBUG_FS |
377 | static void cs_dsp_debugfs_save_wmfwname(struct cs_dsp *dsp, const char *s) |
378 | { |
379 | char *tmp = kasprintf(GFP_KERNEL, fmt: "%s\n" , s); |
380 | |
381 | kfree(objp: dsp->wmfw_file_name); |
382 | dsp->wmfw_file_name = tmp; |
383 | } |
384 | |
385 | static void cs_dsp_debugfs_save_binname(struct cs_dsp *dsp, const char *s) |
386 | { |
387 | char *tmp = kasprintf(GFP_KERNEL, fmt: "%s\n" , s); |
388 | |
389 | kfree(objp: dsp->bin_file_name); |
390 | dsp->bin_file_name = tmp; |
391 | } |
392 | |
393 | static void cs_dsp_debugfs_clear(struct cs_dsp *dsp) |
394 | { |
395 | kfree(objp: dsp->wmfw_file_name); |
396 | kfree(objp: dsp->bin_file_name); |
397 | dsp->wmfw_file_name = NULL; |
398 | dsp->bin_file_name = NULL; |
399 | } |
400 | |
401 | static ssize_t cs_dsp_debugfs_wmfw_read(struct file *file, |
402 | char __user *user_buf, |
403 | size_t count, loff_t *ppos) |
404 | { |
405 | struct cs_dsp *dsp = file->private_data; |
406 | ssize_t ret; |
407 | |
408 | mutex_lock(&dsp->pwr_lock); |
409 | |
410 | if (!dsp->wmfw_file_name || !dsp->booted) |
411 | ret = 0; |
412 | else |
413 | ret = simple_read_from_buffer(to: user_buf, count, ppos, |
414 | from: dsp->wmfw_file_name, |
415 | strlen(dsp->wmfw_file_name)); |
416 | |
417 | mutex_unlock(lock: &dsp->pwr_lock); |
418 | return ret; |
419 | } |
420 | |
421 | static ssize_t cs_dsp_debugfs_bin_read(struct file *file, |
422 | char __user *user_buf, |
423 | size_t count, loff_t *ppos) |
424 | { |
425 | struct cs_dsp *dsp = file->private_data; |
426 | ssize_t ret; |
427 | |
428 | mutex_lock(&dsp->pwr_lock); |
429 | |
430 | if (!dsp->bin_file_name || !dsp->booted) |
431 | ret = 0; |
432 | else |
433 | ret = simple_read_from_buffer(to: user_buf, count, ppos, |
434 | from: dsp->bin_file_name, |
435 | strlen(dsp->bin_file_name)); |
436 | |
437 | mutex_unlock(lock: &dsp->pwr_lock); |
438 | return ret; |
439 | } |
440 | |
441 | static const struct { |
442 | const char *name; |
443 | const struct file_operations fops; |
444 | } cs_dsp_debugfs_fops[] = { |
445 | { |
446 | .name = "wmfw_file_name" , |
447 | .fops = { |
448 | .open = simple_open, |
449 | .read = cs_dsp_debugfs_wmfw_read, |
450 | }, |
451 | }, |
452 | { |
453 | .name = "bin_file_name" , |
454 | .fops = { |
455 | .open = simple_open, |
456 | .read = cs_dsp_debugfs_bin_read, |
457 | }, |
458 | }, |
459 | }; |
460 | |
461 | static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg, |
462 | unsigned int off); |
463 | |
464 | static int cs_dsp_debugfs_read_controls_show(struct seq_file *s, void *ignored) |
465 | { |
466 | struct cs_dsp *dsp = s->private; |
467 | struct cs_dsp_coeff_ctl *ctl; |
468 | unsigned int reg; |
469 | |
470 | list_for_each_entry(ctl, &dsp->ctl_list, list) { |
471 | cs_dsp_coeff_base_reg(ctl, reg: ®, off: 0); |
472 | seq_printf(m: s, fmt: "%22.*s: %#8zx %s:%08x %#8x %s %#8x %#4x %c%c%c%c %s %s\n" , |
473 | ctl->subname_len, ctl->subname, ctl->len, |
474 | cs_dsp_mem_region_name(ctl->alg_region.type), |
475 | ctl->offset, reg, ctl->fw_name, ctl->alg_region.alg, ctl->type, |
476 | ctl->flags & WMFW_CTL_FLAG_VOLATILE ? 'V' : '-', |
477 | ctl->flags & WMFW_CTL_FLAG_SYS ? 'S' : '-', |
478 | ctl->flags & WMFW_CTL_FLAG_READABLE ? 'R' : '-', |
479 | ctl->flags & WMFW_CTL_FLAG_WRITEABLE ? 'W' : '-', |
480 | ctl->enabled ? "enabled" : "disabled" , |
481 | ctl->set ? "dirty" : "clean" ); |
482 | } |
483 | |
484 | return 0; |
485 | } |
486 | DEFINE_SHOW_ATTRIBUTE(cs_dsp_debugfs_read_controls); |
487 | |
488 | /** |
489 | * cs_dsp_init_debugfs() - Create and populate DSP representation in debugfs |
490 | * @dsp: pointer to DSP structure |
491 | * @debugfs_root: pointer to debugfs directory in which to create this DSP |
492 | * representation |
493 | */ |
494 | void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root) |
495 | { |
496 | struct dentry *root = NULL; |
497 | int i; |
498 | |
499 | root = debugfs_create_dir(name: dsp->name, parent: debugfs_root); |
500 | |
501 | debugfs_create_bool(name: "booted" , mode: 0444, parent: root, value: &dsp->booted); |
502 | debugfs_create_bool(name: "running" , mode: 0444, parent: root, value: &dsp->running); |
503 | debugfs_create_x32(name: "fw_id" , mode: 0444, parent: root, value: &dsp->fw_id); |
504 | debugfs_create_x32(name: "fw_version" , mode: 0444, parent: root, value: &dsp->fw_id_version); |
505 | |
506 | for (i = 0; i < ARRAY_SIZE(cs_dsp_debugfs_fops); ++i) |
507 | debugfs_create_file(name: cs_dsp_debugfs_fops[i].name, mode: 0444, parent: root, |
508 | data: dsp, fops: &cs_dsp_debugfs_fops[i].fops); |
509 | |
510 | debugfs_create_file(name: "controls" , mode: 0444, parent: root, data: dsp, |
511 | fops: &cs_dsp_debugfs_read_controls_fops); |
512 | |
513 | dsp->debugfs_root = root; |
514 | } |
515 | EXPORT_SYMBOL_NS_GPL(cs_dsp_init_debugfs, FW_CS_DSP); |
516 | |
517 | /** |
518 | * cs_dsp_cleanup_debugfs() - Removes DSP representation from debugfs |
519 | * @dsp: pointer to DSP structure |
520 | */ |
521 | void cs_dsp_cleanup_debugfs(struct cs_dsp *dsp) |
522 | { |
523 | cs_dsp_debugfs_clear(dsp); |
524 | debugfs_remove_recursive(dentry: dsp->debugfs_root); |
525 | dsp->debugfs_root = ERR_PTR(error: -ENODEV); |
526 | } |
527 | EXPORT_SYMBOL_NS_GPL(cs_dsp_cleanup_debugfs, FW_CS_DSP); |
528 | #else |
529 | void cs_dsp_init_debugfs(struct cs_dsp *dsp, struct dentry *debugfs_root) |
530 | { |
531 | } |
532 | EXPORT_SYMBOL_NS_GPL(cs_dsp_init_debugfs, FW_CS_DSP); |
533 | |
534 | void cs_dsp_cleanup_debugfs(struct cs_dsp *dsp) |
535 | { |
536 | } |
537 | EXPORT_SYMBOL_NS_GPL(cs_dsp_cleanup_debugfs, FW_CS_DSP); |
538 | |
539 | static inline void cs_dsp_debugfs_save_wmfwname(struct cs_dsp *dsp, |
540 | const char *s) |
541 | { |
542 | } |
543 | |
544 | static inline void cs_dsp_debugfs_save_binname(struct cs_dsp *dsp, |
545 | const char *s) |
546 | { |
547 | } |
548 | |
549 | static inline void cs_dsp_debugfs_clear(struct cs_dsp *dsp) |
550 | { |
551 | } |
552 | #endif |
553 | |
554 | static const struct cs_dsp_region *cs_dsp_find_region(struct cs_dsp *dsp, |
555 | int type) |
556 | { |
557 | int i; |
558 | |
559 | for (i = 0; i < dsp->num_mems; i++) |
560 | if (dsp->mem[i].type == type) |
561 | return &dsp->mem[i]; |
562 | |
563 | return NULL; |
564 | } |
565 | |
566 | static unsigned int cs_dsp_region_to_reg(struct cs_dsp_region const *mem, |
567 | unsigned int offset) |
568 | { |
569 | switch (mem->type) { |
570 | case WMFW_ADSP1_PM: |
571 | return mem->base + (offset * 3); |
572 | case WMFW_ADSP1_DM: |
573 | case WMFW_ADSP2_XM: |
574 | case WMFW_ADSP2_YM: |
575 | case WMFW_ADSP1_ZM: |
576 | return mem->base + (offset * 2); |
577 | default: |
578 | WARN(1, "Unknown memory region type" ); |
579 | return offset; |
580 | } |
581 | } |
582 | |
583 | static unsigned int cs_dsp_halo_region_to_reg(struct cs_dsp_region const *mem, |
584 | unsigned int offset) |
585 | { |
586 | switch (mem->type) { |
587 | case WMFW_ADSP2_XM: |
588 | case WMFW_ADSP2_YM: |
589 | return mem->base + (offset * 4); |
590 | case WMFW_HALO_XM_PACKED: |
591 | case WMFW_HALO_YM_PACKED: |
592 | return (mem->base + (offset * 3)) & ~0x3; |
593 | case WMFW_HALO_PM_PACKED: |
594 | return mem->base + (offset * 5); |
595 | default: |
596 | WARN(1, "Unknown memory region type" ); |
597 | return offset; |
598 | } |
599 | } |
600 | |
601 | static void cs_dsp_read_fw_status(struct cs_dsp *dsp, |
602 | int noffs, unsigned int *offs) |
603 | { |
604 | unsigned int i; |
605 | int ret; |
606 | |
607 | for (i = 0; i < noffs; ++i) { |
608 | ret = regmap_read(map: dsp->regmap, reg: dsp->base + offs[i], val: &offs[i]); |
609 | if (ret) { |
610 | cs_dsp_err(dsp, "Failed to read SCRATCH%u: %d\n" , i, ret); |
611 | return; |
612 | } |
613 | } |
614 | } |
615 | |
616 | static void cs_dsp_adsp2_show_fw_status(struct cs_dsp *dsp) |
617 | { |
618 | unsigned int offs[] = { |
619 | ADSP2_SCRATCH0, ADSP2_SCRATCH1, ADSP2_SCRATCH2, ADSP2_SCRATCH3, |
620 | }; |
621 | |
622 | cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); |
623 | |
624 | cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n" , |
625 | offs[0], offs[1], offs[2], offs[3]); |
626 | } |
627 | |
628 | static void cs_dsp_adsp2v2_show_fw_status(struct cs_dsp *dsp) |
629 | { |
630 | unsigned int offs[] = { ADSP2V2_SCRATCH0_1, ADSP2V2_SCRATCH2_3 }; |
631 | |
632 | cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); |
633 | |
634 | cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n" , |
635 | offs[0] & 0xFFFF, offs[0] >> 16, |
636 | offs[1] & 0xFFFF, offs[1] >> 16); |
637 | } |
638 | |
639 | static void cs_dsp_halo_show_fw_status(struct cs_dsp *dsp) |
640 | { |
641 | unsigned int offs[] = { |
642 | HALO_SCRATCH1, HALO_SCRATCH2, HALO_SCRATCH3, HALO_SCRATCH4, |
643 | }; |
644 | |
645 | cs_dsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); |
646 | |
647 | cs_dsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n" , |
648 | offs[0], offs[1], offs[2], offs[3]); |
649 | } |
650 | |
651 | static int cs_dsp_coeff_base_reg(struct cs_dsp_coeff_ctl *ctl, unsigned int *reg, |
652 | unsigned int off) |
653 | { |
654 | const struct cs_dsp_alg_region *alg_region = &ctl->alg_region; |
655 | struct cs_dsp *dsp = ctl->dsp; |
656 | const struct cs_dsp_region *mem; |
657 | |
658 | mem = cs_dsp_find_region(dsp, type: alg_region->type); |
659 | if (!mem) { |
660 | cs_dsp_err(dsp, "No base for region %x\n" , |
661 | alg_region->type); |
662 | return -EINVAL; |
663 | } |
664 | |
665 | *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset + off); |
666 | |
667 | return 0; |
668 | } |
669 | |
670 | /** |
671 | * cs_dsp_coeff_write_acked_control() - Sends event_id to the acked control |
672 | * @ctl: pointer to acked coefficient control |
673 | * @event_id: the value to write to the given acked control |
674 | * |
675 | * Once the value has been written to the control the function shall block |
676 | * until the running firmware acknowledges the write or timeout is exceeded. |
677 | * |
678 | * Must be called with pwr_lock held. |
679 | * |
680 | * Return: Zero for success, a negative number on error. |
681 | */ |
682 | int cs_dsp_coeff_write_acked_control(struct cs_dsp_coeff_ctl *ctl, unsigned int event_id) |
683 | { |
684 | struct cs_dsp *dsp = ctl->dsp; |
685 | __be32 val = cpu_to_be32(event_id); |
686 | unsigned int reg; |
687 | int i, ret; |
688 | |
689 | lockdep_assert_held(&dsp->pwr_lock); |
690 | |
691 | if (!dsp->running) |
692 | return -EPERM; |
693 | |
694 | ret = cs_dsp_coeff_base_reg(ctl, reg: ®, off: 0); |
695 | if (ret) |
696 | return ret; |
697 | |
698 | cs_dsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n" , |
699 | event_id, ctl->alg_region.alg, |
700 | cs_dsp_mem_region_name(ctl->alg_region.type), ctl->offset); |
701 | |
702 | ret = regmap_raw_write(map: dsp->regmap, reg, val: &val, val_len: sizeof(val)); |
703 | if (ret) { |
704 | cs_dsp_err(dsp, "Failed to write %x: %d\n" , reg, ret); |
705 | return ret; |
706 | } |
707 | |
708 | /* |
709 | * Poll for ack, we initially poll at ~1ms intervals for firmwares |
710 | * that respond quickly, then go to ~10ms polls. A firmware is unlikely |
711 | * to ack instantly so we do the first 1ms delay before reading the |
712 | * control to avoid a pointless bus transaction |
713 | */ |
714 | for (i = 0; i < CS_DSP_ACKED_CTL_TIMEOUT_MS;) { |
715 | switch (i) { |
716 | case 0 ... CS_DSP_ACKED_CTL_N_QUICKPOLLS - 1: |
717 | usleep_range(min: 1000, max: 2000); |
718 | i++; |
719 | break; |
720 | default: |
721 | usleep_range(min: 10000, max: 20000); |
722 | i += 10; |
723 | break; |
724 | } |
725 | |
726 | ret = regmap_raw_read(map: dsp->regmap, reg, val: &val, val_len: sizeof(val)); |
727 | if (ret) { |
728 | cs_dsp_err(dsp, "Failed to read %x: %d\n" , reg, ret); |
729 | return ret; |
730 | } |
731 | |
732 | if (val == 0) { |
733 | cs_dsp_dbg(dsp, "Acked control ACKED at poll %u\n" , i); |
734 | return 0; |
735 | } |
736 | } |
737 | |
738 | cs_dsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n" , |
739 | reg, ctl->alg_region.alg, |
740 | cs_dsp_mem_region_name(ctl->alg_region.type), |
741 | ctl->offset); |
742 | |
743 | return -ETIMEDOUT; |
744 | } |
745 | EXPORT_SYMBOL_NS_GPL(cs_dsp_coeff_write_acked_control, FW_CS_DSP); |
746 | |
747 | static int cs_dsp_coeff_write_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, |
748 | unsigned int off, const void *buf, size_t len) |
749 | { |
750 | struct cs_dsp *dsp = ctl->dsp; |
751 | void *scratch; |
752 | int ret; |
753 | unsigned int reg; |
754 | |
755 | ret = cs_dsp_coeff_base_reg(ctl, reg: ®, off); |
756 | if (ret) |
757 | return ret; |
758 | |
759 | scratch = kmemdup(p: buf, size: len, GFP_KERNEL | GFP_DMA); |
760 | if (!scratch) |
761 | return -ENOMEM; |
762 | |
763 | ret = regmap_raw_write(map: dsp->regmap, reg, val: scratch, |
764 | val_len: len); |
765 | if (ret) { |
766 | cs_dsp_err(dsp, "Failed to write %zu bytes to %x: %d\n" , |
767 | len, reg, ret); |
768 | kfree(objp: scratch); |
769 | return ret; |
770 | } |
771 | cs_dsp_dbg(dsp, "Wrote %zu bytes to %x\n" , len, reg); |
772 | |
773 | kfree(objp: scratch); |
774 | |
775 | return 0; |
776 | } |
777 | |
778 | /** |
779 | * cs_dsp_coeff_write_ctrl() - Writes the given buffer to the given coefficient control |
780 | * @ctl: pointer to coefficient control |
781 | * @off: word offset at which data should be written |
782 | * @buf: the buffer to write to the given control |
783 | * @len: the length of the buffer in bytes |
784 | * |
785 | * Must be called with pwr_lock held. |
786 | * |
787 | * Return: < 0 on error, 1 when the control value changed and 0 when it has not. |
788 | */ |
789 | int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, |
790 | unsigned int off, const void *buf, size_t len) |
791 | { |
792 | int ret = 0; |
793 | |
794 | if (!ctl) |
795 | return -ENOENT; |
796 | |
797 | lockdep_assert_held(&ctl->dsp->pwr_lock); |
798 | |
799 | if (len + off * sizeof(u32) > ctl->len) |
800 | return -EINVAL; |
801 | |
802 | if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { |
803 | ret = -EPERM; |
804 | } else if (buf != ctl->cache) { |
805 | if (memcmp(p: ctl->cache + off * sizeof(u32), q: buf, size: len)) |
806 | memcpy(ctl->cache + off * sizeof(u32), buf, len); |
807 | else |
808 | return 0; |
809 | } |
810 | |
811 | ctl->set = 1; |
812 | if (ctl->enabled && ctl->dsp->running) |
813 | ret = cs_dsp_coeff_write_ctrl_raw(ctl, off, buf, len); |
814 | |
815 | if (ret < 0) |
816 | return ret; |
817 | |
818 | return 1; |
819 | } |
820 | EXPORT_SYMBOL_NS_GPL(cs_dsp_coeff_write_ctrl, FW_CS_DSP); |
821 | |
822 | static int cs_dsp_coeff_read_ctrl_raw(struct cs_dsp_coeff_ctl *ctl, |
823 | unsigned int off, void *buf, size_t len) |
824 | { |
825 | struct cs_dsp *dsp = ctl->dsp; |
826 | void *scratch; |
827 | int ret; |
828 | unsigned int reg; |
829 | |
830 | ret = cs_dsp_coeff_base_reg(ctl, reg: ®, off); |
831 | if (ret) |
832 | return ret; |
833 | |
834 | scratch = kmalloc(size: len, GFP_KERNEL | GFP_DMA); |
835 | if (!scratch) |
836 | return -ENOMEM; |
837 | |
838 | ret = regmap_raw_read(map: dsp->regmap, reg, val: scratch, val_len: len); |
839 | if (ret) { |
840 | cs_dsp_err(dsp, "Failed to read %zu bytes from %x: %d\n" , |
841 | len, reg, ret); |
842 | kfree(objp: scratch); |
843 | return ret; |
844 | } |
845 | cs_dsp_dbg(dsp, "Read %zu bytes from %x\n" , len, reg); |
846 | |
847 | memcpy(buf, scratch, len); |
848 | kfree(objp: scratch); |
849 | |
850 | return 0; |
851 | } |
852 | |
853 | /** |
854 | * cs_dsp_coeff_read_ctrl() - Reads the given coefficient control into the given buffer |
855 | * @ctl: pointer to coefficient control |
856 | * @off: word offset at which data should be read |
857 | * @buf: the buffer to store to the given control |
858 | * @len: the length of the buffer in bytes |
859 | * |
860 | * Must be called with pwr_lock held. |
861 | * |
862 | * Return: Zero for success, a negative number on error. |
863 | */ |
864 | int cs_dsp_coeff_read_ctrl(struct cs_dsp_coeff_ctl *ctl, |
865 | unsigned int off, void *buf, size_t len) |
866 | { |
867 | int ret = 0; |
868 | |
869 | if (!ctl) |
870 | return -ENOENT; |
871 | |
872 | lockdep_assert_held(&ctl->dsp->pwr_lock); |
873 | |
874 | if (len + off * sizeof(u32) > ctl->len) |
875 | return -EINVAL; |
876 | |
877 | if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { |
878 | if (ctl->enabled && ctl->dsp->running) |
879 | return cs_dsp_coeff_read_ctrl_raw(ctl, off, buf, len); |
880 | else |
881 | return -EPERM; |
882 | } else { |
883 | if (!ctl->flags && ctl->enabled && ctl->dsp->running) |
884 | ret = cs_dsp_coeff_read_ctrl_raw(ctl, off: 0, buf: ctl->cache, len: ctl->len); |
885 | |
886 | if (buf != ctl->cache) |
887 | memcpy(buf, ctl->cache + off * sizeof(u32), len); |
888 | } |
889 | |
890 | return ret; |
891 | } |
892 | EXPORT_SYMBOL_NS_GPL(cs_dsp_coeff_read_ctrl, FW_CS_DSP); |
893 | |
894 | static int cs_dsp_coeff_init_control_caches(struct cs_dsp *dsp) |
895 | { |
896 | struct cs_dsp_coeff_ctl *ctl; |
897 | int ret; |
898 | |
899 | list_for_each_entry(ctl, &dsp->ctl_list, list) { |
900 | if (!ctl->enabled || ctl->set) |
901 | continue; |
902 | if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) |
903 | continue; |
904 | |
905 | /* |
906 | * For readable controls populate the cache from the DSP memory. |
907 | * For non-readable controls the cache was zero-filled when |
908 | * created so we don't need to do anything. |
909 | */ |
910 | if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { |
911 | ret = cs_dsp_coeff_read_ctrl_raw(ctl, off: 0, buf: ctl->cache, len: ctl->len); |
912 | if (ret < 0) |
913 | return ret; |
914 | } |
915 | } |
916 | |
917 | return 0; |
918 | } |
919 | |
920 | static int cs_dsp_coeff_sync_controls(struct cs_dsp *dsp) |
921 | { |
922 | struct cs_dsp_coeff_ctl *ctl; |
923 | int ret; |
924 | |
925 | list_for_each_entry(ctl, &dsp->ctl_list, list) { |
926 | if (!ctl->enabled) |
927 | continue; |
928 | if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { |
929 | ret = cs_dsp_coeff_write_ctrl_raw(ctl, off: 0, buf: ctl->cache, |
930 | len: ctl->len); |
931 | if (ret < 0) |
932 | return ret; |
933 | } |
934 | } |
935 | |
936 | return 0; |
937 | } |
938 | |
939 | static void cs_dsp_signal_event_controls(struct cs_dsp *dsp, |
940 | unsigned int event) |
941 | { |
942 | struct cs_dsp_coeff_ctl *ctl; |
943 | int ret; |
944 | |
945 | list_for_each_entry(ctl, &dsp->ctl_list, list) { |
946 | if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) |
947 | continue; |
948 | |
949 | if (!ctl->enabled) |
950 | continue; |
951 | |
952 | ret = cs_dsp_coeff_write_acked_control(ctl, event); |
953 | if (ret) |
954 | cs_dsp_warn(dsp, |
955 | "Failed to send 0x%x event to alg 0x%x (%d)\n" , |
956 | event, ctl->alg_region.alg, ret); |
957 | } |
958 | } |
959 | |
960 | static void cs_dsp_free_ctl_blk(struct cs_dsp_coeff_ctl *ctl) |
961 | { |
962 | kfree(objp: ctl->cache); |
963 | kfree(objp: ctl->subname); |
964 | kfree(objp: ctl); |
965 | } |
966 | |
967 | static int cs_dsp_create_control(struct cs_dsp *dsp, |
968 | const struct cs_dsp_alg_region *alg_region, |
969 | unsigned int offset, unsigned int len, |
970 | const char *subname, unsigned int subname_len, |
971 | unsigned int flags, unsigned int type) |
972 | { |
973 | struct cs_dsp_coeff_ctl *ctl; |
974 | int ret; |
975 | |
976 | list_for_each_entry(ctl, &dsp->ctl_list, list) { |
977 | if (ctl->fw_name == dsp->fw_name && |
978 | ctl->alg_region.alg == alg_region->alg && |
979 | ctl->alg_region.type == alg_region->type) { |
980 | if ((!subname && !ctl->subname) || |
981 | (subname && (ctl->subname_len == subname_len) && |
982 | !strncmp(ctl->subname, subname, ctl->subname_len))) { |
983 | if (!ctl->enabled) |
984 | ctl->enabled = 1; |
985 | return 0; |
986 | } |
987 | } |
988 | } |
989 | |
990 | ctl = kzalloc(size: sizeof(*ctl), GFP_KERNEL); |
991 | if (!ctl) |
992 | return -ENOMEM; |
993 | |
994 | ctl->fw_name = dsp->fw_name; |
995 | ctl->alg_region = *alg_region; |
996 | if (subname && dsp->fw_ver >= 2) { |
997 | ctl->subname_len = subname_len; |
998 | ctl->subname = kasprintf(GFP_KERNEL, fmt: "%.*s" , subname_len, subname); |
999 | if (!ctl->subname) { |
1000 | ret = -ENOMEM; |
1001 | goto err_ctl; |
1002 | } |
1003 | } |
1004 | ctl->enabled = 1; |
1005 | ctl->set = 0; |
1006 | ctl->dsp = dsp; |
1007 | |
1008 | ctl->flags = flags; |
1009 | ctl->type = type; |
1010 | ctl->offset = offset; |
1011 | ctl->len = len; |
1012 | ctl->cache = kzalloc(size: ctl->len, GFP_KERNEL); |
1013 | if (!ctl->cache) { |
1014 | ret = -ENOMEM; |
1015 | goto err_ctl_subname; |
1016 | } |
1017 | |
1018 | list_add(new: &ctl->list, head: &dsp->ctl_list); |
1019 | |
1020 | if (dsp->client_ops->control_add) { |
1021 | ret = dsp->client_ops->control_add(ctl); |
1022 | if (ret) |
1023 | goto err_list_del; |
1024 | } |
1025 | |
1026 | return 0; |
1027 | |
1028 | err_list_del: |
1029 | list_del(entry: &ctl->list); |
1030 | kfree(objp: ctl->cache); |
1031 | err_ctl_subname: |
1032 | kfree(objp: ctl->subname); |
1033 | err_ctl: |
1034 | kfree(objp: ctl); |
1035 | |
1036 | return ret; |
1037 | } |
1038 | |
1039 | struct cs_dsp_coeff_parsed_alg { |
1040 | int id; |
1041 | const u8 *name; |
1042 | int name_len; |
1043 | int ncoeff; |
1044 | }; |
1045 | |
1046 | struct cs_dsp_coeff_parsed_coeff { |
1047 | int offset; |
1048 | int mem_type; |
1049 | const u8 *name; |
1050 | int name_len; |
1051 | unsigned int ctl_type; |
1052 | int flags; |
1053 | int len; |
1054 | }; |
1055 | |
1056 | static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) |
1057 | { |
1058 | int length; |
1059 | |
1060 | switch (bytes) { |
1061 | case 1: |
1062 | length = **pos; |
1063 | break; |
1064 | case 2: |
1065 | length = le16_to_cpu(*((__le16 *)*pos)); |
1066 | break; |
1067 | default: |
1068 | return 0; |
1069 | } |
1070 | |
1071 | if (str) |
1072 | *str = *pos + bytes; |
1073 | |
1074 | *pos += ((length + bytes) + 3) & ~0x03; |
1075 | |
1076 | return length; |
1077 | } |
1078 | |
1079 | static int cs_dsp_coeff_parse_int(int bytes, const u8 **pos) |
1080 | { |
1081 | int val = 0; |
1082 | |
1083 | switch (bytes) { |
1084 | case 2: |
1085 | val = le16_to_cpu(*((__le16 *)*pos)); |
1086 | break; |
1087 | case 4: |
1088 | val = le32_to_cpu(*((__le32 *)*pos)); |
1089 | break; |
1090 | default: |
1091 | break; |
1092 | } |
1093 | |
1094 | *pos += bytes; |
1095 | |
1096 | return val; |
1097 | } |
1098 | |
1099 | static inline void cs_dsp_coeff_parse_alg(struct cs_dsp *dsp, const u8 **data, |
1100 | struct cs_dsp_coeff_parsed_alg *blk) |
1101 | { |
1102 | const struct wmfw_adsp_alg_data *raw; |
1103 | |
1104 | switch (dsp->fw_ver) { |
1105 | case 0: |
1106 | case 1: |
1107 | raw = (const struct wmfw_adsp_alg_data *)*data; |
1108 | *data = raw->data; |
1109 | |
1110 | blk->id = le32_to_cpu(raw->id); |
1111 | blk->name = raw->name; |
1112 | blk->name_len = strlen(raw->name); |
1113 | blk->ncoeff = le32_to_cpu(raw->ncoeff); |
1114 | break; |
1115 | default: |
1116 | blk->id = cs_dsp_coeff_parse_int(bytes: sizeof(raw->id), pos: data); |
1117 | blk->name_len = cs_dsp_coeff_parse_string(bytes: sizeof(u8), pos: data, |
1118 | str: &blk->name); |
1119 | cs_dsp_coeff_parse_string(bytes: sizeof(u16), pos: data, NULL); |
1120 | blk->ncoeff = cs_dsp_coeff_parse_int(bytes: sizeof(raw->ncoeff), pos: data); |
1121 | break; |
1122 | } |
1123 | |
1124 | cs_dsp_dbg(dsp, "Algorithm ID: %#x\n" , blk->id); |
1125 | cs_dsp_dbg(dsp, "Algorithm name: %.*s\n" , blk->name_len, blk->name); |
1126 | cs_dsp_dbg(dsp, "# of coefficient descriptors: %#x\n" , blk->ncoeff); |
1127 | } |
1128 | |
1129 | static inline void cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, const u8 **data, |
1130 | struct cs_dsp_coeff_parsed_coeff *blk) |
1131 | { |
1132 | const struct wmfw_adsp_coeff_data *raw; |
1133 | const u8 *tmp; |
1134 | int length; |
1135 | |
1136 | switch (dsp->fw_ver) { |
1137 | case 0: |
1138 | case 1: |
1139 | raw = (const struct wmfw_adsp_coeff_data *)*data; |
1140 | *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); |
1141 | |
1142 | blk->offset = le16_to_cpu(raw->hdr.offset); |
1143 | blk->mem_type = le16_to_cpu(raw->hdr.type); |
1144 | blk->name = raw->name; |
1145 | blk->name_len = strlen(raw->name); |
1146 | blk->ctl_type = le16_to_cpu(raw->ctl_type); |
1147 | blk->flags = le16_to_cpu(raw->flags); |
1148 | blk->len = le32_to_cpu(raw->len); |
1149 | break; |
1150 | default: |
1151 | tmp = *data; |
1152 | blk->offset = cs_dsp_coeff_parse_int(bytes: sizeof(raw->hdr.offset), pos: &tmp); |
1153 | blk->mem_type = cs_dsp_coeff_parse_int(bytes: sizeof(raw->hdr.type), pos: &tmp); |
1154 | length = cs_dsp_coeff_parse_int(bytes: sizeof(raw->hdr.size), pos: &tmp); |
1155 | blk->name_len = cs_dsp_coeff_parse_string(bytes: sizeof(u8), pos: &tmp, |
1156 | str: &blk->name); |
1157 | cs_dsp_coeff_parse_string(bytes: sizeof(u8), pos: &tmp, NULL); |
1158 | cs_dsp_coeff_parse_string(bytes: sizeof(u16), pos: &tmp, NULL); |
1159 | blk->ctl_type = cs_dsp_coeff_parse_int(bytes: sizeof(raw->ctl_type), pos: &tmp); |
1160 | blk->flags = cs_dsp_coeff_parse_int(bytes: sizeof(raw->flags), pos: &tmp); |
1161 | blk->len = cs_dsp_coeff_parse_int(bytes: sizeof(raw->len), pos: &tmp); |
1162 | |
1163 | *data = *data + sizeof(raw->hdr) + length; |
1164 | break; |
1165 | } |
1166 | |
1167 | cs_dsp_dbg(dsp, "\tCoefficient type: %#x\n" , blk->mem_type); |
1168 | cs_dsp_dbg(dsp, "\tCoefficient offset: %#x\n" , blk->offset); |
1169 | cs_dsp_dbg(dsp, "\tCoefficient name: %.*s\n" , blk->name_len, blk->name); |
1170 | cs_dsp_dbg(dsp, "\tCoefficient flags: %#x\n" , blk->flags); |
1171 | cs_dsp_dbg(dsp, "\tALSA control type: %#x\n" , blk->ctl_type); |
1172 | cs_dsp_dbg(dsp, "\tALSA control len: %#x\n" , blk->len); |
1173 | } |
1174 | |
1175 | static int cs_dsp_check_coeff_flags(struct cs_dsp *dsp, |
1176 | const struct cs_dsp_coeff_parsed_coeff *coeff_blk, |
1177 | unsigned int f_required, |
1178 | unsigned int f_illegal) |
1179 | { |
1180 | if ((coeff_blk->flags & f_illegal) || |
1181 | ((coeff_blk->flags & f_required) != f_required)) { |
1182 | cs_dsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n" , |
1183 | coeff_blk->flags, coeff_blk->ctl_type); |
1184 | return -EINVAL; |
1185 | } |
1186 | |
1187 | return 0; |
1188 | } |
1189 | |
1190 | static int cs_dsp_parse_coeff(struct cs_dsp *dsp, |
1191 | const struct wmfw_region *region) |
1192 | { |
1193 | struct cs_dsp_alg_region alg_region = {}; |
1194 | struct cs_dsp_coeff_parsed_alg alg_blk; |
1195 | struct cs_dsp_coeff_parsed_coeff coeff_blk; |
1196 | const u8 *data = region->data; |
1197 | int i, ret; |
1198 | |
1199 | cs_dsp_coeff_parse_alg(dsp, data: &data, blk: &alg_blk); |
1200 | for (i = 0; i < alg_blk.ncoeff; i++) { |
1201 | cs_dsp_coeff_parse_coeff(dsp, data: &data, blk: &coeff_blk); |
1202 | |
1203 | switch (coeff_blk.ctl_type) { |
1204 | case WMFW_CTL_TYPE_BYTES: |
1205 | break; |
1206 | case WMFW_CTL_TYPE_ACKED: |
1207 | if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) |
1208 | continue; /* ignore */ |
1209 | |
1210 | ret = cs_dsp_check_coeff_flags(dsp, coeff_blk: &coeff_blk, |
1211 | WMFW_CTL_FLAG_VOLATILE | |
1212 | WMFW_CTL_FLAG_WRITEABLE | |
1213 | WMFW_CTL_FLAG_READABLE, |
1214 | f_illegal: 0); |
1215 | if (ret) |
1216 | return -EINVAL; |
1217 | break; |
1218 | case WMFW_CTL_TYPE_HOSTEVENT: |
1219 | case WMFW_CTL_TYPE_FWEVENT: |
1220 | ret = cs_dsp_check_coeff_flags(dsp, coeff_blk: &coeff_blk, |
1221 | WMFW_CTL_FLAG_SYS | |
1222 | WMFW_CTL_FLAG_VOLATILE | |
1223 | WMFW_CTL_FLAG_WRITEABLE | |
1224 | WMFW_CTL_FLAG_READABLE, |
1225 | f_illegal: 0); |
1226 | if (ret) |
1227 | return -EINVAL; |
1228 | break; |
1229 | case WMFW_CTL_TYPE_HOST_BUFFER: |
1230 | ret = cs_dsp_check_coeff_flags(dsp, coeff_blk: &coeff_blk, |
1231 | WMFW_CTL_FLAG_SYS | |
1232 | WMFW_CTL_FLAG_VOLATILE | |
1233 | WMFW_CTL_FLAG_READABLE, |
1234 | f_illegal: 0); |
1235 | if (ret) |
1236 | return -EINVAL; |
1237 | break; |
1238 | default: |
1239 | cs_dsp_err(dsp, "Unknown control type: %d\n" , |
1240 | coeff_blk.ctl_type); |
1241 | return -EINVAL; |
1242 | } |
1243 | |
1244 | alg_region.type = coeff_blk.mem_type; |
1245 | alg_region.alg = alg_blk.id; |
1246 | |
1247 | ret = cs_dsp_create_control(dsp, alg_region: &alg_region, |
1248 | offset: coeff_blk.offset, |
1249 | len: coeff_blk.len, |
1250 | subname: coeff_blk.name, |
1251 | subname_len: coeff_blk.name_len, |
1252 | flags: coeff_blk.flags, |
1253 | type: coeff_blk.ctl_type); |
1254 | if (ret < 0) |
1255 | cs_dsp_err(dsp, "Failed to create control: %.*s, %d\n" , |
1256 | coeff_blk.name_len, coeff_blk.name, ret); |
1257 | } |
1258 | |
1259 | return 0; |
1260 | } |
1261 | |
1262 | static unsigned int cs_dsp_adsp1_parse_sizes(struct cs_dsp *dsp, |
1263 | const char * const file, |
1264 | unsigned int pos, |
1265 | const struct firmware *firmware) |
1266 | { |
1267 | const struct wmfw_adsp1_sizes *adsp1_sizes; |
1268 | |
1269 | adsp1_sizes = (void *)&firmware->data[pos]; |
1270 | |
1271 | cs_dsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n" , file, |
1272 | le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm), |
1273 | le32_to_cpu(adsp1_sizes->zm)); |
1274 | |
1275 | return pos + sizeof(*adsp1_sizes); |
1276 | } |
1277 | |
1278 | static unsigned int cs_dsp_adsp2_parse_sizes(struct cs_dsp *dsp, |
1279 | const char * const file, |
1280 | unsigned int pos, |
1281 | const struct firmware *firmware) |
1282 | { |
1283 | const struct wmfw_adsp2_sizes *adsp2_sizes; |
1284 | |
1285 | adsp2_sizes = (void *)&firmware->data[pos]; |
1286 | |
1287 | cs_dsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n" , file, |
1288 | le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym), |
1289 | le32_to_cpu(adsp2_sizes->pm), le32_to_cpu(adsp2_sizes->zm)); |
1290 | |
1291 | return pos + sizeof(*adsp2_sizes); |
1292 | } |
1293 | |
1294 | static bool cs_dsp_validate_version(struct cs_dsp *dsp, unsigned int version) |
1295 | { |
1296 | switch (version) { |
1297 | case 0: |
1298 | cs_dsp_warn(dsp, "Deprecated file format %d\n" , version); |
1299 | return true; |
1300 | case 1: |
1301 | case 2: |
1302 | return true; |
1303 | default: |
1304 | return false; |
1305 | } |
1306 | } |
1307 | |
1308 | static bool cs_dsp_halo_validate_version(struct cs_dsp *dsp, unsigned int version) |
1309 | { |
1310 | switch (version) { |
1311 | case 3: |
1312 | return true; |
1313 | default: |
1314 | return false; |
1315 | } |
1316 | } |
1317 | |
1318 | static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, |
1319 | const char *file) |
1320 | { |
1321 | LIST_HEAD(buf_list); |
1322 | struct regmap *regmap = dsp->regmap; |
1323 | unsigned int pos = 0; |
1324 | const struct wmfw_header *; |
1325 | const struct wmfw_adsp1_sizes *adsp1_sizes; |
1326 | const struct wmfw_footer *; |
1327 | const struct wmfw_region *region; |
1328 | const struct cs_dsp_region *mem; |
1329 | const char *region_name; |
1330 | char *text = NULL; |
1331 | struct cs_dsp_buf *buf; |
1332 | unsigned int reg; |
1333 | int regions = 0; |
1334 | int ret, offset, type; |
1335 | |
1336 | if (!firmware) |
1337 | return 0; |
1338 | |
1339 | ret = -EINVAL; |
1340 | |
1341 | pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); |
1342 | if (pos >= firmware->size) { |
1343 | cs_dsp_err(dsp, "%s: file too short, %zu bytes\n" , |
1344 | file, firmware->size); |
1345 | goto out_fw; |
1346 | } |
1347 | |
1348 | header = (void *)&firmware->data[0]; |
1349 | |
1350 | if (memcmp(p: &header->magic[0], q: "WMFW" , size: 4) != 0) { |
1351 | cs_dsp_err(dsp, "%s: invalid magic\n" , file); |
1352 | goto out_fw; |
1353 | } |
1354 | |
1355 | if (!dsp->ops->validate_version(dsp, header->ver)) { |
1356 | cs_dsp_err(dsp, "%s: unknown file format %d\n" , |
1357 | file, header->ver); |
1358 | goto out_fw; |
1359 | } |
1360 | |
1361 | cs_dsp_info(dsp, "Firmware version: %d\n" , header->ver); |
1362 | dsp->fw_ver = header->ver; |
1363 | |
1364 | if (header->core != dsp->type) { |
1365 | cs_dsp_err(dsp, "%s: invalid core %d != %d\n" , |
1366 | file, header->core, dsp->type); |
1367 | goto out_fw; |
1368 | } |
1369 | |
1370 | pos = sizeof(*header); |
1371 | pos = dsp->ops->parse_sizes(dsp, file, pos, firmware); |
1372 | |
1373 | footer = (void *)&firmware->data[pos]; |
1374 | pos += sizeof(*footer); |
1375 | |
1376 | if (le32_to_cpu(header->len) != pos) { |
1377 | cs_dsp_err(dsp, "%s: unexpected header length %d\n" , |
1378 | file, le32_to_cpu(header->len)); |
1379 | goto out_fw; |
1380 | } |
1381 | |
1382 | cs_dsp_dbg(dsp, "%s: timestamp %llu\n" , file, |
1383 | le64_to_cpu(footer->timestamp)); |
1384 | |
1385 | while (pos < firmware->size && |
1386 | sizeof(*region) < firmware->size - pos) { |
1387 | region = (void *)&(firmware->data[pos]); |
1388 | region_name = "Unknown" ; |
1389 | reg = 0; |
1390 | text = NULL; |
1391 | offset = le32_to_cpu(region->offset) & 0xffffff; |
1392 | type = be32_to_cpu(region->type) & 0xff; |
1393 | |
1394 | switch (type) { |
1395 | case WMFW_NAME_TEXT: |
1396 | region_name = "Firmware name" ; |
1397 | text = kzalloc(le32_to_cpu(region->len) + 1, |
1398 | GFP_KERNEL); |
1399 | break; |
1400 | case WMFW_ALGORITHM_DATA: |
1401 | region_name = "Algorithm" ; |
1402 | ret = cs_dsp_parse_coeff(dsp, region); |
1403 | if (ret != 0) |
1404 | goto out_fw; |
1405 | break; |
1406 | case WMFW_INFO_TEXT: |
1407 | region_name = "Information" ; |
1408 | text = kzalloc(le32_to_cpu(region->len) + 1, |
1409 | GFP_KERNEL); |
1410 | break; |
1411 | case WMFW_ABSOLUTE: |
1412 | region_name = "Absolute" ; |
1413 | reg = offset; |
1414 | break; |
1415 | case WMFW_ADSP1_PM: |
1416 | case WMFW_ADSP1_DM: |
1417 | case WMFW_ADSP2_XM: |
1418 | case WMFW_ADSP2_YM: |
1419 | case WMFW_ADSP1_ZM: |
1420 | case WMFW_HALO_PM_PACKED: |
1421 | case WMFW_HALO_XM_PACKED: |
1422 | case WMFW_HALO_YM_PACKED: |
1423 | mem = cs_dsp_find_region(dsp, type); |
1424 | if (!mem) { |
1425 | cs_dsp_err(dsp, "No region of type: %x\n" , type); |
1426 | ret = -EINVAL; |
1427 | goto out_fw; |
1428 | } |
1429 | |
1430 | region_name = cs_dsp_mem_region_name(type); |
1431 | reg = dsp->ops->region_to_reg(mem, offset); |
1432 | break; |
1433 | default: |
1434 | cs_dsp_warn(dsp, |
1435 | "%s.%d: Unknown region type %x at %d(%x)\n" , |
1436 | file, regions, type, pos, pos); |
1437 | break; |
1438 | } |
1439 | |
1440 | cs_dsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n" , file, |
1441 | regions, le32_to_cpu(region->len), offset, |
1442 | region_name); |
1443 | |
1444 | if (le32_to_cpu(region->len) > |
1445 | firmware->size - pos - sizeof(*region)) { |
1446 | cs_dsp_err(dsp, |
1447 | "%s.%d: %s region len %d bytes exceeds file length %zu\n" , |
1448 | file, regions, region_name, |
1449 | le32_to_cpu(region->len), firmware->size); |
1450 | ret = -EINVAL; |
1451 | goto out_fw; |
1452 | } |
1453 | |
1454 | if (text) { |
1455 | memcpy(text, region->data, le32_to_cpu(region->len)); |
1456 | cs_dsp_info(dsp, "%s: %s\n" , file, text); |
1457 | kfree(objp: text); |
1458 | text = NULL; |
1459 | } |
1460 | |
1461 | if (reg) { |
1462 | buf = cs_dsp_buf_alloc(src: region->data, |
1463 | le32_to_cpu(region->len), |
1464 | list: &buf_list); |
1465 | if (!buf) { |
1466 | cs_dsp_err(dsp, "Out of memory\n" ); |
1467 | ret = -ENOMEM; |
1468 | goto out_fw; |
1469 | } |
1470 | |
1471 | ret = regmap_raw_write_async(map: regmap, reg, val: buf->buf, |
1472 | le32_to_cpu(region->len)); |
1473 | if (ret != 0) { |
1474 | cs_dsp_err(dsp, |
1475 | "%s.%d: Failed to write %d bytes at %d in %s: %d\n" , |
1476 | file, regions, |
1477 | le32_to_cpu(region->len), offset, |
1478 | region_name, ret); |
1479 | goto out_fw; |
1480 | } |
1481 | } |
1482 | |
1483 | pos += le32_to_cpu(region->len) + sizeof(*region); |
1484 | regions++; |
1485 | } |
1486 | |
1487 | ret = regmap_async_complete(map: regmap); |
1488 | if (ret != 0) { |
1489 | cs_dsp_err(dsp, "Failed to complete async write: %d\n" , ret); |
1490 | goto out_fw; |
1491 | } |
1492 | |
1493 | if (pos > firmware->size) |
1494 | cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n" , |
1495 | file, regions, pos - firmware->size); |
1496 | |
1497 | cs_dsp_debugfs_save_wmfwname(dsp, s: file); |
1498 | |
1499 | out_fw: |
1500 | regmap_async_complete(map: regmap); |
1501 | cs_dsp_buf_free(list: &buf_list); |
1502 | kfree(objp: text); |
1503 | |
1504 | return ret; |
1505 | } |
1506 | |
1507 | /** |
1508 | * cs_dsp_get_ctl() - Finds a matching coefficient control |
1509 | * @dsp: pointer to DSP structure |
1510 | * @name: pointer to string to match with a control's subname |
1511 | * @type: the algorithm type to match |
1512 | * @alg: the algorithm id to match |
1513 | * |
1514 | * Find cs_dsp_coeff_ctl with input name as its subname |
1515 | * |
1516 | * Return: pointer to the control on success, NULL if not found |
1517 | */ |
1518 | struct cs_dsp_coeff_ctl *cs_dsp_get_ctl(struct cs_dsp *dsp, const char *name, int type, |
1519 | unsigned int alg) |
1520 | { |
1521 | struct cs_dsp_coeff_ctl *pos, *rslt = NULL; |
1522 | |
1523 | lockdep_assert_held(&dsp->pwr_lock); |
1524 | |
1525 | list_for_each_entry(pos, &dsp->ctl_list, list) { |
1526 | if (!pos->subname) |
1527 | continue; |
1528 | if (strncmp(pos->subname, name, pos->subname_len) == 0 && |
1529 | pos->fw_name == dsp->fw_name && |
1530 | pos->alg_region.alg == alg && |
1531 | pos->alg_region.type == type) { |
1532 | rslt = pos; |
1533 | break; |
1534 | } |
1535 | } |
1536 | |
1537 | return rslt; |
1538 | } |
1539 | EXPORT_SYMBOL_NS_GPL(cs_dsp_get_ctl, FW_CS_DSP); |
1540 | |
1541 | static void cs_dsp_ctl_fixup_base(struct cs_dsp *dsp, |
1542 | const struct cs_dsp_alg_region *alg_region) |
1543 | { |
1544 | struct cs_dsp_coeff_ctl *ctl; |
1545 | |
1546 | list_for_each_entry(ctl, &dsp->ctl_list, list) { |
1547 | if (ctl->fw_name == dsp->fw_name && |
1548 | alg_region->alg == ctl->alg_region.alg && |
1549 | alg_region->type == ctl->alg_region.type) { |
1550 | ctl->alg_region.base = alg_region->base; |
1551 | } |
1552 | } |
1553 | } |
1554 | |
1555 | static void *cs_dsp_read_algs(struct cs_dsp *dsp, size_t n_algs, |
1556 | const struct cs_dsp_region *mem, |
1557 | unsigned int pos, unsigned int len) |
1558 | { |
1559 | void *alg; |
1560 | unsigned int reg; |
1561 | int ret; |
1562 | __be32 val; |
1563 | |
1564 | if (n_algs == 0) { |
1565 | cs_dsp_err(dsp, "No algorithms\n" ); |
1566 | return ERR_PTR(error: -EINVAL); |
1567 | } |
1568 | |
1569 | if (n_algs > 1024) { |
1570 | cs_dsp_err(dsp, "Algorithm count %zx excessive\n" , n_algs); |
1571 | return ERR_PTR(error: -EINVAL); |
1572 | } |
1573 | |
1574 | /* Read the terminator first to validate the length */ |
1575 | reg = dsp->ops->region_to_reg(mem, pos + len); |
1576 | |
1577 | ret = regmap_raw_read(map: dsp->regmap, reg, val: &val, val_len: sizeof(val)); |
1578 | if (ret != 0) { |
1579 | cs_dsp_err(dsp, "Failed to read algorithm list end: %d\n" , |
1580 | ret); |
1581 | return ERR_PTR(error: ret); |
1582 | } |
1583 | |
1584 | if (be32_to_cpu(val) != 0xbedead) |
1585 | cs_dsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n" , |
1586 | reg, be32_to_cpu(val)); |
1587 | |
1588 | /* Convert length from DSP words to bytes */ |
1589 | len *= sizeof(u32); |
1590 | |
1591 | alg = kzalloc(size: len, GFP_KERNEL | GFP_DMA); |
1592 | if (!alg) |
1593 | return ERR_PTR(error: -ENOMEM); |
1594 | |
1595 | reg = dsp->ops->region_to_reg(mem, pos); |
1596 | |
1597 | ret = regmap_raw_read(map: dsp->regmap, reg, val: alg, val_len: len); |
1598 | if (ret != 0) { |
1599 | cs_dsp_err(dsp, "Failed to read algorithm list: %d\n" , ret); |
1600 | kfree(objp: alg); |
1601 | return ERR_PTR(error: ret); |
1602 | } |
1603 | |
1604 | return alg; |
1605 | } |
1606 | |
1607 | /** |
1608 | * cs_dsp_find_alg_region() - Finds a matching algorithm region |
1609 | * @dsp: pointer to DSP structure |
1610 | * @type: the algorithm type to match |
1611 | * @id: the algorithm id to match |
1612 | * |
1613 | * Return: Pointer to matching algorithm region, or NULL if not found. |
1614 | */ |
1615 | struct cs_dsp_alg_region *cs_dsp_find_alg_region(struct cs_dsp *dsp, |
1616 | int type, unsigned int id) |
1617 | { |
1618 | struct cs_dsp_alg_region *alg_region; |
1619 | |
1620 | lockdep_assert_held(&dsp->pwr_lock); |
1621 | |
1622 | list_for_each_entry(alg_region, &dsp->alg_regions, list) { |
1623 | if (id == alg_region->alg && type == alg_region->type) |
1624 | return alg_region; |
1625 | } |
1626 | |
1627 | return NULL; |
1628 | } |
1629 | EXPORT_SYMBOL_NS_GPL(cs_dsp_find_alg_region, FW_CS_DSP); |
1630 | |
1631 | static struct cs_dsp_alg_region *cs_dsp_create_region(struct cs_dsp *dsp, |
1632 | int type, __be32 id, |
1633 | __be32 ver, __be32 base) |
1634 | { |
1635 | struct cs_dsp_alg_region *alg_region; |
1636 | |
1637 | alg_region = kzalloc(size: sizeof(*alg_region), GFP_KERNEL); |
1638 | if (!alg_region) |
1639 | return ERR_PTR(error: -ENOMEM); |
1640 | |
1641 | alg_region->type = type; |
1642 | alg_region->alg = be32_to_cpu(id); |
1643 | alg_region->ver = be32_to_cpu(ver); |
1644 | alg_region->base = be32_to_cpu(base); |
1645 | |
1646 | list_add_tail(new: &alg_region->list, head: &dsp->alg_regions); |
1647 | |
1648 | if (dsp->fw_ver > 0) |
1649 | cs_dsp_ctl_fixup_base(dsp, alg_region); |
1650 | |
1651 | return alg_region; |
1652 | } |
1653 | |
1654 | static void cs_dsp_free_alg_regions(struct cs_dsp *dsp) |
1655 | { |
1656 | struct cs_dsp_alg_region *alg_region; |
1657 | |
1658 | while (!list_empty(head: &dsp->alg_regions)) { |
1659 | alg_region = list_first_entry(&dsp->alg_regions, |
1660 | struct cs_dsp_alg_region, |
1661 | list); |
1662 | list_del(entry: &alg_region->list); |
1663 | kfree(objp: alg_region); |
1664 | } |
1665 | } |
1666 | |
1667 | static void (struct cs_dsp *dsp, |
1668 | struct wmfw_id_hdr *fw, int nalgs) |
1669 | { |
1670 | dsp->fw_id = be32_to_cpu(fw->id); |
1671 | dsp->fw_id_version = be32_to_cpu(fw->ver); |
1672 | |
1673 | cs_dsp_info(dsp, "Firmware: %x v%d.%d.%d, %d algorithms\n" , |
1674 | dsp->fw_id, (dsp->fw_id_version & 0xff0000) >> 16, |
1675 | (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, |
1676 | nalgs); |
1677 | } |
1678 | |
1679 | static void (struct cs_dsp *dsp, |
1680 | struct wmfw_v3_id_hdr *fw, int nalgs) |
1681 | { |
1682 | dsp->fw_id = be32_to_cpu(fw->id); |
1683 | dsp->fw_id_version = be32_to_cpu(fw->ver); |
1684 | dsp->fw_vendor_id = be32_to_cpu(fw->vendor_id); |
1685 | |
1686 | cs_dsp_info(dsp, "Firmware: %x vendor: 0x%x v%d.%d.%d, %d algorithms\n" , |
1687 | dsp->fw_id, dsp->fw_vendor_id, |
1688 | (dsp->fw_id_version & 0xff0000) >> 16, |
1689 | (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, |
1690 | nalgs); |
1691 | } |
1692 | |
1693 | static int cs_dsp_create_regions(struct cs_dsp *dsp, __be32 id, __be32 ver, |
1694 | int nregions, const int *type, __be32 *base) |
1695 | { |
1696 | struct cs_dsp_alg_region *alg_region; |
1697 | int i; |
1698 | |
1699 | for (i = 0; i < nregions; i++) { |
1700 | alg_region = cs_dsp_create_region(dsp, type: type[i], id, ver, base: base[i]); |
1701 | if (IS_ERR(ptr: alg_region)) |
1702 | return PTR_ERR(ptr: alg_region); |
1703 | } |
1704 | |
1705 | return 0; |
1706 | } |
1707 | |
1708 | static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp) |
1709 | { |
1710 | struct wmfw_adsp1_id_hdr adsp1_id; |
1711 | struct wmfw_adsp1_alg_hdr *adsp1_alg; |
1712 | struct cs_dsp_alg_region *alg_region; |
1713 | const struct cs_dsp_region *mem; |
1714 | unsigned int pos, len; |
1715 | size_t n_algs; |
1716 | int i, ret; |
1717 | |
1718 | mem = cs_dsp_find_region(dsp, WMFW_ADSP1_DM); |
1719 | if (WARN_ON(!mem)) |
1720 | return -EINVAL; |
1721 | |
1722 | ret = regmap_raw_read(map: dsp->regmap, reg: mem->base, val: &adsp1_id, |
1723 | val_len: sizeof(adsp1_id)); |
1724 | if (ret != 0) { |
1725 | cs_dsp_err(dsp, "Failed to read algorithm info: %d\n" , |
1726 | ret); |
1727 | return ret; |
1728 | } |
1729 | |
1730 | n_algs = be32_to_cpu(adsp1_id.n_algs); |
1731 | |
1732 | cs_dsp_parse_wmfw_id_header(dsp, fw: &adsp1_id.fw, nalgs: n_algs); |
1733 | |
1734 | alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM, |
1735 | id: adsp1_id.fw.id, ver: adsp1_id.fw.ver, |
1736 | base: adsp1_id.zm); |
1737 | if (IS_ERR(ptr: alg_region)) |
1738 | return PTR_ERR(ptr: alg_region); |
1739 | |
1740 | alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM, |
1741 | id: adsp1_id.fw.id, ver: adsp1_id.fw.ver, |
1742 | base: adsp1_id.dm); |
1743 | if (IS_ERR(ptr: alg_region)) |
1744 | return PTR_ERR(ptr: alg_region); |
1745 | |
1746 | /* Calculate offset and length in DSP words */ |
1747 | pos = sizeof(adsp1_id) / sizeof(u32); |
1748 | len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32); |
1749 | |
1750 | adsp1_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); |
1751 | if (IS_ERR(ptr: adsp1_alg)) |
1752 | return PTR_ERR(ptr: adsp1_alg); |
1753 | |
1754 | for (i = 0; i < n_algs; i++) { |
1755 | cs_dsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n" , |
1756 | i, be32_to_cpu(adsp1_alg[i].alg.id), |
1757 | (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, |
1758 | (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, |
1759 | be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, |
1760 | be32_to_cpu(adsp1_alg[i].dm), |
1761 | be32_to_cpu(adsp1_alg[i].zm)); |
1762 | |
1763 | alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_DM, |
1764 | id: adsp1_alg[i].alg.id, |
1765 | ver: adsp1_alg[i].alg.ver, |
1766 | base: adsp1_alg[i].dm); |
1767 | if (IS_ERR(ptr: alg_region)) { |
1768 | ret = PTR_ERR(ptr: alg_region); |
1769 | goto out; |
1770 | } |
1771 | if (dsp->fw_ver == 0) { |
1772 | if (i + 1 < n_algs) { |
1773 | len = be32_to_cpu(adsp1_alg[i + 1].dm); |
1774 | len -= be32_to_cpu(adsp1_alg[i].dm); |
1775 | len *= 4; |
1776 | cs_dsp_create_control(dsp, alg_region, offset: 0, |
1777 | len, NULL, subname_len: 0, flags: 0, |
1778 | WMFW_CTL_TYPE_BYTES); |
1779 | } else { |
1780 | cs_dsp_warn(dsp, "Missing length info for region DM with ID %x\n" , |
1781 | be32_to_cpu(adsp1_alg[i].alg.id)); |
1782 | } |
1783 | } |
1784 | |
1785 | alg_region = cs_dsp_create_region(dsp, WMFW_ADSP1_ZM, |
1786 | id: adsp1_alg[i].alg.id, |
1787 | ver: adsp1_alg[i].alg.ver, |
1788 | base: adsp1_alg[i].zm); |
1789 | if (IS_ERR(ptr: alg_region)) { |
1790 | ret = PTR_ERR(ptr: alg_region); |
1791 | goto out; |
1792 | } |
1793 | if (dsp->fw_ver == 0) { |
1794 | if (i + 1 < n_algs) { |
1795 | len = be32_to_cpu(adsp1_alg[i + 1].zm); |
1796 | len -= be32_to_cpu(adsp1_alg[i].zm); |
1797 | len *= 4; |
1798 | cs_dsp_create_control(dsp, alg_region, offset: 0, |
1799 | len, NULL, subname_len: 0, flags: 0, |
1800 | WMFW_CTL_TYPE_BYTES); |
1801 | } else { |
1802 | cs_dsp_warn(dsp, "Missing length info for region ZM with ID %x\n" , |
1803 | be32_to_cpu(adsp1_alg[i].alg.id)); |
1804 | } |
1805 | } |
1806 | } |
1807 | |
1808 | out: |
1809 | kfree(objp: adsp1_alg); |
1810 | return ret; |
1811 | } |
1812 | |
1813 | static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) |
1814 | { |
1815 | struct wmfw_adsp2_id_hdr adsp2_id; |
1816 | struct wmfw_adsp2_alg_hdr *adsp2_alg; |
1817 | struct cs_dsp_alg_region *alg_region; |
1818 | const struct cs_dsp_region *mem; |
1819 | unsigned int pos, len; |
1820 | size_t n_algs; |
1821 | int i, ret; |
1822 | |
1823 | mem = cs_dsp_find_region(dsp, WMFW_ADSP2_XM); |
1824 | if (WARN_ON(!mem)) |
1825 | return -EINVAL; |
1826 | |
1827 | ret = regmap_raw_read(map: dsp->regmap, reg: mem->base, val: &adsp2_id, |
1828 | val_len: sizeof(adsp2_id)); |
1829 | if (ret != 0) { |
1830 | cs_dsp_err(dsp, "Failed to read algorithm info: %d\n" , |
1831 | ret); |
1832 | return ret; |
1833 | } |
1834 | |
1835 | n_algs = be32_to_cpu(adsp2_id.n_algs); |
1836 | |
1837 | cs_dsp_parse_wmfw_id_header(dsp, fw: &adsp2_id.fw, nalgs: n_algs); |
1838 | |
1839 | alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, |
1840 | id: adsp2_id.fw.id, ver: adsp2_id.fw.ver, |
1841 | base: adsp2_id.xm); |
1842 | if (IS_ERR(ptr: alg_region)) |
1843 | return PTR_ERR(ptr: alg_region); |
1844 | |
1845 | alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM, |
1846 | id: adsp2_id.fw.id, ver: adsp2_id.fw.ver, |
1847 | base: adsp2_id.ym); |
1848 | if (IS_ERR(ptr: alg_region)) |
1849 | return PTR_ERR(ptr: alg_region); |
1850 | |
1851 | alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM, |
1852 | id: adsp2_id.fw.id, ver: adsp2_id.fw.ver, |
1853 | base: adsp2_id.zm); |
1854 | if (IS_ERR(ptr: alg_region)) |
1855 | return PTR_ERR(ptr: alg_region); |
1856 | |
1857 | /* Calculate offset and length in DSP words */ |
1858 | pos = sizeof(adsp2_id) / sizeof(u32); |
1859 | len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32); |
1860 | |
1861 | adsp2_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); |
1862 | if (IS_ERR(ptr: adsp2_alg)) |
1863 | return PTR_ERR(ptr: adsp2_alg); |
1864 | |
1865 | for (i = 0; i < n_algs; i++) { |
1866 | cs_dsp_dbg(dsp, |
1867 | "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n" , |
1868 | i, be32_to_cpu(adsp2_alg[i].alg.id), |
1869 | (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, |
1870 | (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, |
1871 | be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, |
1872 | be32_to_cpu(adsp2_alg[i].xm), |
1873 | be32_to_cpu(adsp2_alg[i].ym), |
1874 | be32_to_cpu(adsp2_alg[i].zm)); |
1875 | |
1876 | alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, |
1877 | id: adsp2_alg[i].alg.id, |
1878 | ver: adsp2_alg[i].alg.ver, |
1879 | base: adsp2_alg[i].xm); |
1880 | if (IS_ERR(ptr: alg_region)) { |
1881 | ret = PTR_ERR(ptr: alg_region); |
1882 | goto out; |
1883 | } |
1884 | if (dsp->fw_ver == 0) { |
1885 | if (i + 1 < n_algs) { |
1886 | len = be32_to_cpu(adsp2_alg[i + 1].xm); |
1887 | len -= be32_to_cpu(adsp2_alg[i].xm); |
1888 | len *= 4; |
1889 | cs_dsp_create_control(dsp, alg_region, offset: 0, |
1890 | len, NULL, subname_len: 0, flags: 0, |
1891 | WMFW_CTL_TYPE_BYTES); |
1892 | } else { |
1893 | cs_dsp_warn(dsp, "Missing length info for region XM with ID %x\n" , |
1894 | be32_to_cpu(adsp2_alg[i].alg.id)); |
1895 | } |
1896 | } |
1897 | |
1898 | alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_YM, |
1899 | id: adsp2_alg[i].alg.id, |
1900 | ver: adsp2_alg[i].alg.ver, |
1901 | base: adsp2_alg[i].ym); |
1902 | if (IS_ERR(ptr: alg_region)) { |
1903 | ret = PTR_ERR(ptr: alg_region); |
1904 | goto out; |
1905 | } |
1906 | if (dsp->fw_ver == 0) { |
1907 | if (i + 1 < n_algs) { |
1908 | len = be32_to_cpu(adsp2_alg[i + 1].ym); |
1909 | len -= be32_to_cpu(adsp2_alg[i].ym); |
1910 | len *= 4; |
1911 | cs_dsp_create_control(dsp, alg_region, offset: 0, |
1912 | len, NULL, subname_len: 0, flags: 0, |
1913 | WMFW_CTL_TYPE_BYTES); |
1914 | } else { |
1915 | cs_dsp_warn(dsp, "Missing length info for region YM with ID %x\n" , |
1916 | be32_to_cpu(adsp2_alg[i].alg.id)); |
1917 | } |
1918 | } |
1919 | |
1920 | alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_ZM, |
1921 | id: adsp2_alg[i].alg.id, |
1922 | ver: adsp2_alg[i].alg.ver, |
1923 | base: adsp2_alg[i].zm); |
1924 | if (IS_ERR(ptr: alg_region)) { |
1925 | ret = PTR_ERR(ptr: alg_region); |
1926 | goto out; |
1927 | } |
1928 | if (dsp->fw_ver == 0) { |
1929 | if (i + 1 < n_algs) { |
1930 | len = be32_to_cpu(adsp2_alg[i + 1].zm); |
1931 | len -= be32_to_cpu(adsp2_alg[i].zm); |
1932 | len *= 4; |
1933 | cs_dsp_create_control(dsp, alg_region, offset: 0, |
1934 | len, NULL, subname_len: 0, flags: 0, |
1935 | WMFW_CTL_TYPE_BYTES); |
1936 | } else { |
1937 | cs_dsp_warn(dsp, "Missing length info for region ZM with ID %x\n" , |
1938 | be32_to_cpu(adsp2_alg[i].alg.id)); |
1939 | } |
1940 | } |
1941 | } |
1942 | |
1943 | out: |
1944 | kfree(objp: adsp2_alg); |
1945 | return ret; |
1946 | } |
1947 | |
1948 | static int cs_dsp_halo_create_regions(struct cs_dsp *dsp, __be32 id, __be32 ver, |
1949 | __be32 xm_base, __be32 ym_base) |
1950 | { |
1951 | static const int types[] = { |
1952 | WMFW_ADSP2_XM, WMFW_HALO_XM_PACKED, |
1953 | WMFW_ADSP2_YM, WMFW_HALO_YM_PACKED |
1954 | }; |
1955 | __be32 bases[] = { xm_base, xm_base, ym_base, ym_base }; |
1956 | |
1957 | return cs_dsp_create_regions(dsp, id, ver, ARRAY_SIZE(types), type: types, base: bases); |
1958 | } |
1959 | |
1960 | static int cs_dsp_halo_setup_algs(struct cs_dsp *dsp) |
1961 | { |
1962 | struct wmfw_halo_id_hdr halo_id; |
1963 | struct wmfw_halo_alg_hdr *halo_alg; |
1964 | const struct cs_dsp_region *mem; |
1965 | unsigned int pos, len; |
1966 | size_t n_algs; |
1967 | int i, ret; |
1968 | |
1969 | mem = cs_dsp_find_region(dsp, WMFW_ADSP2_XM); |
1970 | if (WARN_ON(!mem)) |
1971 | return -EINVAL; |
1972 | |
1973 | ret = regmap_raw_read(map: dsp->regmap, reg: mem->base, val: &halo_id, |
1974 | val_len: sizeof(halo_id)); |
1975 | if (ret != 0) { |
1976 | cs_dsp_err(dsp, "Failed to read algorithm info: %d\n" , |
1977 | ret); |
1978 | return ret; |
1979 | } |
1980 | |
1981 | n_algs = be32_to_cpu(halo_id.n_algs); |
1982 | |
1983 | cs_dsp_parse_wmfw_v3_id_header(dsp, fw: &halo_id.fw, nalgs: n_algs); |
1984 | |
1985 | ret = cs_dsp_halo_create_regions(dsp, id: halo_id.fw.id, ver: halo_id.fw.ver, |
1986 | xm_base: halo_id.xm_base, ym_base: halo_id.ym_base); |
1987 | if (ret) |
1988 | return ret; |
1989 | |
1990 | /* Calculate offset and length in DSP words */ |
1991 | pos = sizeof(halo_id) / sizeof(u32); |
1992 | len = (sizeof(*halo_alg) * n_algs) / sizeof(u32); |
1993 | |
1994 | halo_alg = cs_dsp_read_algs(dsp, n_algs, mem, pos, len); |
1995 | if (IS_ERR(ptr: halo_alg)) |
1996 | return PTR_ERR(ptr: halo_alg); |
1997 | |
1998 | for (i = 0; i < n_algs; i++) { |
1999 | cs_dsp_dbg(dsp, |
2000 | "%d: ID %x v%d.%d.%d XM@%x YM@%x\n" , |
2001 | i, be32_to_cpu(halo_alg[i].alg.id), |
2002 | (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16, |
2003 | (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8, |
2004 | be32_to_cpu(halo_alg[i].alg.ver) & 0xff, |
2005 | be32_to_cpu(halo_alg[i].xm_base), |
2006 | be32_to_cpu(halo_alg[i].ym_base)); |
2007 | |
2008 | ret = cs_dsp_halo_create_regions(dsp, id: halo_alg[i].alg.id, |
2009 | ver: halo_alg[i].alg.ver, |
2010 | xm_base: halo_alg[i].xm_base, |
2011 | ym_base: halo_alg[i].ym_base); |
2012 | if (ret) |
2013 | goto out; |
2014 | } |
2015 | |
2016 | out: |
2017 | kfree(objp: halo_alg); |
2018 | return ret; |
2019 | } |
2020 | |
2021 | static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware, |
2022 | const char *file) |
2023 | { |
2024 | LIST_HEAD(buf_list); |
2025 | struct regmap *regmap = dsp->regmap; |
2026 | struct wmfw_coeff_hdr *hdr; |
2027 | struct wmfw_coeff_item *blk; |
2028 | const struct cs_dsp_region *mem; |
2029 | struct cs_dsp_alg_region *alg_region; |
2030 | const char *region_name; |
2031 | int ret, pos, blocks, type, offset, reg, version; |
2032 | char *text = NULL; |
2033 | struct cs_dsp_buf *buf; |
2034 | |
2035 | if (!firmware) |
2036 | return 0; |
2037 | |
2038 | ret = -EINVAL; |
2039 | |
2040 | if (sizeof(*hdr) >= firmware->size) { |
2041 | cs_dsp_err(dsp, "%s: coefficient file too short, %zu bytes\n" , |
2042 | file, firmware->size); |
2043 | goto out_fw; |
2044 | } |
2045 | |
2046 | hdr = (void *)&firmware->data[0]; |
2047 | if (memcmp(p: hdr->magic, q: "WMDR" , size: 4) != 0) { |
2048 | cs_dsp_err(dsp, "%s: invalid coefficient magic\n" , file); |
2049 | goto out_fw; |
2050 | } |
2051 | |
2052 | switch (be32_to_cpu(hdr->rev) & 0xff) { |
2053 | case 1: |
2054 | case 2: |
2055 | break; |
2056 | default: |
2057 | cs_dsp_err(dsp, "%s: Unsupported coefficient file format %d\n" , |
2058 | file, be32_to_cpu(hdr->rev) & 0xff); |
2059 | ret = -EINVAL; |
2060 | goto out_fw; |
2061 | } |
2062 | |
2063 | cs_dsp_info(dsp, "%s: v%d.%d.%d\n" , file, |
2064 | (le32_to_cpu(hdr->ver) >> 16) & 0xff, |
2065 | (le32_to_cpu(hdr->ver) >> 8) & 0xff, |
2066 | le32_to_cpu(hdr->ver) & 0xff); |
2067 | |
2068 | pos = le32_to_cpu(hdr->len); |
2069 | |
2070 | blocks = 0; |
2071 | while (pos < firmware->size && |
2072 | sizeof(*blk) < firmware->size - pos) { |
2073 | blk = (void *)(&firmware->data[pos]); |
2074 | |
2075 | type = le16_to_cpu(blk->type); |
2076 | offset = le16_to_cpu(blk->offset); |
2077 | version = le32_to_cpu(blk->ver) >> 8; |
2078 | |
2079 | cs_dsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n" , |
2080 | file, blocks, le32_to_cpu(blk->id), |
2081 | (le32_to_cpu(blk->ver) >> 16) & 0xff, |
2082 | (le32_to_cpu(blk->ver) >> 8) & 0xff, |
2083 | le32_to_cpu(blk->ver) & 0xff); |
2084 | cs_dsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n" , |
2085 | file, blocks, le32_to_cpu(blk->len), offset, type); |
2086 | |
2087 | reg = 0; |
2088 | region_name = "Unknown" ; |
2089 | switch (type) { |
2090 | case (WMFW_NAME_TEXT << 8): |
2091 | text = kzalloc(le32_to_cpu(blk->len) + 1, GFP_KERNEL); |
2092 | break; |
2093 | case (WMFW_INFO_TEXT << 8): |
2094 | case (WMFW_METADATA << 8): |
2095 | break; |
2096 | case (WMFW_ABSOLUTE << 8): |
2097 | /* |
2098 | * Old files may use this for global |
2099 | * coefficients. |
2100 | */ |
2101 | if (le32_to_cpu(blk->id) == dsp->fw_id && |
2102 | offset == 0) { |
2103 | region_name = "global coefficients" ; |
2104 | mem = cs_dsp_find_region(dsp, type); |
2105 | if (!mem) { |
2106 | cs_dsp_err(dsp, "No ZM\n" ); |
2107 | break; |
2108 | } |
2109 | reg = dsp->ops->region_to_reg(mem, 0); |
2110 | |
2111 | } else { |
2112 | region_name = "register" ; |
2113 | reg = offset; |
2114 | } |
2115 | break; |
2116 | |
2117 | case WMFW_ADSP1_DM: |
2118 | case WMFW_ADSP1_ZM: |
2119 | case WMFW_ADSP2_XM: |
2120 | case WMFW_ADSP2_YM: |
2121 | case WMFW_HALO_XM_PACKED: |
2122 | case WMFW_HALO_YM_PACKED: |
2123 | case WMFW_HALO_PM_PACKED: |
2124 | cs_dsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n" , |
2125 | file, blocks, le32_to_cpu(blk->len), |
2126 | type, le32_to_cpu(blk->id)); |
2127 | |
2128 | region_name = cs_dsp_mem_region_name(type); |
2129 | mem = cs_dsp_find_region(dsp, type); |
2130 | if (!mem) { |
2131 | cs_dsp_err(dsp, "No base for region %x\n" , type); |
2132 | break; |
2133 | } |
2134 | |
2135 | alg_region = cs_dsp_find_alg_region(dsp, type, |
2136 | le32_to_cpu(blk->id)); |
2137 | if (alg_region) { |
2138 | if (version != alg_region->ver) |
2139 | cs_dsp_warn(dsp, |
2140 | "Algorithm coefficient version %d.%d.%d but expected %d.%d.%d\n" , |
2141 | (version >> 16) & 0xFF, |
2142 | (version >> 8) & 0xFF, |
2143 | version & 0xFF, |
2144 | (alg_region->ver >> 16) & 0xFF, |
2145 | (alg_region->ver >> 8) & 0xFF, |
2146 | alg_region->ver & 0xFF); |
2147 | |
2148 | reg = alg_region->base; |
2149 | reg = dsp->ops->region_to_reg(mem, reg); |
2150 | reg += offset; |
2151 | } else { |
2152 | cs_dsp_err(dsp, "No %s for algorithm %x\n" , |
2153 | region_name, le32_to_cpu(blk->id)); |
2154 | } |
2155 | break; |
2156 | |
2157 | default: |
2158 | cs_dsp_err(dsp, "%s.%d: Unknown region type %x at %d\n" , |
2159 | file, blocks, type, pos); |
2160 | break; |
2161 | } |
2162 | |
2163 | if (text) { |
2164 | memcpy(text, blk->data, le32_to_cpu(blk->len)); |
2165 | cs_dsp_info(dsp, "%s: %s\n" , dsp->fw_name, text); |
2166 | kfree(objp: text); |
2167 | text = NULL; |
2168 | } |
2169 | |
2170 | if (reg) { |
2171 | if (le32_to_cpu(blk->len) > |
2172 | firmware->size - pos - sizeof(*blk)) { |
2173 | cs_dsp_err(dsp, |
2174 | "%s.%d: %s region len %d bytes exceeds file length %zu\n" , |
2175 | file, blocks, region_name, |
2176 | le32_to_cpu(blk->len), |
2177 | firmware->size); |
2178 | ret = -EINVAL; |
2179 | goto out_fw; |
2180 | } |
2181 | |
2182 | buf = cs_dsp_buf_alloc(src: blk->data, |
2183 | le32_to_cpu(blk->len), |
2184 | list: &buf_list); |
2185 | if (!buf) { |
2186 | cs_dsp_err(dsp, "Out of memory\n" ); |
2187 | ret = -ENOMEM; |
2188 | goto out_fw; |
2189 | } |
2190 | |
2191 | cs_dsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n" , |
2192 | file, blocks, le32_to_cpu(blk->len), |
2193 | reg); |
2194 | ret = regmap_raw_write_async(map: regmap, reg, val: buf->buf, |
2195 | le32_to_cpu(blk->len)); |
2196 | if (ret != 0) { |
2197 | cs_dsp_err(dsp, |
2198 | "%s.%d: Failed to write to %x in %s: %d\n" , |
2199 | file, blocks, reg, region_name, ret); |
2200 | } |
2201 | } |
2202 | |
2203 | pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; |
2204 | blocks++; |
2205 | } |
2206 | |
2207 | ret = regmap_async_complete(map: regmap); |
2208 | if (ret != 0) |
2209 | cs_dsp_err(dsp, "Failed to complete async write: %d\n" , ret); |
2210 | |
2211 | if (pos > firmware->size) |
2212 | cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n" , |
2213 | file, blocks, pos - firmware->size); |
2214 | |
2215 | cs_dsp_debugfs_save_binname(dsp, s: file); |
2216 | |
2217 | out_fw: |
2218 | regmap_async_complete(map: regmap); |
2219 | cs_dsp_buf_free(list: &buf_list); |
2220 | kfree(objp: text); |
2221 | return ret; |
2222 | } |
2223 | |
2224 | static int cs_dsp_create_name(struct cs_dsp *dsp) |
2225 | { |
2226 | if (!dsp->name) { |
2227 | dsp->name = devm_kasprintf(dev: dsp->dev, GFP_KERNEL, fmt: "DSP%d" , |
2228 | dsp->num); |
2229 | if (!dsp->name) |
2230 | return -ENOMEM; |
2231 | } |
2232 | |
2233 | return 0; |
2234 | } |
2235 | |
2236 | static int cs_dsp_common_init(struct cs_dsp *dsp) |
2237 | { |
2238 | int ret; |
2239 | |
2240 | ret = cs_dsp_create_name(dsp); |
2241 | if (ret) |
2242 | return ret; |
2243 | |
2244 | INIT_LIST_HEAD(list: &dsp->alg_regions); |
2245 | INIT_LIST_HEAD(list: &dsp->ctl_list); |
2246 | |
2247 | mutex_init(&dsp->pwr_lock); |
2248 | |
2249 | #ifdef CONFIG_DEBUG_FS |
2250 | /* Ensure this is invalid if client never provides a debugfs root */ |
2251 | dsp->debugfs_root = ERR_PTR(error: -ENODEV); |
2252 | #endif |
2253 | |
2254 | return 0; |
2255 | } |
2256 | |
2257 | /** |
2258 | * cs_dsp_adsp1_init() - Initialise a cs_dsp structure representing a ADSP1 device |
2259 | * @dsp: pointer to DSP structure |
2260 | * |
2261 | * Return: Zero for success, a negative number on error. |
2262 | */ |
2263 | int cs_dsp_adsp1_init(struct cs_dsp *dsp) |
2264 | { |
2265 | dsp->ops = &cs_dsp_adsp1_ops; |
2266 | |
2267 | return cs_dsp_common_init(dsp); |
2268 | } |
2269 | EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp1_init, FW_CS_DSP); |
2270 | |
2271 | /** |
2272 | * cs_dsp_adsp1_power_up() - Load and start the named firmware |
2273 | * @dsp: pointer to DSP structure |
2274 | * @wmfw_firmware: the firmware to be sent |
2275 | * @wmfw_filename: file name of firmware to be sent |
2276 | * @coeff_firmware: the coefficient data to be sent |
2277 | * @coeff_filename: file name of coefficient to data be sent |
2278 | * @fw_name: the user-friendly firmware name |
2279 | * |
2280 | * Return: Zero for success, a negative number on error. |
2281 | */ |
2282 | int cs_dsp_adsp1_power_up(struct cs_dsp *dsp, |
2283 | const struct firmware *wmfw_firmware, char *wmfw_filename, |
2284 | const struct firmware *coeff_firmware, char *coeff_filename, |
2285 | const char *fw_name) |
2286 | { |
2287 | unsigned int val; |
2288 | int ret; |
2289 | |
2290 | mutex_lock(&dsp->pwr_lock); |
2291 | |
2292 | dsp->fw_name = fw_name; |
2293 | |
2294 | regmap_update_bits(map: dsp->regmap, reg: dsp->base + ADSP1_CONTROL_30, |
2295 | ADSP1_SYS_ENA, ADSP1_SYS_ENA); |
2296 | |
2297 | /* |
2298 | * For simplicity set the DSP clock rate to be the |
2299 | * SYSCLK rate rather than making it configurable. |
2300 | */ |
2301 | if (dsp->sysclk_reg) { |
2302 | ret = regmap_read(map: dsp->regmap, reg: dsp->sysclk_reg, val: &val); |
2303 | if (ret != 0) { |
2304 | cs_dsp_err(dsp, "Failed to read SYSCLK state: %d\n" , ret); |
2305 | goto err_mutex; |
2306 | } |
2307 | |
2308 | val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; |
2309 | |
2310 | ret = regmap_update_bits(map: dsp->regmap, |
2311 | reg: dsp->base + ADSP1_CONTROL_31, |
2312 | ADSP1_CLK_SEL_MASK, val); |
2313 | if (ret != 0) { |
2314 | cs_dsp_err(dsp, "Failed to set clock rate: %d\n" , ret); |
2315 | goto err_mutex; |
2316 | } |
2317 | } |
2318 | |
2319 | ret = cs_dsp_load(dsp, firmware: wmfw_firmware, file: wmfw_filename); |
2320 | if (ret != 0) |
2321 | goto err_ena; |
2322 | |
2323 | ret = cs_dsp_adsp1_setup_algs(dsp); |
2324 | if (ret != 0) |
2325 | goto err_ena; |
2326 | |
2327 | ret = cs_dsp_load_coeff(dsp, firmware: coeff_firmware, file: coeff_filename); |
2328 | if (ret != 0) |
2329 | goto err_ena; |
2330 | |
2331 | /* Initialize caches for enabled and unset controls */ |
2332 | ret = cs_dsp_coeff_init_control_caches(dsp); |
2333 | if (ret != 0) |
2334 | goto err_ena; |
2335 | |
2336 | /* Sync set controls */ |
2337 | ret = cs_dsp_coeff_sync_controls(dsp); |
2338 | if (ret != 0) |
2339 | goto err_ena; |
2340 | |
2341 | dsp->booted = true; |
2342 | |
2343 | /* Start the core running */ |
2344 | regmap_update_bits(map: dsp->regmap, reg: dsp->base + ADSP1_CONTROL_30, |
2345 | ADSP1_CORE_ENA | ADSP1_START, |
2346 | ADSP1_CORE_ENA | ADSP1_START); |
2347 | |
2348 | dsp->running = true; |
2349 | |
2350 | mutex_unlock(lock: &dsp->pwr_lock); |
2351 | |
2352 | return 0; |
2353 | |
2354 | err_ena: |
2355 | regmap_update_bits(map: dsp->regmap, reg: dsp->base + ADSP1_CONTROL_30, |
2356 | ADSP1_SYS_ENA, val: 0); |
2357 | err_mutex: |
2358 | mutex_unlock(lock: &dsp->pwr_lock); |
2359 | return ret; |
2360 | } |
2361 | EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp1_power_up, FW_CS_DSP); |
2362 | |
2363 | /** |
2364 | * cs_dsp_adsp1_power_down() - Halts the DSP |
2365 | * @dsp: pointer to DSP structure |
2366 | */ |
2367 | void cs_dsp_adsp1_power_down(struct cs_dsp *dsp) |
2368 | { |
2369 | struct cs_dsp_coeff_ctl *ctl; |
2370 | |
2371 | mutex_lock(&dsp->pwr_lock); |
2372 | |
2373 | dsp->running = false; |
2374 | dsp->booted = false; |
2375 | |
2376 | /* Halt the core */ |
2377 | regmap_update_bits(map: dsp->regmap, reg: dsp->base + ADSP1_CONTROL_30, |
2378 | ADSP1_CORE_ENA | ADSP1_START, val: 0); |
2379 | |
2380 | regmap_update_bits(map: dsp->regmap, reg: dsp->base + ADSP1_CONTROL_19, |
2381 | ADSP1_WDMA_BUFFER_LENGTH_MASK, val: 0); |
2382 | |
2383 | regmap_update_bits(map: dsp->regmap, reg: dsp->base + ADSP1_CONTROL_30, |
2384 | ADSP1_SYS_ENA, val: 0); |
2385 | |
2386 | list_for_each_entry(ctl, &dsp->ctl_list, list) |
2387 | ctl->enabled = 0; |
2388 | |
2389 | cs_dsp_free_alg_regions(dsp); |
2390 | |
2391 | mutex_unlock(lock: &dsp->pwr_lock); |
2392 | } |
2393 | EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp1_power_down, FW_CS_DSP); |
2394 | |
2395 | static int cs_dsp_adsp2v2_enable_core(struct cs_dsp *dsp) |
2396 | { |
2397 | unsigned int val; |
2398 | int ret, count; |
2399 | |
2400 | /* Wait for the RAM to start, should be near instantaneous */ |
2401 | for (count = 0; count < 10; ++count) { |
2402 | ret = regmap_read(map: dsp->regmap, reg: dsp->base + ADSP2_STATUS1, val: &val); |
2403 | if (ret != 0) |
2404 | return ret; |
2405 | |
2406 | if (val & ADSP2_RAM_RDY) |
2407 | break; |
2408 | |
2409 | usleep_range(min: 250, max: 500); |
2410 | } |
2411 | |
2412 | if (!(val & ADSP2_RAM_RDY)) { |
2413 | cs_dsp_err(dsp, "Failed to start DSP RAM\n" ); |
2414 | return -EBUSY; |
2415 | } |
2416 | |
2417 | cs_dsp_dbg(dsp, "RAM ready after %d polls\n" , count); |
2418 | |
2419 | return 0; |
2420 | } |
2421 | |
2422 | static int cs_dsp_adsp2_enable_core(struct cs_dsp *dsp) |
2423 | { |
2424 | int ret; |
2425 | |
2426 | ret = regmap_update_bits_async(map: dsp->regmap, reg: dsp->base + ADSP2_CONTROL, |
2427 | ADSP2_SYS_ENA, ADSP2_SYS_ENA); |
2428 | if (ret != 0) |
2429 | return ret; |
2430 | |
2431 | return cs_dsp_adsp2v2_enable_core(dsp); |
2432 | } |
2433 | |
2434 | static int cs_dsp_adsp2_lock(struct cs_dsp *dsp, unsigned int lock_regions) |
2435 | { |
2436 | struct regmap *regmap = dsp->regmap; |
2437 | unsigned int code0, code1, lock_reg; |
2438 | |
2439 | if (!(lock_regions & CS_ADSP2_REGION_ALL)) |
2440 | return 0; |
2441 | |
2442 | lock_regions &= CS_ADSP2_REGION_ALL; |
2443 | lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0; |
2444 | |
2445 | while (lock_regions) { |
2446 | code0 = code1 = 0; |
2447 | if (lock_regions & BIT(0)) { |
2448 | code0 = ADSP2_LOCK_CODE_0; |
2449 | code1 = ADSP2_LOCK_CODE_1; |
2450 | } |
2451 | if (lock_regions & BIT(1)) { |
2452 | code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT; |
2453 | code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT; |
2454 | } |
2455 | regmap_write(map: regmap, reg: lock_reg, val: code0); |
2456 | regmap_write(map: regmap, reg: lock_reg, val: code1); |
2457 | lock_regions >>= 2; |
2458 | lock_reg += 2; |
2459 | } |
2460 | |
2461 | return 0; |
2462 | } |
2463 | |
2464 | static int cs_dsp_adsp2_enable_memory(struct cs_dsp *dsp) |
2465 | { |
2466 | return regmap_update_bits(map: dsp->regmap, reg: dsp->base + ADSP2_CONTROL, |
2467 | ADSP2_MEM_ENA, ADSP2_MEM_ENA); |
2468 | } |
2469 | |
2470 | static void cs_dsp_adsp2_disable_memory(struct cs_dsp *dsp) |
2471 | { |
2472 | regmap_update_bits(map: dsp->regmap, reg: dsp->base + ADSP2_CONTROL, |
2473 | ADSP2_MEM_ENA, val: 0); |
2474 | } |
2475 | |
2476 | static void cs_dsp_adsp2_disable_core(struct cs_dsp *dsp) |
2477 | { |
2478 | regmap_write(map: dsp->regmap, reg: dsp->base + ADSP2_RDMA_CONFIG_1, val: 0); |
2479 | regmap_write(map: dsp->regmap, reg: dsp->base + ADSP2_WDMA_CONFIG_1, val: 0); |
2480 | regmap_write(map: dsp->regmap, reg: dsp->base + ADSP2_WDMA_CONFIG_2, val: 0); |
2481 | |
2482 | regmap_update_bits(map: dsp->regmap, reg: dsp->base + ADSP2_CONTROL, |
2483 | ADSP2_SYS_ENA, val: 0); |
2484 | } |
2485 | |
2486 | static void cs_dsp_adsp2v2_disable_core(struct cs_dsp *dsp) |
2487 | { |
2488 | regmap_write(map: dsp->regmap, reg: dsp->base + ADSP2_RDMA_CONFIG_1, val: 0); |
2489 | regmap_write(map: dsp->regmap, reg: dsp->base + ADSP2_WDMA_CONFIG_1, val: 0); |
2490 | regmap_write(map: dsp->regmap, reg: dsp->base + ADSP2V2_WDMA_CONFIG_2, val: 0); |
2491 | } |
2492 | |
2493 | static int cs_dsp_halo_configure_mpu(struct cs_dsp *dsp, unsigned int lock_regions) |
2494 | { |
2495 | struct reg_sequence config[] = { |
2496 | { dsp->base + HALO_MPU_LOCK_CONFIG, 0x5555 }, |
2497 | { dsp->base + HALO_MPU_LOCK_CONFIG, 0xAAAA }, |
2498 | { dsp->base + HALO_MPU_XMEM_ACCESS_0, 0xFFFFFFFF }, |
2499 | { dsp->base + HALO_MPU_YMEM_ACCESS_0, 0xFFFFFFFF }, |
2500 | { dsp->base + HALO_MPU_WINDOW_ACCESS_0, lock_regions }, |
2501 | { dsp->base + HALO_MPU_XREG_ACCESS_0, lock_regions }, |
2502 | { dsp->base + HALO_MPU_YREG_ACCESS_0, lock_regions }, |
2503 | { dsp->base + HALO_MPU_XMEM_ACCESS_1, 0xFFFFFFFF }, |
2504 | { dsp->base + HALO_MPU_YMEM_ACCESS_1, 0xFFFFFFFF }, |
2505 | { dsp->base + HALO_MPU_WINDOW_ACCESS_1, lock_regions }, |
2506 | { dsp->base + HALO_MPU_XREG_ACCESS_1, lock_regions }, |
2507 | { dsp->base + HALO_MPU_YREG_ACCESS_1, lock_regions }, |
2508 | { dsp->base + HALO_MPU_XMEM_ACCESS_2, 0xFFFFFFFF }, |
2509 | { dsp->base + HALO_MPU_YMEM_ACCESS_2, 0xFFFFFFFF }, |
2510 | { dsp->base + HALO_MPU_WINDOW_ACCESS_2, lock_regions }, |
2511 | { dsp->base + HALO_MPU_XREG_ACCESS_2, lock_regions }, |
2512 | { dsp->base + HALO_MPU_YREG_ACCESS_2, lock_regions }, |
2513 | { dsp->base + HALO_MPU_XMEM_ACCESS_3, 0xFFFFFFFF }, |
2514 | { dsp->base + HALO_MPU_YMEM_ACCESS_3, 0xFFFFFFFF }, |
2515 | { dsp->base + HALO_MPU_WINDOW_ACCESS_3, lock_regions }, |
2516 | { dsp->base + HALO_MPU_XREG_ACCESS_3, lock_regions }, |
2517 | { dsp->base + HALO_MPU_YREG_ACCESS_3, lock_regions }, |
2518 | { dsp->base + HALO_MPU_LOCK_CONFIG, 0 }, |
2519 | }; |
2520 | |
2521 | return regmap_multi_reg_write(map: dsp->regmap, regs: config, ARRAY_SIZE(config)); |
2522 | } |
2523 | |
2524 | /** |
2525 | * cs_dsp_set_dspclk() - Applies the given frequency to the given cs_dsp |
2526 | * @dsp: pointer to DSP structure |
2527 | * @freq: clock rate to set |
2528 | * |
2529 | * This is only for use on ADSP2 cores. |
2530 | * |
2531 | * Return: Zero for success, a negative number on error. |
2532 | */ |
2533 | int cs_dsp_set_dspclk(struct cs_dsp *dsp, unsigned int freq) |
2534 | { |
2535 | int ret; |
2536 | |
2537 | ret = regmap_update_bits(map: dsp->regmap, reg: dsp->base + ADSP2_CLOCKING, |
2538 | ADSP2_CLK_SEL_MASK, |
2539 | val: freq << ADSP2_CLK_SEL_SHIFT); |
2540 | if (ret) |
2541 | cs_dsp_err(dsp, "Failed to set clock rate: %d\n" , ret); |
2542 | |
2543 | return ret; |
2544 | } |
2545 | EXPORT_SYMBOL_NS_GPL(cs_dsp_set_dspclk, FW_CS_DSP); |
2546 | |
2547 | static void cs_dsp_stop_watchdog(struct cs_dsp *dsp) |
2548 | { |
2549 | regmap_update_bits(map: dsp->regmap, reg: dsp->base + ADSP2_WATCHDOG, |
2550 | ADSP2_WDT_ENA_MASK, val: 0); |
2551 | } |
2552 | |
2553 | static void cs_dsp_halo_stop_watchdog(struct cs_dsp *dsp) |
2554 | { |
2555 | regmap_update_bits(map: dsp->regmap, reg: dsp->base + HALO_WDT_CONTROL, |
2556 | HALO_WDT_EN_MASK, val: 0); |
2557 | } |
2558 | |
2559 | /** |
2560 | * cs_dsp_power_up() - Downloads firmware to the DSP |
2561 | * @dsp: pointer to DSP structure |
2562 | * @wmfw_firmware: the firmware to be sent |
2563 | * @wmfw_filename: file name of firmware to be sent |
2564 | * @coeff_firmware: the coefficient data to be sent |
2565 | * @coeff_filename: file name of coefficient to data be sent |
2566 | * @fw_name: the user-friendly firmware name |
2567 | * |
2568 | * This function is used on ADSP2 and Halo DSP cores, it powers-up the DSP core |
2569 | * and downloads the firmware but does not start the firmware running. The |
2570 | * cs_dsp booted flag will be set once completed and if the core has a low-power |
2571 | * memory retention mode it will be put into this state after the firmware is |
2572 | * downloaded. |
2573 | * |
2574 | * Return: Zero for success, a negative number on error. |
2575 | */ |
2576 | int cs_dsp_power_up(struct cs_dsp *dsp, |
2577 | const struct firmware *wmfw_firmware, char *wmfw_filename, |
2578 | const struct firmware *coeff_firmware, char *coeff_filename, |
2579 | const char *fw_name) |
2580 | { |
2581 | int ret; |
2582 | |
2583 | mutex_lock(&dsp->pwr_lock); |
2584 | |
2585 | dsp->fw_name = fw_name; |
2586 | |
2587 | if (dsp->ops->enable_memory) { |
2588 | ret = dsp->ops->enable_memory(dsp); |
2589 | if (ret != 0) |
2590 | goto err_mutex; |
2591 | } |
2592 | |
2593 | if (dsp->ops->enable_core) { |
2594 | ret = dsp->ops->enable_core(dsp); |
2595 | if (ret != 0) |
2596 | goto err_mem; |
2597 | } |
2598 | |
2599 | ret = cs_dsp_load(dsp, firmware: wmfw_firmware, file: wmfw_filename); |
2600 | if (ret != 0) |
2601 | goto err_ena; |
2602 | |
2603 | ret = dsp->ops->setup_algs(dsp); |
2604 | if (ret != 0) |
2605 | goto err_ena; |
2606 | |
2607 | ret = cs_dsp_load_coeff(dsp, firmware: coeff_firmware, file: coeff_filename); |
2608 | if (ret != 0) |
2609 | goto err_ena; |
2610 | |
2611 | /* Initialize caches for enabled and unset controls */ |
2612 | ret = cs_dsp_coeff_init_control_caches(dsp); |
2613 | if (ret != 0) |
2614 | goto err_ena; |
2615 | |
2616 | if (dsp->ops->disable_core) |
2617 | dsp->ops->disable_core(dsp); |
2618 | |
2619 | dsp->booted = true; |
2620 | |
2621 | mutex_unlock(lock: &dsp->pwr_lock); |
2622 | |
2623 | return 0; |
2624 | err_ena: |
2625 | if (dsp->ops->disable_core) |
2626 | dsp->ops->disable_core(dsp); |
2627 | err_mem: |
2628 | if (dsp->ops->disable_memory) |
2629 | dsp->ops->disable_memory(dsp); |
2630 | err_mutex: |
2631 | mutex_unlock(lock: &dsp->pwr_lock); |
2632 | |
2633 | return ret; |
2634 | } |
2635 | EXPORT_SYMBOL_NS_GPL(cs_dsp_power_up, FW_CS_DSP); |
2636 | |
2637 | /** |
2638 | * cs_dsp_power_down() - Powers-down the DSP |
2639 | * @dsp: pointer to DSP structure |
2640 | * |
2641 | * cs_dsp_stop() must have been called before this function. The core will be |
2642 | * fully powered down and so the memory will not be retained. |
2643 | */ |
2644 | void cs_dsp_power_down(struct cs_dsp *dsp) |
2645 | { |
2646 | struct cs_dsp_coeff_ctl *ctl; |
2647 | |
2648 | mutex_lock(&dsp->pwr_lock); |
2649 | |
2650 | cs_dsp_debugfs_clear(dsp); |
2651 | |
2652 | dsp->fw_id = 0; |
2653 | dsp->fw_id_version = 0; |
2654 | |
2655 | dsp->booted = false; |
2656 | |
2657 | if (dsp->ops->disable_memory) |
2658 | dsp->ops->disable_memory(dsp); |
2659 | |
2660 | list_for_each_entry(ctl, &dsp->ctl_list, list) |
2661 | ctl->enabled = 0; |
2662 | |
2663 | cs_dsp_free_alg_regions(dsp); |
2664 | |
2665 | mutex_unlock(lock: &dsp->pwr_lock); |
2666 | |
2667 | cs_dsp_dbg(dsp, "Shutdown complete\n" ); |
2668 | } |
2669 | EXPORT_SYMBOL_NS_GPL(cs_dsp_power_down, FW_CS_DSP); |
2670 | |
2671 | static int cs_dsp_adsp2_start_core(struct cs_dsp *dsp) |
2672 | { |
2673 | return regmap_update_bits(map: dsp->regmap, reg: dsp->base + ADSP2_CONTROL, |
2674 | ADSP2_CORE_ENA | ADSP2_START, |
2675 | ADSP2_CORE_ENA | ADSP2_START); |
2676 | } |
2677 | |
2678 | static void cs_dsp_adsp2_stop_core(struct cs_dsp *dsp) |
2679 | { |
2680 | regmap_update_bits(map: dsp->regmap, reg: dsp->base + ADSP2_CONTROL, |
2681 | ADSP2_CORE_ENA | ADSP2_START, val: 0); |
2682 | } |
2683 | |
2684 | /** |
2685 | * cs_dsp_run() - Starts the firmware running |
2686 | * @dsp: pointer to DSP structure |
2687 | * |
2688 | * cs_dsp_power_up() must have previously been called successfully. |
2689 | * |
2690 | * Return: Zero for success, a negative number on error. |
2691 | */ |
2692 | int cs_dsp_run(struct cs_dsp *dsp) |
2693 | { |
2694 | int ret; |
2695 | |
2696 | mutex_lock(&dsp->pwr_lock); |
2697 | |
2698 | if (!dsp->booted) { |
2699 | ret = -EIO; |
2700 | goto err; |
2701 | } |
2702 | |
2703 | if (dsp->ops->enable_core) { |
2704 | ret = dsp->ops->enable_core(dsp); |
2705 | if (ret != 0) |
2706 | goto err; |
2707 | } |
2708 | |
2709 | if (dsp->client_ops->pre_run) { |
2710 | ret = dsp->client_ops->pre_run(dsp); |
2711 | if (ret) |
2712 | goto err; |
2713 | } |
2714 | |
2715 | /* Sync set controls */ |
2716 | ret = cs_dsp_coeff_sync_controls(dsp); |
2717 | if (ret != 0) |
2718 | goto err; |
2719 | |
2720 | if (dsp->ops->lock_memory) { |
2721 | ret = dsp->ops->lock_memory(dsp, dsp->lock_regions); |
2722 | if (ret != 0) { |
2723 | cs_dsp_err(dsp, "Error configuring MPU: %d\n" , ret); |
2724 | goto err; |
2725 | } |
2726 | } |
2727 | |
2728 | if (dsp->ops->start_core) { |
2729 | ret = dsp->ops->start_core(dsp); |
2730 | if (ret != 0) |
2731 | goto err; |
2732 | } |
2733 | |
2734 | dsp->running = true; |
2735 | |
2736 | if (dsp->client_ops->post_run) { |
2737 | ret = dsp->client_ops->post_run(dsp); |
2738 | if (ret) |
2739 | goto err; |
2740 | } |
2741 | |
2742 | mutex_unlock(lock: &dsp->pwr_lock); |
2743 | |
2744 | return 0; |
2745 | |
2746 | err: |
2747 | if (dsp->ops->stop_core) |
2748 | dsp->ops->stop_core(dsp); |
2749 | if (dsp->ops->disable_core) |
2750 | dsp->ops->disable_core(dsp); |
2751 | mutex_unlock(lock: &dsp->pwr_lock); |
2752 | |
2753 | return ret; |
2754 | } |
2755 | EXPORT_SYMBOL_NS_GPL(cs_dsp_run, FW_CS_DSP); |
2756 | |
2757 | /** |
2758 | * cs_dsp_stop() - Stops the firmware |
2759 | * @dsp: pointer to DSP structure |
2760 | * |
2761 | * Memory will not be disabled so firmware will remain loaded. |
2762 | */ |
2763 | void cs_dsp_stop(struct cs_dsp *dsp) |
2764 | { |
2765 | /* Tell the firmware to cleanup */ |
2766 | cs_dsp_signal_event_controls(dsp, CS_DSP_FW_EVENT_SHUTDOWN); |
2767 | |
2768 | if (dsp->ops->stop_watchdog) |
2769 | dsp->ops->stop_watchdog(dsp); |
2770 | |
2771 | /* Log firmware state, it can be useful for analysis */ |
2772 | if (dsp->ops->show_fw_status) |
2773 | dsp->ops->show_fw_status(dsp); |
2774 | |
2775 | mutex_lock(&dsp->pwr_lock); |
2776 | |
2777 | if (dsp->client_ops->pre_stop) |
2778 | dsp->client_ops->pre_stop(dsp); |
2779 | |
2780 | dsp->running = false; |
2781 | |
2782 | if (dsp->ops->stop_core) |
2783 | dsp->ops->stop_core(dsp); |
2784 | if (dsp->ops->disable_core) |
2785 | dsp->ops->disable_core(dsp); |
2786 | |
2787 | if (dsp->client_ops->post_stop) |
2788 | dsp->client_ops->post_stop(dsp); |
2789 | |
2790 | mutex_unlock(lock: &dsp->pwr_lock); |
2791 | |
2792 | cs_dsp_dbg(dsp, "Execution stopped\n" ); |
2793 | } |
2794 | EXPORT_SYMBOL_NS_GPL(cs_dsp_stop, FW_CS_DSP); |
2795 | |
2796 | static int cs_dsp_halo_start_core(struct cs_dsp *dsp) |
2797 | { |
2798 | int ret; |
2799 | |
2800 | ret = regmap_update_bits(map: dsp->regmap, reg: dsp->base + HALO_CCM_CORE_CONTROL, |
2801 | HALO_CORE_RESET | HALO_CORE_EN, |
2802 | HALO_CORE_RESET | HALO_CORE_EN); |
2803 | if (ret) |
2804 | return ret; |
2805 | |
2806 | return regmap_update_bits(map: dsp->regmap, reg: dsp->base + HALO_CCM_CORE_CONTROL, |
2807 | HALO_CORE_RESET, val: 0); |
2808 | } |
2809 | |
2810 | static void cs_dsp_halo_stop_core(struct cs_dsp *dsp) |
2811 | { |
2812 | regmap_update_bits(map: dsp->regmap, reg: dsp->base + HALO_CCM_CORE_CONTROL, |
2813 | HALO_CORE_EN, val: 0); |
2814 | |
2815 | /* reset halo core with CORE_SOFT_RESET */ |
2816 | regmap_update_bits(map: dsp->regmap, reg: dsp->base + HALO_CORE_SOFT_RESET, |
2817 | HALO_CORE_SOFT_RESET_MASK, val: 1); |
2818 | } |
2819 | |
2820 | /** |
2821 | * cs_dsp_adsp2_init() - Initialise a cs_dsp structure representing a ADSP2 core |
2822 | * @dsp: pointer to DSP structure |
2823 | * |
2824 | * Return: Zero for success, a negative number on error. |
2825 | */ |
2826 | int cs_dsp_adsp2_init(struct cs_dsp *dsp) |
2827 | { |
2828 | int ret; |
2829 | |
2830 | switch (dsp->rev) { |
2831 | case 0: |
2832 | /* |
2833 | * Disable the DSP memory by default when in reset for a small |
2834 | * power saving. |
2835 | */ |
2836 | ret = regmap_update_bits(map: dsp->regmap, reg: dsp->base + ADSP2_CONTROL, |
2837 | ADSP2_MEM_ENA, val: 0); |
2838 | if (ret) { |
2839 | cs_dsp_err(dsp, |
2840 | "Failed to clear memory retention: %d\n" , ret); |
2841 | return ret; |
2842 | } |
2843 | |
2844 | dsp->ops = &cs_dsp_adsp2_ops[0]; |
2845 | break; |
2846 | case 1: |
2847 | dsp->ops = &cs_dsp_adsp2_ops[1]; |
2848 | break; |
2849 | default: |
2850 | dsp->ops = &cs_dsp_adsp2_ops[2]; |
2851 | break; |
2852 | } |
2853 | |
2854 | return cs_dsp_common_init(dsp); |
2855 | } |
2856 | EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp2_init, FW_CS_DSP); |
2857 | |
2858 | /** |
2859 | * cs_dsp_halo_init() - Initialise a cs_dsp structure representing a HALO Core DSP |
2860 | * @dsp: pointer to DSP structure |
2861 | * |
2862 | * Return: Zero for success, a negative number on error. |
2863 | */ |
2864 | int cs_dsp_halo_init(struct cs_dsp *dsp) |
2865 | { |
2866 | if (dsp->no_core_startstop) |
2867 | dsp->ops = &cs_dsp_halo_ao_ops; |
2868 | else |
2869 | dsp->ops = &cs_dsp_halo_ops; |
2870 | |
2871 | return cs_dsp_common_init(dsp); |
2872 | } |
2873 | EXPORT_SYMBOL_NS_GPL(cs_dsp_halo_init, FW_CS_DSP); |
2874 | |
2875 | /** |
2876 | * cs_dsp_remove() - Clean a cs_dsp before deletion |
2877 | * @dsp: pointer to DSP structure |
2878 | */ |
2879 | void cs_dsp_remove(struct cs_dsp *dsp) |
2880 | { |
2881 | struct cs_dsp_coeff_ctl *ctl; |
2882 | |
2883 | while (!list_empty(head: &dsp->ctl_list)) { |
2884 | ctl = list_first_entry(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list); |
2885 | |
2886 | if (dsp->client_ops->control_remove) |
2887 | dsp->client_ops->control_remove(ctl); |
2888 | |
2889 | list_del(entry: &ctl->list); |
2890 | cs_dsp_free_ctl_blk(ctl); |
2891 | } |
2892 | } |
2893 | EXPORT_SYMBOL_NS_GPL(cs_dsp_remove, FW_CS_DSP); |
2894 | |
2895 | /** |
2896 | * cs_dsp_read_raw_data_block() - Reads a block of data from DSP memory |
2897 | * @dsp: pointer to DSP structure |
2898 | * @mem_type: the type of DSP memory containing the data to be read |
2899 | * @mem_addr: the address of the data within the memory region |
2900 | * @num_words: the length of the data to read |
2901 | * @data: a buffer to store the fetched data |
2902 | * |
2903 | * If this is used to read unpacked 24-bit memory, each 24-bit DSP word will |
2904 | * occupy 32-bits in data (MSbyte will be 0). This padding can be removed using |
2905 | * cs_dsp_remove_padding() |
2906 | * |
2907 | * Return: Zero for success, a negative number on error. |
2908 | */ |
2909 | int cs_dsp_read_raw_data_block(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr, |
2910 | unsigned int num_words, __be32 *data) |
2911 | { |
2912 | struct cs_dsp_region const *mem = cs_dsp_find_region(dsp, type: mem_type); |
2913 | unsigned int reg; |
2914 | int ret; |
2915 | |
2916 | lockdep_assert_held(&dsp->pwr_lock); |
2917 | |
2918 | if (!mem) |
2919 | return -EINVAL; |
2920 | |
2921 | reg = dsp->ops->region_to_reg(mem, mem_addr); |
2922 | |
2923 | ret = regmap_raw_read(map: dsp->regmap, reg, val: data, |
2924 | val_len: sizeof(*data) * num_words); |
2925 | if (ret < 0) |
2926 | return ret; |
2927 | |
2928 | return 0; |
2929 | } |
2930 | EXPORT_SYMBOL_NS_GPL(cs_dsp_read_raw_data_block, FW_CS_DSP); |
2931 | |
2932 | /** |
2933 | * cs_dsp_read_data_word() - Reads a word from DSP memory |
2934 | * @dsp: pointer to DSP structure |
2935 | * @mem_type: the type of DSP memory containing the data to be read |
2936 | * @mem_addr: the address of the data within the memory region |
2937 | * @data: a buffer to store the fetched data |
2938 | * |
2939 | * Return: Zero for success, a negative number on error. |
2940 | */ |
2941 | int cs_dsp_read_data_word(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr, u32 *data) |
2942 | { |
2943 | __be32 raw; |
2944 | int ret; |
2945 | |
2946 | ret = cs_dsp_read_raw_data_block(dsp, mem_type, mem_addr, 1, &raw); |
2947 | if (ret < 0) |
2948 | return ret; |
2949 | |
2950 | *data = be32_to_cpu(raw) & 0x00ffffffu; |
2951 | |
2952 | return 0; |
2953 | } |
2954 | EXPORT_SYMBOL_NS_GPL(cs_dsp_read_data_word, FW_CS_DSP); |
2955 | |
2956 | /** |
2957 | * cs_dsp_write_data_word() - Writes a word to DSP memory |
2958 | * @dsp: pointer to DSP structure |
2959 | * @mem_type: the type of DSP memory containing the data to be written |
2960 | * @mem_addr: the address of the data within the memory region |
2961 | * @data: the data to be written |
2962 | * |
2963 | * Return: Zero for success, a negative number on error. |
2964 | */ |
2965 | int cs_dsp_write_data_word(struct cs_dsp *dsp, int mem_type, unsigned int mem_addr, u32 data) |
2966 | { |
2967 | struct cs_dsp_region const *mem = cs_dsp_find_region(dsp, type: mem_type); |
2968 | __be32 val = cpu_to_be32(data & 0x00ffffffu); |
2969 | unsigned int reg; |
2970 | |
2971 | lockdep_assert_held(&dsp->pwr_lock); |
2972 | |
2973 | if (!mem) |
2974 | return -EINVAL; |
2975 | |
2976 | reg = dsp->ops->region_to_reg(mem, mem_addr); |
2977 | |
2978 | return regmap_raw_write(map: dsp->regmap, reg, val: &val, val_len: sizeof(val)); |
2979 | } |
2980 | EXPORT_SYMBOL_NS_GPL(cs_dsp_write_data_word, FW_CS_DSP); |
2981 | |
2982 | /** |
2983 | * cs_dsp_remove_padding() - Convert unpacked words to packed bytes |
2984 | * @buf: buffer containing DSP words read from DSP memory |
2985 | * @nwords: number of words to convert |
2986 | * |
2987 | * DSP words from the register map have pad bytes and the data bytes |
2988 | * are in swapped order. This swaps to the native endian order and |
2989 | * strips the pad bytes. |
2990 | */ |
2991 | void cs_dsp_remove_padding(u32 *buf, int nwords) |
2992 | { |
2993 | const __be32 *pack_in = (__be32 *)buf; |
2994 | u8 *pack_out = (u8 *)buf; |
2995 | int i; |
2996 | |
2997 | for (i = 0; i < nwords; i++) { |
2998 | u32 word = be32_to_cpu(*pack_in++); |
2999 | *pack_out++ = (u8)word; |
3000 | *pack_out++ = (u8)(word >> 8); |
3001 | *pack_out++ = (u8)(word >> 16); |
3002 | } |
3003 | } |
3004 | EXPORT_SYMBOL_NS_GPL(cs_dsp_remove_padding, FW_CS_DSP); |
3005 | |
3006 | /** |
3007 | * cs_dsp_adsp2_bus_error() - Handle a DSP bus error interrupt |
3008 | * @dsp: pointer to DSP structure |
3009 | * |
3010 | * The firmware and DSP state will be logged for future analysis. |
3011 | */ |
3012 | void cs_dsp_adsp2_bus_error(struct cs_dsp *dsp) |
3013 | { |
3014 | unsigned int val; |
3015 | struct regmap *regmap = dsp->regmap; |
3016 | int ret = 0; |
3017 | |
3018 | mutex_lock(&dsp->pwr_lock); |
3019 | |
3020 | ret = regmap_read(map: regmap, reg: dsp->base + ADSP2_LOCK_REGION_CTRL, val: &val); |
3021 | if (ret) { |
3022 | cs_dsp_err(dsp, |
3023 | "Failed to read Region Lock Ctrl register: %d\n" , ret); |
3024 | goto error; |
3025 | } |
3026 | |
3027 | if (val & ADSP2_WDT_TIMEOUT_STS_MASK) { |
3028 | cs_dsp_err(dsp, "watchdog timeout error\n" ); |
3029 | dsp->ops->stop_watchdog(dsp); |
3030 | if (dsp->client_ops->watchdog_expired) |
3031 | dsp->client_ops->watchdog_expired(dsp); |
3032 | } |
3033 | |
3034 | if (val & (ADSP2_ADDR_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { |
3035 | if (val & ADSP2_ADDR_ERR_MASK) |
3036 | cs_dsp_err(dsp, "bus error: address error\n" ); |
3037 | else |
3038 | cs_dsp_err(dsp, "bus error: region lock error\n" ); |
3039 | |
3040 | ret = regmap_read(map: regmap, reg: dsp->base + ADSP2_BUS_ERR_ADDR, val: &val); |
3041 | if (ret) { |
3042 | cs_dsp_err(dsp, |
3043 | "Failed to read Bus Err Addr register: %d\n" , |
3044 | ret); |
3045 | goto error; |
3046 | } |
3047 | |
3048 | cs_dsp_err(dsp, "bus error address = 0x%x\n" , |
3049 | val & ADSP2_BUS_ERR_ADDR_MASK); |
3050 | |
3051 | ret = regmap_read(map: regmap, |
3052 | reg: dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR, |
3053 | val: &val); |
3054 | if (ret) { |
3055 | cs_dsp_err(dsp, |
3056 | "Failed to read Pmem Xmem Err Addr register: %d\n" , |
3057 | ret); |
3058 | goto error; |
3059 | } |
3060 | |
3061 | cs_dsp_err(dsp, "xmem error address = 0x%x\n" , |
3062 | val & ADSP2_XMEM_ERR_ADDR_MASK); |
3063 | cs_dsp_err(dsp, "pmem error address = 0x%x\n" , |
3064 | (val & ADSP2_PMEM_ERR_ADDR_MASK) >> |
3065 | ADSP2_PMEM_ERR_ADDR_SHIFT); |
3066 | } |
3067 | |
3068 | regmap_update_bits(map: regmap, reg: dsp->base + ADSP2_LOCK_REGION_CTRL, |
3069 | ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT); |
3070 | |
3071 | error: |
3072 | mutex_unlock(lock: &dsp->pwr_lock); |
3073 | } |
3074 | EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp2_bus_error, FW_CS_DSP); |
3075 | |
3076 | /** |
3077 | * cs_dsp_halo_bus_error() - Handle a DSP bus error interrupt |
3078 | * @dsp: pointer to DSP structure |
3079 | * |
3080 | * The firmware and DSP state will be logged for future analysis. |
3081 | */ |
3082 | void cs_dsp_halo_bus_error(struct cs_dsp *dsp) |
3083 | { |
3084 | struct regmap *regmap = dsp->regmap; |
3085 | unsigned int fault[6]; |
3086 | struct reg_sequence clear[] = { |
3087 | { dsp->base + HALO_MPU_XM_VIO_STATUS, 0x0 }, |
3088 | { dsp->base + HALO_MPU_YM_VIO_STATUS, 0x0 }, |
3089 | { dsp->base + HALO_MPU_PM_VIO_STATUS, 0x0 }, |
3090 | }; |
3091 | int ret; |
3092 | |
3093 | mutex_lock(&dsp->pwr_lock); |
3094 | |
3095 | ret = regmap_read(map: regmap, reg: dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_1, |
3096 | val: fault); |
3097 | if (ret) { |
3098 | cs_dsp_warn(dsp, "Failed to read AHB DEBUG_1: %d\n" , ret); |
3099 | goto exit_unlock; |
3100 | } |
3101 | |
3102 | cs_dsp_warn(dsp, "AHB: STATUS: 0x%x ADDR: 0x%x\n" , |
3103 | *fault & HALO_AHBM_FLAGS_ERR_MASK, |
3104 | (*fault & HALO_AHBM_CORE_ERR_ADDR_MASK) >> |
3105 | HALO_AHBM_CORE_ERR_ADDR_SHIFT); |
3106 | |
3107 | ret = regmap_read(map: regmap, reg: dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_0, |
3108 | val: fault); |
3109 | if (ret) { |
3110 | cs_dsp_warn(dsp, "Failed to read AHB DEBUG_0: %d\n" , ret); |
3111 | goto exit_unlock; |
3112 | } |
3113 | |
3114 | cs_dsp_warn(dsp, "AHB: SYS_ADDR: 0x%x\n" , *fault); |
3115 | |
3116 | ret = regmap_bulk_read(map: regmap, reg: dsp->base + HALO_MPU_XM_VIO_ADDR, |
3117 | val: fault, ARRAY_SIZE(fault)); |
3118 | if (ret) { |
3119 | cs_dsp_warn(dsp, "Failed to read MPU fault info: %d\n" , ret); |
3120 | goto exit_unlock; |
3121 | } |
3122 | |
3123 | cs_dsp_warn(dsp, "XM: STATUS:0x%x ADDR:0x%x\n" , fault[1], fault[0]); |
3124 | cs_dsp_warn(dsp, "YM: STATUS:0x%x ADDR:0x%x\n" , fault[3], fault[2]); |
3125 | cs_dsp_warn(dsp, "PM: STATUS:0x%x ADDR:0x%x\n" , fault[5], fault[4]); |
3126 | |
3127 | ret = regmap_multi_reg_write(map: dsp->regmap, regs: clear, ARRAY_SIZE(clear)); |
3128 | if (ret) |
3129 | cs_dsp_warn(dsp, "Failed to clear MPU status: %d\n" , ret); |
3130 | |
3131 | exit_unlock: |
3132 | mutex_unlock(lock: &dsp->pwr_lock); |
3133 | } |
3134 | EXPORT_SYMBOL_NS_GPL(cs_dsp_halo_bus_error, FW_CS_DSP); |
3135 | |
3136 | /** |
3137 | * cs_dsp_halo_wdt_expire() - Handle DSP watchdog expiry |
3138 | * @dsp: pointer to DSP structure |
3139 | * |
3140 | * This is logged for future analysis. |
3141 | */ |
3142 | void cs_dsp_halo_wdt_expire(struct cs_dsp *dsp) |
3143 | { |
3144 | mutex_lock(&dsp->pwr_lock); |
3145 | |
3146 | cs_dsp_warn(dsp, "WDT Expiry Fault\n" ); |
3147 | |
3148 | dsp->ops->stop_watchdog(dsp); |
3149 | if (dsp->client_ops->watchdog_expired) |
3150 | dsp->client_ops->watchdog_expired(dsp); |
3151 | |
3152 | mutex_unlock(lock: &dsp->pwr_lock); |
3153 | } |
3154 | EXPORT_SYMBOL_NS_GPL(cs_dsp_halo_wdt_expire, FW_CS_DSP); |
3155 | |
3156 | static const struct cs_dsp_ops cs_dsp_adsp1_ops = { |
3157 | .validate_version = cs_dsp_validate_version, |
3158 | .parse_sizes = cs_dsp_adsp1_parse_sizes, |
3159 | .region_to_reg = cs_dsp_region_to_reg, |
3160 | }; |
3161 | |
3162 | static const struct cs_dsp_ops cs_dsp_adsp2_ops[] = { |
3163 | { |
3164 | .parse_sizes = cs_dsp_adsp2_parse_sizes, |
3165 | .validate_version = cs_dsp_validate_version, |
3166 | .setup_algs = cs_dsp_adsp2_setup_algs, |
3167 | .region_to_reg = cs_dsp_region_to_reg, |
3168 | |
3169 | .show_fw_status = cs_dsp_adsp2_show_fw_status, |
3170 | |
3171 | .enable_memory = cs_dsp_adsp2_enable_memory, |
3172 | .disable_memory = cs_dsp_adsp2_disable_memory, |
3173 | |
3174 | .enable_core = cs_dsp_adsp2_enable_core, |
3175 | .disable_core = cs_dsp_adsp2_disable_core, |
3176 | |
3177 | .start_core = cs_dsp_adsp2_start_core, |
3178 | .stop_core = cs_dsp_adsp2_stop_core, |
3179 | |
3180 | }, |
3181 | { |
3182 | .parse_sizes = cs_dsp_adsp2_parse_sizes, |
3183 | .validate_version = cs_dsp_validate_version, |
3184 | .setup_algs = cs_dsp_adsp2_setup_algs, |
3185 | .region_to_reg = cs_dsp_region_to_reg, |
3186 | |
3187 | .show_fw_status = cs_dsp_adsp2v2_show_fw_status, |
3188 | |
3189 | .enable_memory = cs_dsp_adsp2_enable_memory, |
3190 | .disable_memory = cs_dsp_adsp2_disable_memory, |
3191 | .lock_memory = cs_dsp_adsp2_lock, |
3192 | |
3193 | .enable_core = cs_dsp_adsp2v2_enable_core, |
3194 | .disable_core = cs_dsp_adsp2v2_disable_core, |
3195 | |
3196 | .start_core = cs_dsp_adsp2_start_core, |
3197 | .stop_core = cs_dsp_adsp2_stop_core, |
3198 | }, |
3199 | { |
3200 | .parse_sizes = cs_dsp_adsp2_parse_sizes, |
3201 | .validate_version = cs_dsp_validate_version, |
3202 | .setup_algs = cs_dsp_adsp2_setup_algs, |
3203 | .region_to_reg = cs_dsp_region_to_reg, |
3204 | |
3205 | .show_fw_status = cs_dsp_adsp2v2_show_fw_status, |
3206 | .stop_watchdog = cs_dsp_stop_watchdog, |
3207 | |
3208 | .enable_memory = cs_dsp_adsp2_enable_memory, |
3209 | .disable_memory = cs_dsp_adsp2_disable_memory, |
3210 | .lock_memory = cs_dsp_adsp2_lock, |
3211 | |
3212 | .enable_core = cs_dsp_adsp2v2_enable_core, |
3213 | .disable_core = cs_dsp_adsp2v2_disable_core, |
3214 | |
3215 | .start_core = cs_dsp_adsp2_start_core, |
3216 | .stop_core = cs_dsp_adsp2_stop_core, |
3217 | }, |
3218 | }; |
3219 | |
3220 | static const struct cs_dsp_ops cs_dsp_halo_ops = { |
3221 | .parse_sizes = cs_dsp_adsp2_parse_sizes, |
3222 | .validate_version = cs_dsp_halo_validate_version, |
3223 | .setup_algs = cs_dsp_halo_setup_algs, |
3224 | .region_to_reg = cs_dsp_halo_region_to_reg, |
3225 | |
3226 | .show_fw_status = cs_dsp_halo_show_fw_status, |
3227 | .stop_watchdog = cs_dsp_halo_stop_watchdog, |
3228 | |
3229 | .lock_memory = cs_dsp_halo_configure_mpu, |
3230 | |
3231 | .start_core = cs_dsp_halo_start_core, |
3232 | .stop_core = cs_dsp_halo_stop_core, |
3233 | }; |
3234 | |
3235 | static const struct cs_dsp_ops cs_dsp_halo_ao_ops = { |
3236 | .parse_sizes = cs_dsp_adsp2_parse_sizes, |
3237 | .validate_version = cs_dsp_halo_validate_version, |
3238 | .setup_algs = cs_dsp_halo_setup_algs, |
3239 | .region_to_reg = cs_dsp_halo_region_to_reg, |
3240 | .show_fw_status = cs_dsp_halo_show_fw_status, |
3241 | }; |
3242 | |
3243 | /** |
3244 | * cs_dsp_chunk_write() - Format data to a DSP memory chunk |
3245 | * @ch: Pointer to the chunk structure |
3246 | * @nbits: Number of bits to write |
3247 | * @val: Value to write |
3248 | * |
3249 | * This function sequentially writes values into the format required for DSP |
3250 | * memory, it handles both inserting of the padding bytes and converting to |
3251 | * big endian. Note that data is only committed to the chunk when a whole DSP |
3252 | * words worth of data is available. |
3253 | * |
3254 | * Return: Zero for success, a negative number on error. |
3255 | */ |
3256 | int cs_dsp_chunk_write(struct cs_dsp_chunk *ch, int nbits, u32 val) |
3257 | { |
3258 | int nwrite, i; |
3259 | |
3260 | nwrite = min(CS_DSP_DATA_WORD_BITS - ch->cachebits, nbits); |
3261 | |
3262 | ch->cache <<= nwrite; |
3263 | ch->cache |= val >> (nbits - nwrite); |
3264 | ch->cachebits += nwrite; |
3265 | nbits -= nwrite; |
3266 | |
3267 | if (ch->cachebits == CS_DSP_DATA_WORD_BITS) { |
3268 | if (cs_dsp_chunk_end(ch)) |
3269 | return -ENOSPC; |
3270 | |
3271 | ch->cache &= 0xFFFFFF; |
3272 | for (i = 0; i < sizeof(ch->cache); i++, ch->cache <<= BITS_PER_BYTE) |
3273 | *ch->data++ = (ch->cache & 0xFF000000) >> CS_DSP_DATA_WORD_BITS; |
3274 | |
3275 | ch->bytes += sizeof(ch->cache); |
3276 | ch->cachebits = 0; |
3277 | } |
3278 | |
3279 | if (nbits) |
3280 | return cs_dsp_chunk_write(ch, nbits, val); |
3281 | |
3282 | return 0; |
3283 | } |
3284 | EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_write, FW_CS_DSP); |
3285 | |
3286 | /** |
3287 | * cs_dsp_chunk_flush() - Pad remaining data with zero and commit to chunk |
3288 | * @ch: Pointer to the chunk structure |
3289 | * |
3290 | * As cs_dsp_chunk_write only writes data when a whole DSP word is ready to |
3291 | * be written out it is possible that some data will remain in the cache, this |
3292 | * function will pad that data with zeros upto a whole DSP word and write out. |
3293 | * |
3294 | * Return: Zero for success, a negative number on error. |
3295 | */ |
3296 | int cs_dsp_chunk_flush(struct cs_dsp_chunk *ch) |
3297 | { |
3298 | if (!ch->cachebits) |
3299 | return 0; |
3300 | |
3301 | return cs_dsp_chunk_write(ch, CS_DSP_DATA_WORD_BITS - ch->cachebits, 0); |
3302 | } |
3303 | EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_flush, FW_CS_DSP); |
3304 | |
3305 | /** |
3306 | * cs_dsp_chunk_read() - Parse data from a DSP memory chunk |
3307 | * @ch: Pointer to the chunk structure |
3308 | * @nbits: Number of bits to read |
3309 | * |
3310 | * This function sequentially reads values from a DSP memory formatted buffer, |
3311 | * it handles both removing of the padding bytes and converting from big endian. |
3312 | * |
3313 | * Return: A negative number is returned on error, otherwise the read value. |
3314 | */ |
3315 | int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits) |
3316 | { |
3317 | int nread, i; |
3318 | u32 result; |
3319 | |
3320 | if (!ch->cachebits) { |
3321 | if (cs_dsp_chunk_end(ch)) |
3322 | return -ENOSPC; |
3323 | |
3324 | ch->cache = 0; |
3325 | ch->cachebits = CS_DSP_DATA_WORD_BITS; |
3326 | |
3327 | for (i = 0; i < sizeof(ch->cache); i++, ch->cache <<= BITS_PER_BYTE) |
3328 | ch->cache |= *ch->data++; |
3329 | |
3330 | ch->bytes += sizeof(ch->cache); |
3331 | } |
3332 | |
3333 | nread = min(ch->cachebits, nbits); |
3334 | nbits -= nread; |
3335 | |
3336 | result = ch->cache >> ((sizeof(ch->cache) * BITS_PER_BYTE) - nread); |
3337 | ch->cache <<= nread; |
3338 | ch->cachebits -= nread; |
3339 | |
3340 | if (nbits) |
3341 | result = (result << nbits) | cs_dsp_chunk_read(ch, nbits); |
3342 | |
3343 | return result; |
3344 | } |
3345 | EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_read, FW_CS_DSP); |
3346 | |
3347 | MODULE_DESCRIPTION("Cirrus Logic DSP Support" ); |
3348 | MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>" ); |
3349 | MODULE_LICENSE("GPL v2" ); |
3350 | |