1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * wm_adsp.c -- Wolfson ADSP support |
4 | * |
5 | * Copyright 2012 Wolfson Microelectronics plc |
6 | * |
7 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> |
8 | */ |
9 | |
10 | #include <linux/ctype.h> |
11 | #include <linux/module.h> |
12 | #include <linux/moduleparam.h> |
13 | #include <linux/init.h> |
14 | #include <linux/delay.h> |
15 | #include <linux/firmware.h> |
16 | #include <linux/list.h> |
17 | #include <linux/pm.h> |
18 | #include <linux/regmap.h> |
19 | #include <linux/regulator/consumer.h> |
20 | #include <linux/slab.h> |
21 | #include <linux/vmalloc.h> |
22 | #include <linux/workqueue.h> |
23 | #include <linux/debugfs.h> |
24 | #include <sound/core.h> |
25 | #include <sound/pcm.h> |
26 | #include <sound/pcm_params.h> |
27 | #include <sound/soc.h> |
28 | #include <sound/jack.h> |
29 | #include <sound/initval.h> |
30 | #include <sound/tlv.h> |
31 | |
32 | #include "wm_adsp.h" |
33 | |
34 | #define adsp_crit(_dsp, fmt, ...) \ |
35 | dev_crit(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) |
36 | #define adsp_err(_dsp, fmt, ...) \ |
37 | dev_err(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) |
38 | #define adsp_warn(_dsp, fmt, ...) \ |
39 | dev_warn(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) |
40 | #define adsp_info(_dsp, fmt, ...) \ |
41 | dev_info(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) |
42 | #define adsp_dbg(_dsp, fmt, ...) \ |
43 | dev_dbg(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) |
44 | |
45 | #define compr_err(_obj, fmt, ...) \ |
46 | adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ |
47 | ##__VA_ARGS__) |
48 | #define compr_dbg(_obj, fmt, ...) \ |
49 | adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ |
50 | ##__VA_ARGS__) |
51 | |
52 | #define ADSP_MAX_STD_CTRL_SIZE 512 |
53 | |
54 | static const struct cs_dsp_client_ops wm_adsp1_client_ops; |
55 | static const struct cs_dsp_client_ops wm_adsp2_client_ops; |
56 | |
57 | #define WM_ADSP_FW_MBC_VSS 0 |
58 | #define WM_ADSP_FW_HIFI 1 |
59 | #define WM_ADSP_FW_TX 2 |
60 | #define WM_ADSP_FW_TX_SPK 3 |
61 | #define WM_ADSP_FW_RX 4 |
62 | #define WM_ADSP_FW_RX_ANC 5 |
63 | #define WM_ADSP_FW_CTRL 6 |
64 | #define WM_ADSP_FW_ASR 7 |
65 | #define WM_ADSP_FW_TRACE 8 |
66 | #define WM_ADSP_FW_SPK_PROT 9 |
67 | #define WM_ADSP_FW_SPK_CALI 10 |
68 | #define WM_ADSP_FW_SPK_DIAG 11 |
69 | #define WM_ADSP_FW_MISC 12 |
70 | |
71 | #define WM_ADSP_NUM_FW 13 |
72 | |
73 | static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { |
74 | [WM_ADSP_FW_MBC_VSS] = "MBC/VSS" , |
75 | [WM_ADSP_FW_HIFI] = "MasterHiFi" , |
76 | [WM_ADSP_FW_TX] = "Tx" , |
77 | [WM_ADSP_FW_TX_SPK] = "Tx Speaker" , |
78 | [WM_ADSP_FW_RX] = "Rx" , |
79 | [WM_ADSP_FW_RX_ANC] = "Rx ANC" , |
80 | [WM_ADSP_FW_CTRL] = "Voice Ctrl" , |
81 | [WM_ADSP_FW_ASR] = "ASR Assist" , |
82 | [WM_ADSP_FW_TRACE] = "Dbg Trace" , |
83 | [WM_ADSP_FW_SPK_PROT] = "Protection" , |
84 | [WM_ADSP_FW_SPK_CALI] = "Calibration" , |
85 | [WM_ADSP_FW_SPK_DIAG] = "Diagnostic" , |
86 | [WM_ADSP_FW_MISC] = "Misc" , |
87 | }; |
88 | |
89 | struct wm_adsp_system_config_xm_hdr { |
90 | __be32 sys_enable; |
91 | __be32 fw_id; |
92 | __be32 fw_rev; |
93 | __be32 boot_status; |
94 | __be32 watchdog; |
95 | __be32 dma_buffer_size; |
96 | __be32 rdma[6]; |
97 | __be32 wdma[8]; |
98 | __be32 build_job_name[3]; |
99 | __be32 build_job_number; |
100 | } __packed; |
101 | |
102 | struct wm_halo_system_config_xm_hdr { |
103 | __be32 halo_heartbeat; |
104 | __be32 build_job_name[3]; |
105 | __be32 build_job_number; |
106 | } __packed; |
107 | |
108 | struct wm_adsp_alg_xm_struct { |
109 | __be32 magic; |
110 | __be32 smoothing; |
111 | __be32 threshold; |
112 | __be32 host_buf_ptr; |
113 | __be32 start_seq; |
114 | __be32 high_water_mark; |
115 | __be32 low_water_mark; |
116 | __be64 smoothed_power; |
117 | } __packed; |
118 | |
119 | struct wm_adsp_host_buf_coeff_v1 { |
120 | __be32 host_buf_ptr; /* Host buffer pointer */ |
121 | __be32 versions; /* Version numbers */ |
122 | __be32 name[4]; /* The buffer name */ |
123 | } __packed; |
124 | |
125 | struct wm_adsp_buffer { |
126 | __be32 buf1_base; /* Base addr of first buffer area */ |
127 | __be32 buf1_size; /* Size of buf1 area in DSP words */ |
128 | __be32 buf2_base; /* Base addr of 2nd buffer area */ |
129 | __be32 buf1_buf2_size; /* Size of buf1+buf2 in DSP words */ |
130 | __be32 buf3_base; /* Base addr of buf3 area */ |
131 | __be32 buf_total_size; /* Size of buf1+buf2+buf3 in DSP words */ |
132 | __be32 high_water_mark; /* Point at which IRQ is asserted */ |
133 | __be32 irq_count; /* bits 1-31 count IRQ assertions */ |
134 | __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ |
135 | __be32 next_write_index; /* word index of next write */ |
136 | __be32 next_read_index; /* word index of next read */ |
137 | __be32 error; /* error if any */ |
138 | __be32 oldest_block_index; /* word index of oldest surviving */ |
139 | __be32 requested_rewind; /* how many blocks rewind was done */ |
140 | __be32 reserved_space; /* internal */ |
141 | __be32 min_free; /* min free space since stream start */ |
142 | __be32 blocks_written[2]; /* total blocks written (64 bit) */ |
143 | __be32 words_written[2]; /* total words written (64 bit) */ |
144 | } __packed; |
145 | |
146 | struct wm_adsp_compr; |
147 | |
148 | struct wm_adsp_compr_buf { |
149 | struct list_head list; |
150 | struct wm_adsp *dsp; |
151 | struct wm_adsp_compr *compr; |
152 | |
153 | struct wm_adsp_buffer_region *regions; |
154 | u32 host_buf_ptr; |
155 | |
156 | u32 error; |
157 | u32 irq_count; |
158 | int read_index; |
159 | int avail; |
160 | int host_buf_mem_type; |
161 | |
162 | char *name; |
163 | }; |
164 | |
165 | struct wm_adsp_compr { |
166 | struct list_head list; |
167 | struct wm_adsp *dsp; |
168 | struct wm_adsp_compr_buf *buf; |
169 | |
170 | struct snd_compr_stream *stream; |
171 | struct snd_compressed_buffer size; |
172 | |
173 | u32 *raw_buf; |
174 | unsigned int copied_total; |
175 | |
176 | unsigned int sample_rate; |
177 | |
178 | const char *name; |
179 | }; |
180 | |
181 | #define WM_ADSP_MIN_FRAGMENTS 1 |
182 | #define WM_ADSP_MAX_FRAGMENTS 256 |
183 | #define WM_ADSP_MIN_FRAGMENT_SIZE (16 * CS_DSP_DATA_WORD_SIZE) |
184 | #define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * CS_DSP_DATA_WORD_SIZE) |
185 | |
186 | #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 |
187 | |
188 | #define HOST_BUFFER_FIELD(field) \ |
189 | (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) |
190 | |
191 | #define ALG_XM_FIELD(field) \ |
192 | (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) |
193 | |
194 | #define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER 1 |
195 | |
196 | #define HOST_BUF_COEFF_COMPAT_VER_MASK 0xFF00 |
197 | #define HOST_BUF_COEFF_COMPAT_VER_SHIFT 8 |
198 | |
199 | static int wm_adsp_buffer_init(struct wm_adsp *dsp); |
200 | static int wm_adsp_buffer_free(struct wm_adsp *dsp); |
201 | |
202 | struct wm_adsp_buffer_region { |
203 | unsigned int offset; |
204 | unsigned int cumulative_size; |
205 | unsigned int mem_type; |
206 | unsigned int base_addr; |
207 | }; |
208 | |
209 | struct wm_adsp_buffer_region_def { |
210 | unsigned int mem_type; |
211 | unsigned int base_offset; |
212 | unsigned int size_offset; |
213 | }; |
214 | |
215 | static const struct wm_adsp_buffer_region_def default_regions[] = { |
216 | { |
217 | .mem_type = WMFW_ADSP2_XM, |
218 | .base_offset = HOST_BUFFER_FIELD(buf1_base), |
219 | .size_offset = HOST_BUFFER_FIELD(buf1_size), |
220 | }, |
221 | { |
222 | .mem_type = WMFW_ADSP2_XM, |
223 | .base_offset = HOST_BUFFER_FIELD(buf2_base), |
224 | .size_offset = HOST_BUFFER_FIELD(buf1_buf2_size), |
225 | }, |
226 | { |
227 | .mem_type = WMFW_ADSP2_YM, |
228 | .base_offset = HOST_BUFFER_FIELD(buf3_base), |
229 | .size_offset = HOST_BUFFER_FIELD(buf_total_size), |
230 | }, |
231 | }; |
232 | |
233 | struct wm_adsp_fw_caps { |
234 | u32 id; |
235 | struct snd_codec_desc desc; |
236 | int num_regions; |
237 | const struct wm_adsp_buffer_region_def *region_defs; |
238 | }; |
239 | |
240 | static const struct wm_adsp_fw_caps ctrl_caps[] = { |
241 | { |
242 | .id = SND_AUDIOCODEC_BESPOKE, |
243 | .desc = { |
244 | .max_ch = 8, |
245 | .sample_rates = { 16000 }, |
246 | .num_sample_rates = 1, |
247 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
248 | }, |
249 | .num_regions = ARRAY_SIZE(default_regions), |
250 | .region_defs = default_regions, |
251 | }, |
252 | }; |
253 | |
254 | static const struct wm_adsp_fw_caps trace_caps[] = { |
255 | { |
256 | .id = SND_AUDIOCODEC_BESPOKE, |
257 | .desc = { |
258 | .max_ch = 8, |
259 | .sample_rates = { |
260 | 4000, 8000, 11025, 12000, 16000, 22050, |
261 | 24000, 32000, 44100, 48000, 64000, 88200, |
262 | 96000, 176400, 192000 |
263 | }, |
264 | .num_sample_rates = 15, |
265 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
266 | }, |
267 | .num_regions = ARRAY_SIZE(default_regions), |
268 | .region_defs = default_regions, |
269 | }, |
270 | }; |
271 | |
272 | static const struct { |
273 | const char *file; |
274 | int compr_direction; |
275 | int num_caps; |
276 | const struct wm_adsp_fw_caps *caps; |
277 | bool voice_trigger; |
278 | } wm_adsp_fw[WM_ADSP_NUM_FW] = { |
279 | [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, |
280 | [WM_ADSP_FW_HIFI] = { .file = "hifi" }, |
281 | [WM_ADSP_FW_TX] = { .file = "tx" }, |
282 | [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, |
283 | [WM_ADSP_FW_RX] = { .file = "rx" }, |
284 | [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, |
285 | [WM_ADSP_FW_CTRL] = { |
286 | .file = "ctrl" , |
287 | .compr_direction = SND_COMPRESS_CAPTURE, |
288 | .num_caps = ARRAY_SIZE(ctrl_caps), |
289 | .caps = ctrl_caps, |
290 | .voice_trigger = true, |
291 | }, |
292 | [WM_ADSP_FW_ASR] = { .file = "asr" }, |
293 | [WM_ADSP_FW_TRACE] = { |
294 | .file = "trace" , |
295 | .compr_direction = SND_COMPRESS_CAPTURE, |
296 | .num_caps = ARRAY_SIZE(trace_caps), |
297 | .caps = trace_caps, |
298 | }, |
299 | [WM_ADSP_FW_SPK_PROT] = { |
300 | .file = "spk-prot" , |
301 | .compr_direction = SND_COMPRESS_CAPTURE, |
302 | .num_caps = ARRAY_SIZE(trace_caps), |
303 | .caps = trace_caps, |
304 | }, |
305 | [WM_ADSP_FW_SPK_CALI] = { .file = "spk-cali" }, |
306 | [WM_ADSP_FW_SPK_DIAG] = { .file = "spk-diag" }, |
307 | [WM_ADSP_FW_MISC] = { .file = "misc" }, |
308 | }; |
309 | |
310 | struct wm_coeff_ctl { |
311 | const char *name; |
312 | struct cs_dsp_coeff_ctl *cs_ctl; |
313 | struct soc_bytes_ext bytes_ext; |
314 | struct work_struct work; |
315 | }; |
316 | |
317 | int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, |
318 | struct snd_ctl_elem_value *ucontrol) |
319 | { |
320 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
321 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
322 | struct wm_adsp *dsp = snd_soc_component_get_drvdata(c: component); |
323 | |
324 | ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; |
325 | |
326 | return 0; |
327 | } |
328 | EXPORT_SYMBOL_GPL(wm_adsp_fw_get); |
329 | |
330 | int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, |
331 | struct snd_ctl_elem_value *ucontrol) |
332 | { |
333 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
334 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
335 | struct wm_adsp *dsp = snd_soc_component_get_drvdata(c: component); |
336 | int ret = 1; |
337 | |
338 | if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) |
339 | return 0; |
340 | |
341 | if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) |
342 | return -EINVAL; |
343 | |
344 | mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock); |
345 | |
346 | if (dsp[e->shift_l].cs_dsp.booted || !list_empty(head: &dsp[e->shift_l].compr_list)) |
347 | ret = -EBUSY; |
348 | else |
349 | dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; |
350 | |
351 | mutex_unlock(lock: &dsp[e->shift_l].cs_dsp.pwr_lock); |
352 | |
353 | return ret; |
354 | } |
355 | EXPORT_SYMBOL_GPL(wm_adsp_fw_put); |
356 | |
357 | const struct soc_enum wm_adsp_fw_enum[] = { |
358 | SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), |
359 | SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), |
360 | SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), |
361 | SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), |
362 | SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), |
363 | SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), |
364 | SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), |
365 | }; |
366 | EXPORT_SYMBOL_GPL(wm_adsp_fw_enum); |
367 | |
368 | static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) |
369 | { |
370 | return container_of(ext, struct wm_coeff_ctl, bytes_ext); |
371 | } |
372 | |
373 | static int wm_coeff_info(struct snd_kcontrol *kctl, |
374 | struct snd_ctl_elem_info *uinfo) |
375 | { |
376 | struct soc_bytes_ext *bytes_ext = |
377 | (struct soc_bytes_ext *)kctl->private_value; |
378 | struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(ext: bytes_ext); |
379 | struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; |
380 | |
381 | switch (cs_ctl->type) { |
382 | case WMFW_CTL_TYPE_ACKED: |
383 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
384 | uinfo->value.integer.min = CS_DSP_ACKED_CTL_MIN_VALUE; |
385 | uinfo->value.integer.max = CS_DSP_ACKED_CTL_MAX_VALUE; |
386 | uinfo->value.integer.step = 1; |
387 | uinfo->count = 1; |
388 | break; |
389 | default: |
390 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; |
391 | uinfo->count = cs_ctl->len; |
392 | break; |
393 | } |
394 | |
395 | return 0; |
396 | } |
397 | |
398 | static int wm_coeff_put(struct snd_kcontrol *kctl, |
399 | struct snd_ctl_elem_value *ucontrol) |
400 | { |
401 | struct soc_bytes_ext *bytes_ext = |
402 | (struct soc_bytes_ext *)kctl->private_value; |
403 | struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(ext: bytes_ext); |
404 | struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; |
405 | char *p = ucontrol->value.bytes.data; |
406 | int ret = 0; |
407 | |
408 | mutex_lock(&cs_ctl->dsp->pwr_lock); |
409 | ret = cs_dsp_coeff_write_ctrl(ctl: cs_ctl, off: 0, buf: p, len: cs_ctl->len); |
410 | mutex_unlock(lock: &cs_ctl->dsp->pwr_lock); |
411 | |
412 | return ret; |
413 | } |
414 | |
415 | static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, |
416 | const unsigned int __user *bytes, unsigned int size) |
417 | { |
418 | struct soc_bytes_ext *bytes_ext = |
419 | (struct soc_bytes_ext *)kctl->private_value; |
420 | struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(ext: bytes_ext); |
421 | struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; |
422 | void *scratch; |
423 | int ret = 0; |
424 | |
425 | scratch = vmalloc(size); |
426 | if (!scratch) |
427 | return -ENOMEM; |
428 | |
429 | if (copy_from_user(to: scratch, from: bytes, n: size)) { |
430 | ret = -EFAULT; |
431 | } else { |
432 | mutex_lock(&cs_ctl->dsp->pwr_lock); |
433 | ret = cs_dsp_coeff_write_ctrl(ctl: cs_ctl, off: 0, buf: scratch, len: size); |
434 | mutex_unlock(lock: &cs_ctl->dsp->pwr_lock); |
435 | } |
436 | vfree(addr: scratch); |
437 | |
438 | return ret; |
439 | } |
440 | |
441 | static int wm_coeff_put_acked(struct snd_kcontrol *kctl, |
442 | struct snd_ctl_elem_value *ucontrol) |
443 | { |
444 | struct soc_bytes_ext *bytes_ext = |
445 | (struct soc_bytes_ext *)kctl->private_value; |
446 | struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(ext: bytes_ext); |
447 | struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; |
448 | unsigned int val = ucontrol->value.integer.value[0]; |
449 | int ret; |
450 | |
451 | if (val == 0) |
452 | return 0; /* 0 means no event */ |
453 | |
454 | mutex_lock(&cs_ctl->dsp->pwr_lock); |
455 | |
456 | if (cs_ctl->enabled) |
457 | ret = cs_dsp_coeff_write_acked_control(ctl: cs_ctl, event_id: val); |
458 | else |
459 | ret = -EPERM; |
460 | |
461 | mutex_unlock(lock: &cs_ctl->dsp->pwr_lock); |
462 | |
463 | if (ret < 0) |
464 | return ret; |
465 | |
466 | return 1; |
467 | } |
468 | |
469 | static int wm_coeff_get(struct snd_kcontrol *kctl, |
470 | struct snd_ctl_elem_value *ucontrol) |
471 | { |
472 | struct soc_bytes_ext *bytes_ext = |
473 | (struct soc_bytes_ext *)kctl->private_value; |
474 | struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(ext: bytes_ext); |
475 | struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; |
476 | char *p = ucontrol->value.bytes.data; |
477 | int ret; |
478 | |
479 | mutex_lock(&cs_ctl->dsp->pwr_lock); |
480 | ret = cs_dsp_coeff_read_ctrl(ctl: cs_ctl, off: 0, buf: p, len: cs_ctl->len); |
481 | mutex_unlock(lock: &cs_ctl->dsp->pwr_lock); |
482 | |
483 | return ret; |
484 | } |
485 | |
486 | static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, |
487 | unsigned int __user *bytes, unsigned int size) |
488 | { |
489 | struct soc_bytes_ext *bytes_ext = |
490 | (struct soc_bytes_ext *)kctl->private_value; |
491 | struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(ext: bytes_ext); |
492 | struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; |
493 | int ret = 0; |
494 | |
495 | mutex_lock(&cs_ctl->dsp->pwr_lock); |
496 | |
497 | ret = cs_dsp_coeff_read_ctrl(ctl: cs_ctl, off: 0, buf: cs_ctl->cache, len: size); |
498 | |
499 | if (!ret && copy_to_user(to: bytes, from: cs_ctl->cache, n: size)) |
500 | ret = -EFAULT; |
501 | |
502 | mutex_unlock(lock: &cs_ctl->dsp->pwr_lock); |
503 | |
504 | return ret; |
505 | } |
506 | |
507 | static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, |
508 | struct snd_ctl_elem_value *ucontrol) |
509 | { |
510 | /* |
511 | * Although it's not useful to read an acked control, we must satisfy |
512 | * user-side assumptions that all controls are readable and that a |
513 | * write of the same value should be filtered out (it's valid to send |
514 | * the same event number again to the firmware). We therefore return 0, |
515 | * meaning "no event" so valid event numbers will always be a change |
516 | */ |
517 | ucontrol->value.integer.value[0] = 0; |
518 | |
519 | return 0; |
520 | } |
521 | |
522 | static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) |
523 | { |
524 | unsigned int out, rd, wr, vol; |
525 | |
526 | if (len > ADSP_MAX_STD_CTRL_SIZE) { |
527 | rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; |
528 | wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; |
529 | vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; |
530 | |
531 | out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; |
532 | } else { |
533 | rd = SNDRV_CTL_ELEM_ACCESS_READ; |
534 | wr = SNDRV_CTL_ELEM_ACCESS_WRITE; |
535 | vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; |
536 | |
537 | out = 0; |
538 | } |
539 | |
540 | if (in) { |
541 | out |= rd; |
542 | if (in & WMFW_CTL_FLAG_WRITEABLE) |
543 | out |= wr; |
544 | if (in & WMFW_CTL_FLAG_VOLATILE) |
545 | out |= vol; |
546 | } else { |
547 | out |= rd | wr | vol; |
548 | } |
549 | |
550 | return out; |
551 | } |
552 | |
553 | static void wm_adsp_ctl_work(struct work_struct *work) |
554 | { |
555 | struct wm_coeff_ctl *ctl = container_of(work, |
556 | struct wm_coeff_ctl, |
557 | work); |
558 | struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; |
559 | struct wm_adsp *dsp = container_of(cs_ctl->dsp, |
560 | struct wm_adsp, |
561 | cs_dsp); |
562 | struct snd_kcontrol_new *kcontrol; |
563 | |
564 | kcontrol = kzalloc(size: sizeof(*kcontrol), GFP_KERNEL); |
565 | if (!kcontrol) |
566 | return; |
567 | |
568 | kcontrol->name = ctl->name; |
569 | kcontrol->info = wm_coeff_info; |
570 | kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; |
571 | kcontrol->tlv.c = snd_soc_bytes_tlv_callback; |
572 | kcontrol->private_value = (unsigned long)&ctl->bytes_ext; |
573 | kcontrol->access = wmfw_convert_flags(in: cs_ctl->flags, len: cs_ctl->len); |
574 | |
575 | switch (cs_ctl->type) { |
576 | case WMFW_CTL_TYPE_ACKED: |
577 | kcontrol->get = wm_coeff_get_acked; |
578 | kcontrol->put = wm_coeff_put_acked; |
579 | break; |
580 | default: |
581 | if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { |
582 | ctl->bytes_ext.max = cs_ctl->len; |
583 | ctl->bytes_ext.get = wm_coeff_tlv_get; |
584 | ctl->bytes_ext.put = wm_coeff_tlv_put; |
585 | } else { |
586 | kcontrol->get = wm_coeff_get; |
587 | kcontrol->put = wm_coeff_put; |
588 | } |
589 | break; |
590 | } |
591 | |
592 | snd_soc_add_component_controls(component: dsp->component, controls: kcontrol, num_controls: 1); |
593 | |
594 | kfree(objp: kcontrol); |
595 | } |
596 | |
597 | static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl) |
598 | { |
599 | struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp); |
600 | struct cs_dsp *cs_dsp = &dsp->cs_dsp; |
601 | struct wm_coeff_ctl *ctl; |
602 | char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; |
603 | const char *region_name; |
604 | int ret; |
605 | |
606 | if (cs_ctl->flags & WMFW_CTL_FLAG_SYS) |
607 | return 0; |
608 | |
609 | region_name = cs_dsp_mem_region_name(type: cs_ctl->alg_region.type); |
610 | if (!region_name) { |
611 | adsp_err(dsp, "Unknown region type: %d\n" , cs_ctl->alg_region.type); |
612 | return -EINVAL; |
613 | } |
614 | |
615 | switch (cs_dsp->fw_ver) { |
616 | case 0: |
617 | case 1: |
618 | ret = scnprintf(buf: name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, |
619 | fmt: "%s %s %x" , cs_dsp->name, region_name, |
620 | cs_ctl->alg_region.alg); |
621 | break; |
622 | case 2: |
623 | ret = scnprintf(buf: name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, |
624 | fmt: "%s%c %.12s %x" , cs_dsp->name, *region_name, |
625 | wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg); |
626 | break; |
627 | default: |
628 | ret = scnprintf(buf: name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, |
629 | fmt: "%s %.12s %x" , cs_dsp->name, |
630 | wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg); |
631 | break; |
632 | } |
633 | |
634 | if (cs_ctl->subname) { |
635 | int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; |
636 | int skip = 0; |
637 | |
638 | if (dsp->component->name_prefix) |
639 | avail -= strlen(dsp->component->name_prefix) + 1; |
640 | |
641 | /* Truncate the subname from the start if it is too long */ |
642 | if (cs_ctl->subname_len > avail) |
643 | skip = cs_ctl->subname_len - avail; |
644 | |
645 | snprintf(buf: name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, |
646 | fmt: " %.*s" , cs_ctl->subname_len - skip, cs_ctl->subname + skip); |
647 | } |
648 | |
649 | ctl = kzalloc(size: sizeof(*ctl), GFP_KERNEL); |
650 | if (!ctl) |
651 | return -ENOMEM; |
652 | ctl->cs_ctl = cs_ctl; |
653 | |
654 | ctl->name = kmemdup(p: name, strlen(name) + 1, GFP_KERNEL); |
655 | if (!ctl->name) { |
656 | ret = -ENOMEM; |
657 | goto err_ctl; |
658 | } |
659 | |
660 | cs_ctl->priv = ctl; |
661 | |
662 | INIT_WORK(&ctl->work, wm_adsp_ctl_work); |
663 | schedule_work(work: &ctl->work); |
664 | |
665 | return 0; |
666 | |
667 | err_ctl: |
668 | kfree(objp: ctl); |
669 | |
670 | return ret; |
671 | } |
672 | |
673 | static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl) |
674 | { |
675 | struct wm_coeff_ctl *ctl = cs_ctl->priv; |
676 | |
677 | cancel_work_sync(work: &ctl->work); |
678 | |
679 | kfree(objp: ctl->name); |
680 | kfree(objp: ctl); |
681 | } |
682 | |
683 | int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, |
684 | unsigned int alg, void *buf, size_t len) |
685 | { |
686 | struct cs_dsp_coeff_ctl *cs_ctl; |
687 | struct wm_coeff_ctl *ctl; |
688 | int ret; |
689 | |
690 | mutex_lock(&dsp->cs_dsp.pwr_lock); |
691 | cs_ctl = cs_dsp_get_ctl(dsp: &dsp->cs_dsp, name, type, alg); |
692 | ret = cs_dsp_coeff_write_ctrl(ctl: cs_ctl, off: 0, buf, len); |
693 | mutex_unlock(lock: &dsp->cs_dsp.pwr_lock); |
694 | |
695 | if (ret < 0) |
696 | return ret; |
697 | |
698 | if (ret == 0 || (cs_ctl->flags & WMFW_CTL_FLAG_SYS)) |
699 | return 0; |
700 | |
701 | ctl = cs_ctl->priv; |
702 | |
703 | return snd_soc_component_notify_control(component: dsp->component, ctl: ctl->name); |
704 | } |
705 | EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); |
706 | |
707 | int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, |
708 | unsigned int alg, void *buf, size_t len) |
709 | { |
710 | int ret; |
711 | |
712 | mutex_lock(&dsp->cs_dsp.pwr_lock); |
713 | ret = cs_dsp_coeff_read_ctrl(ctl: cs_dsp_get_ctl(dsp: &dsp->cs_dsp, name, type, alg), |
714 | off: 0, buf, len); |
715 | mutex_unlock(lock: &dsp->cs_dsp.pwr_lock); |
716 | |
717 | return ret; |
718 | } |
719 | EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); |
720 | |
721 | static void wm_adsp_release_firmware_files(struct wm_adsp *dsp, |
722 | const struct firmware *wmfw_firmware, |
723 | char *wmfw_filename, |
724 | const struct firmware *coeff_firmware, |
725 | char *coeff_filename) |
726 | { |
727 | if (wmfw_firmware) |
728 | release_firmware(fw: wmfw_firmware); |
729 | kfree(objp: wmfw_filename); |
730 | |
731 | if (coeff_firmware) |
732 | release_firmware(fw: coeff_firmware); |
733 | kfree(objp: coeff_filename); |
734 | } |
735 | |
736 | static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, |
737 | const struct firmware **firmware, char **filename, |
738 | const char *dir, const char *system_name, |
739 | const char *asoc_component_prefix, |
740 | const char *filetype) |
741 | { |
742 | struct cs_dsp *cs_dsp = &dsp->cs_dsp; |
743 | const char *fwf; |
744 | char *s, c; |
745 | int ret = 0; |
746 | |
747 | if (dsp->fwf_name) |
748 | fwf = dsp->fwf_name; |
749 | else |
750 | fwf = dsp->cs_dsp.name; |
751 | |
752 | if (system_name && asoc_component_prefix) |
753 | *filename = kasprintf(GFP_KERNEL, fmt: "%s%s-%s-%s-%s-%s.%s" , dir, dsp->part, |
754 | fwf, wm_adsp_fw[dsp->fw].file, system_name, |
755 | asoc_component_prefix, filetype); |
756 | else if (system_name) |
757 | *filename = kasprintf(GFP_KERNEL, fmt: "%s%s-%s-%s-%s.%s" , dir, dsp->part, |
758 | fwf, wm_adsp_fw[dsp->fw].file, system_name, |
759 | filetype); |
760 | else |
761 | *filename = kasprintf(GFP_KERNEL, fmt: "%s%s-%s-%s.%s" , dir, dsp->part, fwf, |
762 | wm_adsp_fw[dsp->fw].file, filetype); |
763 | |
764 | if (*filename == NULL) |
765 | return -ENOMEM; |
766 | |
767 | /* |
768 | * Make sure that filename is lower-case and any non alpha-numeric |
769 | * characters except full stop and forward slash are replaced with |
770 | * hyphens. |
771 | */ |
772 | s = *filename; |
773 | while (*s) { |
774 | c = *s; |
775 | if (isalnum(c)) |
776 | *s = tolower(c); |
777 | else if ((c != '.') && (c != '/')) |
778 | *s = '-'; |
779 | s++; |
780 | } |
781 | |
782 | ret = firmware_request_nowarn(fw: firmware, name: *filename, device: cs_dsp->dev); |
783 | if (ret != 0) { |
784 | adsp_dbg(dsp, "Failed to request '%s'\n" , *filename); |
785 | kfree(objp: *filename); |
786 | *filename = NULL; |
787 | } else { |
788 | adsp_dbg(dsp, "Found '%s'\n" , *filename); |
789 | } |
790 | |
791 | return ret; |
792 | } |
793 | |
794 | static const char *cirrus_dir = "cirrus/" ; |
795 | static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, |
796 | const struct firmware **wmfw_firmware, |
797 | char **wmfw_filename, |
798 | const struct firmware **coeff_firmware, |
799 | char **coeff_filename) |
800 | { |
801 | const char *system_name = dsp->system_name; |
802 | const char *asoc_component_prefix = dsp->component->name_prefix; |
803 | int ret = 0; |
804 | |
805 | if (system_name && asoc_component_prefix) { |
806 | if (!wm_adsp_request_firmware_file(dsp, firmware: wmfw_firmware, filename: wmfw_filename, |
807 | dir: cirrus_dir, system_name, |
808 | asoc_component_prefix, filetype: "wmfw" )) { |
809 | wm_adsp_request_firmware_file(dsp, firmware: coeff_firmware, filename: coeff_filename, |
810 | dir: cirrus_dir, system_name, |
811 | asoc_component_prefix, filetype: "bin" ); |
812 | return 0; |
813 | } |
814 | } |
815 | |
816 | if (system_name) { |
817 | if (!wm_adsp_request_firmware_file(dsp, firmware: wmfw_firmware, filename: wmfw_filename, |
818 | dir: cirrus_dir, system_name, |
819 | NULL, filetype: "wmfw" )) { |
820 | if (asoc_component_prefix) |
821 | wm_adsp_request_firmware_file(dsp, firmware: coeff_firmware, filename: coeff_filename, |
822 | dir: cirrus_dir, system_name, |
823 | asoc_component_prefix, filetype: "bin" ); |
824 | |
825 | if (!*coeff_firmware) |
826 | wm_adsp_request_firmware_file(dsp, firmware: coeff_firmware, filename: coeff_filename, |
827 | dir: cirrus_dir, system_name, |
828 | NULL, filetype: "bin" ); |
829 | return 0; |
830 | } |
831 | } |
832 | |
833 | /* Check system-specific bin without wmfw before falling back to generic */ |
834 | if (dsp->wmfw_optional && system_name) { |
835 | if (asoc_component_prefix) |
836 | wm_adsp_request_firmware_file(dsp, firmware: coeff_firmware, filename: coeff_filename, |
837 | dir: cirrus_dir, system_name, |
838 | asoc_component_prefix, filetype: "bin" ); |
839 | |
840 | if (!*coeff_firmware) |
841 | wm_adsp_request_firmware_file(dsp, firmware: coeff_firmware, filename: coeff_filename, |
842 | dir: cirrus_dir, system_name, |
843 | NULL, filetype: "bin" ); |
844 | |
845 | if (*coeff_firmware) |
846 | return 0; |
847 | } |
848 | |
849 | /* Check legacy location */ |
850 | if (!wm_adsp_request_firmware_file(dsp, firmware: wmfw_firmware, filename: wmfw_filename, |
851 | dir: "" , NULL, NULL, filetype: "wmfw" )) { |
852 | wm_adsp_request_firmware_file(dsp, firmware: coeff_firmware, filename: coeff_filename, |
853 | dir: "" , NULL, NULL, filetype: "bin" ); |
854 | return 0; |
855 | } |
856 | |
857 | /* Fall back to generic wmfw and optional matching bin */ |
858 | ret = wm_adsp_request_firmware_file(dsp, firmware: wmfw_firmware, filename: wmfw_filename, |
859 | dir: cirrus_dir, NULL, NULL, filetype: "wmfw" ); |
860 | if (!ret || dsp->wmfw_optional) { |
861 | wm_adsp_request_firmware_file(dsp, firmware: coeff_firmware, filename: coeff_filename, |
862 | dir: cirrus_dir, NULL, NULL, filetype: "bin" ); |
863 | return 0; |
864 | } |
865 | |
866 | adsp_err(dsp, "Failed to request firmware <%s>%s-%s-%s<-%s<%s>>.wmfw\n" , |
867 | cirrus_dir, dsp->part, |
868 | dsp->fwf_name ? dsp->fwf_name : dsp->cs_dsp.name, |
869 | wm_adsp_fw[dsp->fw].file, system_name, asoc_component_prefix); |
870 | |
871 | return -ENOENT; |
872 | } |
873 | |
874 | static int wm_adsp_common_init(struct wm_adsp *dsp) |
875 | { |
876 | INIT_LIST_HEAD(list: &dsp->compr_list); |
877 | INIT_LIST_HEAD(list: &dsp->buffer_list); |
878 | |
879 | return 0; |
880 | } |
881 | |
882 | int wm_adsp1_init(struct wm_adsp *dsp) |
883 | { |
884 | int ret; |
885 | |
886 | dsp->cs_dsp.client_ops = &wm_adsp1_client_ops; |
887 | |
888 | ret = cs_dsp_adsp1_init(dsp: &dsp->cs_dsp); |
889 | if (ret) |
890 | return ret; |
891 | |
892 | return wm_adsp_common_init(dsp); |
893 | } |
894 | EXPORT_SYMBOL_GPL(wm_adsp1_init); |
895 | |
896 | int wm_adsp1_event(struct snd_soc_dapm_widget *w, |
897 | struct snd_kcontrol *kcontrol, |
898 | int event) |
899 | { |
900 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
901 | struct wm_adsp *dsps = snd_soc_component_get_drvdata(c: component); |
902 | struct wm_adsp *dsp = &dsps[w->shift]; |
903 | int ret = 0; |
904 | char *wmfw_filename = NULL; |
905 | const struct firmware *wmfw_firmware = NULL; |
906 | char *coeff_filename = NULL; |
907 | const struct firmware *coeff_firmware = NULL; |
908 | |
909 | dsp->component = component; |
910 | |
911 | switch (event) { |
912 | case SND_SOC_DAPM_POST_PMU: |
913 | ret = wm_adsp_request_firmware_files(dsp, |
914 | wmfw_firmware: &wmfw_firmware, wmfw_filename: &wmfw_filename, |
915 | coeff_firmware: &coeff_firmware, coeff_filename: &coeff_filename); |
916 | if (ret) |
917 | break; |
918 | |
919 | ret = cs_dsp_adsp1_power_up(dsp: &dsp->cs_dsp, |
920 | wmfw_firmware, wmfw_filename, |
921 | coeff_firmware, coeff_filename, |
922 | fw_name: wm_adsp_fw_text[dsp->fw]); |
923 | |
924 | wm_adsp_release_firmware_files(dsp, |
925 | wmfw_firmware, wmfw_filename, |
926 | coeff_firmware, coeff_filename); |
927 | break; |
928 | case SND_SOC_DAPM_PRE_PMD: |
929 | cs_dsp_adsp1_power_down(dsp: &dsp->cs_dsp); |
930 | break; |
931 | default: |
932 | break; |
933 | } |
934 | |
935 | return ret; |
936 | } |
937 | EXPORT_SYMBOL_GPL(wm_adsp1_event); |
938 | |
939 | int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq) |
940 | { |
941 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
942 | struct wm_adsp *dsps = snd_soc_component_get_drvdata(c: component); |
943 | struct wm_adsp *dsp = &dsps[w->shift]; |
944 | |
945 | return cs_dsp_set_dspclk(dsp: &dsp->cs_dsp, freq); |
946 | } |
947 | EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk); |
948 | |
949 | int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, |
950 | struct snd_ctl_elem_value *ucontrol) |
951 | { |
952 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
953 | struct wm_adsp *dsps = snd_soc_component_get_drvdata(c: component); |
954 | struct soc_mixer_control *mc = |
955 | (struct soc_mixer_control *)kcontrol->private_value; |
956 | struct wm_adsp *dsp = &dsps[mc->shift - 1]; |
957 | |
958 | ucontrol->value.integer.value[0] = dsp->preloaded; |
959 | |
960 | return 0; |
961 | } |
962 | EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get); |
963 | |
964 | int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, |
965 | struct snd_ctl_elem_value *ucontrol) |
966 | { |
967 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
968 | struct wm_adsp *dsps = snd_soc_component_get_drvdata(c: component); |
969 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
970 | struct soc_mixer_control *mc = |
971 | (struct soc_mixer_control *)kcontrol->private_value; |
972 | struct wm_adsp *dsp = &dsps[mc->shift - 1]; |
973 | char preload[32]; |
974 | |
975 | if (dsp->preloaded == ucontrol->value.integer.value[0]) |
976 | return 0; |
977 | |
978 | snprintf(buf: preload, ARRAY_SIZE(preload), fmt: "%s Preload" , dsp->cs_dsp.name); |
979 | |
980 | if (ucontrol->value.integer.value[0] || dsp->toggle_preload) |
981 | snd_soc_component_force_enable_pin(component, pin: preload); |
982 | else |
983 | snd_soc_component_disable_pin(component, pin: preload); |
984 | |
985 | snd_soc_dapm_sync(dapm); |
986 | |
987 | flush_work(work: &dsp->boot_work); |
988 | |
989 | dsp->preloaded = ucontrol->value.integer.value[0]; |
990 | |
991 | if (dsp->toggle_preload) { |
992 | snd_soc_component_disable_pin(component, pin: preload); |
993 | snd_soc_dapm_sync(dapm); |
994 | } |
995 | |
996 | return 1; |
997 | } |
998 | EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); |
999 | |
1000 | int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware) |
1001 | { |
1002 | int ret = 0; |
1003 | char *wmfw_filename = NULL; |
1004 | const struct firmware *wmfw_firmware = NULL; |
1005 | char *coeff_filename = NULL; |
1006 | const struct firmware *coeff_firmware = NULL; |
1007 | |
1008 | if (load_firmware) { |
1009 | ret = wm_adsp_request_firmware_files(dsp, |
1010 | wmfw_firmware: &wmfw_firmware, wmfw_filename: &wmfw_filename, |
1011 | coeff_firmware: &coeff_firmware, coeff_filename: &coeff_filename); |
1012 | if (ret) |
1013 | return ret; |
1014 | } |
1015 | |
1016 | ret = cs_dsp_power_up(dsp: &dsp->cs_dsp, |
1017 | wmfw_firmware, wmfw_filename, |
1018 | coeff_firmware, coeff_filename, |
1019 | fw_name: wm_adsp_fw_text[dsp->fw]); |
1020 | |
1021 | wm_adsp_release_firmware_files(dsp, |
1022 | wmfw_firmware, wmfw_filename, |
1023 | coeff_firmware, coeff_filename); |
1024 | |
1025 | return ret; |
1026 | } |
1027 | EXPORT_SYMBOL_GPL(wm_adsp_power_up); |
1028 | |
1029 | void wm_adsp_power_down(struct wm_adsp *dsp) |
1030 | { |
1031 | cs_dsp_power_down(dsp: &dsp->cs_dsp); |
1032 | } |
1033 | EXPORT_SYMBOL_GPL(wm_adsp_power_down); |
1034 | |
1035 | static void wm_adsp_boot_work(struct work_struct *work) |
1036 | { |
1037 | struct wm_adsp *dsp = container_of(work, |
1038 | struct wm_adsp, |
1039 | boot_work); |
1040 | |
1041 | wm_adsp_power_up(dsp, true); |
1042 | } |
1043 | |
1044 | int wm_adsp_early_event(struct snd_soc_dapm_widget *w, |
1045 | struct snd_kcontrol *kcontrol, int event) |
1046 | { |
1047 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
1048 | struct wm_adsp *dsps = snd_soc_component_get_drvdata(c: component); |
1049 | struct wm_adsp *dsp = &dsps[w->shift]; |
1050 | |
1051 | switch (event) { |
1052 | case SND_SOC_DAPM_PRE_PMU: |
1053 | queue_work(wq: system_unbound_wq, work: &dsp->boot_work); |
1054 | break; |
1055 | case SND_SOC_DAPM_PRE_PMD: |
1056 | wm_adsp_power_down(dsp); |
1057 | break; |
1058 | default: |
1059 | break; |
1060 | } |
1061 | |
1062 | return 0; |
1063 | } |
1064 | EXPORT_SYMBOL_GPL(wm_adsp_early_event); |
1065 | |
1066 | static int wm_adsp_pre_run(struct cs_dsp *cs_dsp) |
1067 | { |
1068 | struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); |
1069 | |
1070 | if (!dsp->pre_run) |
1071 | return 0; |
1072 | |
1073 | return (*dsp->pre_run)(dsp); |
1074 | } |
1075 | |
1076 | static int wm_adsp_event_post_run(struct cs_dsp *cs_dsp) |
1077 | { |
1078 | struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); |
1079 | |
1080 | if (wm_adsp_fw[dsp->fw].num_caps != 0) |
1081 | return wm_adsp_buffer_init(dsp); |
1082 | |
1083 | return 0; |
1084 | } |
1085 | |
1086 | static void wm_adsp_event_post_stop(struct cs_dsp *cs_dsp) |
1087 | { |
1088 | struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); |
1089 | |
1090 | if (wm_adsp_fw[dsp->fw].num_caps != 0) |
1091 | wm_adsp_buffer_free(dsp); |
1092 | |
1093 | dsp->fatal_error = false; |
1094 | } |
1095 | |
1096 | int wm_adsp_run(struct wm_adsp *dsp) |
1097 | { |
1098 | flush_work(work: &dsp->boot_work); |
1099 | |
1100 | return cs_dsp_run(dsp: &dsp->cs_dsp); |
1101 | } |
1102 | EXPORT_SYMBOL_GPL(wm_adsp_run); |
1103 | |
1104 | void wm_adsp_stop(struct wm_adsp *dsp) |
1105 | { |
1106 | cs_dsp_stop(dsp: &dsp->cs_dsp); |
1107 | } |
1108 | EXPORT_SYMBOL_GPL(wm_adsp_stop); |
1109 | |
1110 | int wm_adsp_event(struct snd_soc_dapm_widget *w, |
1111 | struct snd_kcontrol *kcontrol, int event) |
1112 | { |
1113 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
1114 | struct wm_adsp *dsps = snd_soc_component_get_drvdata(c: component); |
1115 | struct wm_adsp *dsp = &dsps[w->shift]; |
1116 | |
1117 | switch (event) { |
1118 | case SND_SOC_DAPM_POST_PMU: |
1119 | return wm_adsp_run(dsp); |
1120 | case SND_SOC_DAPM_PRE_PMD: |
1121 | wm_adsp_stop(dsp); |
1122 | return 0; |
1123 | default: |
1124 | return 0; |
1125 | } |
1126 | } |
1127 | EXPORT_SYMBOL_GPL(wm_adsp_event); |
1128 | |
1129 | int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component) |
1130 | { |
1131 | char preload[32]; |
1132 | |
1133 | if (!dsp->cs_dsp.no_core_startstop) { |
1134 | snprintf(buf: preload, ARRAY_SIZE(preload), fmt: "%s Preload" , dsp->cs_dsp.name); |
1135 | snd_soc_component_disable_pin(component, pin: preload); |
1136 | } |
1137 | |
1138 | cs_dsp_init_debugfs(dsp: &dsp->cs_dsp, debugfs_root: component->debugfs_root); |
1139 | |
1140 | dsp->component = component; |
1141 | |
1142 | return 0; |
1143 | } |
1144 | EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); |
1145 | |
1146 | int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component) |
1147 | { |
1148 | cs_dsp_cleanup_debugfs(dsp: &dsp->cs_dsp); |
1149 | |
1150 | return 0; |
1151 | } |
1152 | EXPORT_SYMBOL_GPL(wm_adsp2_component_remove); |
1153 | |
1154 | int wm_adsp2_init(struct wm_adsp *dsp) |
1155 | { |
1156 | int ret; |
1157 | |
1158 | INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); |
1159 | |
1160 | dsp->sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr); |
1161 | dsp->cs_dsp.client_ops = &wm_adsp2_client_ops; |
1162 | |
1163 | ret = cs_dsp_adsp2_init(dsp: &dsp->cs_dsp); |
1164 | if (ret) |
1165 | return ret; |
1166 | |
1167 | return wm_adsp_common_init(dsp); |
1168 | } |
1169 | EXPORT_SYMBOL_GPL(wm_adsp2_init); |
1170 | |
1171 | int wm_halo_init(struct wm_adsp *dsp) |
1172 | { |
1173 | int ret; |
1174 | |
1175 | INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); |
1176 | |
1177 | dsp->sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr); |
1178 | dsp->cs_dsp.client_ops = &wm_adsp2_client_ops; |
1179 | |
1180 | ret = cs_dsp_halo_init(dsp: &dsp->cs_dsp); |
1181 | if (ret) |
1182 | return ret; |
1183 | |
1184 | return wm_adsp_common_init(dsp); |
1185 | } |
1186 | EXPORT_SYMBOL_GPL(wm_halo_init); |
1187 | |
1188 | void wm_adsp2_remove(struct wm_adsp *dsp) |
1189 | { |
1190 | cs_dsp_remove(dsp: &dsp->cs_dsp); |
1191 | } |
1192 | EXPORT_SYMBOL_GPL(wm_adsp2_remove); |
1193 | |
1194 | static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) |
1195 | { |
1196 | return compr->buf != NULL; |
1197 | } |
1198 | |
1199 | static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) |
1200 | { |
1201 | struct wm_adsp_compr_buf *buf = NULL, *tmp; |
1202 | |
1203 | if (compr->dsp->fatal_error) |
1204 | return -EINVAL; |
1205 | |
1206 | list_for_each_entry(tmp, &compr->dsp->buffer_list, list) { |
1207 | if (!tmp->name || !strcmp(compr->name, tmp->name)) { |
1208 | buf = tmp; |
1209 | break; |
1210 | } |
1211 | } |
1212 | |
1213 | if (!buf) |
1214 | return -EINVAL; |
1215 | |
1216 | compr->buf = buf; |
1217 | buf->compr = compr; |
1218 | |
1219 | return 0; |
1220 | } |
1221 | |
1222 | static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) |
1223 | { |
1224 | if (!compr) |
1225 | return; |
1226 | |
1227 | /* Wake the poll so it can see buffer is no longer attached */ |
1228 | if (compr->stream) |
1229 | snd_compr_fragment_elapsed(stream: compr->stream); |
1230 | |
1231 | if (wm_adsp_compr_attached(compr)) { |
1232 | compr->buf->compr = NULL; |
1233 | compr->buf = NULL; |
1234 | } |
1235 | } |
1236 | |
1237 | int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) |
1238 | { |
1239 | struct wm_adsp_compr *compr, *tmp; |
1240 | struct snd_soc_pcm_runtime *rtd = stream->private_data; |
1241 | int ret = 0; |
1242 | |
1243 | mutex_lock(&dsp->cs_dsp.pwr_lock); |
1244 | |
1245 | if (wm_adsp_fw[dsp->fw].num_caps == 0) { |
1246 | adsp_err(dsp, "%s: Firmware does not support compressed API\n" , |
1247 | snd_soc_rtd_to_codec(rtd, 0)->name); |
1248 | ret = -ENXIO; |
1249 | goto out; |
1250 | } |
1251 | |
1252 | if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { |
1253 | adsp_err(dsp, "%s: Firmware does not support stream direction\n" , |
1254 | snd_soc_rtd_to_codec(rtd, 0)->name); |
1255 | ret = -EINVAL; |
1256 | goto out; |
1257 | } |
1258 | |
1259 | list_for_each_entry(tmp, &dsp->compr_list, list) { |
1260 | if (!strcmp(tmp->name, snd_soc_rtd_to_codec(rtd, 0)->name)) { |
1261 | adsp_err(dsp, "%s: Only a single stream supported per dai\n" , |
1262 | snd_soc_rtd_to_codec(rtd, 0)->name); |
1263 | ret = -EBUSY; |
1264 | goto out; |
1265 | } |
1266 | } |
1267 | |
1268 | compr = kzalloc(size: sizeof(*compr), GFP_KERNEL); |
1269 | if (!compr) { |
1270 | ret = -ENOMEM; |
1271 | goto out; |
1272 | } |
1273 | |
1274 | compr->dsp = dsp; |
1275 | compr->stream = stream; |
1276 | compr->name = snd_soc_rtd_to_codec(rtd, 0)->name; |
1277 | |
1278 | list_add_tail(new: &compr->list, head: &dsp->compr_list); |
1279 | |
1280 | stream->runtime->private_data = compr; |
1281 | |
1282 | out: |
1283 | mutex_unlock(lock: &dsp->cs_dsp.pwr_lock); |
1284 | |
1285 | return ret; |
1286 | } |
1287 | EXPORT_SYMBOL_GPL(wm_adsp_compr_open); |
1288 | |
1289 | int wm_adsp_compr_free(struct snd_soc_component *component, |
1290 | struct snd_compr_stream *stream) |
1291 | { |
1292 | struct wm_adsp_compr *compr = stream->runtime->private_data; |
1293 | struct wm_adsp *dsp = compr->dsp; |
1294 | |
1295 | mutex_lock(&dsp->cs_dsp.pwr_lock); |
1296 | |
1297 | wm_adsp_compr_detach(compr); |
1298 | list_del(entry: &compr->list); |
1299 | |
1300 | kfree(objp: compr->raw_buf); |
1301 | kfree(objp: compr); |
1302 | |
1303 | mutex_unlock(lock: &dsp->cs_dsp.pwr_lock); |
1304 | |
1305 | return 0; |
1306 | } |
1307 | EXPORT_SYMBOL_GPL(wm_adsp_compr_free); |
1308 | |
1309 | static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, |
1310 | struct snd_compr_params *params) |
1311 | { |
1312 | struct wm_adsp_compr *compr = stream->runtime->private_data; |
1313 | struct wm_adsp *dsp = compr->dsp; |
1314 | const struct wm_adsp_fw_caps *caps; |
1315 | const struct snd_codec_desc *desc; |
1316 | int i, j; |
1317 | |
1318 | if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || |
1319 | params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || |
1320 | params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || |
1321 | params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || |
1322 | params->buffer.fragment_size % CS_DSP_DATA_WORD_SIZE) { |
1323 | compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n" , |
1324 | params->buffer.fragment_size, |
1325 | params->buffer.fragments); |
1326 | |
1327 | return -EINVAL; |
1328 | } |
1329 | |
1330 | for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { |
1331 | caps = &wm_adsp_fw[dsp->fw].caps[i]; |
1332 | desc = &caps->desc; |
1333 | |
1334 | if (caps->id != params->codec.id) |
1335 | continue; |
1336 | |
1337 | if (stream->direction == SND_COMPRESS_PLAYBACK) { |
1338 | if (desc->max_ch < params->codec.ch_out) |
1339 | continue; |
1340 | } else { |
1341 | if (desc->max_ch < params->codec.ch_in) |
1342 | continue; |
1343 | } |
1344 | |
1345 | if (!(desc->formats & (1 << params->codec.format))) |
1346 | continue; |
1347 | |
1348 | for (j = 0; j < desc->num_sample_rates; ++j) |
1349 | if (desc->sample_rates[j] == params->codec.sample_rate) |
1350 | return 0; |
1351 | } |
1352 | |
1353 | compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n" , |
1354 | params->codec.id, params->codec.ch_in, params->codec.ch_out, |
1355 | params->codec.sample_rate, params->codec.format); |
1356 | return -EINVAL; |
1357 | } |
1358 | |
1359 | static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) |
1360 | { |
1361 | return compr->size.fragment_size / CS_DSP_DATA_WORD_SIZE; |
1362 | } |
1363 | |
1364 | int wm_adsp_compr_set_params(struct snd_soc_component *component, |
1365 | struct snd_compr_stream *stream, |
1366 | struct snd_compr_params *params) |
1367 | { |
1368 | struct wm_adsp_compr *compr = stream->runtime->private_data; |
1369 | unsigned int size; |
1370 | int ret; |
1371 | |
1372 | ret = wm_adsp_compr_check_params(stream, params); |
1373 | if (ret) |
1374 | return ret; |
1375 | |
1376 | compr->size = params->buffer; |
1377 | |
1378 | compr_dbg(compr, "fragment_size=%d fragments=%d\n" , |
1379 | compr->size.fragment_size, compr->size.fragments); |
1380 | |
1381 | size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); |
1382 | compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); |
1383 | if (!compr->raw_buf) |
1384 | return -ENOMEM; |
1385 | |
1386 | compr->sample_rate = params->codec.sample_rate; |
1387 | |
1388 | return 0; |
1389 | } |
1390 | EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); |
1391 | |
1392 | int wm_adsp_compr_get_caps(struct snd_soc_component *component, |
1393 | struct snd_compr_stream *stream, |
1394 | struct snd_compr_caps *caps) |
1395 | { |
1396 | struct wm_adsp_compr *compr = stream->runtime->private_data; |
1397 | int fw = compr->dsp->fw; |
1398 | int i; |
1399 | |
1400 | if (wm_adsp_fw[fw].caps) { |
1401 | for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) |
1402 | caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; |
1403 | |
1404 | caps->num_codecs = i; |
1405 | caps->direction = wm_adsp_fw[fw].compr_direction; |
1406 | |
1407 | caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; |
1408 | caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; |
1409 | caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; |
1410 | caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; |
1411 | } |
1412 | |
1413 | return 0; |
1414 | } |
1415 | EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); |
1416 | |
1417 | static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, |
1418 | unsigned int field_offset, u32 *data) |
1419 | { |
1420 | return cs_dsp_read_data_word(dsp: &buf->dsp->cs_dsp, mem_type: buf->host_buf_mem_type, |
1421 | mem_addr: buf->host_buf_ptr + field_offset, data); |
1422 | } |
1423 | |
1424 | static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, |
1425 | unsigned int field_offset, u32 data) |
1426 | { |
1427 | return cs_dsp_write_data_word(dsp: &buf->dsp->cs_dsp, mem_type: buf->host_buf_mem_type, |
1428 | mem_addr: buf->host_buf_ptr + field_offset, |
1429 | data); |
1430 | } |
1431 | |
1432 | static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) |
1433 | { |
1434 | const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; |
1435 | struct wm_adsp_buffer_region *region; |
1436 | u32 offset = 0; |
1437 | int i, ret; |
1438 | |
1439 | buf->regions = kcalloc(n: caps->num_regions, size: sizeof(*buf->regions), |
1440 | GFP_KERNEL); |
1441 | if (!buf->regions) |
1442 | return -ENOMEM; |
1443 | |
1444 | for (i = 0; i < caps->num_regions; ++i) { |
1445 | region = &buf->regions[i]; |
1446 | |
1447 | region->offset = offset; |
1448 | region->mem_type = caps->region_defs[i].mem_type; |
1449 | |
1450 | ret = wm_adsp_buffer_read(buf, field_offset: caps->region_defs[i].base_offset, |
1451 | data: ®ion->base_addr); |
1452 | if (ret < 0) |
1453 | goto err; |
1454 | |
1455 | ret = wm_adsp_buffer_read(buf, field_offset: caps->region_defs[i].size_offset, |
1456 | data: &offset); |
1457 | if (ret < 0) |
1458 | goto err; |
1459 | |
1460 | region->cumulative_size = offset; |
1461 | |
1462 | compr_dbg(buf, |
1463 | "region=%d type=%d base=%08x off=%08x size=%08x\n" , |
1464 | i, region->mem_type, region->base_addr, |
1465 | region->offset, region->cumulative_size); |
1466 | } |
1467 | |
1468 | return 0; |
1469 | |
1470 | err: |
1471 | kfree(objp: buf->regions); |
1472 | return ret; |
1473 | } |
1474 | |
1475 | static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf) |
1476 | { |
1477 | buf->irq_count = 0xFFFFFFFF; |
1478 | buf->read_index = -1; |
1479 | buf->avail = 0; |
1480 | } |
1481 | |
1482 | static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp) |
1483 | { |
1484 | struct wm_adsp_compr_buf *buf; |
1485 | |
1486 | buf = kzalloc(size: sizeof(*buf), GFP_KERNEL); |
1487 | if (!buf) |
1488 | return NULL; |
1489 | |
1490 | buf->dsp = dsp; |
1491 | |
1492 | wm_adsp_buffer_clear(buf); |
1493 | |
1494 | return buf; |
1495 | } |
1496 | |
1497 | static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) |
1498 | { |
1499 | struct cs_dsp_alg_region *alg_region; |
1500 | struct wm_adsp_compr_buf *buf; |
1501 | u32 xmalg, addr, magic; |
1502 | int i, ret; |
1503 | |
1504 | alg_region = cs_dsp_find_alg_region(dsp: &dsp->cs_dsp, WMFW_ADSP2_XM, id: dsp->cs_dsp.fw_id); |
1505 | if (!alg_region) { |
1506 | adsp_err(dsp, "No algorithm region found\n" ); |
1507 | return -EINVAL; |
1508 | } |
1509 | |
1510 | xmalg = dsp->sys_config_size / sizeof(__be32); |
1511 | |
1512 | addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); |
1513 | ret = cs_dsp_read_data_word(dsp: &dsp->cs_dsp, WMFW_ADSP2_XM, mem_addr: addr, data: &magic); |
1514 | if (ret < 0) |
1515 | return ret; |
1516 | |
1517 | if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) |
1518 | return -ENODEV; |
1519 | |
1520 | buf = wm_adsp_buffer_alloc(dsp); |
1521 | if (!buf) |
1522 | return -ENOMEM; |
1523 | |
1524 | addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); |
1525 | for (i = 0; i < 5; ++i) { |
1526 | ret = cs_dsp_read_data_word(dsp: &dsp->cs_dsp, WMFW_ADSP2_XM, mem_addr: addr, |
1527 | data: &buf->host_buf_ptr); |
1528 | if (ret < 0) |
1529 | goto err; |
1530 | |
1531 | if (buf->host_buf_ptr) |
1532 | break; |
1533 | |
1534 | usleep_range(min: 1000, max: 2000); |
1535 | } |
1536 | |
1537 | if (!buf->host_buf_ptr) { |
1538 | ret = -EIO; |
1539 | goto err; |
1540 | } |
1541 | |
1542 | buf->host_buf_mem_type = WMFW_ADSP2_XM; |
1543 | |
1544 | ret = wm_adsp_buffer_populate(buf); |
1545 | if (ret < 0) |
1546 | goto err; |
1547 | |
1548 | list_add_tail(new: &buf->list, head: &dsp->buffer_list); |
1549 | |
1550 | compr_dbg(buf, "legacy host_buf_ptr=%x\n" , buf->host_buf_ptr); |
1551 | |
1552 | return 0; |
1553 | |
1554 | err: |
1555 | kfree(objp: buf); |
1556 | |
1557 | return ret; |
1558 | } |
1559 | |
1560 | static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl) |
1561 | { |
1562 | struct wm_adsp_host_buf_coeff_v1 coeff_v1; |
1563 | struct wm_adsp_compr_buf *buf; |
1564 | struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp); |
1565 | unsigned int version = 0; |
1566 | int ret, i; |
1567 | |
1568 | for (i = 0; i < 5; ++i) { |
1569 | ret = cs_dsp_coeff_read_ctrl(ctl: cs_ctl, off: 0, buf: &coeff_v1, |
1570 | min(cs_ctl->len, sizeof(coeff_v1))); |
1571 | if (ret < 0) |
1572 | return ret; |
1573 | |
1574 | if (coeff_v1.host_buf_ptr) |
1575 | break; |
1576 | |
1577 | usleep_range(min: 1000, max: 2000); |
1578 | } |
1579 | |
1580 | if (!coeff_v1.host_buf_ptr) { |
1581 | adsp_err(dsp, "Failed to acquire host buffer\n" ); |
1582 | return -EIO; |
1583 | } |
1584 | |
1585 | buf = wm_adsp_buffer_alloc(dsp); |
1586 | if (!buf) |
1587 | return -ENOMEM; |
1588 | |
1589 | buf->host_buf_mem_type = cs_ctl->alg_region.type; |
1590 | buf->host_buf_ptr = be32_to_cpu(coeff_v1.host_buf_ptr); |
1591 | |
1592 | ret = wm_adsp_buffer_populate(buf); |
1593 | if (ret < 0) |
1594 | goto err; |
1595 | |
1596 | /* |
1597 | * v0 host_buffer coefficients didn't have versioning, so if the |
1598 | * control is one word, assume version 0. |
1599 | */ |
1600 | if (cs_ctl->len == 4) |
1601 | goto done; |
1602 | |
1603 | version = be32_to_cpu(coeff_v1.versions) & HOST_BUF_COEFF_COMPAT_VER_MASK; |
1604 | version >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT; |
1605 | |
1606 | if (version > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) { |
1607 | adsp_err(dsp, |
1608 | "Host buffer coeff ver %u > supported version %u\n" , |
1609 | version, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER); |
1610 | ret = -EINVAL; |
1611 | goto err; |
1612 | } |
1613 | |
1614 | cs_dsp_remove_padding(buf: (u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name)); |
1615 | |
1616 | buf->name = kasprintf(GFP_KERNEL, fmt: "%s-dsp-%s" , dsp->part, |
1617 | (char *)&coeff_v1.name); |
1618 | |
1619 | done: |
1620 | list_add_tail(new: &buf->list, head: &dsp->buffer_list); |
1621 | |
1622 | compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n" , |
1623 | buf->host_buf_ptr, version); |
1624 | |
1625 | return version; |
1626 | |
1627 | err: |
1628 | kfree(objp: buf); |
1629 | |
1630 | return ret; |
1631 | } |
1632 | |
1633 | static int wm_adsp_buffer_init(struct wm_adsp *dsp) |
1634 | { |
1635 | struct cs_dsp_coeff_ctl *cs_ctl; |
1636 | int ret; |
1637 | |
1638 | list_for_each_entry(cs_ctl, &dsp->cs_dsp.ctl_list, list) { |
1639 | if (cs_ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) |
1640 | continue; |
1641 | |
1642 | if (!cs_ctl->enabled) |
1643 | continue; |
1644 | |
1645 | ret = wm_adsp_buffer_parse_coeff(cs_ctl); |
1646 | if (ret < 0) { |
1647 | adsp_err(dsp, "Failed to parse coeff: %d\n" , ret); |
1648 | goto error; |
1649 | } else if (ret == 0) { |
1650 | /* Only one buffer supported for version 0 */ |
1651 | return 0; |
1652 | } |
1653 | } |
1654 | |
1655 | if (list_empty(head: &dsp->buffer_list)) { |
1656 | /* Fall back to legacy support */ |
1657 | ret = wm_adsp_buffer_parse_legacy(dsp); |
1658 | if (ret == -ENODEV) |
1659 | adsp_info(dsp, "Legacy support not available\n" ); |
1660 | else if (ret) |
1661 | adsp_warn(dsp, "Failed to parse legacy: %d\n" , ret); |
1662 | } |
1663 | |
1664 | return 0; |
1665 | |
1666 | error: |
1667 | wm_adsp_buffer_free(dsp); |
1668 | return ret; |
1669 | } |
1670 | |
1671 | static int wm_adsp_buffer_free(struct wm_adsp *dsp) |
1672 | { |
1673 | struct wm_adsp_compr_buf *buf, *tmp; |
1674 | |
1675 | list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) { |
1676 | wm_adsp_compr_detach(compr: buf->compr); |
1677 | |
1678 | kfree(objp: buf->name); |
1679 | kfree(objp: buf->regions); |
1680 | list_del(entry: &buf->list); |
1681 | kfree(objp: buf); |
1682 | } |
1683 | |
1684 | return 0; |
1685 | } |
1686 | |
1687 | static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) |
1688 | { |
1689 | int ret; |
1690 | |
1691 | ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), data: &buf->error); |
1692 | if (ret < 0) { |
1693 | compr_err(buf, "Failed to check buffer error: %d\n" , ret); |
1694 | return ret; |
1695 | } |
1696 | if (buf->error != 0) { |
1697 | compr_err(buf, "Buffer error occurred: %d\n" , buf->error); |
1698 | return -EIO; |
1699 | } |
1700 | |
1701 | return 0; |
1702 | } |
1703 | |
1704 | int wm_adsp_compr_trigger(struct snd_soc_component *component, |
1705 | struct snd_compr_stream *stream, int cmd) |
1706 | { |
1707 | struct wm_adsp_compr *compr = stream->runtime->private_data; |
1708 | struct wm_adsp *dsp = compr->dsp; |
1709 | int ret = 0; |
1710 | |
1711 | compr_dbg(compr, "Trigger: %d\n" , cmd); |
1712 | |
1713 | mutex_lock(&dsp->cs_dsp.pwr_lock); |
1714 | |
1715 | switch (cmd) { |
1716 | case SNDRV_PCM_TRIGGER_START: |
1717 | if (!wm_adsp_compr_attached(compr)) { |
1718 | ret = wm_adsp_compr_attach(compr); |
1719 | if (ret < 0) { |
1720 | compr_err(compr, "Failed to link buffer and stream: %d\n" , |
1721 | ret); |
1722 | break; |
1723 | } |
1724 | } |
1725 | |
1726 | ret = wm_adsp_buffer_get_error(buf: compr->buf); |
1727 | if (ret < 0) |
1728 | break; |
1729 | |
1730 | /* Trigger the IRQ at one fragment of data */ |
1731 | ret = wm_adsp_buffer_write(buf: compr->buf, |
1732 | HOST_BUFFER_FIELD(high_water_mark), |
1733 | data: wm_adsp_compr_frag_words(compr)); |
1734 | if (ret < 0) { |
1735 | compr_err(compr, "Failed to set high water mark: %d\n" , |
1736 | ret); |
1737 | break; |
1738 | } |
1739 | break; |
1740 | case SNDRV_PCM_TRIGGER_STOP: |
1741 | if (wm_adsp_compr_attached(compr)) |
1742 | wm_adsp_buffer_clear(buf: compr->buf); |
1743 | break; |
1744 | default: |
1745 | ret = -EINVAL; |
1746 | break; |
1747 | } |
1748 | |
1749 | mutex_unlock(lock: &dsp->cs_dsp.pwr_lock); |
1750 | |
1751 | return ret; |
1752 | } |
1753 | EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); |
1754 | |
1755 | static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) |
1756 | { |
1757 | int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; |
1758 | |
1759 | return buf->regions[last_region].cumulative_size; |
1760 | } |
1761 | |
1762 | static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) |
1763 | { |
1764 | u32 next_read_index, next_write_index; |
1765 | int write_index, read_index, avail; |
1766 | int ret; |
1767 | |
1768 | /* Only sync read index if we haven't already read a valid index */ |
1769 | if (buf->read_index < 0) { |
1770 | ret = wm_adsp_buffer_read(buf, |
1771 | HOST_BUFFER_FIELD(next_read_index), |
1772 | data: &next_read_index); |
1773 | if (ret < 0) |
1774 | return ret; |
1775 | |
1776 | read_index = sign_extend32(value: next_read_index, index: 23); |
1777 | |
1778 | if (read_index < 0) { |
1779 | compr_dbg(buf, "Avail check on unstarted stream\n" ); |
1780 | return 0; |
1781 | } |
1782 | |
1783 | buf->read_index = read_index; |
1784 | } |
1785 | |
1786 | ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), |
1787 | data: &next_write_index); |
1788 | if (ret < 0) |
1789 | return ret; |
1790 | |
1791 | write_index = sign_extend32(value: next_write_index, index: 23); |
1792 | |
1793 | avail = write_index - buf->read_index; |
1794 | if (avail < 0) |
1795 | avail += wm_adsp_buffer_size(buf); |
1796 | |
1797 | compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n" , |
1798 | buf->read_index, write_index, avail * CS_DSP_DATA_WORD_SIZE); |
1799 | |
1800 | buf->avail = avail; |
1801 | |
1802 | return 0; |
1803 | } |
1804 | |
1805 | int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) |
1806 | { |
1807 | struct wm_adsp_compr_buf *buf; |
1808 | struct wm_adsp_compr *compr; |
1809 | int ret = 0; |
1810 | |
1811 | mutex_lock(&dsp->cs_dsp.pwr_lock); |
1812 | |
1813 | if (list_empty(head: &dsp->buffer_list)) { |
1814 | ret = -ENODEV; |
1815 | goto out; |
1816 | } |
1817 | |
1818 | adsp_dbg(dsp, "Handling buffer IRQ\n" ); |
1819 | |
1820 | list_for_each_entry(buf, &dsp->buffer_list, list) { |
1821 | compr = buf->compr; |
1822 | |
1823 | ret = wm_adsp_buffer_get_error(buf); |
1824 | if (ret < 0) |
1825 | goto out_notify; /* Wake poll to report error */ |
1826 | |
1827 | ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), |
1828 | data: &buf->irq_count); |
1829 | if (ret < 0) { |
1830 | compr_err(buf, "Failed to get irq_count: %d\n" , ret); |
1831 | goto out; |
1832 | } |
1833 | |
1834 | ret = wm_adsp_buffer_update_avail(buf); |
1835 | if (ret < 0) { |
1836 | compr_err(buf, "Error reading avail: %d\n" , ret); |
1837 | goto out; |
1838 | } |
1839 | |
1840 | if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) |
1841 | ret = WM_ADSP_COMPR_VOICE_TRIGGER; |
1842 | |
1843 | out_notify: |
1844 | if (compr && compr->stream) |
1845 | snd_compr_fragment_elapsed(stream: compr->stream); |
1846 | } |
1847 | |
1848 | out: |
1849 | mutex_unlock(lock: &dsp->cs_dsp.pwr_lock); |
1850 | |
1851 | return ret; |
1852 | } |
1853 | EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); |
1854 | |
1855 | static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) |
1856 | { |
1857 | if (buf->irq_count & 0x01) |
1858 | return 0; |
1859 | |
1860 | compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n" , buf->irq_count); |
1861 | |
1862 | buf->irq_count |= 0x01; |
1863 | |
1864 | return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), |
1865 | data: buf->irq_count); |
1866 | } |
1867 | |
1868 | int wm_adsp_compr_pointer(struct snd_soc_component *component, |
1869 | struct snd_compr_stream *stream, |
1870 | struct snd_compr_tstamp *tstamp) |
1871 | { |
1872 | struct wm_adsp_compr *compr = stream->runtime->private_data; |
1873 | struct wm_adsp *dsp = compr->dsp; |
1874 | struct wm_adsp_compr_buf *buf; |
1875 | int ret = 0; |
1876 | |
1877 | compr_dbg(compr, "Pointer request\n" ); |
1878 | |
1879 | mutex_lock(&dsp->cs_dsp.pwr_lock); |
1880 | |
1881 | buf = compr->buf; |
1882 | |
1883 | if (dsp->fatal_error || !buf || buf->error) { |
1884 | snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); |
1885 | ret = -EIO; |
1886 | goto out; |
1887 | } |
1888 | |
1889 | if (buf->avail < wm_adsp_compr_frag_words(compr)) { |
1890 | ret = wm_adsp_buffer_update_avail(buf); |
1891 | if (ret < 0) { |
1892 | compr_err(compr, "Error reading avail: %d\n" , ret); |
1893 | goto out; |
1894 | } |
1895 | |
1896 | /* |
1897 | * If we really have less than 1 fragment available tell the |
1898 | * DSP to inform us once a whole fragment is available. |
1899 | */ |
1900 | if (buf->avail < wm_adsp_compr_frag_words(compr)) { |
1901 | ret = wm_adsp_buffer_get_error(buf); |
1902 | if (ret < 0) { |
1903 | if (buf->error) |
1904 | snd_compr_stop_error(stream, |
1905 | SNDRV_PCM_STATE_XRUN); |
1906 | goto out; |
1907 | } |
1908 | |
1909 | ret = wm_adsp_buffer_reenable_irq(buf); |
1910 | if (ret < 0) { |
1911 | compr_err(compr, "Failed to re-enable buffer IRQ: %d\n" , |
1912 | ret); |
1913 | goto out; |
1914 | } |
1915 | } |
1916 | } |
1917 | |
1918 | tstamp->copied_total = compr->copied_total; |
1919 | tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE; |
1920 | tstamp->sampling_rate = compr->sample_rate; |
1921 | |
1922 | out: |
1923 | mutex_unlock(lock: &dsp->cs_dsp.pwr_lock); |
1924 | |
1925 | return ret; |
1926 | } |
1927 | EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); |
1928 | |
1929 | static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) |
1930 | { |
1931 | struct wm_adsp_compr_buf *buf = compr->buf; |
1932 | unsigned int adsp_addr; |
1933 | int mem_type, nwords, max_read; |
1934 | int i, ret; |
1935 | |
1936 | /* Calculate read parameters */ |
1937 | for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) |
1938 | if (buf->read_index < buf->regions[i].cumulative_size) |
1939 | break; |
1940 | |
1941 | if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) |
1942 | return -EINVAL; |
1943 | |
1944 | mem_type = buf->regions[i].mem_type; |
1945 | adsp_addr = buf->regions[i].base_addr + |
1946 | (buf->read_index - buf->regions[i].offset); |
1947 | |
1948 | max_read = wm_adsp_compr_frag_words(compr); |
1949 | nwords = buf->regions[i].cumulative_size - buf->read_index; |
1950 | |
1951 | if (nwords > target) |
1952 | nwords = target; |
1953 | if (nwords > buf->avail) |
1954 | nwords = buf->avail; |
1955 | if (nwords > max_read) |
1956 | nwords = max_read; |
1957 | if (!nwords) |
1958 | return 0; |
1959 | |
1960 | /* Read data from DSP */ |
1961 | ret = cs_dsp_read_raw_data_block(dsp: &buf->dsp->cs_dsp, mem_type, mem_addr: adsp_addr, |
1962 | num_words: nwords, data: (__be32 *)compr->raw_buf); |
1963 | if (ret < 0) |
1964 | return ret; |
1965 | |
1966 | cs_dsp_remove_padding(buf: compr->raw_buf, nwords); |
1967 | |
1968 | /* update read index to account for words read */ |
1969 | buf->read_index += nwords; |
1970 | if (buf->read_index == wm_adsp_buffer_size(buf)) |
1971 | buf->read_index = 0; |
1972 | |
1973 | ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), |
1974 | data: buf->read_index); |
1975 | if (ret < 0) |
1976 | return ret; |
1977 | |
1978 | /* update avail to account for words read */ |
1979 | buf->avail -= nwords; |
1980 | |
1981 | return nwords; |
1982 | } |
1983 | |
1984 | static int wm_adsp_compr_read(struct wm_adsp_compr *compr, |
1985 | char __user *buf, size_t count) |
1986 | { |
1987 | struct wm_adsp *dsp = compr->dsp; |
1988 | int ntotal = 0; |
1989 | int nwords, nbytes; |
1990 | |
1991 | compr_dbg(compr, "Requested read of %zu bytes\n" , count); |
1992 | |
1993 | if (dsp->fatal_error || !compr->buf || compr->buf->error) { |
1994 | snd_compr_stop_error(stream: compr->stream, SNDRV_PCM_STATE_XRUN); |
1995 | return -EIO; |
1996 | } |
1997 | |
1998 | count /= CS_DSP_DATA_WORD_SIZE; |
1999 | |
2000 | do { |
2001 | nwords = wm_adsp_buffer_capture_block(compr, target: count); |
2002 | if (nwords < 0) { |
2003 | compr_err(compr, "Failed to capture block: %d\n" , |
2004 | nwords); |
2005 | return nwords; |
2006 | } |
2007 | |
2008 | nbytes = nwords * CS_DSP_DATA_WORD_SIZE; |
2009 | |
2010 | compr_dbg(compr, "Read %d bytes\n" , nbytes); |
2011 | |
2012 | if (copy_to_user(to: buf + ntotal, from: compr->raw_buf, n: nbytes)) { |
2013 | compr_err(compr, "Failed to copy data to user: %d, %d\n" , |
2014 | ntotal, nbytes); |
2015 | return -EFAULT; |
2016 | } |
2017 | |
2018 | count -= nwords; |
2019 | ntotal += nbytes; |
2020 | } while (nwords > 0 && count > 0); |
2021 | |
2022 | compr->copied_total += ntotal; |
2023 | |
2024 | return ntotal; |
2025 | } |
2026 | |
2027 | int wm_adsp_compr_copy(struct snd_soc_component *component, |
2028 | struct snd_compr_stream *stream, char __user *buf, |
2029 | size_t count) |
2030 | { |
2031 | struct wm_adsp_compr *compr = stream->runtime->private_data; |
2032 | struct wm_adsp *dsp = compr->dsp; |
2033 | int ret; |
2034 | |
2035 | mutex_lock(&dsp->cs_dsp.pwr_lock); |
2036 | |
2037 | if (stream->direction == SND_COMPRESS_CAPTURE) |
2038 | ret = wm_adsp_compr_read(compr, buf, count); |
2039 | else |
2040 | ret = -ENOTSUPP; |
2041 | |
2042 | mutex_unlock(lock: &dsp->cs_dsp.pwr_lock); |
2043 | |
2044 | return ret; |
2045 | } |
2046 | EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); |
2047 | |
2048 | static void wm_adsp_fatal_error(struct cs_dsp *cs_dsp) |
2049 | { |
2050 | struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); |
2051 | struct wm_adsp_compr *compr; |
2052 | |
2053 | dsp->fatal_error = true; |
2054 | |
2055 | list_for_each_entry(compr, &dsp->compr_list, list) { |
2056 | if (compr->stream) |
2057 | snd_compr_fragment_elapsed(stream: compr->stream); |
2058 | } |
2059 | } |
2060 | |
2061 | irqreturn_t wm_adsp2_bus_error(int irq, void *data) |
2062 | { |
2063 | struct wm_adsp *dsp = (struct wm_adsp *)data; |
2064 | |
2065 | cs_dsp_adsp2_bus_error(dsp: &dsp->cs_dsp); |
2066 | |
2067 | return IRQ_HANDLED; |
2068 | } |
2069 | EXPORT_SYMBOL_GPL(wm_adsp2_bus_error); |
2070 | |
2071 | irqreturn_t wm_halo_bus_error(int irq, void *data) |
2072 | { |
2073 | struct wm_adsp *dsp = (struct wm_adsp *)data; |
2074 | |
2075 | cs_dsp_halo_bus_error(dsp: &dsp->cs_dsp); |
2076 | |
2077 | return IRQ_HANDLED; |
2078 | } |
2079 | EXPORT_SYMBOL_GPL(wm_halo_bus_error); |
2080 | |
2081 | irqreturn_t wm_halo_wdt_expire(int irq, void *data) |
2082 | { |
2083 | struct wm_adsp *dsp = data; |
2084 | |
2085 | cs_dsp_halo_wdt_expire(dsp: &dsp->cs_dsp); |
2086 | |
2087 | return IRQ_HANDLED; |
2088 | } |
2089 | EXPORT_SYMBOL_GPL(wm_halo_wdt_expire); |
2090 | |
2091 | static const struct cs_dsp_client_ops wm_adsp1_client_ops = { |
2092 | .control_add = wm_adsp_control_add, |
2093 | .control_remove = wm_adsp_control_remove, |
2094 | }; |
2095 | |
2096 | static const struct cs_dsp_client_ops wm_adsp2_client_ops = { |
2097 | .control_add = wm_adsp_control_add, |
2098 | .control_remove = wm_adsp_control_remove, |
2099 | .pre_run = wm_adsp_pre_run, |
2100 | .post_run = wm_adsp_event_post_run, |
2101 | .post_stop = wm_adsp_event_post_stop, |
2102 | .watchdog_expired = wm_adsp_fatal_error, |
2103 | }; |
2104 | |
2105 | MODULE_LICENSE("GPL v2" ); |
2106 | MODULE_IMPORT_NS(FW_CS_DSP); |
2107 | |