diff options
Diffstat (limited to 'sound/soc/sh')
-rw-r--r-- | sound/soc/sh/rcar/ssi.c | 110 |
1 files changed, 107 insertions, 3 deletions
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index c48a6c7cd08e..2079ccf5f322 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c | |||
@@ -19,6 +19,7 @@ | |||
19 | * SSICR | 19 | * SSICR |
20 | */ | 20 | */ |
21 | #define FORCE (1 << 31) /* Fixed */ | 21 | #define FORCE (1 << 31) /* Fixed */ |
22 | #define DMEN (1 << 28) /* DMA Enable */ | ||
22 | #define UIEN (1 << 27) /* Underflow Interrupt Enable */ | 23 | #define UIEN (1 << 27) /* Underflow Interrupt Enable */ |
23 | #define OIEN (1 << 26) /* Overflow Interrupt Enable */ | 24 | #define OIEN (1 << 26) /* Overflow Interrupt Enable */ |
24 | #define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ | 25 | #define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ |
@@ -51,6 +52,11 @@ | |||
51 | #define IIRQ (1 << 25) /* Idle Mode Interrupt Status */ | 52 | #define IIRQ (1 << 25) /* Idle Mode Interrupt Status */ |
52 | #define DIRQ (1 << 24) /* Data Interrupt Status Flag */ | 53 | #define DIRQ (1 << 24) /* Data Interrupt Status Flag */ |
53 | 54 | ||
55 | /* | ||
56 | * SSIWSR | ||
57 | */ | ||
58 | #define CONT (1 << 8) /* WS Continue Function */ | ||
59 | |||
54 | struct rsnd_ssi { | 60 | struct rsnd_ssi { |
55 | struct clk *clk; | 61 | struct clk *clk; |
56 | struct rsnd_ssi_platform_info *info; /* rcar_snd.h */ | 62 | struct rsnd_ssi_platform_info *info; /* rcar_snd.h */ |
@@ -63,6 +69,7 @@ struct rsnd_ssi { | |||
63 | u32 cr_clk; | 69 | u32 cr_clk; |
64 | u32 cr_etc; | 70 | u32 cr_etc; |
65 | int err; | 71 | int err; |
72 | int dma_offset; | ||
66 | unsigned int usrcnt; | 73 | unsigned int usrcnt; |
67 | unsigned int rate; | 74 | unsigned int rate; |
68 | }; | 75 | }; |
@@ -83,7 +90,10 @@ struct rsnd_ssiu { | |||
83 | 90 | ||
84 | #define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr) | 91 | #define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr) |
85 | #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) | 92 | #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) |
86 | #define rsnd_ssi_is_pio(ssi) ((ssi)->info->pio_irq > 0) | 93 | #define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) |
94 | #define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0) | ||
95 | #define rsnd_ssi_dma_available(ssi) \ | ||
96 | rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod)) | ||
87 | #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) | 97 | #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) |
88 | #define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) | 98 | #define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) |
89 | #define rsnd_ssi_mode_flags(p) ((p)->info->flags) | 99 | #define rsnd_ssi_mode_flags(p) ((p)->info->flags) |
@@ -477,6 +487,79 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { | |||
477 | .stop = rsnd_ssi_pio_stop, | 487 | .stop = rsnd_ssi_pio_stop, |
478 | }; | 488 | }; |
479 | 489 | ||
490 | static int rsnd_ssi_dma_inquiry(struct rsnd_dma *dma, dma_addr_t *buf, int *len) | ||
491 | { | ||
492 | struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); | ||
493 | struct rsnd_dai_stream *io = ssi->io; | ||
494 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | ||
495 | |||
496 | *len = io->byte_per_period; | ||
497 | *buf = runtime->dma_addr + | ||
498 | rsnd_dai_pointer_offset(io, ssi->dma_offset + *len); | ||
499 | ssi->dma_offset = *len; /* it cares A/B plane */ | ||
500 | |||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | static int rsnd_ssi_dma_complete(struct rsnd_dma *dma) | ||
505 | { | ||
506 | struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); | ||
507 | struct rsnd_dai_stream *io = ssi->io; | ||
508 | u32 status = rsnd_mod_read(&ssi->mod, SSISR); | ||
509 | |||
510 | rsnd_ssi_record_error(ssi, status); | ||
511 | |||
512 | rsnd_dai_pointer_update(ssi->io, io->byte_per_period); | ||
513 | |||
514 | return 0; | ||
515 | } | ||
516 | |||
517 | static int rsnd_ssi_dma_start(struct rsnd_mod *mod, | ||
518 | struct rsnd_dai *rdai, | ||
519 | struct rsnd_dai_stream *io) | ||
520 | { | ||
521 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | ||
522 | struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); | ||
523 | |||
524 | /* enable DMA transfer */ | ||
525 | ssi->cr_etc = DMEN; | ||
526 | ssi->dma_offset = 0; | ||
527 | |||
528 | rsnd_dma_start(dma); | ||
529 | |||
530 | rsnd_ssi_hw_start(ssi, ssi->rdai, io); | ||
531 | |||
532 | /* enable WS continue */ | ||
533 | if (rsnd_rdai_is_clk_master(rdai)) | ||
534 | rsnd_mod_write(&ssi->mod, SSIWSR, CONT); | ||
535 | |||
536 | return 0; | ||
537 | } | ||
538 | |||
539 | static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, | ||
540 | struct rsnd_dai *rdai, | ||
541 | struct rsnd_dai_stream *io) | ||
542 | { | ||
543 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | ||
544 | struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); | ||
545 | |||
546 | ssi->cr_etc = 0; | ||
547 | |||
548 | rsnd_ssi_hw_stop(ssi, rdai); | ||
549 | |||
550 | rsnd_dma_stop(dma); | ||
551 | |||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | static struct rsnd_mod_ops rsnd_ssi_dma_ops = { | ||
556 | .name = "ssi (dma)", | ||
557 | .init = rsnd_ssi_init, | ||
558 | .quit = rsnd_ssi_quit, | ||
559 | .start = rsnd_ssi_dma_start, | ||
560 | .stop = rsnd_ssi_dma_stop, | ||
561 | }; | ||
562 | |||
480 | /* | 563 | /* |
481 | * Non SSI | 564 | * Non SSI |
482 | */ | 565 | */ |
@@ -574,9 +657,26 @@ int rsnd_ssi_probe(struct platform_device *pdev, | |||
574 | ops = &rsnd_ssi_non_ops; | 657 | ops = &rsnd_ssi_non_ops; |
575 | 658 | ||
576 | /* | 659 | /* |
660 | * SSI DMA case | ||
661 | */ | ||
662 | if (pinfo->dma_id > 0) { | ||
663 | ret = rsnd_dma_init( | ||
664 | priv, rsnd_mod_to_dma(&ssi->mod), | ||
665 | (rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY), | ||
666 | pinfo->dma_id, | ||
667 | rsnd_ssi_dma_inquiry, | ||
668 | rsnd_ssi_dma_complete); | ||
669 | if (ret < 0) | ||
670 | dev_info(dev, "SSI DMA failed. try PIO transter\n"); | ||
671 | else | ||
672 | ops = &rsnd_ssi_dma_ops; | ||
673 | } | ||
674 | |||
675 | /* | ||
577 | * SSI PIO case | 676 | * SSI PIO case |
578 | */ | 677 | */ |
579 | if (rsnd_ssi_is_pio(ssi)) { | 678 | if (!rsnd_ssi_dma_available(ssi) && |
679 | rsnd_ssi_pio_available(ssi)) { | ||
580 | ret = devm_request_irq(dev, pinfo->pio_irq, | 680 | ret = devm_request_irq(dev, pinfo->pio_irq, |
581 | &rsnd_ssi_pio_interrupt, | 681 | &rsnd_ssi_pio_interrupt, |
582 | IRQF_SHARED, | 682 | IRQF_SHARED, |
@@ -605,6 +705,10 @@ void rsnd_ssi_remove(struct platform_device *pdev, | |||
605 | struct rsnd_ssi *ssi; | 705 | struct rsnd_ssi *ssi; |
606 | int i; | 706 | int i; |
607 | 707 | ||
608 | for_each_rsnd_ssi(ssi, priv, i) | 708 | for_each_rsnd_ssi(ssi, priv, i) { |
609 | clk_put(ssi->clk); | 709 | clk_put(ssi->clk); |
710 | if (rsnd_ssi_dma_available(ssi)) | ||
711 | rsnd_dma_quit(priv, rsnd_mod_to_dma(&ssi->mod)); | ||
712 | } | ||
713 | |||
610 | } | 714 | } |