diff options
author | Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | 2016-10-20 10:20:46 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-10-24 13:02:31 -0400 |
commit | 150db8c5afa10c43597dbc4db1c3e4af630e2ac0 (patch) | |
tree | f8cc7fd8cb61f35ad1b4b7bcb805f4f7b9486bec | |
parent | 585e881e5b9e9b495978e93fcf4ed3fedb0b8cdb (diff) |
ASoC: codecs: Add msm8916-wcd digital codec
msm8916-wcd codec is found in Qualcomm msm8916 and apq8016 processors.
This codec IP is split in to two parts(Digital & Analog).
Analog part is integrated in to PMIC PM8916 and the digital part is
integrated into Application processor. Data transfer between Analog and
Digital Die is done via a internal bus called PDM.
This patch adds support to Digital part of the Codec which is integrated
into Application Processor.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt | 20 | ||||
-rw-r--r-- | sound/soc/codecs/Kconfig | 3 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/msm8916-wcd-digital.c | 923 |
4 files changed, 948 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt new file mode 100644 index 000000000000..1c8e4cb25176 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt | |||
@@ -0,0 +1,20 @@ | |||
1 | msm8916 digital audio CODEC | ||
2 | |||
3 | ## Bindings for codec core in lpass: | ||
4 | |||
5 | Required properties | ||
6 | - compatible = "qcom,msm8916-wcd-digital-codec"; | ||
7 | - reg: address space for lpass codec. | ||
8 | - clocks: Handle to mclk and ahbclk | ||
9 | - clock-names: should be "mclk", "ahbix-clk". | ||
10 | |||
11 | Example: | ||
12 | |||
13 | audio-codec@771c000{ | ||
14 | compatible = "qcom,msm8916-wcd-digital-codec"; | ||
15 | reg = <0x0771c000 0x400>; | ||
16 | clocks = <&gcc GCC_ULTAUDIO_AHBFABRIC_IXFABRIC_CLK>, | ||
17 | <&gcc GCC_CODEC_DIGCODEC_CLK>; | ||
18 | clock-names = "ahbix-clk", "mclk"; | ||
19 | #sound-dai-cells = <1>; | ||
20 | }; | ||
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 4a98ce65b993..28c7b84f13a5 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -585,6 +585,9 @@ config SND_SOC_MSM8916_WCD_ANALOG | |||
585 | tristate "Qualcomm MSM8916 WCD Analog Codec" | 585 | tristate "Qualcomm MSM8916 WCD Analog Codec" |
586 | depends on SPMI || COMPILE_TEST | 586 | depends on SPMI || COMPILE_TEST |
587 | 587 | ||
588 | config SND_SOC_MSM8916_WCD_DIGITAL | ||
589 | tristate "Qualcomm MSM8916 WCD DIGITAL Codec" | ||
590 | |||
588 | config SND_SOC_PCM1681 | 591 | config SND_SOC_PCM1681 |
589 | tristate "Texas Instruments PCM1681 CODEC" | 592 | tristate "Texas Instruments PCM1681 CODEC" |
590 | depends on I2C | 593 | depends on I2C |
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 32d90184f764..472a7720a316 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -87,6 +87,7 @@ snd-soc-max9860-objs := max9860.o | |||
87 | snd-soc-mc13783-objs := mc13783.o | 87 | snd-soc-mc13783-objs := mc13783.o |
88 | snd-soc-ml26124-objs := ml26124.o | 88 | snd-soc-ml26124-objs := ml26124.o |
89 | snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o | 89 | snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o |
90 | snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o | ||
90 | snd-soc-nau8810-objs := nau8810.o | 91 | snd-soc-nau8810-objs := nau8810.o |
91 | snd-soc-nau8825-objs := nau8825.o | 92 | snd-soc-nau8825-objs := nau8825.o |
92 | snd-soc-hdmi-codec-objs := hdmi-codec.o | 93 | snd-soc-hdmi-codec-objs := hdmi-codec.o |
@@ -311,6 +312,7 @@ obj-$(CONFIG_SND_SOC_MAX9860) += snd-soc-max9860.o | |||
311 | obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o | 312 | obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o |
312 | obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o | 313 | obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o |
313 | obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o | 314 | obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o |
315 | obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o | ||
314 | obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o | 316 | obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o |
315 | obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o | 317 | obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o |
316 | obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o | 318 | obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o |
diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c new file mode 100644 index 000000000000..e35501af91ab --- /dev/null +++ b/sound/soc/codecs/msm8916-wcd-digital.c | |||
@@ -0,0 +1,923 @@ | |||
1 | /* Copyright (c) 2016, The Linux Foundation. All rights reserved. | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License version 2 and | ||
5 | * only version 2 as published by the Free Software Foundation. | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU General Public License for more details. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/err.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/clk.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/regmap.h> | ||
22 | #include <linux/mfd/syscon.h> | ||
23 | #include <sound/soc.h> | ||
24 | #include <sound/pcm.h> | ||
25 | #include <sound/pcm_params.h> | ||
26 | #include <sound/tlv.h> | ||
27 | |||
28 | #define LPASS_CDC_CLK_RX_RESET_CTL (0x000) | ||
29 | #define LPASS_CDC_CLK_TX_RESET_B1_CTL (0x004) | ||
30 | #define CLK_RX_RESET_B1_CTL_TX1_RESET_MASK BIT(0) | ||
31 | #define CLK_RX_RESET_B1_CTL_TX2_RESET_MASK BIT(1) | ||
32 | #define LPASS_CDC_CLK_DMIC_B1_CTL (0x008) | ||
33 | #define DMIC_B1_CTL_DMIC0_CLK_SEL_MASK GENMASK(3, 1) | ||
34 | #define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV2 (0x0 << 1) | ||
35 | #define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV3 (0x1 << 1) | ||
36 | #define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV4 (0x2 << 1) | ||
37 | #define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV6 (0x3 << 1) | ||
38 | #define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV16 (0x4 << 1) | ||
39 | #define DMIC_B1_CTL_DMIC0_CLK_EN_MASK BIT(0) | ||
40 | #define DMIC_B1_CTL_DMIC0_CLK_EN_ENABLE BIT(0) | ||
41 | |||
42 | #define LPASS_CDC_CLK_RX_I2S_CTL (0x00C) | ||
43 | #define RX_I2S_CTL_RX_I2S_MODE_MASK BIT(5) | ||
44 | #define RX_I2S_CTL_RX_I2S_MODE_16 BIT(5) | ||
45 | #define RX_I2S_CTL_RX_I2S_MODE_32 0 | ||
46 | #define RX_I2S_CTL_RX_I2S_FS_RATE_MASK GENMASK(2, 0) | ||
47 | #define RX_I2S_CTL_RX_I2S_FS_RATE_F_8_KHZ 0x0 | ||
48 | #define RX_I2S_CTL_RX_I2S_FS_RATE_F_16_KHZ 0x1 | ||
49 | #define RX_I2S_CTL_RX_I2S_FS_RATE_F_32_KHZ 0x2 | ||
50 | #define RX_I2S_CTL_RX_I2S_FS_RATE_F_48_KHZ 0x3 | ||
51 | #define RX_I2S_CTL_RX_I2S_FS_RATE_F_96_KHZ 0x4 | ||
52 | #define RX_I2S_CTL_RX_I2S_FS_RATE_F_192_KHZ 0x5 | ||
53 | #define LPASS_CDC_CLK_TX_I2S_CTL (0x010) | ||
54 | #define TX_I2S_CTL_TX_I2S_MODE_MASK BIT(5) | ||
55 | #define TX_I2S_CTL_TX_I2S_MODE_16 BIT(5) | ||
56 | #define TX_I2S_CTL_TX_I2S_MODE_32 0 | ||
57 | #define TX_I2S_CTL_TX_I2S_FS_RATE_MASK GENMASK(2, 0) | ||
58 | #define TX_I2S_CTL_TX_I2S_FS_RATE_F_8_KHZ 0x0 | ||
59 | #define TX_I2S_CTL_TX_I2S_FS_RATE_F_16_KHZ 0x1 | ||
60 | #define TX_I2S_CTL_TX_I2S_FS_RATE_F_32_KHZ 0x2 | ||
61 | #define TX_I2S_CTL_TX_I2S_FS_RATE_F_48_KHZ 0x3 | ||
62 | #define TX_I2S_CTL_TX_I2S_FS_RATE_F_96_KHZ 0x4 | ||
63 | #define TX_I2S_CTL_TX_I2S_FS_RATE_F_192_KHZ 0x5 | ||
64 | |||
65 | #define LPASS_CDC_CLK_OTHR_RESET_B1_CTL (0x014) | ||
66 | #define LPASS_CDC_CLK_TX_CLK_EN_B1_CTL (0x018) | ||
67 | #define LPASS_CDC_CLK_OTHR_CTL (0x01C) | ||
68 | #define LPASS_CDC_CLK_RX_B1_CTL (0x020) | ||
69 | #define LPASS_CDC_CLK_MCLK_CTL (0x024) | ||
70 | #define MCLK_CTL_MCLK_EN_MASK BIT(0) | ||
71 | #define MCLK_CTL_MCLK_EN_ENABLE BIT(0) | ||
72 | #define MCLK_CTL_MCLK_EN_DISABLE 0 | ||
73 | #define LPASS_CDC_CLK_PDM_CTL (0x028) | ||
74 | #define LPASS_CDC_CLK_PDM_CTL_PDM_EN_MASK BIT(0) | ||
75 | #define LPASS_CDC_CLK_PDM_CTL_PDM_EN BIT(0) | ||
76 | #define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK BIT(1) | ||
77 | #define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_FB BIT(1) | ||
78 | #define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_PDM_CLK 0 | ||
79 | |||
80 | #define LPASS_CDC_CLK_SD_CTL (0x02C) | ||
81 | #define LPASS_CDC_RX1_B1_CTL (0x040) | ||
82 | #define LPASS_CDC_RX2_B1_CTL (0x060) | ||
83 | #define LPASS_CDC_RX3_B1_CTL (0x080) | ||
84 | #define LPASS_CDC_RX1_B2_CTL (0x044) | ||
85 | #define LPASS_CDC_RX2_B2_CTL (0x064) | ||
86 | #define LPASS_CDC_RX3_B2_CTL (0x084) | ||
87 | #define LPASS_CDC_RX1_B3_CTL (0x048) | ||
88 | #define LPASS_CDC_RX2_B3_CTL (0x068) | ||
89 | #define LPASS_CDC_RX3_B3_CTL (0x088) | ||
90 | #define LPASS_CDC_RX1_B4_CTL (0x04C) | ||
91 | #define LPASS_CDC_RX2_B4_CTL (0x06C) | ||
92 | #define LPASS_CDC_RX3_B4_CTL (0x08C) | ||
93 | #define LPASS_CDC_RX1_B5_CTL (0x050) | ||
94 | #define LPASS_CDC_RX2_B5_CTL (0x070) | ||
95 | #define LPASS_CDC_RX3_B5_CTL (0x090) | ||
96 | #define LPASS_CDC_RX1_B6_CTL (0x054) | ||
97 | #define RXn_B6_CTL_MUTE_MASK BIT(0) | ||
98 | #define RXn_B6_CTL_MUTE_ENABLE BIT(0) | ||
99 | #define RXn_B6_CTL_MUTE_DISABLE 0 | ||
100 | #define LPASS_CDC_RX2_B6_CTL (0x074) | ||
101 | #define LPASS_CDC_RX3_B6_CTL (0x094) | ||
102 | #define LPASS_CDC_RX1_VOL_CTL_B1_CTL (0x058) | ||
103 | #define LPASS_CDC_RX2_VOL_CTL_B1_CTL (0x078) | ||
104 | #define LPASS_CDC_RX3_VOL_CTL_B1_CTL (0x098) | ||
105 | #define LPASS_CDC_RX1_VOL_CTL_B2_CTL (0x05C) | ||
106 | #define LPASS_CDC_RX2_VOL_CTL_B2_CTL (0x07C) | ||
107 | #define LPASS_CDC_RX3_VOL_CTL_B2_CTL (0x09C) | ||
108 | #define LPASS_CDC_TOP_GAIN_UPDATE (0x0A0) | ||
109 | #define LPASS_CDC_TOP_CTL (0x0A4) | ||
110 | #define TOP_CTL_DIG_MCLK_FREQ_MASK BIT(0) | ||
111 | #define TOP_CTL_DIG_MCLK_FREQ_F_12_288MHZ 0 | ||
112 | #define TOP_CTL_DIG_MCLK_FREQ_F_9_6MHZ BIT(0) | ||
113 | |||
114 | #define LPASS_CDC_DEBUG_DESER1_CTL (0x0E0) | ||
115 | #define LPASS_CDC_DEBUG_DESER2_CTL (0x0E4) | ||
116 | #define LPASS_CDC_DEBUG_B1_CTL_CFG (0x0E8) | ||
117 | #define LPASS_CDC_DEBUG_B2_CTL_CFG (0x0EC) | ||
118 | #define LPASS_CDC_DEBUG_B3_CTL_CFG (0x0F0) | ||
119 | #define LPASS_CDC_IIR1_GAIN_B1_CTL (0x100) | ||
120 | #define LPASS_CDC_IIR2_GAIN_B1_CTL (0x140) | ||
121 | #define LPASS_CDC_IIR1_GAIN_B2_CTL (0x104) | ||
122 | #define LPASS_CDC_IIR2_GAIN_B2_CTL (0x144) | ||
123 | #define LPASS_CDC_IIR1_GAIN_B3_CTL (0x108) | ||
124 | #define LPASS_CDC_IIR2_GAIN_B3_CTL (0x148) | ||
125 | #define LPASS_CDC_IIR1_GAIN_B4_CTL (0x10C) | ||
126 | #define LPASS_CDC_IIR2_GAIN_B4_CTL (0x14C) | ||
127 | #define LPASS_CDC_IIR1_GAIN_B5_CTL (0x110) | ||
128 | #define LPASS_CDC_IIR2_GAIN_B5_CTL (0x150) | ||
129 | #define LPASS_CDC_IIR1_GAIN_B6_CTL (0x114) | ||
130 | #define LPASS_CDC_IIR2_GAIN_B6_CTL (0x154) | ||
131 | #define LPASS_CDC_IIR1_GAIN_B7_CTL (0x118) | ||
132 | #define LPASS_CDC_IIR2_GAIN_B7_CTL (0x158) | ||
133 | #define LPASS_CDC_IIR1_GAIN_B8_CTL (0x11C) | ||
134 | #define LPASS_CDC_IIR2_GAIN_B8_CTL (0x15C) | ||
135 | #define LPASS_CDC_IIR1_CTL (0x120) | ||
136 | #define LPASS_CDC_IIR2_CTL (0x160) | ||
137 | #define LPASS_CDC_IIR1_GAIN_TIMER_CTL (0x124) | ||
138 | #define LPASS_CDC_IIR2_GAIN_TIMER_CTL (0x164) | ||
139 | #define LPASS_CDC_IIR1_COEF_B1_CTL (0x128) | ||
140 | #define LPASS_CDC_IIR2_COEF_B1_CTL (0x168) | ||
141 | #define LPASS_CDC_IIR1_COEF_B2_CTL (0x12C) | ||
142 | #define LPASS_CDC_IIR2_COEF_B2_CTL (0x16C) | ||
143 | #define LPASS_CDC_CONN_RX1_B1_CTL (0x180) | ||
144 | #define LPASS_CDC_CONN_RX1_B2_CTL (0x184) | ||
145 | #define LPASS_CDC_CONN_RX1_B3_CTL (0x188) | ||
146 | #define LPASS_CDC_CONN_RX2_B1_CTL (0x18C) | ||
147 | #define LPASS_CDC_CONN_RX2_B2_CTL (0x190) | ||
148 | #define LPASS_CDC_CONN_RX2_B3_CTL (0x194) | ||
149 | #define LPASS_CDC_CONN_RX3_B1_CTL (0x198) | ||
150 | #define LPASS_CDC_CONN_RX3_B2_CTL (0x19C) | ||
151 | #define LPASS_CDC_CONN_TX_B1_CTL (0x1A0) | ||
152 | #define LPASS_CDC_CONN_EQ1_B1_CTL (0x1A8) | ||
153 | #define LPASS_CDC_CONN_EQ1_B2_CTL (0x1AC) | ||
154 | #define LPASS_CDC_CONN_EQ1_B3_CTL (0x1B0) | ||
155 | #define LPASS_CDC_CONN_EQ1_B4_CTL (0x1B4) | ||
156 | #define LPASS_CDC_CONN_EQ2_B1_CTL (0x1B8) | ||
157 | #define LPASS_CDC_CONN_EQ2_B2_CTL (0x1BC) | ||
158 | #define LPASS_CDC_CONN_EQ2_B3_CTL (0x1C0) | ||
159 | #define LPASS_CDC_CONN_EQ2_B4_CTL (0x1C4) | ||
160 | #define LPASS_CDC_CONN_TX_I2S_SD1_CTL (0x1C8) | ||
161 | #define LPASS_CDC_TX1_VOL_CTL_TIMER (0x280) | ||
162 | #define LPASS_CDC_TX2_VOL_CTL_TIMER (0x2A0) | ||
163 | #define LPASS_CDC_TX1_VOL_CTL_GAIN (0x284) | ||
164 | #define LPASS_CDC_TX2_VOL_CTL_GAIN (0x2A4) | ||
165 | #define LPASS_CDC_TX1_VOL_CTL_CFG (0x288) | ||
166 | #define TX_VOL_CTL_CFG_MUTE_EN_MASK BIT(0) | ||
167 | #define TX_VOL_CTL_CFG_MUTE_EN_ENABLE BIT(0) | ||
168 | |||
169 | #define LPASS_CDC_TX2_VOL_CTL_CFG (0x2A8) | ||
170 | #define LPASS_CDC_TX1_MUX_CTL (0x28C) | ||
171 | #define TX_MUX_CTL_CUT_OFF_FREQ_MASK GENMASK(5, 4) | ||
172 | #define TX_MUX_CTL_CUT_OFF_FREQ_SHIFT 4 | ||
173 | #define TX_MUX_CTL_CF_NEG_3DB_4HZ (0x0 << 4) | ||
174 | #define TX_MUX_CTL_CF_NEG_3DB_75HZ (0x1 << 4) | ||
175 | #define TX_MUX_CTL_CF_NEG_3DB_150HZ (0x2 << 4) | ||
176 | #define TX_MUX_CTL_HPF_BP_SEL_MASK BIT(3) | ||
177 | #define TX_MUX_CTL_HPF_BP_SEL_BYPASS BIT(3) | ||
178 | #define TX_MUX_CTL_HPF_BP_SEL_NO_BYPASS 0 | ||
179 | |||
180 | #define LPASS_CDC_TX2_MUX_CTL (0x2AC) | ||
181 | #define LPASS_CDC_TX1_CLK_FS_CTL (0x290) | ||
182 | #define LPASS_CDC_TX2_CLK_FS_CTL (0x2B0) | ||
183 | #define LPASS_CDC_TX1_DMIC_CTL (0x294) | ||
184 | #define LPASS_CDC_TX2_DMIC_CTL (0x2B4) | ||
185 | #define TXN_DMIC_CTL_CLK_SEL_MASK GENMASK(2, 0) | ||
186 | #define TXN_DMIC_CTL_CLK_SEL_DIV2 0x0 | ||
187 | #define TXN_DMIC_CTL_CLK_SEL_DIV3 0x1 | ||
188 | #define TXN_DMIC_CTL_CLK_SEL_DIV4 0x2 | ||
189 | #define TXN_DMIC_CTL_CLK_SEL_DIV6 0x3 | ||
190 | #define TXN_DMIC_CTL_CLK_SEL_DIV16 0x4 | ||
191 | |||
192 | #define MSM8916_WCD_DIGITAL_RATES (SNDRV_PCM_RATE_8000 | \ | ||
193 | SNDRV_PCM_RATE_16000 | \ | ||
194 | SNDRV_PCM_RATE_32000 | \ | ||
195 | SNDRV_PCM_RATE_48000) | ||
196 | #define MSM8916_WCD_DIGITAL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ | ||
197 | SNDRV_PCM_FMTBIT_S24_LE) | ||
198 | |||
199 | struct msm8916_wcd_digital_priv { | ||
200 | struct clk *ahbclk, *mclk; | ||
201 | }; | ||
202 | |||
203 | static const unsigned long rx_gain_reg[] = { | ||
204 | LPASS_CDC_RX1_VOL_CTL_B2_CTL, | ||
205 | LPASS_CDC_RX2_VOL_CTL_B2_CTL, | ||
206 | LPASS_CDC_RX3_VOL_CTL_B2_CTL, | ||
207 | }; | ||
208 | |||
209 | static const unsigned long tx_gain_reg[] = { | ||
210 | LPASS_CDC_TX1_VOL_CTL_GAIN, | ||
211 | LPASS_CDC_TX2_VOL_CTL_GAIN, | ||
212 | }; | ||
213 | |||
214 | static const char *const rx_mix1_text[] = { | ||
215 | "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3" | ||
216 | }; | ||
217 | |||
218 | static const char *const dec_mux_text[] = { | ||
219 | "ZERO", "ADC1", "ADC2", "ADC3", "DMIC1", "DMIC2" | ||
220 | }; | ||
221 | static const char *const rx_mix2_text[] = { "ZERO", "IIR1", "IIR2" }; | ||
222 | static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" }; | ||
223 | |||
224 | /* RX1 MIX1 */ | ||
225 | static const struct soc_enum rx_mix1_inp_enum[] = { | ||
226 | SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B1_CTL, 0, 6, rx_mix1_text), | ||
227 | SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B1_CTL, 3, 6, rx_mix1_text), | ||
228 | SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B2_CTL, 0, 6, rx_mix1_text), | ||
229 | }; | ||
230 | |||
231 | /* RX1 MIX2 */ | ||
232 | static const struct soc_enum rx_mix2_inp1_chain_enum = SOC_ENUM_SINGLE( | ||
233 | LPASS_CDC_CONN_RX1_B3_CTL, 0, 3, rx_mix2_text); | ||
234 | |||
235 | /* RX2 MIX1 */ | ||
236 | static const struct soc_enum rx2_mix1_inp_enum[] = { | ||
237 | SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text), | ||
238 | SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 3, 6, rx_mix1_text), | ||
239 | SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text), | ||
240 | }; | ||
241 | |||
242 | /* RX2 MIX2 */ | ||
243 | static const struct soc_enum rx2_mix2_inp1_chain_enum = SOC_ENUM_SINGLE( | ||
244 | LPASS_CDC_CONN_RX2_B3_CTL, 0, 3, rx_mix2_text); | ||
245 | |||
246 | /* RX3 MIX1 */ | ||
247 | static const struct soc_enum rx3_mix1_inp_enum[] = { | ||
248 | SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text), | ||
249 | SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 3, 6, rx_mix1_text), | ||
250 | SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text), | ||
251 | }; | ||
252 | |||
253 | /* DEC */ | ||
254 | static const struct soc_enum dec1_mux_enum = SOC_ENUM_SINGLE( | ||
255 | LPASS_CDC_CONN_TX_B1_CTL, 0, 6, dec_mux_text); | ||
256 | static const struct soc_enum dec2_mux_enum = SOC_ENUM_SINGLE( | ||
257 | LPASS_CDC_CONN_TX_B1_CTL, 3, 6, dec_mux_text); | ||
258 | |||
259 | /* RDAC2 MUX */ | ||
260 | static const struct snd_kcontrol_new dec1_mux = SOC_DAPM_ENUM( | ||
261 | "DEC1 MUX Mux", dec1_mux_enum); | ||
262 | static const struct snd_kcontrol_new dec2_mux = SOC_DAPM_ENUM( | ||
263 | "DEC2 MUX Mux", dec2_mux_enum); | ||
264 | static const struct snd_kcontrol_new rx_mix1_inp1_mux = SOC_DAPM_ENUM( | ||
265 | "RX1 MIX1 INP1 Mux", rx_mix1_inp_enum[0]); | ||
266 | static const struct snd_kcontrol_new rx_mix1_inp2_mux = SOC_DAPM_ENUM( | ||
267 | "RX1 MIX1 INP2 Mux", rx_mix1_inp_enum[1]); | ||
268 | static const struct snd_kcontrol_new rx_mix1_inp3_mux = SOC_DAPM_ENUM( | ||
269 | "RX1 MIX1 INP3 Mux", rx_mix1_inp_enum[2]); | ||
270 | static const struct snd_kcontrol_new rx2_mix1_inp1_mux = SOC_DAPM_ENUM( | ||
271 | "RX2 MIX1 INP1 Mux", rx2_mix1_inp_enum[0]); | ||
272 | static const struct snd_kcontrol_new rx2_mix1_inp2_mux = SOC_DAPM_ENUM( | ||
273 | "RX2 MIX1 INP2 Mux", rx2_mix1_inp_enum[1]); | ||
274 | static const struct snd_kcontrol_new rx2_mix1_inp3_mux = SOC_DAPM_ENUM( | ||
275 | "RX2 MIX1 INP3 Mux", rx2_mix1_inp_enum[2]); | ||
276 | static const struct snd_kcontrol_new rx3_mix1_inp1_mux = SOC_DAPM_ENUM( | ||
277 | "RX3 MIX1 INP1 Mux", rx3_mix1_inp_enum[0]); | ||
278 | static const struct snd_kcontrol_new rx3_mix1_inp2_mux = SOC_DAPM_ENUM( | ||
279 | "RX3 MIX1 INP2 Mux", rx3_mix1_inp_enum[1]); | ||
280 | static const struct snd_kcontrol_new rx3_mix1_inp3_mux = SOC_DAPM_ENUM( | ||
281 | "RX3 MIX1 INP3 Mux", rx3_mix1_inp_enum[2]); | ||
282 | |||
283 | /* Digital Gain control -38.4 dB to +38.4 dB in 0.3 dB steps */ | ||
284 | static const DECLARE_TLV_DB_SCALE(digital_gain, -3840, 30, 0); | ||
285 | |||
286 | /* Cutoff Freq for High Pass Filter at -3dB */ | ||
287 | static const char * const hpf_cutoff_text[] = { | ||
288 | "4Hz", "75Hz", "150Hz", | ||
289 | }; | ||
290 | |||
291 | static SOC_ENUM_SINGLE_DECL(tx1_hpf_cutoff_enum, LPASS_CDC_TX1_MUX_CTL, 4, | ||
292 | hpf_cutoff_text); | ||
293 | static SOC_ENUM_SINGLE_DECL(tx2_hpf_cutoff_enum, LPASS_CDC_TX2_MUX_CTL, 4, | ||
294 | hpf_cutoff_text); | ||
295 | |||
296 | /* cut off for dc blocker inside rx chain */ | ||
297 | static const char * const dc_blocker_cutoff_text[] = { | ||
298 | "4Hz", "75Hz", "150Hz", | ||
299 | }; | ||
300 | |||
301 | static SOC_ENUM_SINGLE_DECL(rx1_dcb_cutoff_enum, LPASS_CDC_RX1_B4_CTL, 0, | ||
302 | dc_blocker_cutoff_text); | ||
303 | static SOC_ENUM_SINGLE_DECL(rx2_dcb_cutoff_enum, LPASS_CDC_RX2_B4_CTL, 0, | ||
304 | dc_blocker_cutoff_text); | ||
305 | static SOC_ENUM_SINGLE_DECL(rx3_dcb_cutoff_enum, LPASS_CDC_RX3_B4_CTL, 0, | ||
306 | dc_blocker_cutoff_text); | ||
307 | |||
308 | static const struct snd_kcontrol_new msm8916_wcd_digital_snd_controls[] = { | ||
309 | SOC_SINGLE_S8_TLV("RX1 Digital Volume", LPASS_CDC_RX1_VOL_CTL_B2_CTL, | ||
310 | -128, 127, digital_gain), | ||
311 | SOC_SINGLE_S8_TLV("RX2 Digital Volume", LPASS_CDC_RX2_VOL_CTL_B2_CTL, | ||
312 | -128, 127, digital_gain), | ||
313 | SOC_SINGLE_S8_TLV("RX3 Digital Volume", LPASS_CDC_RX3_VOL_CTL_B2_CTL, | ||
314 | -128, 127, digital_gain), | ||
315 | SOC_SINGLE_S8_TLV("TX1 Digital Volume", LPASS_CDC_TX1_VOL_CTL_GAIN, | ||
316 | -128, 127, digital_gain), | ||
317 | SOC_SINGLE_S8_TLV("TX2 Digital Volume", LPASS_CDC_TX2_VOL_CTL_GAIN, | ||
318 | -128, 127, digital_gain), | ||
319 | SOC_ENUM("TX1 HPF Cutoff", tx1_hpf_cutoff_enum), | ||
320 | SOC_ENUM("TX2 HPF Cutoff", tx2_hpf_cutoff_enum), | ||
321 | SOC_SINGLE("TX1 HPF Switch", LPASS_CDC_TX1_MUX_CTL, 3, 1, 0), | ||
322 | SOC_SINGLE("TX2 HPF Switch", LPASS_CDC_TX2_MUX_CTL, 3, 1, 0), | ||
323 | SOC_ENUM("RX1 DCB Cutoff", rx1_dcb_cutoff_enum), | ||
324 | SOC_ENUM("RX2 DCB Cutoff", rx2_dcb_cutoff_enum), | ||
325 | SOC_ENUM("RX3 DCB Cutoff", rx3_dcb_cutoff_enum), | ||
326 | SOC_SINGLE("RX1 DCB Switch", LPASS_CDC_RX1_B5_CTL, 2, 1, 0), | ||
327 | SOC_SINGLE("RX2 DCB Switch", LPASS_CDC_RX2_B5_CTL, 2, 1, 0), | ||
328 | SOC_SINGLE("RX3 DCB Switch", LPASS_CDC_RX3_B5_CTL, 2, 1, 0), | ||
329 | SOC_SINGLE("RX1 Mute Switch", LPASS_CDC_RX1_B6_CTL, 0, 1, 0), | ||
330 | SOC_SINGLE("RX2 Mute Switch", LPASS_CDC_RX2_B6_CTL, 0, 1, 0), | ||
331 | SOC_SINGLE("RX3 Mute Switch", LPASS_CDC_RX3_B6_CTL, 0, 1, 0), | ||
332 | }; | ||
333 | |||
334 | static int msm8916_wcd_digital_enable_interpolator( | ||
335 | struct snd_soc_dapm_widget *w, | ||
336 | struct snd_kcontrol *kcontrol, | ||
337 | int event) | ||
338 | { | ||
339 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); | ||
340 | |||
341 | switch (event) { | ||
342 | case SND_SOC_DAPM_POST_PMU: | ||
343 | /* apply the digital gain after the interpolator is enabled */ | ||
344 | usleep_range(10000, 10100); | ||
345 | snd_soc_write(codec, rx_gain_reg[w->shift], | ||
346 | snd_soc_read(codec, rx_gain_reg[w->shift])); | ||
347 | break; | ||
348 | } | ||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | static int msm8916_wcd_digital_enable_dec(struct snd_soc_dapm_widget *w, | ||
353 | struct snd_kcontrol *kcontrol, | ||
354 | int event) | ||
355 | { | ||
356 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); | ||
357 | unsigned int decimator = w->shift + 1; | ||
358 | u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg; | ||
359 | u8 dec_hpf_cut_of_freq; | ||
360 | |||
361 | dec_reset_reg = LPASS_CDC_CLK_TX_RESET_B1_CTL; | ||
362 | tx_vol_ctl_reg = LPASS_CDC_TX1_VOL_CTL_CFG + 32 * (decimator - 1); | ||
363 | tx_mux_ctl_reg = LPASS_CDC_TX1_MUX_CTL + 32 * (decimator - 1); | ||
364 | |||
365 | switch (event) { | ||
366 | case SND_SOC_DAPM_PRE_PMU: | ||
367 | /* Enable TX digital mute */ | ||
368 | snd_soc_update_bits(codec, tx_vol_ctl_reg, | ||
369 | TX_VOL_CTL_CFG_MUTE_EN_MASK, | ||
370 | TX_VOL_CTL_CFG_MUTE_EN_ENABLE); | ||
371 | dec_hpf_cut_of_freq = snd_soc_read(codec, tx_mux_ctl_reg) & | ||
372 | TX_MUX_CTL_CUT_OFF_FREQ_MASK; | ||
373 | dec_hpf_cut_of_freq >>= TX_MUX_CTL_CUT_OFF_FREQ_SHIFT; | ||
374 | if (dec_hpf_cut_of_freq != TX_MUX_CTL_CF_NEG_3DB_150HZ) { | ||
375 | /* set cut of freq to CF_MIN_3DB_150HZ (0x1) */ | ||
376 | snd_soc_update_bits(codec, tx_mux_ctl_reg, | ||
377 | TX_MUX_CTL_CUT_OFF_FREQ_MASK, | ||
378 | TX_MUX_CTL_CF_NEG_3DB_150HZ); | ||
379 | } | ||
380 | break; | ||
381 | case SND_SOC_DAPM_POST_PMU: | ||
382 | /* enable HPF */ | ||
383 | snd_soc_update_bits(codec, tx_mux_ctl_reg, | ||
384 | TX_MUX_CTL_HPF_BP_SEL_MASK, | ||
385 | TX_MUX_CTL_HPF_BP_SEL_NO_BYPASS); | ||
386 | /* apply the digital gain after the decimator is enabled */ | ||
387 | snd_soc_write(codec, tx_gain_reg[w->shift], | ||
388 | snd_soc_read(codec, tx_gain_reg[w->shift])); | ||
389 | snd_soc_update_bits(codec, tx_vol_ctl_reg, | ||
390 | TX_VOL_CTL_CFG_MUTE_EN_MASK, 0); | ||
391 | break; | ||
392 | case SND_SOC_DAPM_PRE_PMD: | ||
393 | snd_soc_update_bits(codec, tx_vol_ctl_reg, | ||
394 | TX_VOL_CTL_CFG_MUTE_EN_MASK, | ||
395 | TX_VOL_CTL_CFG_MUTE_EN_ENABLE); | ||
396 | snd_soc_update_bits(codec, tx_mux_ctl_reg, | ||
397 | TX_MUX_CTL_HPF_BP_SEL_MASK, | ||
398 | TX_MUX_CTL_HPF_BP_SEL_BYPASS); | ||
399 | break; | ||
400 | case SND_SOC_DAPM_POST_PMD: | ||
401 | snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, | ||
402 | 1 << w->shift); | ||
403 | snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, 0x0); | ||
404 | snd_soc_update_bits(codec, tx_mux_ctl_reg, | ||
405 | TX_MUX_CTL_HPF_BP_SEL_MASK, | ||
406 | TX_MUX_CTL_HPF_BP_SEL_BYPASS); | ||
407 | snd_soc_update_bits(codec, tx_vol_ctl_reg, | ||
408 | TX_VOL_CTL_CFG_MUTE_EN_MASK, 0); | ||
409 | break; | ||
410 | } | ||
411 | |||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | static int msm8916_wcd_digital_enable_dmic(struct snd_soc_dapm_widget *w, | ||
416 | struct snd_kcontrol *kcontrol, | ||
417 | int event) | ||
418 | { | ||
419 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); | ||
420 | unsigned int dmic; | ||
421 | int ret; | ||
422 | /* get dmic number out of widget name */ | ||
423 | char *dmic_num = strpbrk(w->name, "12"); | ||
424 | |||
425 | if (dmic_num == NULL) { | ||
426 | dev_err(codec->dev, "Invalid DMIC\n"); | ||
427 | return -EINVAL; | ||
428 | } | ||
429 | ret = kstrtouint(dmic_num, 10, &dmic); | ||
430 | if (ret < 0 || dmic > 2) { | ||
431 | dev_err(codec->dev, "Invalid DMIC line on the codec\n"); | ||
432 | return -EINVAL; | ||
433 | } | ||
434 | |||
435 | switch (event) { | ||
436 | case SND_SOC_DAPM_PRE_PMU: | ||
437 | snd_soc_update_bits(codec, LPASS_CDC_CLK_DMIC_B1_CTL, | ||
438 | DMIC_B1_CTL_DMIC0_CLK_SEL_MASK, | ||
439 | DMIC_B1_CTL_DMIC0_CLK_SEL_DIV3); | ||
440 | switch (dmic) { | ||
441 | case 1: | ||
442 | snd_soc_update_bits(codec, LPASS_CDC_TX1_DMIC_CTL, | ||
443 | TXN_DMIC_CTL_CLK_SEL_MASK, | ||
444 | TXN_DMIC_CTL_CLK_SEL_DIV3); | ||
445 | break; | ||
446 | case 2: | ||
447 | snd_soc_update_bits(codec, LPASS_CDC_TX2_DMIC_CTL, | ||
448 | TXN_DMIC_CTL_CLK_SEL_MASK, | ||
449 | TXN_DMIC_CTL_CLK_SEL_DIV3); | ||
450 | break; | ||
451 | } | ||
452 | break; | ||
453 | } | ||
454 | |||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | static const struct snd_soc_dapm_widget msm8916_wcd_digital_dapm_widgets[] = { | ||
459 | /*RX stuff */ | ||
460 | SND_SOC_DAPM_AIF_IN("I2S RX1", NULL, 0, SND_SOC_NOPM, 0, 0), | ||
461 | SND_SOC_DAPM_AIF_IN("I2S RX2", NULL, 0, SND_SOC_NOPM, 0, 0), | ||
462 | SND_SOC_DAPM_AIF_IN("I2S RX3", NULL, 0, SND_SOC_NOPM, 0, 0), | ||
463 | |||
464 | SND_SOC_DAPM_OUTPUT("PDM_RX1"), | ||
465 | SND_SOC_DAPM_OUTPUT("PDM_RX2"), | ||
466 | SND_SOC_DAPM_OUTPUT("PDM_RX3"), | ||
467 | |||
468 | SND_SOC_DAPM_INPUT("LPASS_PDM_TX"), | ||
469 | |||
470 | SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
471 | SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
472 | SND_SOC_DAPM_MIXER("RX3 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
473 | |||
474 | /* Interpolator */ | ||
475 | SND_SOC_DAPM_MIXER_E("RX1 INT", LPASS_CDC_CLK_RX_B1_CTL, 0, 0, NULL, | ||
476 | 0, msm8916_wcd_digital_enable_interpolator, | ||
477 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | ||
478 | SND_SOC_DAPM_MIXER_E("RX2 INT", LPASS_CDC_CLK_RX_B1_CTL, 1, 0, NULL, | ||
479 | 0, msm8916_wcd_digital_enable_interpolator, | ||
480 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | ||
481 | SND_SOC_DAPM_MIXER_E("RX3 INT", LPASS_CDC_CLK_RX_B1_CTL, 2, 0, NULL, | ||
482 | 0, msm8916_wcd_digital_enable_interpolator, | ||
483 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | ||
484 | SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0, | ||
485 | &rx_mix1_inp1_mux), | ||
486 | SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0, | ||
487 | &rx_mix1_inp2_mux), | ||
488 | SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0, | ||
489 | &rx_mix1_inp3_mux), | ||
490 | SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0, | ||
491 | &rx2_mix1_inp1_mux), | ||
492 | SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0, | ||
493 | &rx2_mix1_inp2_mux), | ||
494 | SND_SOC_DAPM_MUX("RX2 MIX1 INP3", SND_SOC_NOPM, 0, 0, | ||
495 | &rx2_mix1_inp3_mux), | ||
496 | SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0, | ||
497 | &rx3_mix1_inp1_mux), | ||
498 | SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0, | ||
499 | &rx3_mix1_inp2_mux), | ||
500 | SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0, | ||
501 | &rx3_mix1_inp3_mux), | ||
502 | |||
503 | /* TX */ | ||
504 | SND_SOC_DAPM_MIXER("ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
505 | SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
506 | SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
507 | |||
508 | SND_SOC_DAPM_MUX_E("DEC1 MUX", LPASS_CDC_CLK_TX_CLK_EN_B1_CTL, 0, 0, | ||
509 | &dec1_mux, msm8916_wcd_digital_enable_dec, | ||
510 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | | ||
511 | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), | ||
512 | SND_SOC_DAPM_MUX_E("DEC2 MUX", LPASS_CDC_CLK_TX_CLK_EN_B1_CTL, 1, 0, | ||
513 | &dec2_mux, msm8916_wcd_digital_enable_dec, | ||
514 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | | ||
515 | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), | ||
516 | SND_SOC_DAPM_AIF_OUT("I2S TX1", NULL, 0, SND_SOC_NOPM, 0, 0), | ||
517 | SND_SOC_DAPM_AIF_OUT("I2S TX2", NULL, 0, SND_SOC_NOPM, 0, 0), | ||
518 | SND_SOC_DAPM_AIF_OUT("I2S TX3", NULL, 0, SND_SOC_NOPM, 0, 0), | ||
519 | |||
520 | /* Digital Mic Inputs */ | ||
521 | SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, | ||
522 | msm8916_wcd_digital_enable_dmic, | ||
523 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), | ||
524 | SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, | ||
525 | msm8916_wcd_digital_enable_dmic, | ||
526 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), | ||
527 | SND_SOC_DAPM_SUPPLY("DMIC_CLK", LPASS_CDC_CLK_DMIC_B1_CTL, 0, 0, | ||
528 | NULL, 0), | ||
529 | SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", LPASS_CDC_CLK_RX_I2S_CTL, | ||
530 | 4, 0, NULL, 0), | ||
531 | SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", LPASS_CDC_CLK_TX_I2S_CTL, 4, 0, | ||
532 | NULL, 0), | ||
533 | |||
534 | SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
535 | SND_SOC_DAPM_SUPPLY("PDM_CLK", LPASS_CDC_CLK_PDM_CTL, 0, 0, NULL, 0), | ||
536 | /* Connectivity Clock */ | ||
537 | SND_SOC_DAPM_SUPPLY_S("CDC_CONN", -2, LPASS_CDC_CLK_OTHR_CTL, 2, 0, | ||
538 | NULL, 0), | ||
539 | |||
540 | }; | ||
541 | |||
542 | static int msm8916_wcd_digital_parse_dt(struct platform_device *pdev, | ||
543 | struct msm8916_wcd_digital_priv *priv) | ||
544 | { | ||
545 | struct device *dev = &pdev->dev; | ||
546 | |||
547 | priv->ahbclk = devm_clk_get(dev, "ahbix-clk"); | ||
548 | if (IS_ERR(priv->ahbclk)) { | ||
549 | dev_err(dev, "failed to get ahbix clk\n"); | ||
550 | return PTR_ERR(priv->ahbclk); | ||
551 | } | ||
552 | |||
553 | priv->mclk = devm_clk_get(dev, "mclk"); | ||
554 | if (IS_ERR(priv->mclk)) { | ||
555 | dev_err(dev, "failed to get mclk\n"); | ||
556 | return PTR_ERR(priv->mclk); | ||
557 | } | ||
558 | |||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | static int msm8916_wcd_digital_codec_probe(struct snd_soc_codec *codec) | ||
563 | { | ||
564 | struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(codec->dev); | ||
565 | |||
566 | snd_soc_codec_set_drvdata(codec, priv); | ||
567 | |||
568 | return 0; | ||
569 | } | ||
570 | |||
571 | static int msm8916_wcd_digital_hw_params(struct snd_pcm_substream *substream, | ||
572 | struct snd_pcm_hw_params *params, | ||
573 | struct snd_soc_dai *dai) | ||
574 | { | ||
575 | u8 tx_fs_rate; | ||
576 | u8 rx_fs_rate; | ||
577 | |||
578 | switch (params_rate(params)) { | ||
579 | case 8000: | ||
580 | tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_8_KHZ; | ||
581 | rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_8_KHZ; | ||
582 | break; | ||
583 | case 16000: | ||
584 | tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_16_KHZ; | ||
585 | rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_16_KHZ; | ||
586 | break; | ||
587 | case 32000: | ||
588 | tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_32_KHZ; | ||
589 | rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_32_KHZ; | ||
590 | break; | ||
591 | case 48000: | ||
592 | tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_48_KHZ; | ||
593 | rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_48_KHZ; | ||
594 | break; | ||
595 | default: | ||
596 | dev_err(dai->codec->dev, "Invalid sampling rate %d\n", | ||
597 | params_rate(params)); | ||
598 | return -EINVAL; | ||
599 | } | ||
600 | |||
601 | switch (substream->stream) { | ||
602 | case SNDRV_PCM_STREAM_CAPTURE: | ||
603 | snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_TX_I2S_CTL, | ||
604 | TX_I2S_CTL_TX_I2S_FS_RATE_MASK, tx_fs_rate); | ||
605 | break; | ||
606 | case SNDRV_PCM_STREAM_PLAYBACK: | ||
607 | snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_RX_I2S_CTL, | ||
608 | RX_I2S_CTL_RX_I2S_FS_RATE_MASK, rx_fs_rate); | ||
609 | break; | ||
610 | default: | ||
611 | return -EINVAL; | ||
612 | } | ||
613 | |||
614 | switch (params_format(params)) { | ||
615 | case SNDRV_PCM_FORMAT_S16_LE: | ||
616 | snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_TX_I2S_CTL, | ||
617 | TX_I2S_CTL_TX_I2S_MODE_MASK, | ||
618 | TX_I2S_CTL_TX_I2S_MODE_16); | ||
619 | snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_RX_I2S_CTL, | ||
620 | RX_I2S_CTL_RX_I2S_MODE_MASK, | ||
621 | RX_I2S_CTL_RX_I2S_MODE_16); | ||
622 | break; | ||
623 | case SNDRV_PCM_FORMAT_S24_LE: | ||
624 | snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_TX_I2S_CTL, | ||
625 | TX_I2S_CTL_TX_I2S_MODE_MASK, | ||
626 | TX_I2S_CTL_TX_I2S_MODE_32); | ||
627 | snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_RX_I2S_CTL, | ||
628 | RX_I2S_CTL_RX_I2S_MODE_MASK, | ||
629 | RX_I2S_CTL_RX_I2S_MODE_32); | ||
630 | break; | ||
631 | default: | ||
632 | dev_err(dai->dev, "%s: wrong format selected\n", __func__); | ||
633 | return -EINVAL; | ||
634 | } | ||
635 | |||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | static const struct snd_soc_dapm_route msm8916_wcd_digital_audio_map[] = { | ||
640 | |||
641 | {"I2S RX1", NULL, "AIF1 Playback"}, | ||
642 | {"I2S RX2", NULL, "AIF1 Playback"}, | ||
643 | {"I2S RX3", NULL, "AIF1 Playback"}, | ||
644 | |||
645 | {"AIF1 Capture", NULL, "I2S TX1"}, | ||
646 | {"AIF1 Capture", NULL, "I2S TX2"}, | ||
647 | {"AIF1 Capture", NULL, "I2S TX3"}, | ||
648 | |||
649 | /* Decimator Inputs */ | ||
650 | {"DEC1 MUX", "DMIC1", "DMIC1"}, | ||
651 | {"DEC1 MUX", "DMIC2", "DMIC2"}, | ||
652 | {"DEC1 MUX", "ADC1", "ADC1"}, | ||
653 | {"DEC1 MUX", "ADC2", "ADC2"}, | ||
654 | {"DEC1 MUX", "ADC3", "ADC3"}, | ||
655 | {"DEC1 MUX", NULL, "CDC_CONN"}, | ||
656 | |||
657 | {"DEC2 MUX", "DMIC1", "DMIC1"}, | ||
658 | {"DEC2 MUX", "DMIC2", "DMIC2"}, | ||
659 | {"DEC2 MUX", "ADC1", "ADC1"}, | ||
660 | {"DEC2 MUX", "ADC2", "ADC2"}, | ||
661 | {"DEC2 MUX", "ADC3", "ADC3"}, | ||
662 | {"DEC2 MUX", NULL, "CDC_CONN"}, | ||
663 | |||
664 | {"DMIC1", NULL, "DMIC_CLK"}, | ||
665 | {"DMIC2", NULL, "DMIC_CLK"}, | ||
666 | |||
667 | {"I2S TX1", NULL, "DEC1 MUX"}, | ||
668 | {"I2S TX2", NULL, "DEC2 MUX"}, | ||
669 | |||
670 | {"I2S TX1", NULL, "TX_I2S_CLK"}, | ||
671 | {"I2S TX2", NULL, "TX_I2S_CLK"}, | ||
672 | |||
673 | {"TX_I2S_CLK", NULL, "MCLK"}, | ||
674 | {"TX_I2S_CLK", NULL, "PDM_CLK"}, | ||
675 | |||
676 | {"ADC1", NULL, "LPASS_PDM_TX"}, | ||
677 | {"ADC2", NULL, "LPASS_PDM_TX"}, | ||
678 | {"ADC3", NULL, "LPASS_PDM_TX"}, | ||
679 | |||
680 | {"I2S RX1", NULL, "RX_I2S_CLK"}, | ||
681 | {"I2S RX2", NULL, "RX_I2S_CLK"}, | ||
682 | {"I2S RX3", NULL, "RX_I2S_CLK"}, | ||
683 | |||
684 | {"RX_I2S_CLK", NULL, "PDM_CLK"}, | ||
685 | {"RX_I2S_CLK", NULL, "MCLK"}, | ||
686 | {"RX_I2S_CLK", NULL, "CDC_CONN"}, | ||
687 | |||
688 | /* RX1 PATH.. */ | ||
689 | {"PDM_RX1", NULL, "RX1 INT"}, | ||
690 | {"RX1 INT", NULL, "RX1 MIX1"}, | ||
691 | |||
692 | {"RX1 MIX1", NULL, "RX1 MIX1 INP1"}, | ||
693 | {"RX1 MIX1", NULL, "RX1 MIX1 INP2"}, | ||
694 | {"RX1 MIX1", NULL, "RX1 MIX1 INP3"}, | ||
695 | |||
696 | {"RX1 MIX1 INP1", "RX1", "I2S RX1"}, | ||
697 | {"RX1 MIX1 INP1", "RX2", "I2S RX2"}, | ||
698 | {"RX1 MIX1 INP1", "RX3", "I2S RX3"}, | ||
699 | |||
700 | {"RX1 MIX1 INP2", "RX1", "I2S RX1"}, | ||
701 | {"RX1 MIX1 INP2", "RX2", "I2S RX2"}, | ||
702 | {"RX1 MIX1 INP2", "RX3", "I2S RX3"}, | ||
703 | |||
704 | {"RX1 MIX1 INP3", "RX1", "I2S RX1"}, | ||
705 | {"RX1 MIX1 INP3", "RX2", "I2S RX2"}, | ||
706 | {"RX1 MIX1 INP3", "RX3", "I2S RX3"}, | ||
707 | |||
708 | /* RX2 PATH */ | ||
709 | {"PDM_RX2", NULL, "RX2 INT"}, | ||
710 | {"RX2 INT", NULL, "RX2 MIX1"}, | ||
711 | |||
712 | {"RX2 MIX1", NULL, "RX2 MIX1 INP1"}, | ||
713 | {"RX2 MIX1", NULL, "RX2 MIX1 INP2"}, | ||
714 | {"RX2 MIX1", NULL, "RX2 MIX1 INP3"}, | ||
715 | |||
716 | {"RX2 MIX1 INP1", "RX1", "I2S RX1"}, | ||
717 | {"RX2 MIX1 INP1", "RX2", "I2S RX2"}, | ||
718 | {"RX2 MIX1 INP1", "RX3", "I2S RX3"}, | ||
719 | |||
720 | {"RX2 MIX1 INP2", "RX1", "I2S RX1"}, | ||
721 | {"RX2 MIX1 INP2", "RX2", "I2S RX2"}, | ||
722 | {"RX2 MIX1 INP2", "RX3", "I2S RX3"}, | ||
723 | |||
724 | {"RX2 MIX1 INP3", "RX1", "I2S RX1"}, | ||
725 | {"RX2 MIX1 INP3", "RX2", "I2S RX2"}, | ||
726 | {"RX2 MIX1 INP3", "RX3", "I2S RX3"}, | ||
727 | |||
728 | /* RX3 PATH */ | ||
729 | {"PDM_RX3", NULL, "RX3 INT"}, | ||
730 | {"RX3 INT", NULL, "RX3 MIX1"}, | ||
731 | |||
732 | {"RX3 MIX1", NULL, "RX3 MIX1 INP1"}, | ||
733 | {"RX3 MIX1", NULL, "RX3 MIX1 INP2"}, | ||
734 | {"RX3 MIX1", NULL, "RX3 MIX1 INP3"}, | ||
735 | |||
736 | {"RX3 MIX1 INP1", "RX1", "I2S RX1"}, | ||
737 | {"RX3 MIX1 INP1", "RX2", "I2S RX2"}, | ||
738 | {"RX3 MIX1 INP1", "RX3", "I2S RX3"}, | ||
739 | |||
740 | {"RX3 MIX1 INP2", "RX1", "I2S RX1"}, | ||
741 | {"RX3 MIX1 INP2", "RX2", "I2S RX2"}, | ||
742 | {"RX3 MIX1 INP2", "RX3", "I2S RX3"}, | ||
743 | |||
744 | {"RX3 MIX1 INP3", "RX1", "I2S RX1"}, | ||
745 | {"RX3 MIX1 INP3", "RX2", "I2S RX2"}, | ||
746 | {"RX3 MIX1 INP3", "RX3", "I2S RX3"}, | ||
747 | |||
748 | }; | ||
749 | |||
750 | static int msm8916_wcd_digital_startup(struct snd_pcm_substream *substream, | ||
751 | struct snd_soc_dai *dai) | ||
752 | { | ||
753 | struct snd_soc_codec *codec = dai->codec; | ||
754 | struct msm8916_wcd_digital_priv *msm8916_wcd; | ||
755 | unsigned long mclk_rate; | ||
756 | |||
757 | msm8916_wcd = snd_soc_codec_get_drvdata(codec); | ||
758 | snd_soc_update_bits(codec, LPASS_CDC_CLK_MCLK_CTL, | ||
759 | MCLK_CTL_MCLK_EN_MASK, | ||
760 | MCLK_CTL_MCLK_EN_ENABLE); | ||
761 | snd_soc_update_bits(codec, LPASS_CDC_CLK_PDM_CTL, | ||
762 | LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK, | ||
763 | LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_FB); | ||
764 | |||
765 | mclk_rate = clk_get_rate(msm8916_wcd->mclk); | ||
766 | switch (mclk_rate) { | ||
767 | case 12288000: | ||
768 | snd_soc_update_bits(codec, LPASS_CDC_TOP_CTL, | ||
769 | TOP_CTL_DIG_MCLK_FREQ_MASK, | ||
770 | TOP_CTL_DIG_MCLK_FREQ_F_12_288MHZ); | ||
771 | break; | ||
772 | case 9600000: | ||
773 | snd_soc_update_bits(codec, LPASS_CDC_TOP_CTL, | ||
774 | TOP_CTL_DIG_MCLK_FREQ_MASK, | ||
775 | TOP_CTL_DIG_MCLK_FREQ_F_9_6MHZ); | ||
776 | break; | ||
777 | default: | ||
778 | dev_err(codec->dev, "Invalid mclk rate %ld\n", mclk_rate); | ||
779 | break; | ||
780 | } | ||
781 | return 0; | ||
782 | } | ||
783 | |||
784 | static void msm8916_wcd_digital_shutdown(struct snd_pcm_substream *substream, | ||
785 | struct snd_soc_dai *dai) | ||
786 | { | ||
787 | snd_soc_update_bits(dai->codec, LPASS_CDC_CLK_PDM_CTL, | ||
788 | LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK, 0); | ||
789 | } | ||
790 | |||
791 | static struct snd_soc_dai_ops msm8916_wcd_digital_dai_ops = { | ||
792 | .startup = msm8916_wcd_digital_startup, | ||
793 | .shutdown = msm8916_wcd_digital_shutdown, | ||
794 | .hw_params = msm8916_wcd_digital_hw_params, | ||
795 | }; | ||
796 | |||
797 | static struct snd_soc_dai_driver msm8916_wcd_digital_dai[] = { | ||
798 | [0] = { | ||
799 | .name = "msm8916_wcd_digital_i2s_rx1", | ||
800 | .id = 0, | ||
801 | .playback = { | ||
802 | .stream_name = "AIF1 Playback", | ||
803 | .rates = MSM8916_WCD_DIGITAL_RATES, | ||
804 | .formats = MSM8916_WCD_DIGITAL_FORMATS, | ||
805 | .channels_min = 1, | ||
806 | .channels_max = 3, | ||
807 | }, | ||
808 | .ops = &msm8916_wcd_digital_dai_ops, | ||
809 | }, | ||
810 | [1] = { | ||
811 | .name = "msm8916_wcd_digital_i2s_tx1", | ||
812 | .id = 1, | ||
813 | .capture = { | ||
814 | .stream_name = "AIF1 Capture", | ||
815 | .rates = MSM8916_WCD_DIGITAL_RATES, | ||
816 | .formats = MSM8916_WCD_DIGITAL_FORMATS, | ||
817 | .channels_min = 1, | ||
818 | .channels_max = 4, | ||
819 | }, | ||
820 | .ops = &msm8916_wcd_digital_dai_ops, | ||
821 | }, | ||
822 | }; | ||
823 | |||
824 | static struct snd_soc_codec_driver msm8916_wcd_digital = { | ||
825 | .probe = msm8916_wcd_digital_codec_probe, | ||
826 | .component_driver = { | ||
827 | .controls = msm8916_wcd_digital_snd_controls, | ||
828 | .num_controls = ARRAY_SIZE(msm8916_wcd_digital_snd_controls), | ||
829 | .dapm_widgets = msm8916_wcd_digital_dapm_widgets, | ||
830 | .num_dapm_widgets = | ||
831 | ARRAY_SIZE(msm8916_wcd_digital_dapm_widgets), | ||
832 | .dapm_routes = msm8916_wcd_digital_audio_map, | ||
833 | .num_dapm_routes = ARRAY_SIZE(msm8916_wcd_digital_audio_map), | ||
834 | }, | ||
835 | }; | ||
836 | |||
837 | static const struct regmap_config msm8916_codec_regmap_config = { | ||
838 | .reg_bits = 32, | ||
839 | .reg_stride = 4, | ||
840 | .val_bits = 32, | ||
841 | .max_register = LPASS_CDC_TX2_DMIC_CTL, | ||
842 | .cache_type = REGCACHE_FLAT, | ||
843 | }; | ||
844 | |||
845 | static int msm8916_wcd_digital_probe(struct platform_device *pdev) | ||
846 | { | ||
847 | struct msm8916_wcd_digital_priv *priv; | ||
848 | struct device *dev = &pdev->dev; | ||
849 | void __iomem *base; | ||
850 | struct resource *mem_res; | ||
851 | struct regmap *digital_map; | ||
852 | int ret; | ||
853 | |||
854 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
855 | if (!priv) | ||
856 | return -ENOMEM; | ||
857 | |||
858 | mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
859 | base = devm_ioremap_resource(&pdev->dev, mem_res); | ||
860 | if (IS_ERR(base)) | ||
861 | return PTR_ERR(base); | ||
862 | |||
863 | digital_map = | ||
864 | devm_regmap_init_mmio(&pdev->dev, base, | ||
865 | &msm8916_codec_regmap_config); | ||
866 | if (IS_ERR(digital_map)) | ||
867 | return PTR_ERR(digital_map); | ||
868 | |||
869 | ret = msm8916_wcd_digital_parse_dt(pdev, priv); | ||
870 | if (ret < 0) | ||
871 | return ret; | ||
872 | |||
873 | ret = clk_prepare_enable(priv->ahbclk); | ||
874 | if (ret < 0) { | ||
875 | dev_err(dev, "failed to enable ahbclk %d\n", ret); | ||
876 | return ret; | ||
877 | } | ||
878 | |||
879 | ret = clk_prepare_enable(priv->mclk); | ||
880 | if (ret < 0) { | ||
881 | dev_err(dev, "failed to enable mclk %d\n", ret); | ||
882 | return ret; | ||
883 | } | ||
884 | |||
885 | dev_set_drvdata(dev, priv); | ||
886 | |||
887 | return snd_soc_register_codec(dev, &msm8916_wcd_digital, | ||
888 | msm8916_wcd_digital_dai, | ||
889 | ARRAY_SIZE(msm8916_wcd_digital_dai)); | ||
890 | } | ||
891 | |||
892 | static int msm8916_wcd_digital_remove(struct platform_device *pdev) | ||
893 | { | ||
894 | struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(&pdev->dev); | ||
895 | |||
896 | snd_soc_unregister_codec(&pdev->dev); | ||
897 | clk_disable_unprepare(priv->mclk); | ||
898 | clk_disable_unprepare(priv->ahbclk); | ||
899 | |||
900 | return 0; | ||
901 | } | ||
902 | |||
903 | static const struct of_device_id msm8916_wcd_digital_match_table[] = { | ||
904 | { .compatible = "qcom,msm8916-wcd-digital-codec" }, | ||
905 | { } | ||
906 | }; | ||
907 | |||
908 | MODULE_DEVICE_TABLE(of, msm8916_wcd_digital_match_table); | ||
909 | |||
910 | static struct platform_driver msm8916_wcd_digital_driver = { | ||
911 | .driver = { | ||
912 | .name = "msm8916-wcd-digital-codec", | ||
913 | .of_match_table = msm8916_wcd_digital_match_table, | ||
914 | }, | ||
915 | .probe = msm8916_wcd_digital_probe, | ||
916 | .remove = msm8916_wcd_digital_remove, | ||
917 | }; | ||
918 | |||
919 | module_platform_driver(msm8916_wcd_digital_driver); | ||
920 | |||
921 | MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>"); | ||
922 | MODULE_DESCRIPTION("MSM8916 WCD Digital Codec driver"); | ||
923 | MODULE_LICENSE("GPL v2"); | ||