diff options
author | Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 2015-01-07 20:52:36 -0500 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2015-01-12 07:59:32 -0500 |
commit | cfcefe01265cbaf5ca7209226d043b07bfa8b587 (patch) | |
tree | 6341e03635512f1842a7cd1a7dc62fca43c00aaf | |
parent | ddf3335b3e716ab2161e4db5b70984aef35075a3 (diff) |
ASoC: rsnd: add recovery support for under/over flow error on SRC
L/R channel will be switched if under/over flow error happen on
Renesas R-Car sound device by the HW bugs. Then, HW restart is required
for salvage. This patch add salvage support for SRC.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | include/sound/rcar_snd.h | 1 | ||||
-rw-r--r-- | sound/soc/sh/rcar/gen.c | 15 | ||||
-rw-r--r-- | sound/soc/sh/rcar/rsnd.h | 8 | ||||
-rw-r--r-- | sound/soc/sh/rcar/src.c | 179 |
4 files changed, 184 insertions, 19 deletions
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 83284cae464c..4cecd0c175f6 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h | |||
@@ -55,6 +55,7 @@ struct rsnd_ssi_platform_info { | |||
55 | struct rsnd_src_platform_info { | 55 | struct rsnd_src_platform_info { |
56 | u32 convert_rate; /* sampling rate convert */ | 56 | u32 convert_rate; /* sampling rate convert */ |
57 | int dma_id; /* for Gen2 SCU */ | 57 | int dma_id; /* for Gen2 SCU */ |
58 | int irq; | ||
58 | }; | 59 | }; |
59 | 60 | ||
60 | /* | 61 | /* |
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 87a6f2d62775..de0685f2abae 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c | |||
@@ -309,8 +309,13 @@ static int rsnd_gen2_probe(struct platform_device *pdev, | |||
309 | RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x0, 0x20), | 309 | RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x0, 0x20), |
310 | RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20), | 310 | RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20), |
311 | RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20), | 311 | RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20), |
312 | RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20), | ||
312 | RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20), | 313 | RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20), |
313 | RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20), | 314 | RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20), |
315 | RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8), | ||
316 | RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc), | ||
317 | RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0), | ||
318 | RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1c4), | ||
314 | RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40), | 319 | RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40), |
315 | RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40), | 320 | RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40), |
316 | RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40), | 321 | RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40), |
@@ -403,6 +408,16 @@ static int rsnd_gen1_probe(struct platform_device *pdev, | |||
403 | RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40), | 408 | RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40), |
404 | RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40), | 409 | RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40), |
405 | RSND_GEN_M_REG(SRC_MNFSR, 0x228, 0x40), | 410 | RSND_GEN_M_REG(SRC_MNFSR, 0x228, 0x40), |
411 | /* | ||
412 | * ADD US | ||
413 | * | ||
414 | * SRC_STATUS | ||
415 | * SRC_INT_EN | ||
416 | * SCU_SYS_STATUS0 | ||
417 | * SCU_SYS_STATUS1 | ||
418 | * SCU_SYS_INT_EN0 | ||
419 | * SCU_SYS_INT_EN1 | ||
420 | */ | ||
406 | }; | 421 | }; |
407 | struct rsnd_regmap_field_conf conf_adg[] = { | 422 | struct rsnd_regmap_field_conf conf_adg[] = { |
408 | RSND_GEN_S_REG(BRRA, 0x00), | 423 | RSND_GEN_S_REG(BRRA, 0x00), |
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 5826c8abf794..c45700380e59 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h | |||
@@ -44,6 +44,8 @@ enum rsnd_reg { | |||
44 | RSND_REG_SRC_IFSCR, | 44 | RSND_REG_SRC_IFSCR, |
45 | RSND_REG_SRC_IFSVR, | 45 | RSND_REG_SRC_IFSVR, |
46 | RSND_REG_SRC_SRCCR, | 46 | RSND_REG_SRC_SRCCR, |
47 | RSND_REG_SCU_SYS_STATUS0, | ||
48 | RSND_REG_SCU_SYS_INT_EN0, | ||
47 | RSND_REG_CMD_ROUTE_SLCT, | 49 | RSND_REG_CMD_ROUTE_SLCT, |
48 | RSND_REG_DVC_SWRSR, | 50 | RSND_REG_DVC_SWRSR, |
49 | RSND_REG_DVC_DVUIR, | 51 | RSND_REG_DVC_DVUIR, |
@@ -94,6 +96,9 @@ enum rsnd_reg { | |||
94 | RSND_REG_SHARE23, | 96 | RSND_REG_SHARE23, |
95 | RSND_REG_SHARE24, | 97 | RSND_REG_SHARE24, |
96 | RSND_REG_SHARE25, | 98 | RSND_REG_SHARE25, |
99 | RSND_REG_SHARE26, | ||
100 | RSND_REG_SHARE27, | ||
101 | RSND_REG_SHARE28, | ||
97 | 102 | ||
98 | RSND_REG_MAX, | 103 | RSND_REG_MAX, |
99 | }; | 104 | }; |
@@ -135,6 +140,9 @@ enum rsnd_reg { | |||
135 | #define RSND_REG_DVC_VRCTR RSND_REG_SHARE23 | 140 | #define RSND_REG_DVC_VRCTR RSND_REG_SHARE23 |
136 | #define RSND_REG_DVC_VRPDR RSND_REG_SHARE24 | 141 | #define RSND_REG_DVC_VRPDR RSND_REG_SHARE24 |
137 | #define RSND_REG_DVC_VRDBR RSND_REG_SHARE25 | 142 | #define RSND_REG_DVC_VRDBR RSND_REG_SHARE25 |
143 | #define RSND_REG_SCU_SYS_STATUS1 RSND_REG_SHARE26 | ||
144 | #define RSND_REG_SCU_SYS_INT_EN1 RSND_REG_SHARE27 | ||
145 | #define RSND_REG_SRC_INT_ENABLE0 RSND_REG_SHARE28 | ||
138 | 146 | ||
139 | struct rsnd_of_data; | 147 | struct rsnd_of_data; |
140 | struct rsnd_priv; | 148 | struct rsnd_priv; |
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index eede3ac6eed2..648b35e7effc 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c | |||
@@ -12,10 +12,18 @@ | |||
12 | 12 | ||
13 | #define SRC_NAME "src" | 13 | #define SRC_NAME "src" |
14 | 14 | ||
15 | /* SRCx_STATUS */ | ||
16 | #define OUF_SRCO ((1 << 12) | (1 << 13)) | ||
17 | #define OUF_SRCI ((1 << 9) | (1 << 8)) | ||
18 | |||
19 | /* SCU_SYSTEM_STATUS0/1 */ | ||
20 | #define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) | ||
21 | |||
15 | struct rsnd_src { | 22 | struct rsnd_src { |
16 | struct rsnd_src_platform_info *info; /* rcar_snd.h */ | 23 | struct rsnd_src_platform_info *info; /* rcar_snd.h */ |
17 | struct rsnd_mod mod; | 24 | struct rsnd_mod mod; |
18 | struct clk *clk; | 25 | struct clk *clk; |
26 | int err; | ||
19 | }; | 27 | }; |
20 | 28 | ||
21 | #define RSND_SRC_NAME_SIZE 16 | 29 | #define RSND_SRC_NAME_SIZE 16 |
@@ -280,6 +288,8 @@ static int rsnd_src_init(struct rsnd_mod *mod, | |||
280 | 288 | ||
281 | clk_prepare_enable(src->clk); | 289 | clk_prepare_enable(src->clk); |
282 | 290 | ||
291 | src->err = 0; | ||
292 | |||
283 | /* | 293 | /* |
284 | * Initialize the operation of the SRC internal circuits | 294 | * Initialize the operation of the SRC internal circuits |
285 | * see rsnd_src_start() | 295 | * see rsnd_src_start() |
@@ -293,9 +303,14 @@ static int rsnd_src_quit(struct rsnd_mod *mod, | |||
293 | struct rsnd_dai *rdai) | 303 | struct rsnd_dai *rdai) |
294 | { | 304 | { |
295 | struct rsnd_src *src = rsnd_mod_to_src(mod); | 305 | struct rsnd_src *src = rsnd_mod_to_src(mod); |
306 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
307 | struct device *dev = rsnd_priv_to_dev(priv); | ||
296 | 308 | ||
297 | clk_disable_unprepare(src->clk); | 309 | clk_disable_unprepare(src->clk); |
298 | 310 | ||
311 | if (src->err) | ||
312 | dev_warn(dev, "src under/over flow err = %d\n", src->err); | ||
313 | |||
299 | return 0; | 314 | return 0; |
300 | } | 315 | } |
301 | 316 | ||
@@ -510,6 +525,110 @@ static struct rsnd_mod_ops rsnd_src_gen1_ops = { | |||
510 | /* | 525 | /* |
511 | * Gen2 functions | 526 | * Gen2 functions |
512 | */ | 527 | */ |
528 | #define rsnd_src_irq_enable_gen2(mod) rsnd_src_irq_ctrol_gen2(mod, 1) | ||
529 | #define rsnd_src_irq_disable_gen2(mod) rsnd_src_irq_ctrol_gen2(mod, 0) | ||
530 | static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable) | ||
531 | { | ||
532 | struct rsnd_src *src = rsnd_mod_to_src(mod); | ||
533 | u32 sys_int_val, int_val, sys_int_mask; | ||
534 | int irq = src->info->irq; | ||
535 | int id = rsnd_mod_id(mod); | ||
536 | |||
537 | sys_int_val = | ||
538 | sys_int_mask = OUF_SRC(id); | ||
539 | int_val = 0x3300; | ||
540 | |||
541 | /* | ||
542 | * IRQ is not supported on non-DT | ||
543 | * see | ||
544 | * rsnd_src_probe_gen2() | ||
545 | */ | ||
546 | if ((irq <= 0) || !enable) { | ||
547 | sys_int_val = 0; | ||
548 | int_val = 0; | ||
549 | } | ||
550 | |||
551 | rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); | ||
552 | rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); | ||
553 | rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); | ||
554 | } | ||
555 | |||
556 | static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod) | ||
557 | { | ||
558 | u32 val = OUF_SRC(rsnd_mod_id(mod)); | ||
559 | |||
560 | rsnd_mod_bset(mod, SCU_SYS_STATUS0, val, val); | ||
561 | rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val); | ||
562 | } | ||
563 | |||
564 | static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) | ||
565 | { | ||
566 | u32 val = OUF_SRC(rsnd_mod_id(mod)); | ||
567 | bool ret = false; | ||
568 | |||
569 | if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val) || | ||
570 | (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val)) { | ||
571 | struct rsnd_src *src = rsnd_mod_to_src(mod); | ||
572 | |||
573 | src->err++; | ||
574 | ret = true; | ||
575 | } | ||
576 | |||
577 | /* clear error static */ | ||
578 | rsnd_src_error_clear_gen2(mod); | ||
579 | |||
580 | return ret; | ||
581 | } | ||
582 | |||
583 | static int _rsnd_src_start_gen2(struct rsnd_mod *mod) | ||
584 | { | ||
585 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
586 | u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; | ||
587 | |||
588 | rsnd_mod_write(mod, SRC_CTRL, val); | ||
589 | |||
590 | rsnd_src_error_clear_gen2(mod); | ||
591 | |||
592 | rsnd_src_start(mod); | ||
593 | |||
594 | rsnd_src_irq_enable_gen2(mod); | ||
595 | |||
596 | return 0; | ||
597 | } | ||
598 | |||
599 | static int _rsnd_src_stop_gen2(struct rsnd_mod *mod) | ||
600 | { | ||
601 | rsnd_src_irq_disable_gen2(mod); | ||
602 | |||
603 | rsnd_mod_write(mod, SRC_CTRL, 0); | ||
604 | |||
605 | rsnd_src_error_record_gen2(mod); | ||
606 | |||
607 | return rsnd_src_stop(mod); | ||
608 | } | ||
609 | |||
610 | static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) | ||
611 | { | ||
612 | struct rsnd_mod *mod = data; | ||
613 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
614 | |||
615 | if (!io) | ||
616 | return IRQ_NONE; | ||
617 | |||
618 | if (rsnd_src_error_record_gen2(mod)) { | ||
619 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
620 | struct device *dev = rsnd_priv_to_dev(priv); | ||
621 | |||
622 | _rsnd_src_stop_gen2(mod); | ||
623 | _rsnd_src_start_gen2(mod); | ||
624 | |||
625 | dev_dbg(dev, "%s[%d] restart\n", | ||
626 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
627 | } | ||
628 | |||
629 | return IRQ_HANDLED; | ||
630 | } | ||
631 | |||
513 | static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, | 632 | static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, |
514 | struct rsnd_dai *rdai) | 633 | struct rsnd_dai *rdai) |
515 | { | 634 | { |
@@ -588,18 +707,38 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, | |||
588 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | 707 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
589 | struct rsnd_src *src = rsnd_mod_to_src(mod); | 708 | struct rsnd_src *src = rsnd_mod_to_src(mod); |
590 | struct device *dev = rsnd_priv_to_dev(priv); | 709 | struct device *dev = rsnd_priv_to_dev(priv); |
710 | int irq = src->info->irq; | ||
591 | int ret; | 711 | int ret; |
592 | 712 | ||
713 | if (irq > 0) { | ||
714 | /* | ||
715 | * IRQ is not supported on non-DT | ||
716 | * see | ||
717 | * rsnd_src_irq_enable_gen2() | ||
718 | */ | ||
719 | ret = devm_request_irq(dev, irq, | ||
720 | rsnd_src_interrupt_gen2, | ||
721 | IRQF_SHARED, | ||
722 | dev_name(dev), mod); | ||
723 | if (ret) | ||
724 | goto rsnd_src_probe_gen2_fail; | ||
725 | } | ||
726 | |||
593 | ret = rsnd_dma_init(priv, | 727 | ret = rsnd_dma_init(priv, |
594 | rsnd_mod_to_dma(mod), | 728 | rsnd_mod_to_dma(mod), |
595 | rsnd_info_is_playback(priv, src), | 729 | rsnd_info_is_playback(priv, src), |
596 | src->info->dma_id); | 730 | src->info->dma_id); |
597 | if (ret < 0) | 731 | if (ret) |
598 | dev_err(dev, "%s[%d] (Gen2) failed\n", | 732 | goto rsnd_src_probe_gen2_fail; |
599 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | 733 | |
600 | else | 734 | dev_dbg(dev, "%s[%d] (Gen2) is probed\n", |
601 | dev_dbg(dev, "%s[%d] (Gen2) is probed\n", | 735 | rsnd_mod_name(mod), rsnd_mod_id(mod)); |
602 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | 736 | |
737 | return ret; | ||
738 | |||
739 | rsnd_src_probe_gen2_fail: | ||
740 | dev_err(dev, "%s[%d] (Gen2) failed\n", | ||
741 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
603 | 742 | ||
604 | return ret; | 743 | return ret; |
605 | } | 744 | } |
@@ -635,27 +774,21 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, | |||
635 | static int rsnd_src_start_gen2(struct rsnd_mod *mod, | 774 | static int rsnd_src_start_gen2(struct rsnd_mod *mod, |
636 | struct rsnd_dai *rdai) | 775 | struct rsnd_dai *rdai) |
637 | { | 776 | { |
638 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | 777 | rsnd_dma_start(rsnd_mod_to_dma(mod)); |
639 | struct rsnd_src *src = rsnd_mod_to_src(mod); | ||
640 | u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; | ||
641 | |||
642 | rsnd_dma_start(rsnd_mod_to_dma(&src->mod)); | ||
643 | 778 | ||
644 | rsnd_mod_write(mod, SRC_CTRL, val); | 779 | return _rsnd_src_start_gen2(mod); |
645 | |||
646 | return rsnd_src_start(mod); | ||
647 | } | 780 | } |
648 | 781 | ||
649 | static int rsnd_src_stop_gen2(struct rsnd_mod *mod, | 782 | static int rsnd_src_stop_gen2(struct rsnd_mod *mod, |
650 | struct rsnd_dai *rdai) | 783 | struct rsnd_dai *rdai) |
651 | { | 784 | { |
652 | struct rsnd_src *src = rsnd_mod_to_src(mod); | 785 | int ret; |
653 | 786 | ||
654 | rsnd_mod_write(mod, SRC_CTRL, 0); | 787 | ret = _rsnd_src_stop_gen2(mod); |
655 | 788 | ||
656 | rsnd_dma_stop(rsnd_mod_to_dma(&src->mod)); | 789 | rsnd_dma_stop(rsnd_mod_to_dma(mod)); |
657 | 790 | ||
658 | return rsnd_src_stop(mod); | 791 | return ret; |
659 | } | 792 | } |
660 | 793 | ||
661 | static struct rsnd_mod_ops rsnd_src_gen2_ops = { | 794 | static struct rsnd_mod_ops rsnd_src_gen2_ops = { |
@@ -681,10 +814,11 @@ static void rsnd_of_parse_src(struct platform_device *pdev, | |||
681 | struct rsnd_priv *priv) | 814 | struct rsnd_priv *priv) |
682 | { | 815 | { |
683 | struct device_node *src_node; | 816 | struct device_node *src_node; |
817 | struct device_node *np; | ||
684 | struct rcar_snd_info *info = rsnd_priv_to_info(priv); | 818 | struct rcar_snd_info *info = rsnd_priv_to_info(priv); |
685 | struct rsnd_src_platform_info *src_info; | 819 | struct rsnd_src_platform_info *src_info; |
686 | struct device *dev = &pdev->dev; | 820 | struct device *dev = &pdev->dev; |
687 | int nr; | 821 | int nr, i; |
688 | 822 | ||
689 | if (!of_data) | 823 | if (!of_data) |
690 | return; | 824 | return; |
@@ -708,6 +842,13 @@ static void rsnd_of_parse_src(struct platform_device *pdev, | |||
708 | info->src_info = src_info; | 842 | info->src_info = src_info; |
709 | info->src_info_nr = nr; | 843 | info->src_info_nr = nr; |
710 | 844 | ||
845 | i = 0; | ||
846 | for_each_child_of_node(src_node, np) { | ||
847 | src_info[i].irq = irq_of_parse_and_map(np, 0); | ||
848 | |||
849 | i++; | ||
850 | } | ||
851 | |||
711 | rsnd_of_parse_src_end: | 852 | rsnd_of_parse_src_end: |
712 | of_node_put(src_node); | 853 | of_node_put(src_node); |
713 | } | 854 | } |