1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * wm0010.c -- WM0010 DSP Driver |
4 | * |
5 | * Copyright 2012 Wolfson Microelectronics PLC. |
6 | * |
7 | * Authors: Mark Brown <broonie@opensource.wolfsonmicro.com> |
8 | * Dimitris Papastamos <dp@opensource.wolfsonmicro.com> |
9 | * Scott Ling <sl@opensource.wolfsonmicro.com> |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/moduleparam.h> |
14 | #include <linux/interrupt.h> |
15 | #include <linux/irqreturn.h> |
16 | #include <linux/init.h> |
17 | #include <linux/spi/spi.h> |
18 | #include <linux/firmware.h> |
19 | #include <linux/delay.h> |
20 | #include <linux/fs.h> |
21 | #include <linux/gpio/consumer.h> |
22 | #include <linux/regulator/consumer.h> |
23 | #include <linux/mutex.h> |
24 | #include <linux/workqueue.h> |
25 | |
26 | #include <sound/soc.h> |
27 | #include <sound/wm0010.h> |
28 | |
29 | #define DEVICE_ID_WM0010 10 |
30 | |
31 | /* We only support v1 of the .dfw INFO record */ |
32 | #define INFO_VERSION 1 |
33 | |
34 | enum dfw_cmd { |
35 | DFW_CMD_FUSE = 0x01, |
36 | DFW_CMD_CODE_HDR, |
37 | DFW_CMD_CODE_DATA, |
38 | DFW_CMD_PLL, |
39 | DFW_CMD_INFO = 0xff |
40 | }; |
41 | |
42 | struct dfw_binrec { |
43 | u8 command; |
44 | u32 length:24; |
45 | u32 address; |
46 | uint8_t data[]; |
47 | } __packed; |
48 | |
49 | struct dfw_inforec { |
50 | u8 info_version; |
51 | u8 tool_major_version; |
52 | u8 tool_minor_version; |
53 | u8 dsp_target; |
54 | }; |
55 | |
56 | struct dfw_pllrec { |
57 | u8 command; |
58 | u32 length:24; |
59 | u32 address; |
60 | u32 clkctrl1; |
61 | u32 clkctrl2; |
62 | u32 clkctrl3; |
63 | u32 ldetctrl; |
64 | u32 uart_div; |
65 | u32 spi_div; |
66 | } __packed; |
67 | |
68 | static struct pll_clock_map { |
69 | int max_sysclk; |
70 | int max_pll_spi_speed; |
71 | u32 pll_clkctrl1; |
72 | } pll_clock_map[] = { /* Dividers */ |
73 | { 22000000, 26000000, 0x00201f11 }, /* 2,32,2 */ |
74 | { 18000000, 26000000, 0x00203f21 }, /* 2,64,4 */ |
75 | { 14000000, 26000000, 0x00202620 }, /* 1,39,4 */ |
76 | { 10000000, 22000000, 0x00203120 }, /* 1,50,4 */ |
77 | { 6500000, 22000000, 0x00204520 }, /* 1,70,4 */ |
78 | { 5500000, 22000000, 0x00103f10 }, /* 1,64,2 */ |
79 | }; |
80 | |
81 | enum wm0010_state { |
82 | WM0010_POWER_OFF, |
83 | WM0010_OUT_OF_RESET, |
84 | WM0010_BOOTROM, |
85 | WM0010_STAGE2, |
86 | WM0010_FIRMWARE, |
87 | }; |
88 | |
89 | struct wm0010_priv { |
90 | struct snd_soc_component *component; |
91 | |
92 | struct mutex lock; |
93 | struct device *dev; |
94 | |
95 | struct wm0010_pdata pdata; |
96 | |
97 | struct gpio_desc *reset; |
98 | |
99 | struct regulator_bulk_data core_supplies[2]; |
100 | struct regulator *dbvdd; |
101 | |
102 | int sysclk; |
103 | |
104 | enum wm0010_state state; |
105 | bool boot_failed; |
106 | bool ready; |
107 | bool pll_running; |
108 | int max_spi_freq; |
109 | int board_max_spi_speed; |
110 | u32 pll_clkctrl1; |
111 | |
112 | spinlock_t irq_lock; |
113 | int irq; |
114 | |
115 | struct completion boot_completion; |
116 | }; |
117 | |
118 | struct wm0010_spi_msg { |
119 | struct spi_message m; |
120 | struct spi_transfer t; |
121 | u8 *tx_buf; |
122 | u8 *rx_buf; |
123 | size_t len; |
124 | }; |
125 | |
126 | static const struct snd_soc_dapm_widget wm0010_dapm_widgets[] = { |
127 | SND_SOC_DAPM_SUPPLY("CLKIN" , SND_SOC_NOPM, 0, 0, NULL, 0), |
128 | }; |
129 | |
130 | static const struct snd_soc_dapm_route wm0010_dapm_routes[] = { |
131 | { "SDI2 Capture" , NULL, "SDI1 Playback" }, |
132 | { "SDI1 Capture" , NULL, "SDI2 Playback" }, |
133 | |
134 | { "SDI1 Capture" , NULL, "CLKIN" }, |
135 | { "SDI2 Capture" , NULL, "CLKIN" }, |
136 | { "SDI1 Playback" , NULL, "CLKIN" }, |
137 | { "SDI2 Playback" , NULL, "CLKIN" }, |
138 | }; |
139 | |
140 | static const char *wm0010_state_to_str(enum wm0010_state state) |
141 | { |
142 | static const char * const state_to_str[] = { |
143 | "Power off" , |
144 | "Out of reset" , |
145 | "Boot ROM" , |
146 | "Stage2" , |
147 | "Firmware" |
148 | }; |
149 | |
150 | if (state < 0 || state >= ARRAY_SIZE(state_to_str)) |
151 | return "null" ; |
152 | return state_to_str[state]; |
153 | } |
154 | |
155 | /* Called with wm0010->lock held */ |
156 | static void wm0010_halt(struct snd_soc_component *component) |
157 | { |
158 | struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(c: component); |
159 | unsigned long flags; |
160 | enum wm0010_state state; |
161 | |
162 | /* Fetch the wm0010 state */ |
163 | spin_lock_irqsave(&wm0010->irq_lock, flags); |
164 | state = wm0010->state; |
165 | spin_unlock_irqrestore(lock: &wm0010->irq_lock, flags); |
166 | |
167 | switch (state) { |
168 | case WM0010_POWER_OFF: |
169 | /* If there's nothing to do, bail out */ |
170 | return; |
171 | case WM0010_OUT_OF_RESET: |
172 | case WM0010_BOOTROM: |
173 | case WM0010_STAGE2: |
174 | case WM0010_FIRMWARE: |
175 | /* Remember to put chip back into reset */ |
176 | gpiod_set_value_cansleep(desc: wm0010->reset, value: 1); |
177 | /* Disable the regulators */ |
178 | regulator_disable(regulator: wm0010->dbvdd); |
179 | regulator_bulk_disable(ARRAY_SIZE(wm0010->core_supplies), |
180 | consumers: wm0010->core_supplies); |
181 | break; |
182 | } |
183 | |
184 | spin_lock_irqsave(&wm0010->irq_lock, flags); |
185 | wm0010->state = WM0010_POWER_OFF; |
186 | spin_unlock_irqrestore(lock: &wm0010->irq_lock, flags); |
187 | } |
188 | |
189 | struct wm0010_boot_xfer { |
190 | struct list_head list; |
191 | struct snd_soc_component *component; |
192 | struct completion *done; |
193 | struct spi_message m; |
194 | struct spi_transfer t; |
195 | }; |
196 | |
197 | /* Called with wm0010->lock held */ |
198 | static void wm0010_mark_boot_failure(struct wm0010_priv *wm0010) |
199 | { |
200 | enum wm0010_state state; |
201 | unsigned long flags; |
202 | |
203 | spin_lock_irqsave(&wm0010->irq_lock, flags); |
204 | state = wm0010->state; |
205 | spin_unlock_irqrestore(lock: &wm0010->irq_lock, flags); |
206 | |
207 | dev_err(wm0010->dev, "Failed to transition from `%s' state to `%s' state\n" , |
208 | wm0010_state_to_str(state), wm0010_state_to_str(state + 1)); |
209 | |
210 | wm0010->boot_failed = true; |
211 | } |
212 | |
213 | static void wm0010_boot_xfer_complete(void *data) |
214 | { |
215 | struct wm0010_boot_xfer *xfer = data; |
216 | struct snd_soc_component *component = xfer->component; |
217 | struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(c: component); |
218 | u32 *out32 = xfer->t.rx_buf; |
219 | int i; |
220 | |
221 | if (xfer->m.status != 0) { |
222 | dev_err(component->dev, "SPI transfer failed: %d\n" , |
223 | xfer->m.status); |
224 | wm0010_mark_boot_failure(wm0010); |
225 | if (xfer->done) |
226 | complete(xfer->done); |
227 | return; |
228 | } |
229 | |
230 | for (i = 0; i < xfer->t.len / 4; i++) { |
231 | dev_dbg(component->dev, "%d: %04x\n" , i, out32[i]); |
232 | |
233 | switch (be32_to_cpu(out32[i])) { |
234 | case 0xe0e0e0e0: |
235 | dev_err(component->dev, |
236 | "%d: ROM error reported in stage 2\n" , i); |
237 | wm0010_mark_boot_failure(wm0010); |
238 | break; |
239 | |
240 | case 0x55555555: |
241 | if (wm0010->state < WM0010_STAGE2) |
242 | break; |
243 | dev_err(component->dev, |
244 | "%d: ROM bootloader running in stage 2\n" , i); |
245 | wm0010_mark_boot_failure(wm0010); |
246 | break; |
247 | |
248 | case 0x0fed0000: |
249 | dev_dbg(component->dev, "Stage2 loader running\n" ); |
250 | break; |
251 | |
252 | case 0x0fed0007: |
253 | dev_dbg(component->dev, "CODE_HDR packet received\n" ); |
254 | break; |
255 | |
256 | case 0x0fed0008: |
257 | dev_dbg(component->dev, "CODE_DATA packet received\n" ); |
258 | break; |
259 | |
260 | case 0x0fed0009: |
261 | dev_dbg(component->dev, "Download complete\n" ); |
262 | break; |
263 | |
264 | case 0x0fed000c: |
265 | dev_dbg(component->dev, "Application start\n" ); |
266 | break; |
267 | |
268 | case 0x0fed000e: |
269 | dev_dbg(component->dev, "PLL packet received\n" ); |
270 | wm0010->pll_running = true; |
271 | break; |
272 | |
273 | case 0x0fed0025: |
274 | dev_err(component->dev, "Device reports image too long\n" ); |
275 | wm0010_mark_boot_failure(wm0010); |
276 | break; |
277 | |
278 | case 0x0fed002c: |
279 | dev_err(component->dev, "Device reports bad SPI packet\n" ); |
280 | wm0010_mark_boot_failure(wm0010); |
281 | break; |
282 | |
283 | case 0x0fed0031: |
284 | dev_err(component->dev, "Device reports SPI read overflow\n" ); |
285 | wm0010_mark_boot_failure(wm0010); |
286 | break; |
287 | |
288 | case 0x0fed0032: |
289 | dev_err(component->dev, "Device reports SPI underclock\n" ); |
290 | wm0010_mark_boot_failure(wm0010); |
291 | break; |
292 | |
293 | case 0x0fed0033: |
294 | dev_err(component->dev, "Device reports bad header packet\n" ); |
295 | wm0010_mark_boot_failure(wm0010); |
296 | break; |
297 | |
298 | case 0x0fed0034: |
299 | dev_err(component->dev, "Device reports invalid packet type\n" ); |
300 | wm0010_mark_boot_failure(wm0010); |
301 | break; |
302 | |
303 | case 0x0fed0035: |
304 | dev_err(component->dev, "Device reports data before header error\n" ); |
305 | wm0010_mark_boot_failure(wm0010); |
306 | break; |
307 | |
308 | case 0x0fed0038: |
309 | dev_err(component->dev, "Device reports invalid PLL packet\n" ); |
310 | break; |
311 | |
312 | case 0x0fed003a: |
313 | dev_err(component->dev, "Device reports packet alignment error\n" ); |
314 | wm0010_mark_boot_failure(wm0010); |
315 | break; |
316 | |
317 | default: |
318 | dev_err(component->dev, "Unrecognised return 0x%x\n" , |
319 | be32_to_cpu(out32[i])); |
320 | wm0010_mark_boot_failure(wm0010); |
321 | break; |
322 | } |
323 | |
324 | if (wm0010->boot_failed) |
325 | break; |
326 | } |
327 | |
328 | if (xfer->done) |
329 | complete(xfer->done); |
330 | } |
331 | |
332 | static void byte_swap_64(u64 *data_in, u64 *data_out, u32 len) |
333 | { |
334 | int i; |
335 | |
336 | for (i = 0; i < len / 8; i++) |
337 | data_out[i] = cpu_to_be64(le64_to_cpu(data_in[i])); |
338 | } |
339 | |
340 | static int wm0010_firmware_load(const char *name, struct snd_soc_component *component) |
341 | { |
342 | struct spi_device *spi = to_spi_device(dev: component->dev); |
343 | struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(c: component); |
344 | struct list_head xfer_list; |
345 | struct wm0010_boot_xfer *xfer; |
346 | int ret; |
347 | DECLARE_COMPLETION_ONSTACK(done); |
348 | const struct firmware *fw; |
349 | const struct dfw_binrec *rec; |
350 | const struct dfw_inforec *inforec; |
351 | u64 *img; |
352 | u8 *out, dsp; |
353 | u32 len, offset; |
354 | |
355 | INIT_LIST_HEAD(list: &xfer_list); |
356 | |
357 | ret = request_firmware(fw: &fw, name, device: component->dev); |
358 | if (ret != 0) { |
359 | dev_err(component->dev, "Failed to request application(%s): %d\n" , |
360 | name, ret); |
361 | return ret; |
362 | } |
363 | |
364 | rec = (const struct dfw_binrec *)fw->data; |
365 | inforec = (const struct dfw_inforec *)rec->data; |
366 | offset = 0; |
367 | dsp = inforec->dsp_target; |
368 | wm0010->boot_failed = false; |
369 | if (WARN_ON(!list_empty(&xfer_list))) |
370 | return -EINVAL; |
371 | |
372 | /* First record should be INFO */ |
373 | if (rec->command != DFW_CMD_INFO) { |
374 | dev_err(component->dev, "First record not INFO\r\n" ); |
375 | ret = -EINVAL; |
376 | goto abort; |
377 | } |
378 | |
379 | if (inforec->info_version != INFO_VERSION) { |
380 | dev_err(component->dev, |
381 | "Unsupported version (%02d) of INFO record\r\n" , |
382 | inforec->info_version); |
383 | ret = -EINVAL; |
384 | goto abort; |
385 | } |
386 | |
387 | dev_dbg(component->dev, "Version v%02d INFO record found\r\n" , |
388 | inforec->info_version); |
389 | |
390 | /* Check it's a DSP file */ |
391 | if (dsp != DEVICE_ID_WM0010) { |
392 | dev_err(component->dev, "Not a WM0010 firmware file.\r\n" ); |
393 | ret = -EINVAL; |
394 | goto abort; |
395 | } |
396 | |
397 | /* Skip the info record as we don't need to send it */ |
398 | offset += ((rec->length) + 8); |
399 | rec = (void *)&rec->data[rec->length]; |
400 | |
401 | while (offset < fw->size) { |
402 | dev_dbg(component->dev, |
403 | "Packet: command %d, data length = 0x%x\r\n" , |
404 | rec->command, rec->length); |
405 | len = rec->length + 8; |
406 | |
407 | xfer = kzalloc(size: sizeof(*xfer), GFP_KERNEL); |
408 | if (!xfer) { |
409 | ret = -ENOMEM; |
410 | goto abort; |
411 | } |
412 | |
413 | xfer->component = component; |
414 | list_add_tail(new: &xfer->list, head: &xfer_list); |
415 | |
416 | out = kzalloc(size: len, GFP_KERNEL | GFP_DMA); |
417 | if (!out) { |
418 | ret = -ENOMEM; |
419 | goto abort1; |
420 | } |
421 | xfer->t.rx_buf = out; |
422 | |
423 | img = kzalloc(size: len, GFP_KERNEL | GFP_DMA); |
424 | if (!img) { |
425 | ret = -ENOMEM; |
426 | goto abort1; |
427 | } |
428 | xfer->t.tx_buf = img; |
429 | |
430 | byte_swap_64(data_in: (u64 *)&rec->command, data_out: img, len); |
431 | |
432 | spi_message_init(m: &xfer->m); |
433 | xfer->m.complete = wm0010_boot_xfer_complete; |
434 | xfer->m.context = xfer; |
435 | xfer->t.len = len; |
436 | xfer->t.bits_per_word = 8; |
437 | |
438 | if (!wm0010->pll_running) { |
439 | xfer->t.speed_hz = wm0010->sysclk / 6; |
440 | } else { |
441 | xfer->t.speed_hz = wm0010->max_spi_freq; |
442 | |
443 | if (wm0010->board_max_spi_speed && |
444 | (wm0010->board_max_spi_speed < wm0010->max_spi_freq)) |
445 | xfer->t.speed_hz = wm0010->board_max_spi_speed; |
446 | } |
447 | |
448 | /* Store max usable spi frequency for later use */ |
449 | wm0010->max_spi_freq = xfer->t.speed_hz; |
450 | |
451 | spi_message_add_tail(t: &xfer->t, m: &xfer->m); |
452 | |
453 | offset += ((rec->length) + 8); |
454 | rec = (void *)&rec->data[rec->length]; |
455 | |
456 | if (offset >= fw->size) { |
457 | dev_dbg(component->dev, "All transfers scheduled\n" ); |
458 | xfer->done = &done; |
459 | } |
460 | |
461 | ret = spi_async(spi, message: &xfer->m); |
462 | if (ret != 0) { |
463 | dev_err(component->dev, "Write failed: %d\n" , ret); |
464 | goto abort1; |
465 | } |
466 | |
467 | if (wm0010->boot_failed) { |
468 | dev_dbg(component->dev, "Boot fail!\n" ); |
469 | ret = -EINVAL; |
470 | goto abort1; |
471 | } |
472 | } |
473 | |
474 | wait_for_completion(&done); |
475 | |
476 | ret = 0; |
477 | |
478 | abort1: |
479 | while (!list_empty(head: &xfer_list)) { |
480 | xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer, |
481 | list); |
482 | kfree(objp: xfer->t.rx_buf); |
483 | kfree(objp: xfer->t.tx_buf); |
484 | list_del(entry: &xfer->list); |
485 | kfree(objp: xfer); |
486 | } |
487 | |
488 | abort: |
489 | release_firmware(fw); |
490 | return ret; |
491 | } |
492 | |
493 | static int wm0010_stage2_load(struct snd_soc_component *component) |
494 | { |
495 | struct spi_device *spi = to_spi_device(dev: component->dev); |
496 | struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(c: component); |
497 | const struct firmware *fw; |
498 | struct spi_message m; |
499 | struct spi_transfer t; |
500 | u32 *img; |
501 | u8 *out; |
502 | int i; |
503 | int ret = 0; |
504 | |
505 | ret = request_firmware(fw: &fw, name: "wm0010_stage2.bin" , device: component->dev); |
506 | if (ret != 0) { |
507 | dev_err(component->dev, "Failed to request stage2 loader: %d\n" , |
508 | ret); |
509 | return ret; |
510 | } |
511 | |
512 | dev_dbg(component->dev, "Downloading %zu byte stage 2 loader\n" , fw->size); |
513 | |
514 | /* Copy to local buffer first as vmalloc causes problems for dma */ |
515 | img = kmemdup(p: &fw->data[0], size: fw->size, GFP_KERNEL | GFP_DMA); |
516 | if (!img) { |
517 | ret = -ENOMEM; |
518 | goto abort2; |
519 | } |
520 | |
521 | out = kzalloc(size: fw->size, GFP_KERNEL | GFP_DMA); |
522 | if (!out) { |
523 | ret = -ENOMEM; |
524 | goto abort1; |
525 | } |
526 | |
527 | spi_message_init(m: &m); |
528 | memset(&t, 0, sizeof(t)); |
529 | t.rx_buf = out; |
530 | t.tx_buf = img; |
531 | t.len = fw->size; |
532 | t.bits_per_word = 8; |
533 | t.speed_hz = wm0010->sysclk / 10; |
534 | spi_message_add_tail(t: &t, m: &m); |
535 | |
536 | dev_dbg(component->dev, "Starting initial download at %dHz\n" , |
537 | t.speed_hz); |
538 | |
539 | ret = spi_sync(spi, message: &m); |
540 | if (ret != 0) { |
541 | dev_err(component->dev, "Initial download failed: %d\n" , ret); |
542 | goto abort; |
543 | } |
544 | |
545 | /* Look for errors from the boot ROM */ |
546 | for (i = 0; i < fw->size; i++) { |
547 | if (out[i] != 0x55) { |
548 | dev_err(component->dev, "Boot ROM error: %x in %d\n" , |
549 | out[i], i); |
550 | wm0010_mark_boot_failure(wm0010); |
551 | ret = -EBUSY; |
552 | goto abort; |
553 | } |
554 | } |
555 | abort: |
556 | kfree(objp: out); |
557 | abort1: |
558 | kfree(objp: img); |
559 | abort2: |
560 | release_firmware(fw); |
561 | |
562 | return ret; |
563 | } |
564 | |
565 | static int wm0010_boot(struct snd_soc_component *component) |
566 | { |
567 | struct spi_device *spi = to_spi_device(dev: component->dev); |
568 | struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(c: component); |
569 | unsigned long flags; |
570 | int ret; |
571 | struct spi_message m; |
572 | struct spi_transfer t; |
573 | struct dfw_pllrec pll_rec; |
574 | u32 *p, len; |
575 | u64 *img_swap; |
576 | u8 *out; |
577 | int i; |
578 | |
579 | spin_lock_irqsave(&wm0010->irq_lock, flags); |
580 | if (wm0010->state != WM0010_POWER_OFF) |
581 | dev_warn(wm0010->dev, "DSP already powered up!\n" ); |
582 | spin_unlock_irqrestore(lock: &wm0010->irq_lock, flags); |
583 | |
584 | if (wm0010->sysclk > 26000000) { |
585 | dev_err(component->dev, "Max DSP clock frequency is 26MHz\n" ); |
586 | ret = -ECANCELED; |
587 | goto err; |
588 | } |
589 | |
590 | mutex_lock(&wm0010->lock); |
591 | wm0010->pll_running = false; |
592 | |
593 | dev_dbg(component->dev, "max_spi_freq: %d\n" , wm0010->max_spi_freq); |
594 | |
595 | ret = regulator_bulk_enable(ARRAY_SIZE(wm0010->core_supplies), |
596 | consumers: wm0010->core_supplies); |
597 | if (ret != 0) { |
598 | dev_err(&spi->dev, "Failed to enable core supplies: %d\n" , |
599 | ret); |
600 | mutex_unlock(lock: &wm0010->lock); |
601 | goto err; |
602 | } |
603 | |
604 | ret = regulator_enable(regulator: wm0010->dbvdd); |
605 | if (ret != 0) { |
606 | dev_err(&spi->dev, "Failed to enable DBVDD: %d\n" , ret); |
607 | goto err_core; |
608 | } |
609 | |
610 | /* Release reset */ |
611 | gpiod_set_value_cansleep(desc: wm0010->reset, value: 0); |
612 | spin_lock_irqsave(&wm0010->irq_lock, flags); |
613 | wm0010->state = WM0010_OUT_OF_RESET; |
614 | spin_unlock_irqrestore(lock: &wm0010->irq_lock, flags); |
615 | |
616 | if (!wait_for_completion_timeout(x: &wm0010->boot_completion, |
617 | timeout: msecs_to_jiffies(m: 20))) |
618 | dev_err(component->dev, "Failed to get interrupt from DSP\n" ); |
619 | |
620 | spin_lock_irqsave(&wm0010->irq_lock, flags); |
621 | wm0010->state = WM0010_BOOTROM; |
622 | spin_unlock_irqrestore(lock: &wm0010->irq_lock, flags); |
623 | |
624 | ret = wm0010_stage2_load(component); |
625 | if (ret) |
626 | goto abort; |
627 | |
628 | if (!wait_for_completion_timeout(x: &wm0010->boot_completion, |
629 | timeout: msecs_to_jiffies(m: 20))) |
630 | dev_err(component->dev, "Failed to get interrupt from DSP loader.\n" ); |
631 | |
632 | spin_lock_irqsave(&wm0010->irq_lock, flags); |
633 | wm0010->state = WM0010_STAGE2; |
634 | spin_unlock_irqrestore(lock: &wm0010->irq_lock, flags); |
635 | |
636 | /* Only initialise PLL if max_spi_freq initialised */ |
637 | if (wm0010->max_spi_freq) { |
638 | |
639 | /* Initialise a PLL record */ |
640 | memset(&pll_rec, 0, sizeof(pll_rec)); |
641 | pll_rec.command = DFW_CMD_PLL; |
642 | pll_rec.length = (sizeof(pll_rec) - 8); |
643 | |
644 | /* On wm0010 only the CLKCTRL1 value is used */ |
645 | pll_rec.clkctrl1 = wm0010->pll_clkctrl1; |
646 | |
647 | ret = -ENOMEM; |
648 | len = pll_rec.length + 8; |
649 | out = kzalloc(size: len, GFP_KERNEL | GFP_DMA); |
650 | if (!out) |
651 | goto abort; |
652 | |
653 | img_swap = kzalloc(size: len, GFP_KERNEL | GFP_DMA); |
654 | if (!img_swap) |
655 | goto abort_out; |
656 | |
657 | /* We need to re-order for 0010 */ |
658 | byte_swap_64(data_in: (u64 *)&pll_rec, data_out: img_swap, len); |
659 | |
660 | spi_message_init(m: &m); |
661 | memset(&t, 0, sizeof(t)); |
662 | t.rx_buf = out; |
663 | t.tx_buf = img_swap; |
664 | t.len = len; |
665 | t.bits_per_word = 8; |
666 | t.speed_hz = wm0010->sysclk / 6; |
667 | spi_message_add_tail(t: &t, m: &m); |
668 | |
669 | ret = spi_sync(spi, message: &m); |
670 | if (ret) { |
671 | dev_err(component->dev, "First PLL write failed: %d\n" , ret); |
672 | goto abort_swap; |
673 | } |
674 | |
675 | /* Use a second send of the message to get the return status */ |
676 | ret = spi_sync(spi, message: &m); |
677 | if (ret) { |
678 | dev_err(component->dev, "Second PLL write failed: %d\n" , ret); |
679 | goto abort_swap; |
680 | } |
681 | |
682 | p = (u32 *)out; |
683 | |
684 | /* Look for PLL active code from the DSP */ |
685 | for (i = 0; i < len / 4; i++) { |
686 | if (*p == 0x0e00ed0f) { |
687 | dev_dbg(component->dev, "PLL packet received\n" ); |
688 | wm0010->pll_running = true; |
689 | break; |
690 | } |
691 | p++; |
692 | } |
693 | |
694 | kfree(objp: img_swap); |
695 | kfree(objp: out); |
696 | } else |
697 | dev_dbg(component->dev, "Not enabling DSP PLL." ); |
698 | |
699 | ret = wm0010_firmware_load(name: "wm0010.dfw" , component); |
700 | |
701 | if (ret != 0) |
702 | goto abort; |
703 | |
704 | spin_lock_irqsave(&wm0010->irq_lock, flags); |
705 | wm0010->state = WM0010_FIRMWARE; |
706 | spin_unlock_irqrestore(lock: &wm0010->irq_lock, flags); |
707 | |
708 | mutex_unlock(lock: &wm0010->lock); |
709 | |
710 | return 0; |
711 | |
712 | abort_swap: |
713 | kfree(objp: img_swap); |
714 | abort_out: |
715 | kfree(objp: out); |
716 | abort: |
717 | /* Put the chip back into reset */ |
718 | wm0010_halt(component); |
719 | mutex_unlock(lock: &wm0010->lock); |
720 | return ret; |
721 | |
722 | err_core: |
723 | mutex_unlock(lock: &wm0010->lock); |
724 | regulator_bulk_disable(ARRAY_SIZE(wm0010->core_supplies), |
725 | consumers: wm0010->core_supplies); |
726 | err: |
727 | return ret; |
728 | } |
729 | |
730 | static int wm0010_set_bias_level(struct snd_soc_component *component, |
731 | enum snd_soc_bias_level level) |
732 | { |
733 | struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(c: component); |
734 | |
735 | switch (level) { |
736 | case SND_SOC_BIAS_ON: |
737 | if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_PREPARE) |
738 | wm0010_boot(component); |
739 | break; |
740 | case SND_SOC_BIAS_PREPARE: |
741 | break; |
742 | case SND_SOC_BIAS_STANDBY: |
743 | if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_PREPARE) { |
744 | mutex_lock(&wm0010->lock); |
745 | wm0010_halt(component); |
746 | mutex_unlock(lock: &wm0010->lock); |
747 | } |
748 | break; |
749 | case SND_SOC_BIAS_OFF: |
750 | break; |
751 | } |
752 | |
753 | return 0; |
754 | } |
755 | |
756 | static int wm0010_set_sysclk(struct snd_soc_component *component, int source, |
757 | int clk_id, unsigned int freq, int dir) |
758 | { |
759 | struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(c: component); |
760 | unsigned int i; |
761 | |
762 | wm0010->sysclk = freq; |
763 | |
764 | if (freq < pll_clock_map[ARRAY_SIZE(pll_clock_map)-1].max_sysclk) { |
765 | wm0010->max_spi_freq = 0; |
766 | } else { |
767 | for (i = 0; i < ARRAY_SIZE(pll_clock_map); i++) |
768 | if (freq >= pll_clock_map[i].max_sysclk) { |
769 | wm0010->max_spi_freq = pll_clock_map[i].max_pll_spi_speed; |
770 | wm0010->pll_clkctrl1 = pll_clock_map[i].pll_clkctrl1; |
771 | break; |
772 | } |
773 | } |
774 | |
775 | return 0; |
776 | } |
777 | |
778 | static int wm0010_probe(struct snd_soc_component *component); |
779 | |
780 | static const struct snd_soc_component_driver soc_component_dev_wm0010 = { |
781 | .probe = wm0010_probe, |
782 | .set_bias_level = wm0010_set_bias_level, |
783 | .set_sysclk = wm0010_set_sysclk, |
784 | .dapm_widgets = wm0010_dapm_widgets, |
785 | .num_dapm_widgets = ARRAY_SIZE(wm0010_dapm_widgets), |
786 | .dapm_routes = wm0010_dapm_routes, |
787 | .num_dapm_routes = ARRAY_SIZE(wm0010_dapm_routes), |
788 | .use_pmdown_time = 1, |
789 | .endianness = 1, |
790 | }; |
791 | |
792 | #define WM0010_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) |
793 | #define WM0010_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ |
794 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |\ |
795 | SNDRV_PCM_FMTBIT_S32_LE) |
796 | |
797 | static struct snd_soc_dai_driver wm0010_dai[] = { |
798 | { |
799 | .name = "wm0010-sdi1" , |
800 | .playback = { |
801 | .stream_name = "SDI1 Playback" , |
802 | .channels_min = 1, |
803 | .channels_max = 2, |
804 | .rates = WM0010_RATES, |
805 | .formats = WM0010_FORMATS, |
806 | }, |
807 | .capture = { |
808 | .stream_name = "SDI1 Capture" , |
809 | .channels_min = 1, |
810 | .channels_max = 2, |
811 | .rates = WM0010_RATES, |
812 | .formats = WM0010_FORMATS, |
813 | }, |
814 | }, |
815 | { |
816 | .name = "wm0010-sdi2" , |
817 | .playback = { |
818 | .stream_name = "SDI2 Playback" , |
819 | .channels_min = 1, |
820 | .channels_max = 2, |
821 | .rates = WM0010_RATES, |
822 | .formats = WM0010_FORMATS, |
823 | }, |
824 | .capture = { |
825 | .stream_name = "SDI2 Capture" , |
826 | .channels_min = 1, |
827 | .channels_max = 2, |
828 | .rates = WM0010_RATES, |
829 | .formats = WM0010_FORMATS, |
830 | }, |
831 | }, |
832 | }; |
833 | |
834 | static irqreturn_t wm0010_irq(int irq, void *data) |
835 | { |
836 | struct wm0010_priv *wm0010 = data; |
837 | |
838 | switch (wm0010->state) { |
839 | case WM0010_OUT_OF_RESET: |
840 | case WM0010_BOOTROM: |
841 | case WM0010_STAGE2: |
842 | spin_lock(lock: &wm0010->irq_lock); |
843 | complete(&wm0010->boot_completion); |
844 | spin_unlock(lock: &wm0010->irq_lock); |
845 | return IRQ_HANDLED; |
846 | default: |
847 | return IRQ_NONE; |
848 | } |
849 | |
850 | return IRQ_NONE; |
851 | } |
852 | |
853 | static int wm0010_probe(struct snd_soc_component *component) |
854 | { |
855 | struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(c: component); |
856 | |
857 | wm0010->component = component; |
858 | |
859 | return 0; |
860 | } |
861 | |
862 | static int wm0010_spi_probe(struct spi_device *spi) |
863 | { |
864 | int ret; |
865 | int trigger; |
866 | int irq; |
867 | struct wm0010_priv *wm0010; |
868 | |
869 | wm0010 = devm_kzalloc(dev: &spi->dev, size: sizeof(*wm0010), |
870 | GFP_KERNEL); |
871 | if (!wm0010) |
872 | return -ENOMEM; |
873 | |
874 | mutex_init(&wm0010->lock); |
875 | spin_lock_init(&wm0010->irq_lock); |
876 | |
877 | spi_set_drvdata(spi, data: wm0010); |
878 | wm0010->dev = &spi->dev; |
879 | |
880 | if (dev_get_platdata(dev: &spi->dev)) |
881 | memcpy(&wm0010->pdata, dev_get_platdata(&spi->dev), |
882 | sizeof(wm0010->pdata)); |
883 | |
884 | init_completion(x: &wm0010->boot_completion); |
885 | |
886 | wm0010->core_supplies[0].supply = "AVDD" ; |
887 | wm0010->core_supplies[1].supply = "DCVDD" ; |
888 | ret = devm_regulator_bulk_get(dev: wm0010->dev, ARRAY_SIZE(wm0010->core_supplies), |
889 | consumers: wm0010->core_supplies); |
890 | if (ret != 0) { |
891 | dev_err(wm0010->dev, "Failed to obtain core supplies: %d\n" , |
892 | ret); |
893 | return ret; |
894 | } |
895 | |
896 | wm0010->dbvdd = devm_regulator_get(dev: wm0010->dev, id: "DBVDD" ); |
897 | if (IS_ERR(ptr: wm0010->dbvdd)) { |
898 | ret = PTR_ERR(ptr: wm0010->dbvdd); |
899 | dev_err(wm0010->dev, "Failed to obtain DBVDD: %d\n" , ret); |
900 | return ret; |
901 | } |
902 | |
903 | wm0010->reset = devm_gpiod_get(dev: wm0010->dev, con_id: "reset" , flags: GPIOD_OUT_HIGH); |
904 | if (IS_ERR(ptr: wm0010->reset)) |
905 | return dev_err_probe(dev: wm0010->dev, err: PTR_ERR(ptr: wm0010->reset), |
906 | fmt: "could not get RESET GPIO\n" ); |
907 | gpiod_set_consumer_name(desc: wm0010->reset, name: "wm0010 reset" ); |
908 | |
909 | wm0010->state = WM0010_POWER_OFF; |
910 | |
911 | irq = spi->irq; |
912 | if (wm0010->pdata.irq_flags) |
913 | trigger = wm0010->pdata.irq_flags; |
914 | else |
915 | trigger = IRQF_TRIGGER_FALLING; |
916 | trigger |= IRQF_ONESHOT; |
917 | |
918 | ret = request_threaded_irq(irq, NULL, thread_fn: wm0010_irq, flags: trigger, |
919 | name: "wm0010" , dev: wm0010); |
920 | if (ret) { |
921 | dev_err(wm0010->dev, "Failed to request IRQ %d: %d\n" , |
922 | irq, ret); |
923 | return ret; |
924 | } |
925 | wm0010->irq = irq; |
926 | |
927 | ret = irq_set_irq_wake(irq, on: 1); |
928 | if (ret) { |
929 | dev_err(wm0010->dev, "Failed to set IRQ %d as wake source: %d\n" , |
930 | irq, ret); |
931 | return ret; |
932 | } |
933 | |
934 | if (spi->max_speed_hz) |
935 | wm0010->board_max_spi_speed = spi->max_speed_hz; |
936 | else |
937 | wm0010->board_max_spi_speed = 0; |
938 | |
939 | ret = devm_snd_soc_register_component(dev: &spi->dev, |
940 | component_driver: &soc_component_dev_wm0010, dai_drv: wm0010_dai, |
941 | ARRAY_SIZE(wm0010_dai)); |
942 | if (ret < 0) |
943 | return ret; |
944 | |
945 | return 0; |
946 | } |
947 | |
948 | static void wm0010_spi_remove(struct spi_device *spi) |
949 | { |
950 | struct wm0010_priv *wm0010 = spi_get_drvdata(spi); |
951 | |
952 | gpiod_set_value_cansleep(desc: wm0010->reset, value: 1); |
953 | |
954 | irq_set_irq_wake(irq: wm0010->irq, on: 0); |
955 | |
956 | if (wm0010->irq) |
957 | free_irq(wm0010->irq, wm0010); |
958 | } |
959 | |
960 | static struct spi_driver wm0010_spi_driver = { |
961 | .driver = { |
962 | .name = "wm0010" , |
963 | }, |
964 | .probe = wm0010_spi_probe, |
965 | .remove = wm0010_spi_remove, |
966 | }; |
967 | |
968 | module_spi_driver(wm0010_spi_driver); |
969 | |
970 | MODULE_DESCRIPTION("ASoC WM0010 driver" ); |
971 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>" ); |
972 | MODULE_LICENSE("GPL" ); |
973 | |
974 | MODULE_FIRMWARE("wm0010.dfw" ); |
975 | MODULE_FIRMWARE("wm0010_stage2.bin" ); |
976 | |