diff options
author | Scott Ling <sl@opensource.wolfsonmicro.com> | 2012-11-07 11:53:17 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-11-09 06:44:31 -0500 |
commit | 8f7d52affeb8341cbc602209b6f35eedb410d3ee (patch) | |
tree | c4938fee6396de621c8d0922bd9367d57da36392 | |
parent | f9baa0ccb2500be60c51aec6d1f3988dec0df3fe (diff) |
ASoC: wm0010: Split out the firmware file parsing from the boot
Move the firmware load and record parsing functionality out into
a separate function from the boot function.
Signed-off-by: Scott Ling <sl@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r-- | sound/soc/codecs/wm0010.c | 263 |
1 files changed, 146 insertions, 117 deletions
diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 5c1291748326..7576330044ba 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c | |||
@@ -332,24 +332,165 @@ static void byte_swap_64(u64 *data_in, u64 *data_out, u32 len) | |||
332 | data_out[i] = cpu_to_be64(le64_to_cpu(data_in[i])); | 332 | data_out[i] = cpu_to_be64(le64_to_cpu(data_in[i])); |
333 | } | 333 | } |
334 | 334 | ||
335 | static int wm0010_boot(struct snd_soc_codec *codec) | 335 | static int wm0010_firmware_load(char *name, struct snd_soc_codec *codec) |
336 | { | 336 | { |
337 | struct spi_device *spi = to_spi_device(codec->dev); | 337 | struct spi_device *spi = to_spi_device(codec->dev); |
338 | struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec); | 338 | struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec); |
339 | unsigned long flags; | ||
340 | struct list_head xfer_list; | 339 | struct list_head xfer_list; |
341 | struct wm0010_boot_xfer *xfer; | 340 | struct wm0010_boot_xfer *xfer; |
342 | int ret; | 341 | int ret; |
343 | struct completion done; | 342 | struct completion done; |
344 | const struct firmware *fw; | 343 | const struct firmware *fw; |
345 | const struct dfw_binrec *rec; | 344 | const struct dfw_binrec *rec; |
345 | u64 *img; | ||
346 | u8 *out, dsp; | ||
347 | u32 len, offset; | ||
348 | |||
349 | INIT_LIST_HEAD(&xfer_list); | ||
350 | |||
351 | ret = request_firmware(&fw, name, codec->dev); | ||
352 | if (ret != 0) { | ||
353 | dev_err(codec->dev, "Failed to request application: %d\n", | ||
354 | ret); | ||
355 | return ret; | ||
356 | } | ||
357 | |||
358 | rec = (const struct dfw_binrec *)fw->data; | ||
359 | offset = 0; | ||
360 | dsp = rec->data[0]; | ||
361 | wm0010->boot_failed = false; | ||
362 | BUG_ON(!list_empty(&xfer_list)); | ||
363 | init_completion(&done); | ||
364 | |||
365 | /* First record should be INFO */ | ||
366 | if (rec->command != DFW_CMD_INFO) { | ||
367 | dev_err(codec->dev, "First record not INFO\r\n"); | ||
368 | ret = -EINVAL; | ||
369 | goto abort; | ||
370 | } | ||
371 | |||
372 | /* Check it's a DSP file */ | ||
373 | if (dsp != DEVICE_ID_WM0010) { | ||
374 | dev_err(codec->dev, "Not a WM0010 firmware file.\r\n"); | ||
375 | ret = -EINVAL; | ||
376 | goto abort; | ||
377 | } | ||
378 | |||
379 | /* Skip the info record as we don't need to send it */ | ||
380 | offset += ((rec->length) + 8); | ||
381 | rec = (void *)&rec->data[rec->length]; | ||
382 | |||
383 | while (offset < fw->size) { | ||
384 | dev_dbg(codec->dev, | ||
385 | "Packet: command %d, data length = 0x%x\r\n", | ||
386 | rec->command, rec->length); | ||
387 | len = rec->length + 8; | ||
388 | |||
389 | out = kzalloc(len, GFP_KERNEL); | ||
390 | if (!out) { | ||
391 | dev_err(codec->dev, | ||
392 | "Failed to allocate RX buffer\n"); | ||
393 | ret = -ENOMEM; | ||
394 | goto abort1; | ||
395 | } | ||
396 | |||
397 | img = kzalloc(len, GFP_KERNEL); | ||
398 | if (!img) { | ||
399 | dev_err(codec->dev, | ||
400 | "Failed to allocate image buffer\n"); | ||
401 | ret = -ENOMEM; | ||
402 | goto abort1; | ||
403 | } | ||
404 | |||
405 | byte_swap_64((u64 *)&rec->command, img, len); | ||
406 | |||
407 | xfer = kzalloc(sizeof(*xfer), GFP_KERNEL); | ||
408 | if (!xfer) { | ||
409 | dev_err(codec->dev, "Failed to allocate xfer\n"); | ||
410 | ret = -ENOMEM; | ||
411 | goto abort1; | ||
412 | } | ||
413 | |||
414 | xfer->codec = codec; | ||
415 | list_add_tail(&xfer->list, &xfer_list); | ||
416 | |||
417 | spi_message_init(&xfer->m); | ||
418 | xfer->m.complete = wm0010_boot_xfer_complete; | ||
419 | xfer->m.context = xfer; | ||
420 | xfer->t.tx_buf = img; | ||
421 | xfer->t.rx_buf = out; | ||
422 | xfer->t.len = len; | ||
423 | xfer->t.bits_per_word = 8; | ||
424 | |||
425 | if (!wm0010->pll_running) { | ||
426 | xfer->t.speed_hz = wm0010->sysclk / 6; | ||
427 | } else { | ||
428 | xfer->t.speed_hz = wm0010->max_spi_freq; | ||
429 | |||
430 | if (wm0010->board_max_spi_speed && | ||
431 | (wm0010->board_max_spi_speed < wm0010->max_spi_freq)) | ||
432 | xfer->t.speed_hz = wm0010->board_max_spi_speed; | ||
433 | } | ||
434 | |||
435 | /* Store max usable spi frequency for later use */ | ||
436 | wm0010->max_spi_freq = xfer->t.speed_hz; | ||
437 | |||
438 | spi_message_add_tail(&xfer->t, &xfer->m); | ||
439 | |||
440 | offset += ((rec->length) + 8); | ||
441 | rec = (void *)&rec->data[rec->length]; | ||
442 | |||
443 | if (offset >= fw->size) { | ||
444 | dev_dbg(codec->dev, "All transfers scheduled\n"); | ||
445 | xfer->done = &done; | ||
446 | } | ||
447 | |||
448 | ret = spi_async(spi, &xfer->m); | ||
449 | if (ret != 0) { | ||
450 | dev_err(codec->dev, "Write failed: %d\n", ret); | ||
451 | goto abort1; | ||
452 | } | ||
453 | |||
454 | if (wm0010->boot_failed) { | ||
455 | dev_dbg(codec->dev, "Boot fail!\n"); | ||
456 | ret = -EINVAL; | ||
457 | goto abort1; | ||
458 | } | ||
459 | } | ||
460 | |||
461 | wait_for_completion(&done); | ||
462 | |||
463 | ret = 0; | ||
464 | |||
465 | abort1: | ||
466 | while (!list_empty(&xfer_list)) { | ||
467 | xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer, | ||
468 | list); | ||
469 | kfree(xfer->t.rx_buf); | ||
470 | kfree(xfer->t.tx_buf); | ||
471 | list_del(&xfer->list); | ||
472 | kfree(xfer); | ||
473 | } | ||
474 | |||
475 | abort: | ||
476 | release_firmware(fw); | ||
477 | return ret; | ||
478 | } | ||
479 | |||
480 | static int wm0010_boot(struct snd_soc_codec *codec) | ||
481 | { | ||
482 | struct spi_device *spi = to_spi_device(codec->dev); | ||
483 | struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec); | ||
484 | unsigned long flags; | ||
485 | int ret; | ||
486 | const struct firmware *fw; | ||
346 | struct spi_message m; | 487 | struct spi_message m; |
347 | struct spi_transfer t; | 488 | struct spi_transfer t; |
348 | struct dfw_pllrec pll_rec; | 489 | struct dfw_pllrec pll_rec; |
349 | u32 *img, *p; | 490 | u32 *img, *p; |
350 | u64 *img_swap; | 491 | u64 *img_swap; |
351 | u8 *out; | 492 | u8 *out; |
352 | u32 len, offset; | 493 | u32 len; |
353 | int i; | 494 | int i; |
354 | 495 | ||
355 | spin_lock_irqsave(&wm0010->irq_lock, flags); | 496 | spin_lock_irqsave(&wm0010->irq_lock, flags); |
@@ -363,8 +504,6 @@ static int wm0010_boot(struct snd_soc_codec *codec) | |||
363 | goto err; | 504 | goto err; |
364 | } | 505 | } |
365 | 506 | ||
366 | INIT_LIST_HEAD(&xfer_list); | ||
367 | |||
368 | mutex_lock(&wm0010->lock); | 507 | mutex_lock(&wm0010->lock); |
369 | wm0010->pll_running = false; | 508 | wm0010->pll_running = false; |
370 | 509 | ||
@@ -533,109 +672,10 @@ static int wm0010_boot(struct snd_soc_codec *codec) | |||
533 | } else | 672 | } else |
534 | dev_dbg(codec->dev, "Not enabling DSP PLL."); | 673 | dev_dbg(codec->dev, "Not enabling DSP PLL."); |
535 | 674 | ||
536 | ret = request_firmware(&fw, "wm0010.dfw", codec->dev); | 675 | ret = wm0010_firmware_load("wm0010.dfw", codec); |
537 | if (ret != 0) { | ||
538 | dev_err(codec->dev, "Failed to request application: %d\n", | ||
539 | ret); | ||
540 | goto abort; | ||
541 | } | ||
542 | 676 | ||
543 | rec = (const struct dfw_binrec *)fw->data; | 677 | if (ret != 0) |
544 | offset = 0; | ||
545 | wm0010->boot_failed = false; | ||
546 | BUG_ON(!list_empty(&xfer_list)); | ||
547 | init_completion(&done); | ||
548 | |||
549 | /* First record should be INFO */ | ||
550 | if (rec->command != DFW_CMD_INFO) { | ||
551 | dev_err(codec->dev, "First record not INFO\r\n"); | ||
552 | goto abort; | ||
553 | } | ||
554 | |||
555 | /* Check it's a 0010 file */ | ||
556 | if (rec->data[0] != DEVICE_ID_WM0010) { | ||
557 | dev_err(codec->dev, "Not a WM0010 firmware file.\r\n"); | ||
558 | goto abort; | 678 | goto abort; |
559 | } | ||
560 | |||
561 | /* Skip the info record as we don't need to send it */ | ||
562 | offset += ((rec->length) + 8); | ||
563 | rec = (void *)&rec->data[rec->length]; | ||
564 | |||
565 | while (offset < fw->size) { | ||
566 | dev_dbg(codec->dev, | ||
567 | "Packet: command %d, data length = 0x%x\r\n", | ||
568 | rec->command, rec->length); | ||
569 | len = rec->length + 8; | ||
570 | |||
571 | out = kzalloc(len, GFP_KERNEL); | ||
572 | if (!out) { | ||
573 | dev_err(codec->dev, | ||
574 | "Failed to allocate RX buffer\n"); | ||
575 | goto abort; | ||
576 | } | ||
577 | |||
578 | img_swap = kzalloc(len, GFP_KERNEL); | ||
579 | if (!img_swap) { | ||
580 | dev_err(codec->dev, | ||
581 | "Failed to allocate image buffer\n"); | ||
582 | goto abort; | ||
583 | } | ||
584 | |||
585 | /* We need to re-order for 0010 */ | ||
586 | byte_swap_64((u64 *)&rec->command, img_swap, len); | ||
587 | |||
588 | xfer = kzalloc(sizeof(*xfer), GFP_KERNEL); | ||
589 | if (!xfer) { | ||
590 | dev_err(codec->dev, "Failed to allocate xfer\n"); | ||
591 | goto abort; | ||
592 | } | ||
593 | |||
594 | xfer->codec = codec; | ||
595 | list_add_tail(&xfer->list, &xfer_list); | ||
596 | |||
597 | spi_message_init(&xfer->m); | ||
598 | xfer->m.complete = wm0010_boot_xfer_complete; | ||
599 | xfer->m.context = xfer; | ||
600 | xfer->t.tx_buf = img_swap; | ||
601 | xfer->t.rx_buf = out; | ||
602 | xfer->t.len = len; | ||
603 | xfer->t.bits_per_word = 8; | ||
604 | |||
605 | if (!wm0010->pll_running) { | ||
606 | xfer->t.speed_hz = wm0010->sysclk / 6; | ||
607 | } else { | ||
608 | xfer->t.speed_hz = wm0010->max_spi_freq; | ||
609 | |||
610 | if (wm0010->board_max_spi_speed && | ||
611 | (wm0010->board_max_spi_speed < wm0010->max_spi_freq)) | ||
612 | xfer->t.speed_hz = wm0010->board_max_spi_speed; | ||
613 | } | ||
614 | |||
615 | /* Store max usable spi frequency for later use */ | ||
616 | wm0010->max_spi_freq = xfer->t.speed_hz; | ||
617 | |||
618 | spi_message_add_tail(&xfer->t, &xfer->m); | ||
619 | |||
620 | offset += ((rec->length) + 8); | ||
621 | rec = (void *)&rec->data[rec->length]; | ||
622 | |||
623 | if (offset >= fw->size) { | ||
624 | dev_dbg(codec->dev, "All transfers scheduled\n"); | ||
625 | xfer->done = &done; | ||
626 | } | ||
627 | |||
628 | ret = spi_async(spi, &xfer->m); | ||
629 | if (ret != 0) { | ||
630 | dev_err(codec->dev, "Write failed: %d\n", ret); | ||
631 | goto abort; | ||
632 | } | ||
633 | |||
634 | if (wm0010->boot_failed) | ||
635 | goto abort; | ||
636 | } | ||
637 | |||
638 | wait_for_completion(&done); | ||
639 | 679 | ||
640 | spin_lock_irqsave(&wm0010->irq_lock, flags); | 680 | spin_lock_irqsave(&wm0010->irq_lock, flags); |
641 | wm0010->state = WM0010_FIRMWARE; | 681 | wm0010->state = WM0010_FIRMWARE; |
@@ -643,17 +683,6 @@ static int wm0010_boot(struct snd_soc_codec *codec) | |||
643 | 683 | ||
644 | mutex_unlock(&wm0010->lock); | 684 | mutex_unlock(&wm0010->lock); |
645 | 685 | ||
646 | release_firmware(fw); | ||
647 | |||
648 | while (!list_empty(&xfer_list)) { | ||
649 | xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer, | ||
650 | list); | ||
651 | kfree(xfer->t.rx_buf); | ||
652 | kfree(xfer->t.tx_buf); | ||
653 | list_del(&xfer->list); | ||
654 | kfree(xfer); | ||
655 | } | ||
656 | |||
657 | return 0; | 686 | return 0; |
658 | 687 | ||
659 | abort: | 688 | abort: |