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