.. | .. |
---|
10 | 10 | #include <linux/clk/rockchip.h> |
---|
11 | 11 | #include <linux/of.h> |
---|
12 | 12 | #include <linux/of_device.h> |
---|
| 13 | +#include <linux/pinctrl/consumer.h> |
---|
13 | 14 | #include <linux/pm_runtime.h> |
---|
14 | 15 | #include <linux/rational.h> |
---|
15 | 16 | #include <linux/regmap.h> |
---|
.. | .. |
---|
48 | 49 | struct regmap *regmap; |
---|
49 | 50 | struct snd_dmaengine_dai_dma_data capture_dma_data; |
---|
50 | 51 | struct reset_control *reset; |
---|
| 52 | + struct pinctrl *pinctrl; |
---|
| 53 | + struct pinctrl_state *clk_state; |
---|
51 | 54 | unsigned int start_delay_ms; |
---|
52 | 55 | unsigned int filter_delay_ms; |
---|
53 | 56 | enum rk_pdm_version version; |
---|
.. | .. |
---|
566 | 569 | return 1; |
---|
567 | 570 | } |
---|
568 | 571 | |
---|
| 572 | +static const char * const rpaths_text[] = { |
---|
| 573 | + "From SDI0", "From SDI1", "From SDI2", "From SDI3" }; |
---|
| 574 | + |
---|
| 575 | +static SOC_ENUM_SINGLE_DECL(rpath3_enum, PDM_CLK_CTRL, 14, rpaths_text); |
---|
| 576 | +static SOC_ENUM_SINGLE_DECL(rpath2_enum, PDM_CLK_CTRL, 12, rpaths_text); |
---|
| 577 | +static SOC_ENUM_SINGLE_DECL(rpath1_enum, PDM_CLK_CTRL, 10, rpaths_text); |
---|
| 578 | +static SOC_ENUM_SINGLE_DECL(rpath0_enum, PDM_CLK_CTRL, 8, rpaths_text); |
---|
| 579 | + |
---|
569 | 580 | static const struct snd_kcontrol_new rockchip_pdm_controls[] = { |
---|
| 581 | + SOC_ENUM("Receive PATH3 Source Select", rpath3_enum), |
---|
| 582 | + SOC_ENUM("Receive PATH2 Source Select", rpath2_enum), |
---|
| 583 | + SOC_ENUM("Receive PATH1 Source Select", rpath1_enum), |
---|
| 584 | + SOC_ENUM("Receive PATH0 Source Select", rpath0_enum), |
---|
| 585 | + |
---|
570 | 586 | { |
---|
571 | 587 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, |
---|
572 | 588 | .name = "PDM Start Delay Ms", |
---|
.. | .. |
---|
637 | 653 | struct rk_pdm_dev *pdm = to_info(dai); |
---|
638 | 654 | |
---|
639 | 655 | dai->capture_dma_data = &pdm->capture_dma_data; |
---|
640 | | - snd_soc_add_dai_controls(dai, rockchip_pdm_controls, |
---|
641 | | - ARRAY_SIZE(rockchip_pdm_controls)); |
---|
| 656 | + |
---|
642 | 657 | if (pdm->clk_calibrate) |
---|
643 | | - snd_soc_add_dai_controls(dai, &rockchip_pdm_compensation_control, 1); |
---|
| 658 | + snd_soc_add_component_controls(dai->component, |
---|
| 659 | + &rockchip_pdm_compensation_control, |
---|
| 660 | + 1); |
---|
| 661 | + |
---|
644 | 662 | return 0; |
---|
645 | 663 | } |
---|
646 | 664 | |
---|
.. | .. |
---|
721 | 739 | |
---|
722 | 740 | static const struct snd_soc_component_driver rockchip_pdm_component = { |
---|
723 | 741 | .name = "rockchip-pdm", |
---|
| 742 | + .controls = rockchip_pdm_controls, |
---|
| 743 | + .num_controls = ARRAY_SIZE(rockchip_pdm_controls), |
---|
724 | 744 | }; |
---|
| 745 | + |
---|
| 746 | +static int rockchip_pdm_pinctrl_select_clk_state(struct device *dev) |
---|
| 747 | +{ |
---|
| 748 | + struct rk_pdm_dev *pdm = dev_get_drvdata(dev); |
---|
| 749 | + |
---|
| 750 | + if (IS_ERR_OR_NULL(pdm->pinctrl) || !pdm->clk_state) |
---|
| 751 | + return 0; |
---|
| 752 | + |
---|
| 753 | + /* |
---|
| 754 | + * A necessary delay to make sure the correct |
---|
| 755 | + * frac div has been applied when resume from |
---|
| 756 | + * power down. |
---|
| 757 | + */ |
---|
| 758 | + udelay(10); |
---|
| 759 | + |
---|
| 760 | + /* |
---|
| 761 | + * Must disable the clk to avoid clk glitch |
---|
| 762 | + * when pinctrl switch from gpio to pdm clk. |
---|
| 763 | + */ |
---|
| 764 | + clk_disable_unprepare(pdm->clk); |
---|
| 765 | + pinctrl_select_state(pdm->pinctrl, pdm->clk_state); |
---|
| 766 | + clk_prepare_enable(pdm->clk); |
---|
| 767 | + |
---|
| 768 | + return 0; |
---|
| 769 | +} |
---|
725 | 770 | |
---|
726 | 771 | static int rockchip_pdm_runtime_suspend(struct device *dev) |
---|
727 | 772 | { |
---|
.. | .. |
---|
730 | 775 | regcache_cache_only(pdm->regmap, true); |
---|
731 | 776 | clk_disable_unprepare(pdm->clk); |
---|
732 | 777 | clk_disable_unprepare(pdm->hclk); |
---|
| 778 | + |
---|
| 779 | + pinctrl_pm_select_idle_state(dev); |
---|
733 | 780 | |
---|
734 | 781 | return 0; |
---|
735 | 782 | } |
---|
.. | .. |
---|
740 | 787 | int ret; |
---|
741 | 788 | |
---|
742 | 789 | ret = clk_prepare_enable(pdm->clk); |
---|
743 | | - if (ret) { |
---|
744 | | - dev_err(pdm->dev, "clock enable failed %d\n", ret); |
---|
745 | | - return ret; |
---|
746 | | - } |
---|
| 790 | + if (ret) |
---|
| 791 | + goto err_clk; |
---|
747 | 792 | |
---|
748 | 793 | ret = clk_prepare_enable(pdm->hclk); |
---|
749 | | - if (ret) { |
---|
750 | | - dev_err(pdm->dev, "hclock enable failed %d\n", ret); |
---|
751 | | - return ret; |
---|
752 | | - } |
---|
| 794 | + if (ret) |
---|
| 795 | + goto err_hclk; |
---|
753 | 796 | |
---|
754 | | - rockchip_pdm_rxctrl(pdm, 0); |
---|
755 | 797 | regcache_cache_only(pdm->regmap, false); |
---|
756 | 798 | regcache_mark_dirty(pdm->regmap); |
---|
757 | 799 | ret = regcache_sync(pdm->regmap); |
---|
758 | | - if (ret) { |
---|
759 | | - clk_disable_unprepare(pdm->clk); |
---|
760 | | - clk_disable_unprepare(pdm->hclk); |
---|
761 | | - } |
---|
| 800 | + if (ret) |
---|
| 801 | + goto err_regmap; |
---|
| 802 | + |
---|
| 803 | + rockchip_pdm_rxctrl(pdm, 0); |
---|
| 804 | + |
---|
| 805 | + rockchip_pdm_pinctrl_select_clk_state(dev); |
---|
| 806 | + |
---|
762 | 807 | return 0; |
---|
| 808 | + |
---|
| 809 | +err_regmap: |
---|
| 810 | + clk_disable_unprepare(pdm->hclk); |
---|
| 811 | +err_hclk: |
---|
| 812 | + clk_disable_unprepare(pdm->clk); |
---|
| 813 | +err_clk: |
---|
| 814 | + return ret; |
---|
763 | 815 | } |
---|
764 | 816 | |
---|
765 | 817 | static bool rockchip_pdm_wr_reg(struct device *dev, unsigned int reg) |
---|
.. | .. |
---|
933 | 985 | pdm->dev = &pdev->dev; |
---|
934 | 986 | dev_set_drvdata(&pdev->dev, pdm); |
---|
935 | 987 | |
---|
| 988 | + pdm->pinctrl = devm_pinctrl_get(&pdev->dev); |
---|
| 989 | + if (!IS_ERR_OR_NULL(pdm->pinctrl)) { |
---|
| 990 | + pdm->clk_state = pinctrl_lookup_state(pdm->pinctrl, "clk"); |
---|
| 991 | + if (IS_ERR(pdm->clk_state)) { |
---|
| 992 | + pdm->clk_state = NULL; |
---|
| 993 | + dev_dbg(pdm->dev, "Have no clk pinctrl state\n"); |
---|
| 994 | + } |
---|
| 995 | + } |
---|
| 996 | + |
---|
936 | 997 | pdm->start_delay_ms = PDM_START_DELAY_MS_DEFAULT; |
---|
937 | 998 | pdm->filter_delay_ms = PDM_FILTER_DELAY_MS_MIN; |
---|
938 | 999 | |
---|
.. | .. |
---|
959 | 1020 | if (ret) |
---|
960 | 1021 | return ret; |
---|
961 | 1022 | |
---|
| 1023 | + rockchip_pdm_set_samplerate(pdm, PDM_DEFAULT_RATE); |
---|
| 1024 | + rockchip_pdm_rxctrl(pdm, 0); |
---|
| 1025 | + |
---|
| 1026 | + ret = rockchip_pdm_path_parse(pdm, node); |
---|
| 1027 | + if (ret != 0 && ret != -ENOENT) |
---|
| 1028 | + goto err_clk; |
---|
| 1029 | + |
---|
| 1030 | + /* |
---|
| 1031 | + * MUST: after pm_runtime_enable step, any register R/W |
---|
| 1032 | + * should be wrapped with pm_runtime_get_sync/put. |
---|
| 1033 | + * |
---|
| 1034 | + * Another approach is to enable the regcache true to |
---|
| 1035 | + * avoid access HW registers. |
---|
| 1036 | + * |
---|
| 1037 | + * Alternatively, performing the registers R/W before |
---|
| 1038 | + * pm_runtime_enable is also a good option. |
---|
| 1039 | + */ |
---|
962 | 1040 | pm_runtime_enable(&pdev->dev); |
---|
963 | 1041 | if (!pm_runtime_enabled(&pdev->dev)) { |
---|
964 | 1042 | ret = rockchip_pdm_runtime_resume(&pdev->dev); |
---|
.. | .. |
---|
975 | 1053 | goto err_suspend; |
---|
976 | 1054 | } |
---|
977 | 1055 | |
---|
978 | | - rockchip_pdm_set_samplerate(pdm, PDM_DEFAULT_RATE); |
---|
979 | | - rockchip_pdm_rxctrl(pdm, 0); |
---|
980 | | - |
---|
981 | | - ret = rockchip_pdm_path_parse(pdm, node); |
---|
982 | | - if (ret != 0 && ret != -ENOENT) |
---|
983 | | - goto err_suspend; |
---|
984 | | - |
---|
985 | 1056 | if (of_property_read_bool(node, "rockchip,no-dmaengine")) { |
---|
986 | 1057 | dev_info(&pdev->dev, "Used for Multi-DAI\n"); |
---|
987 | 1058 | return 0; |
---|
.. | .. |
---|
993 | 1064 | goto err_suspend; |
---|
994 | 1065 | } |
---|
995 | 1066 | |
---|
| 1067 | + clk_disable_unprepare(pdm->hclk); |
---|
| 1068 | + |
---|
996 | 1069 | return 0; |
---|
997 | 1070 | |
---|
998 | 1071 | err_suspend: |
---|
.. | .. |
---|
1000 | 1073 | rockchip_pdm_runtime_suspend(&pdev->dev); |
---|
1001 | 1074 | err_pm_disable: |
---|
1002 | 1075 | pm_runtime_disable(&pdev->dev); |
---|
1003 | | - |
---|
| 1076 | +err_clk: |
---|
1004 | 1077 | clk_disable_unprepare(pdm->hclk); |
---|
1005 | 1078 | |
---|
1006 | 1079 | return ret; |
---|
.. | .. |
---|
1008 | 1081 | |
---|
1009 | 1082 | static int rockchip_pdm_remove(struct platform_device *pdev) |
---|
1010 | 1083 | { |
---|
1011 | | - struct rk_pdm_dev *pdm = dev_get_drvdata(&pdev->dev); |
---|
1012 | | - |
---|
1013 | 1084 | pm_runtime_disable(&pdev->dev); |
---|
1014 | 1085 | if (!pm_runtime_status_suspended(&pdev->dev)) |
---|
1015 | 1086 | rockchip_pdm_runtime_suspend(&pdev->dev); |
---|
1016 | | - |
---|
1017 | | - clk_disable_unprepare(pdm->clk); |
---|
1018 | | - clk_disable_unprepare(pdm->hclk); |
---|
1019 | 1087 | |
---|
1020 | 1088 | return 0; |
---|
1021 | 1089 | } |
---|