Commit | Line | Data |
---|---|---|
80503b23 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
e359dc24 MF |
2 | /* |
3 | * Load Analog Devices SigmaStudio firmware files | |
4 | * | |
d48b088e | 5 | * Copyright 2009-2014 Analog Devices Inc. |
e359dc24 MF |
6 | */ |
7 | ||
8 | #include <linux/crc32.h> | |
e359dc24 MF |
9 | #include <linux/firmware.h> |
10 | #include <linux/kernel.h> | |
11 | #include <linux/i2c.h> | |
38fd54ee | 12 | #include <linux/regmap.h> |
27c46a25 | 13 | #include <linux/module.h> |
d48b088e LPC |
14 | #include <linux/slab.h> |
15 | ||
a35daac7 | 16 | #include <sound/control.h> |
d48b088e | 17 | #include <sound/soc.h> |
40216ce7 LPC |
18 | |
19 | #include "sigmadsp.h" | |
e359dc24 | 20 | |
a4c1d7e6 LPC |
21 | #define SIGMA_MAGIC "ADISIGM" |
22 | ||
a35daac7 LPC |
23 | #define SIGMA_FW_CHUNK_TYPE_DATA 0 |
24 | #define SIGMA_FW_CHUNK_TYPE_CONTROL 1 | |
25 | #define SIGMA_FW_CHUNK_TYPE_SAMPLERATES 2 | |
26 | ||
27 | struct sigmadsp_control { | |
28 | struct list_head head; | |
29 | uint32_t samplerates; | |
30 | unsigned int addr; | |
31 | unsigned int num_bytes; | |
32 | const char *name; | |
33 | struct snd_kcontrol *kcontrol; | |
34 | bool cached; | |
35 | uint8_t cache[]; | |
36 | }; | |
37 | ||
d48b088e LPC |
38 | struct sigmadsp_data { |
39 | struct list_head head; | |
a35daac7 | 40 | uint32_t samplerates; |
d48b088e LPC |
41 | unsigned int addr; |
42 | unsigned int length; | |
43 | uint8_t data[]; | |
44 | }; | |
45 | ||
a35daac7 LPC |
46 | struct sigma_fw_chunk { |
47 | __le32 length; | |
48 | __le32 tag; | |
49 | __le32 samplerates; | |
50 | } __packed; | |
51 | ||
52 | struct sigma_fw_chunk_data { | |
53 | struct sigma_fw_chunk chunk; | |
54 | __le16 addr; | |
55 | uint8_t data[]; | |
56 | } __packed; | |
57 | ||
58 | struct sigma_fw_chunk_control { | |
59 | struct sigma_fw_chunk chunk; | |
60 | __le16 type; | |
61 | __le16 addr; | |
62 | __le16 num_bytes; | |
63 | const char name[]; | |
64 | } __packed; | |
65 | ||
66 | struct sigma_fw_chunk_samplerate { | |
67 | struct sigma_fw_chunk chunk; | |
68 | __le32 samplerates[]; | |
69 | } __packed; | |
70 | ||
a4c1d7e6 LPC |
71 | struct sigma_firmware_header { |
72 | unsigned char magic[7]; | |
73 | u8 version; | |
74 | __le32 crc; | |
75 | } __packed; | |
76 | ||
77 | enum { | |
78 | SIGMA_ACTION_WRITEXBYTES = 0, | |
79 | SIGMA_ACTION_WRITESINGLE, | |
80 | SIGMA_ACTION_WRITESAFELOAD, | |
a4c1d7e6 LPC |
81 | SIGMA_ACTION_END, |
82 | }; | |
83 | ||
d48b088e LPC |
84 | struct sigma_action { |
85 | u8 instr; | |
86 | u8 len_hi; | |
87 | __le16 len; | |
88 | __be16 addr; | |
89 | unsigned char payload[]; | |
90 | } __packed; | |
91 | ||
92 | static int sigmadsp_write(struct sigmadsp *sigmadsp, unsigned int addr, | |
93 | const uint8_t data[], size_t len) | |
94 | { | |
95 | return sigmadsp->write(sigmadsp->control_data, addr, data, len); | |
96 | } | |
97 | ||
a35daac7 LPC |
98 | static int sigmadsp_read(struct sigmadsp *sigmadsp, unsigned int addr, |
99 | uint8_t data[], size_t len) | |
100 | { | |
101 | return sigmadsp->read(sigmadsp->control_data, addr, data, len); | |
102 | } | |
103 | ||
104 | static int sigmadsp_ctrl_info(struct snd_kcontrol *kcontrol, | |
105 | struct snd_ctl_elem_info *info) | |
106 | { | |
107 | struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; | |
108 | ||
109 | info->type = SNDRV_CTL_ELEM_TYPE_BYTES; | |
110 | info->count = ctrl->num_bytes; | |
111 | ||
112 | return 0; | |
113 | } | |
114 | ||
115 | static int sigmadsp_ctrl_write(struct sigmadsp *sigmadsp, | |
116 | struct sigmadsp_control *ctrl, void *data) | |
117 | { | |
118 | /* safeload loads up to 20 bytes in a atomic operation */ | |
5ea752c6 | 119 | if (ctrl->num_bytes <= 20 && sigmadsp->ops && sigmadsp->ops->safeload) |
a35daac7 LPC |
120 | return sigmadsp->ops->safeload(sigmadsp, ctrl->addr, data, |
121 | ctrl->num_bytes); | |
122 | else | |
123 | return sigmadsp_write(sigmadsp, ctrl->addr, data, | |
124 | ctrl->num_bytes); | |
125 | } | |
126 | ||
127 | static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, | |
128 | struct snd_ctl_elem_value *ucontrol) | |
129 | { | |
130 | struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; | |
131 | struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol); | |
132 | uint8_t *data; | |
133 | int ret = 0; | |
134 | ||
135 | mutex_lock(&sigmadsp->lock); | |
136 | ||
137 | data = ucontrol->value.bytes.data; | |
138 | ||
139 | if (!(kcontrol->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) | |
140 | ret = sigmadsp_ctrl_write(sigmadsp, ctrl, data); | |
141 | ||
142 | if (ret == 0) { | |
143 | memcpy(ctrl->cache, data, ctrl->num_bytes); | |
144 | ctrl->cached = true; | |
145 | } | |
146 | ||
147 | mutex_unlock(&sigmadsp->lock); | |
148 | ||
149 | return ret; | |
150 | } | |
151 | ||
152 | static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, | |
153 | struct snd_ctl_elem_value *ucontrol) | |
154 | { | |
155 | struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; | |
156 | struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol); | |
157 | int ret = 0; | |
158 | ||
159 | mutex_lock(&sigmadsp->lock); | |
160 | ||
161 | if (!ctrl->cached) { | |
162 | ret = sigmadsp_read(sigmadsp, ctrl->addr, ctrl->cache, | |
163 | ctrl->num_bytes); | |
164 | } | |
165 | ||
166 | if (ret == 0) { | |
167 | ctrl->cached = true; | |
168 | memcpy(ucontrol->value.bytes.data, ctrl->cache, | |
169 | ctrl->num_bytes); | |
170 | } | |
171 | ||
172 | mutex_unlock(&sigmadsp->lock); | |
173 | ||
174 | return ret; | |
175 | } | |
176 | ||
177 | static void sigmadsp_control_free(struct snd_kcontrol *kcontrol) | |
178 | { | |
179 | struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; | |
180 | ||
181 | ctrl->kcontrol = NULL; | |
182 | } | |
183 | ||
184 | static bool sigma_fw_validate_control_name(const char *name, unsigned int len) | |
185 | { | |
186 | unsigned int i; | |
187 | ||
188 | for (i = 0; i < len; i++) { | |
189 | /* Normal ASCII characters are valid */ | |
190 | if (name[i] < ' ' || name[i] > '~') | |
191 | return false; | |
192 | } | |
193 | ||
194 | return true; | |
195 | } | |
196 | ||
197 | static int sigma_fw_load_control(struct sigmadsp *sigmadsp, | |
198 | const struct sigma_fw_chunk *chunk, unsigned int length) | |
199 | { | |
200 | const struct sigma_fw_chunk_control *ctrl_chunk; | |
201 | struct sigmadsp_control *ctrl; | |
202 | unsigned int num_bytes; | |
203 | size_t name_len; | |
204 | char *name; | |
205 | int ret; | |
206 | ||
207 | if (length <= sizeof(*ctrl_chunk)) | |
208 | return -EINVAL; | |
209 | ||
210 | ctrl_chunk = (const struct sigma_fw_chunk_control *)chunk; | |
211 | ||
212 | name_len = length - sizeof(*ctrl_chunk); | |
213 | if (name_len >= SNDRV_CTL_ELEM_ID_NAME_MAXLEN) | |
214 | name_len = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1; | |
215 | ||
216 | /* Make sure there are no non-displayable characaters in the string */ | |
217 | if (!sigma_fw_validate_control_name(ctrl_chunk->name, name_len)) | |
218 | return -EINVAL; | |
219 | ||
220 | num_bytes = le16_to_cpu(ctrl_chunk->num_bytes); | |
221 | ctrl = kzalloc(sizeof(*ctrl) + num_bytes, GFP_KERNEL); | |
222 | if (!ctrl) | |
223 | return -ENOMEM; | |
224 | ||
225 | name = kzalloc(name_len + 1, GFP_KERNEL); | |
226 | if (!name) { | |
227 | ret = -ENOMEM; | |
228 | goto err_free_ctrl; | |
229 | } | |
230 | memcpy(name, ctrl_chunk->name, name_len); | |
231 | name[name_len] = '\0'; | |
232 | ctrl->name = name; | |
233 | ||
234 | ctrl->addr = le16_to_cpu(ctrl_chunk->addr); | |
235 | ctrl->num_bytes = num_bytes; | |
1fc10044 | 236 | ctrl->samplerates = le32_to_cpu(chunk->samplerates); |
a35daac7 LPC |
237 | |
238 | list_add_tail(&ctrl->head, &sigmadsp->ctrl_list); | |
239 | ||
240 | return 0; | |
241 | ||
242 | err_free_ctrl: | |
243 | kfree(ctrl); | |
244 | ||
245 | return ret; | |
246 | } | |
247 | ||
248 | static int sigma_fw_load_data(struct sigmadsp *sigmadsp, | |
249 | const struct sigma_fw_chunk *chunk, unsigned int length) | |
250 | { | |
251 | const struct sigma_fw_chunk_data *data_chunk; | |
252 | struct sigmadsp_data *data; | |
253 | ||
254 | if (length <= sizeof(*data_chunk)) | |
255 | return -EINVAL; | |
256 | ||
257 | data_chunk = (struct sigma_fw_chunk_data *)chunk; | |
258 | ||
259 | length -= sizeof(*data_chunk); | |
260 | ||
261 | data = kzalloc(sizeof(*data) + length, GFP_KERNEL); | |
262 | if (!data) | |
263 | return -ENOMEM; | |
264 | ||
265 | data->addr = le16_to_cpu(data_chunk->addr); | |
266 | data->length = length; | |
1fc10044 | 267 | data->samplerates = le32_to_cpu(chunk->samplerates); |
a35daac7 LPC |
268 | memcpy(data->data, data_chunk->data, length); |
269 | list_add_tail(&data->head, &sigmadsp->data_list); | |
270 | ||
271 | return 0; | |
272 | } | |
273 | ||
274 | static int sigma_fw_load_samplerates(struct sigmadsp *sigmadsp, | |
275 | const struct sigma_fw_chunk *chunk, unsigned int length) | |
276 | { | |
277 | const struct sigma_fw_chunk_samplerate *rate_chunk; | |
278 | unsigned int num_rates; | |
279 | unsigned int *rates; | |
280 | unsigned int i; | |
281 | ||
282 | rate_chunk = (const struct sigma_fw_chunk_samplerate *)chunk; | |
283 | ||
284 | num_rates = (length - sizeof(*rate_chunk)) / sizeof(__le32); | |
285 | ||
286 | if (num_rates > 32 || num_rates == 0) | |
287 | return -EINVAL; | |
288 | ||
289 | /* We only allow one samplerates block per file */ | |
290 | if (sigmadsp->rate_constraints.count) | |
291 | return -EINVAL; | |
292 | ||
293 | rates = kcalloc(num_rates, sizeof(*rates), GFP_KERNEL); | |
294 | if (!rates) | |
295 | return -ENOMEM; | |
296 | ||
297 | for (i = 0; i < num_rates; i++) | |
298 | rates[i] = le32_to_cpu(rate_chunk->samplerates[i]); | |
299 | ||
300 | sigmadsp->rate_constraints.count = num_rates; | |
301 | sigmadsp->rate_constraints.list = rates; | |
302 | ||
303 | return 0; | |
304 | } | |
305 | ||
306 | static int sigmadsp_fw_load_v2(struct sigmadsp *sigmadsp, | |
307 | const struct firmware *fw) | |
308 | { | |
309 | struct sigma_fw_chunk *chunk; | |
310 | unsigned int length, pos; | |
311 | int ret; | |
312 | ||
313 | /* | |
314 | * Make sure that there is at least one chunk to avoid integer | |
315 | * underflows later on. Empty firmware is still valid though. | |
316 | */ | |
317 | if (fw->size < sizeof(*chunk) + sizeof(struct sigma_firmware_header)) | |
318 | return 0; | |
319 | ||
320 | pos = sizeof(struct sigma_firmware_header); | |
321 | ||
322 | while (pos < fw->size - sizeof(*chunk)) { | |
323 | chunk = (struct sigma_fw_chunk *)(fw->data + pos); | |
324 | ||
325 | length = le32_to_cpu(chunk->length); | |
326 | ||
327 | if (length > fw->size - pos || length < sizeof(*chunk)) | |
328 | return -EINVAL; | |
329 | ||
1fc10044 | 330 | switch (le32_to_cpu(chunk->tag)) { |
a35daac7 LPC |
331 | case SIGMA_FW_CHUNK_TYPE_DATA: |
332 | ret = sigma_fw_load_data(sigmadsp, chunk, length); | |
333 | break; | |
334 | case SIGMA_FW_CHUNK_TYPE_CONTROL: | |
335 | ret = sigma_fw_load_control(sigmadsp, chunk, length); | |
336 | break; | |
337 | case SIGMA_FW_CHUNK_TYPE_SAMPLERATES: | |
338 | ret = sigma_fw_load_samplerates(sigmadsp, chunk, length); | |
339 | break; | |
340 | default: | |
341 | dev_warn(sigmadsp->dev, "Unknown chunk type: %d\n", | |
342 | chunk->tag); | |
343 | ret = 0; | |
344 | break; | |
345 | } | |
346 | ||
347 | if (ret) | |
348 | return ret; | |
349 | ||
350 | /* | |
351 | * This can not overflow since if length is larger than the | |
352 | * maximum firmware size (0x4000000) we'll error out earilier. | |
353 | */ | |
354 | pos += ALIGN(length, sizeof(__le32)); | |
355 | } | |
356 | ||
357 | return 0; | |
358 | } | |
359 | ||
a4c1d7e6 LPC |
360 | static inline u32 sigma_action_len(struct sigma_action *sa) |
361 | { | |
362 | return (sa->len_hi << 16) | le16_to_cpu(sa->len); | |
363 | } | |
364 | ||
4f718a29 LPC |
365 | static size_t sigma_action_size(struct sigma_action *sa) |
366 | { | |
367 | size_t payload = 0; | |
368 | ||
369 | switch (sa->instr) { | |
370 | case SIGMA_ACTION_WRITEXBYTES: | |
371 | case SIGMA_ACTION_WRITESINGLE: | |
372 | case SIGMA_ACTION_WRITESAFELOAD: | |
373 | payload = sigma_action_len(sa); | |
374 | break; | |
375 | default: | |
376 | break; | |
377 | } | |
378 | ||
379 | payload = ALIGN(payload, 2); | |
380 | ||
381 | return payload + sizeof(struct sigma_action); | |
382 | } | |
383 | ||
384 | /* | |
385 | * Returns a negative error value in case of an error, 0 if processing of | |
386 | * the firmware should be stopped after this action, 1 otherwise. | |
387 | */ | |
d48b088e LPC |
388 | static int process_sigma_action(struct sigmadsp *sigmadsp, |
389 | struct sigma_action *sa) | |
e359dc24 | 390 | { |
e359dc24 | 391 | size_t len = sigma_action_len(sa); |
d48b088e | 392 | struct sigmadsp_data *data; |
e359dc24 MF |
393 | |
394 | pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__, | |
395 | sa->instr, sa->addr, len); | |
396 | ||
397 | switch (sa->instr) { | |
398 | case SIGMA_ACTION_WRITEXBYTES: | |
399 | case SIGMA_ACTION_WRITESINGLE: | |
400 | case SIGMA_ACTION_WRITESAFELOAD: | |
d48b088e | 401 | if (len < 3) |
e359dc24 | 402 | return -EINVAL; |
d48b088e LPC |
403 | |
404 | data = kzalloc(sizeof(*data) + len - 2, GFP_KERNEL); | |
405 | if (!data) | |
406 | return -ENOMEM; | |
407 | ||
408 | data->addr = be16_to_cpu(sa->addr); | |
409 | data->length = len - 2; | |
410 | memcpy(data->data, sa->payload, data->length); | |
411 | list_add_tail(&data->head, &sigmadsp->data_list); | |
e359dc24 | 412 | break; |
e359dc24 | 413 | case SIGMA_ACTION_END: |
4f718a29 | 414 | return 0; |
e359dc24 MF |
415 | default: |
416 | return -EINVAL; | |
417 | } | |
418 | ||
4f718a29 | 419 | return 1; |
e359dc24 MF |
420 | } |
421 | ||
d48b088e LPC |
422 | static int sigmadsp_fw_load_v1(struct sigmadsp *sigmadsp, |
423 | const struct firmware *fw) | |
e359dc24 | 424 | { |
4f718a29 | 425 | struct sigma_action *sa; |
d48b088e | 426 | size_t size, pos; |
4f718a29 LPC |
427 | int ret; |
428 | ||
d48b088e LPC |
429 | pos = sizeof(struct sigma_firmware_header); |
430 | ||
431 | while (pos + sizeof(*sa) <= fw->size) { | |
432 | sa = (struct sigma_action *)(fw->data + pos); | |
4f718a29 LPC |
433 | |
434 | size = sigma_action_size(sa); | |
d48b088e LPC |
435 | pos += size; |
436 | if (pos > fw->size || size == 0) | |
4f718a29 LPC |
437 | break; |
438 | ||
d48b088e | 439 | ret = process_sigma_action(sigmadsp, sa); |
e359dc24 | 440 | |
e359dc24 | 441 | pr_debug("%s: action returned %i\n", __func__, ret); |
4f718a29 LPC |
442 | |
443 | if (ret <= 0) | |
e359dc24 MF |
444 | return ret; |
445 | } | |
4f718a29 | 446 | |
d48b088e | 447 | if (pos != fw->size) |
4f718a29 LPC |
448 | return -EINVAL; |
449 | ||
450 | return 0; | |
e359dc24 MF |
451 | } |
452 | ||
d48b088e | 453 | static void sigmadsp_firmware_release(struct sigmadsp *sigmadsp) |
e359dc24 | 454 | { |
a35daac7 | 455 | struct sigmadsp_control *ctrl, *_ctrl; |
d48b088e LPC |
456 | struct sigmadsp_data *data, *_data; |
457 | ||
a35daac7 LPC |
458 | list_for_each_entry_safe(ctrl, _ctrl, &sigmadsp->ctrl_list, head) { |
459 | kfree(ctrl->name); | |
460 | kfree(ctrl); | |
461 | } | |
462 | ||
d48b088e LPC |
463 | list_for_each_entry_safe(data, _data, &sigmadsp->data_list, head) |
464 | kfree(data); | |
465 | ||
a35daac7 | 466 | INIT_LIST_HEAD(&sigmadsp->ctrl_list); |
d48b088e LPC |
467 | INIT_LIST_HEAD(&sigmadsp->data_list); |
468 | } | |
469 | ||
470 | static void devm_sigmadsp_release(struct device *dev, void *res) | |
471 | { | |
472 | sigmadsp_firmware_release((struct sigmadsp *)res); | |
473 | } | |
474 | ||
475 | static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) | |
476 | { | |
477 | const struct sigma_firmware_header *ssfw_head; | |
e359dc24 | 478 | const struct firmware *fw; |
d48b088e | 479 | int ret; |
e359dc24 MF |
480 | u32 crc; |
481 | ||
e359dc24 | 482 | /* first load the blob */ |
d48b088e | 483 | ret = request_firmware(&fw, name, sigmadsp->dev); |
e359dc24 MF |
484 | if (ret) { |
485 | pr_debug("%s: request_firmware() failed with %i\n", __func__, ret); | |
d48b088e | 486 | goto done; |
e359dc24 | 487 | } |
e359dc24 MF |
488 | |
489 | /* then verify the header */ | |
490 | ret = -EINVAL; | |
4f718a29 LPC |
491 | |
492 | /* | |
493 | * Reject too small or unreasonable large files. The upper limit has been | |
494 | * chosen a bit arbitrarily, but it should be enough for all practical | |
495 | * purposes and having the limit makes it easier to avoid integer | |
496 | * overflows later in the loading process. | |
497 | */ | |
48afc527 | 498 | if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) { |
d48b088e | 499 | dev_err(sigmadsp->dev, "Failed to load firmware: Invalid size\n"); |
e359dc24 | 500 | goto done; |
48afc527 | 501 | } |
e359dc24 MF |
502 | |
503 | ssfw_head = (void *)fw->data; | |
48afc527 | 504 | if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) { |
d48b088e | 505 | dev_err(sigmadsp->dev, "Failed to load firmware: Invalid magic\n"); |
50c0f21b LPC |
506 | goto done; |
507 | } | |
508 | ||
c56935bd LPC |
509 | crc = crc32(0, fw->data + sizeof(*ssfw_head), |
510 | fw->size - sizeof(*ssfw_head)); | |
e359dc24 | 511 | pr_debug("%s: crc=%x\n", __func__, crc); |
48afc527 | 512 | if (crc != le32_to_cpu(ssfw_head->crc)) { |
d48b088e | 513 | dev_err(sigmadsp->dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n", |
48afc527 | 514 | le32_to_cpu(ssfw_head->crc), crc); |
e359dc24 | 515 | goto done; |
48afc527 | 516 | } |
e359dc24 | 517 | |
d48b088e LPC |
518 | switch (ssfw_head->version) { |
519 | case 1: | |
520 | ret = sigmadsp_fw_load_v1(sigmadsp, fw); | |
521 | break; | |
a35daac7 LPC |
522 | case 2: |
523 | ret = sigmadsp_fw_load_v2(sigmadsp, fw); | |
524 | break; | |
d48b088e LPC |
525 | default: |
526 | dev_err(sigmadsp->dev, | |
a35daac7 | 527 | "Failed to load firmware: Invalid version %d. Supported firmware versions: 1, 2\n", |
d48b088e LPC |
528 | ssfw_head->version); |
529 | ret = -EINVAL; | |
530 | break; | |
531 | } | |
e359dc24 | 532 | |
d48b088e LPC |
533 | if (ret) |
534 | sigmadsp_firmware_release(sigmadsp); | |
e359dc24 | 535 | |
d48b088e | 536 | done: |
e359dc24 MF |
537 | release_firmware(fw); |
538 | ||
d48b088e LPC |
539 | return ret; |
540 | } | |
541 | ||
542 | static int sigmadsp_init(struct sigmadsp *sigmadsp, struct device *dev, | |
543 | const struct sigmadsp_ops *ops, const char *firmware_name) | |
544 | { | |
545 | sigmadsp->ops = ops; | |
546 | sigmadsp->dev = dev; | |
547 | ||
a35daac7 | 548 | INIT_LIST_HEAD(&sigmadsp->ctrl_list); |
d48b088e | 549 | INIT_LIST_HEAD(&sigmadsp->data_list); |
a35daac7 | 550 | mutex_init(&sigmadsp->lock); |
d48b088e LPC |
551 | |
552 | return sigmadsp_firmware_load(sigmadsp, firmware_name); | |
553 | } | |
554 | ||
555 | /** | |
556 | * devm_sigmadsp_init() - Initialize SigmaDSP instance | |
557 | * @dev: The parent device | |
558 | * @ops: The sigmadsp_ops to use for this instance | |
559 | * @firmware_name: Name of the firmware file to load | |
560 | * | |
561 | * Allocates a SigmaDSP instance and loads the specified firmware file. | |
562 | * | |
563 | * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error. | |
564 | */ | |
565 | struct sigmadsp *devm_sigmadsp_init(struct device *dev, | |
566 | const struct sigmadsp_ops *ops, const char *firmware_name) | |
567 | { | |
568 | struct sigmadsp *sigmadsp; | |
569 | int ret; | |
570 | ||
571 | sigmadsp = devres_alloc(devm_sigmadsp_release, sizeof(*sigmadsp), | |
572 | GFP_KERNEL); | |
573 | if (!sigmadsp) | |
574 | return ERR_PTR(-ENOMEM); | |
575 | ||
576 | ret = sigmadsp_init(sigmadsp, dev, ops, firmware_name); | |
577 | if (ret) { | |
578 | devres_free(sigmadsp); | |
579 | return ERR_PTR(ret); | |
580 | } | |
581 | ||
582 | devres_add(dev, sigmadsp); | |
583 | ||
584 | return sigmadsp; | |
585 | } | |
586 | EXPORT_SYMBOL_GPL(devm_sigmadsp_init); | |
587 | ||
a35daac7 LPC |
588 | static int sigmadsp_rate_to_index(struct sigmadsp *sigmadsp, unsigned int rate) |
589 | { | |
590 | unsigned int i; | |
591 | ||
592 | for (i = 0; i < sigmadsp->rate_constraints.count; i++) { | |
593 | if (sigmadsp->rate_constraints.list[i] == rate) | |
594 | return i; | |
595 | } | |
596 | ||
597 | return -EINVAL; | |
598 | } | |
599 | ||
600 | static unsigned int sigmadsp_get_samplerate_mask(struct sigmadsp *sigmadsp, | |
601 | unsigned int samplerate) | |
602 | { | |
603 | int samplerate_index; | |
604 | ||
605 | if (samplerate == 0) | |
606 | return 0; | |
607 | ||
608 | if (sigmadsp->rate_constraints.count) { | |
609 | samplerate_index = sigmadsp_rate_to_index(sigmadsp, samplerate); | |
610 | if (samplerate_index < 0) | |
611 | return 0; | |
612 | ||
613 | return BIT(samplerate_index); | |
614 | } else { | |
615 | return ~0; | |
616 | } | |
617 | } | |
618 | ||
619 | static bool sigmadsp_samplerate_valid(unsigned int supported, | |
620 | unsigned int requested) | |
621 | { | |
622 | /* All samplerates are supported */ | |
623 | if (!supported) | |
624 | return true; | |
625 | ||
626 | return supported & requested; | |
627 | } | |
628 | ||
629 | static int sigmadsp_alloc_control(struct sigmadsp *sigmadsp, | |
630 | struct sigmadsp_control *ctrl, unsigned int samplerate_mask) | |
631 | { | |
632 | struct snd_kcontrol_new template; | |
633 | struct snd_kcontrol *kcontrol; | |
a35daac7 LPC |
634 | |
635 | memset(&template, 0, sizeof(template)); | |
636 | template.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | |
637 | template.name = ctrl->name; | |
638 | template.info = sigmadsp_ctrl_info; | |
639 | template.get = sigmadsp_ctrl_get; | |
640 | template.put = sigmadsp_ctrl_put; | |
641 | template.private_value = (unsigned long)ctrl; | |
642 | template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; | |
643 | if (!sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask)) | |
644 | template.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; | |
645 | ||
646 | kcontrol = snd_ctl_new1(&template, sigmadsp); | |
647 | if (!kcontrol) | |
648 | return -ENOMEM; | |
649 | ||
650 | kcontrol->private_free = sigmadsp_control_free; | |
651 | ctrl->kcontrol = kcontrol; | |
652 | ||
141f87d4 | 653 | return snd_ctl_add(sigmadsp->component->card->snd_card, kcontrol); |
a35daac7 LPC |
654 | } |
655 | ||
656 | static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp, | |
657 | struct sigmadsp_control *ctrl, unsigned int samplerate_mask) | |
658 | { | |
659 | struct snd_card *card = sigmadsp->component->card->snd_card; | |
660 | struct snd_kcontrol_volatile *vd; | |
661 | struct snd_ctl_elem_id id; | |
d98123a7 DC |
662 | bool active; |
663 | bool changed = false; | |
a35daac7 LPC |
664 | |
665 | active = sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask); | |
666 | ||
667 | down_write(&card->controls_rwsem); | |
668 | if (!ctrl->kcontrol) { | |
669 | up_write(&card->controls_rwsem); | |
670 | return; | |
671 | } | |
672 | ||
673 | id = ctrl->kcontrol->id; | |
674 | vd = &ctrl->kcontrol->vd[0]; | |
675 | if (active == (bool)(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) { | |
676 | vd->access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE; | |
677 | changed = true; | |
678 | } | |
679 | up_write(&card->controls_rwsem); | |
680 | ||
681 | if (active && changed) { | |
682 | mutex_lock(&sigmadsp->lock); | |
683 | if (ctrl->cached) | |
684 | sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); | |
685 | mutex_unlock(&sigmadsp->lock); | |
686 | } | |
687 | ||
688 | if (changed) | |
689 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &id); | |
690 | } | |
691 | ||
d48b088e LPC |
692 | /** |
693 | * sigmadsp_attach() - Attach a sigmadsp instance to a ASoC component | |
694 | * @sigmadsp: The sigmadsp instance to attach | |
695 | * @component: The component to attach to | |
696 | * | |
697 | * Typically called in the components probe callback. | |
698 | * | |
699 | * Note, once this function has been called the firmware must not be released | |
700 | * until after the ALSA snd_card that the component belongs to has been | |
701 | * disconnected, even if sigmadsp_attach() returns an error. | |
702 | */ | |
703 | int sigmadsp_attach(struct sigmadsp *sigmadsp, | |
704 | struct snd_soc_component *component) | |
705 | { | |
a35daac7 LPC |
706 | struct sigmadsp_control *ctrl; |
707 | unsigned int samplerate_mask; | |
708 | int ret; | |
709 | ||
d48b088e LPC |
710 | sigmadsp->component = component; |
711 | ||
a35daac7 LPC |
712 | samplerate_mask = sigmadsp_get_samplerate_mask(sigmadsp, |
713 | sigmadsp->current_samplerate); | |
714 | ||
715 | list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) { | |
716 | ret = sigmadsp_alloc_control(sigmadsp, ctrl, samplerate_mask); | |
717 | if (ret) | |
718 | return ret; | |
719 | } | |
720 | ||
d48b088e LPC |
721 | return 0; |
722 | } | |
723 | EXPORT_SYMBOL_GPL(sigmadsp_attach); | |
724 | ||
725 | /** | |
726 | * sigmadsp_setup() - Setup the DSP for the specified samplerate | |
727 | * @sigmadsp: The sigmadsp instance to configure | |
728 | * @samplerate: The samplerate the DSP should be configured for | |
729 | * | |
730 | * Loads the appropriate firmware program and parameter memory (if not already | |
731 | * loaded) and enables the controls for the specified samplerate. Any control | |
732 | * parameter changes that have been made previously will be restored. | |
733 | * | |
734 | * Returns 0 on success, a negative error code otherwise. | |
735 | */ | |
736 | int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int samplerate) | |
737 | { | |
a35daac7 LPC |
738 | struct sigmadsp_control *ctrl; |
739 | unsigned int samplerate_mask; | |
d48b088e LPC |
740 | struct sigmadsp_data *data; |
741 | int ret; | |
742 | ||
743 | if (sigmadsp->current_samplerate == samplerate) | |
744 | return 0; | |
745 | ||
a35daac7 LPC |
746 | samplerate_mask = sigmadsp_get_samplerate_mask(sigmadsp, samplerate); |
747 | if (samplerate_mask == 0) | |
748 | return -EINVAL; | |
749 | ||
d48b088e | 750 | list_for_each_entry(data, &sigmadsp->data_list, head) { |
a35daac7 LPC |
751 | if (!sigmadsp_samplerate_valid(data->samplerates, |
752 | samplerate_mask)) | |
753 | continue; | |
d48b088e LPC |
754 | ret = sigmadsp_write(sigmadsp, data->addr, data->data, |
755 | data->length); | |
756 | if (ret) | |
757 | goto err; | |
758 | } | |
759 | ||
a35daac7 LPC |
760 | list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) |
761 | sigmadsp_activate_ctrl(sigmadsp, ctrl, samplerate_mask); | |
762 | ||
d48b088e LPC |
763 | sigmadsp->current_samplerate = samplerate; |
764 | ||
765 | return 0; | |
766 | err: | |
767 | sigmadsp_reset(sigmadsp); | |
e359dc24 MF |
768 | |
769 | return ret; | |
770 | } | |
d48b088e LPC |
771 | EXPORT_SYMBOL_GPL(sigmadsp_setup); |
772 | ||
773 | /** | |
774 | * sigmadsp_reset() - Notify the sigmadsp instance that the DSP has been reset | |
775 | * @sigmadsp: The sigmadsp instance to reset | |
776 | * | |
777 | * Should be called whenever the DSP has been reset and parameter and program | |
778 | * memory need to be re-loaded. | |
779 | */ | |
780 | void sigmadsp_reset(struct sigmadsp *sigmadsp) | |
781 | { | |
a35daac7 LPC |
782 | struct sigmadsp_control *ctrl; |
783 | ||
784 | list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) | |
785 | sigmadsp_activate_ctrl(sigmadsp, ctrl, false); | |
786 | ||
d48b088e LPC |
787 | sigmadsp->current_samplerate = 0; |
788 | } | |
789 | EXPORT_SYMBOL_GPL(sigmadsp_reset); | |
790 | ||
791 | /** | |
792 | * sigmadsp_restrict_params() - Applies DSP firmware specific constraints | |
793 | * @sigmadsp: The sigmadsp instance | |
794 | * @substream: The substream to restrict | |
795 | * | |
796 | * Applies samplerate constraints that may be required by the firmware Should | |
797 | * typically be called from the CODEC/component drivers startup callback. | |
798 | * | |
799 | * Returns 0 on success, a negative error code otherwise. | |
800 | */ | |
801 | int sigmadsp_restrict_params(struct sigmadsp *sigmadsp, | |
802 | struct snd_pcm_substream *substream) | |
803 | { | |
a35daac7 LPC |
804 | if (sigmadsp->rate_constraints.count == 0) |
805 | return 0; | |
806 | ||
807 | return snd_pcm_hw_constraint_list(substream->runtime, 0, | |
808 | SNDRV_PCM_HW_PARAM_RATE, &sigmadsp->rate_constraints); | |
d48b088e LPC |
809 | } |
810 | EXPORT_SYMBOL_GPL(sigmadsp_restrict_params); | |
38fd54ee | 811 | |
27c46a25 | 812 | MODULE_LICENSE("GPL"); |