1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * ALSA SoC CPCAP codec driver |
4 | * |
5 | * Copyright (C) 2017 - 2018 Sebastian Reichel <sre@kernel.org> |
6 | * |
7 | * Very loosely based on original driver from Motorola: |
8 | * Copyright (C) 2007 - 2009 Motorola, Inc. |
9 | */ |
10 | |
11 | #include <linux/module.h> |
12 | #include <linux/regmap.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/mfd/motorola-cpcap.h> |
15 | #include <sound/core.h> |
16 | #include <sound/soc.h> |
17 | #include <sound/tlv.h> |
18 | |
19 | /* Register 512 CPCAP_REG_VAUDIOC --- Audio Regulator and Bias Voltage */ |
20 | #define CPCAP_BIT_AUDIO_LOW_PWR 6 |
21 | #define CPCAP_BIT_AUD_LOWPWR_SPEED 5 |
22 | #define CPCAP_BIT_VAUDIOPRISTBY 4 |
23 | #define CPCAP_BIT_VAUDIO_MODE1 2 |
24 | #define CPCAP_BIT_VAUDIO_MODE0 1 |
25 | #define CPCAP_BIT_V_AUDIO_EN 0 |
26 | |
27 | /* Register 513 CPCAP_REG_CC --- CODEC */ |
28 | #define CPCAP_BIT_CDC_CLK2 15 |
29 | #define CPCAP_BIT_CDC_CLK1 14 |
30 | #define CPCAP_BIT_CDC_CLK0 13 |
31 | #define CPCAP_BIT_CDC_SR3 12 |
32 | #define CPCAP_BIT_CDC_SR2 11 |
33 | #define CPCAP_BIT_CDC_SR1 10 |
34 | #define CPCAP_BIT_CDC_SR0 9 |
35 | #define CPCAP_BIT_CDC_CLOCK_TREE_RESET 8 |
36 | #define CPCAP_BIT_MIC2_CDC_EN 7 |
37 | #define CPCAP_BIT_CDC_EN_RX 6 |
38 | #define CPCAP_BIT_DF_RESET 5 |
39 | #define CPCAP_BIT_MIC1_CDC_EN 4 |
40 | #define CPCAP_BIT_AUDOHPF_1 3 |
41 | #define CPCAP_BIT_AUDOHPF_0 2 |
42 | #define CPCAP_BIT_AUDIHPF_1 1 |
43 | #define CPCAP_BIT_AUDIHPF_0 0 |
44 | |
45 | /* Register 514 CPCAP_REG_CDI --- CODEC Digital Audio Interface */ |
46 | #define CPCAP_BIT_CDC_PLL_SEL 15 |
47 | #define CPCAP_BIT_CLK_IN_SEL 13 |
48 | #define CPCAP_BIT_DIG_AUD_IN 12 |
49 | #define CPCAP_BIT_CDC_CLK_EN 11 |
50 | #define CPCAP_BIT_CDC_DIG_AUD_FS1 10 |
51 | #define CPCAP_BIT_CDC_DIG_AUD_FS0 9 |
52 | #define CPCAP_BIT_MIC2_TIMESLOT2 8 |
53 | #define CPCAP_BIT_MIC2_TIMESLOT1 7 |
54 | #define CPCAP_BIT_MIC2_TIMESLOT0 6 |
55 | #define CPCAP_BIT_MIC1_RX_TIMESLOT2 5 |
56 | #define CPCAP_BIT_MIC1_RX_TIMESLOT1 4 |
57 | #define CPCAP_BIT_MIC1_RX_TIMESLOT0 3 |
58 | #define CPCAP_BIT_FS_INV 2 |
59 | #define CPCAP_BIT_CLK_INV 1 |
60 | #define CPCAP_BIT_SMB_CDC 0 |
61 | |
62 | /* Register 515 CPCAP_REG_SDAC --- Stereo DAC */ |
63 | #define CPCAP_BIT_FSYNC_CLK_IN_COMMON 11 |
64 | #define CPCAP_BIT_SLAVE_PLL_CLK_INPUT 10 |
65 | #define CPCAP_BIT_ST_CLOCK_TREE_RESET 9 |
66 | #define CPCAP_BIT_DF_RESET_ST_DAC 8 |
67 | #define CPCAP_BIT_ST_SR3 7 |
68 | #define CPCAP_BIT_ST_SR2 6 |
69 | #define CPCAP_BIT_ST_SR1 5 |
70 | #define CPCAP_BIT_ST_SR0 4 |
71 | #define CPCAP_BIT_ST_DAC_CLK2 3 |
72 | #define CPCAP_BIT_ST_DAC_CLK1 2 |
73 | #define CPCAP_BIT_ST_DAC_CLK0 1 |
74 | #define CPCAP_BIT_ST_DAC_EN 0 |
75 | |
76 | /* Register 516 CPCAP_REG_SDACDI --- Stereo DAC Digital Audio Interface */ |
77 | #define CPCAP_BIT_ST_L_TIMESLOT2 13 |
78 | #define CPCAP_BIT_ST_L_TIMESLOT1 12 |
79 | #define CPCAP_BIT_ST_L_TIMESLOT0 11 |
80 | #define CPCAP_BIT_ST_R_TIMESLOT2 10 |
81 | #define CPCAP_BIT_ST_R_TIMESLOT1 9 |
82 | #define CPCAP_BIT_ST_R_TIMESLOT0 8 |
83 | #define CPCAP_BIT_ST_DAC_CLK_IN_SEL 7 |
84 | #define CPCAP_BIT_ST_FS_INV 6 |
85 | #define CPCAP_BIT_ST_CLK_INV 5 |
86 | #define CPCAP_BIT_ST_DIG_AUD_FS1 4 |
87 | #define CPCAP_BIT_ST_DIG_AUD_FS0 3 |
88 | #define CPCAP_BIT_DIG_AUD_IN_ST_DAC 2 |
89 | #define CPCAP_BIT_ST_CLK_EN 1 |
90 | #define CPCAP_BIT_SMB_ST_DAC 0 |
91 | |
92 | /* Register 517 CPCAP_REG_TXI --- TX Interface */ |
93 | #define CPCAP_BIT_PTT_TH 15 |
94 | #define CPCAP_BIT_PTT_CMP_EN 14 |
95 | #define CPCAP_BIT_HS_ID_TX 13 |
96 | #define CPCAP_BIT_MB_ON2 12 |
97 | #define CPCAP_BIT_MB_ON1L 11 |
98 | #define CPCAP_BIT_MB_ON1R 10 |
99 | #define CPCAP_BIT_RX_L_ENCODE 9 |
100 | #define CPCAP_BIT_RX_R_ENCODE 8 |
101 | #define CPCAP_BIT_MIC2_MUX 7 |
102 | #define CPCAP_BIT_MIC2_PGA_EN 6 |
103 | #define CPCAP_BIT_CDET_DIS 5 |
104 | #define CPCAP_BIT_EMU_MIC_MUX 4 |
105 | #define CPCAP_BIT_HS_MIC_MUX 3 |
106 | #define CPCAP_BIT_MIC1_MUX 2 |
107 | #define CPCAP_BIT_MIC1_PGA_EN 1 |
108 | #define CPCAP_BIT_DLM 0 |
109 | |
110 | /* Register 518 CPCAP_REG_TXMP --- Mic Gain */ |
111 | #define CPCAP_BIT_MB_BIAS_R1 11 |
112 | #define CPCAP_BIT_MB_BIAS_R0 10 |
113 | #define CPCAP_BIT_MIC2_GAIN_4 9 |
114 | #define CPCAP_BIT_MIC2_GAIN_3 8 |
115 | #define CPCAP_BIT_MIC2_GAIN_2 7 |
116 | #define CPCAP_BIT_MIC2_GAIN_1 6 |
117 | #define CPCAP_BIT_MIC2_GAIN_0 5 |
118 | #define CPCAP_BIT_MIC1_GAIN_4 4 |
119 | #define CPCAP_BIT_MIC1_GAIN_3 3 |
120 | #define CPCAP_BIT_MIC1_GAIN_2 2 |
121 | #define CPCAP_BIT_MIC1_GAIN_1 1 |
122 | #define CPCAP_BIT_MIC1_GAIN_0 0 |
123 | |
124 | /* Register 519 CPCAP_REG_RXOA --- RX Output Amplifier */ |
125 | #define CPCAP_BIT_UNUSED_519_15 15 |
126 | #define CPCAP_BIT_UNUSED_519_14 14 |
127 | #define CPCAP_BIT_UNUSED_519_13 13 |
128 | #define CPCAP_BIT_STDAC_LOW_PWR_DISABLE 12 |
129 | #define CPCAP_BIT_HS_LOW_PWR 11 |
130 | #define CPCAP_BIT_HS_ID_RX 10 |
131 | #define CPCAP_BIT_ST_HS_CP_EN 9 |
132 | #define CPCAP_BIT_EMU_SPKR_R_EN 8 |
133 | #define CPCAP_BIT_EMU_SPKR_L_EN 7 |
134 | #define CPCAP_BIT_HS_L_EN 6 |
135 | #define CPCAP_BIT_HS_R_EN 5 |
136 | #define CPCAP_BIT_A4_LINEOUT_L_EN 4 |
137 | #define CPCAP_BIT_A4_LINEOUT_R_EN 3 |
138 | #define CPCAP_BIT_A2_LDSP_L_EN 2 |
139 | #define CPCAP_BIT_A2_LDSP_R_EN 1 |
140 | #define CPCAP_BIT_A1_EAR_EN 0 |
141 | |
142 | /* Register 520 CPCAP_REG_RXVC --- RX Volume Control */ |
143 | #define CPCAP_BIT_VOL_EXT3 15 |
144 | #define CPCAP_BIT_VOL_EXT2 14 |
145 | #define CPCAP_BIT_VOL_EXT1 13 |
146 | #define CPCAP_BIT_VOL_EXT0 12 |
147 | #define CPCAP_BIT_VOL_DAC3 11 |
148 | #define CPCAP_BIT_VOL_DAC2 10 |
149 | #define CPCAP_BIT_VOL_DAC1 9 |
150 | #define CPCAP_BIT_VOL_DAC0 8 |
151 | #define CPCAP_BIT_VOL_DAC_LSB_1dB1 7 |
152 | #define CPCAP_BIT_VOL_DAC_LSB_1dB0 6 |
153 | #define CPCAP_BIT_VOL_CDC3 5 |
154 | #define CPCAP_BIT_VOL_CDC2 4 |
155 | #define CPCAP_BIT_VOL_CDC1 3 |
156 | #define CPCAP_BIT_VOL_CDC0 2 |
157 | #define CPCAP_BIT_VOL_CDC_LSB_1dB1 1 |
158 | #define CPCAP_BIT_VOL_CDC_LSB_1dB0 0 |
159 | |
160 | /* Register 521 CPCAP_REG_RXCOA --- Codec to Output Amp Switches */ |
161 | #define CPCAP_BIT_PGA_CDC_EN 10 |
162 | #define CPCAP_BIT_CDC_SW 9 |
163 | #define CPCAP_BIT_PGA_OUTR_USBDP_CDC_SW 8 |
164 | #define CPCAP_BIT_PGA_OUTL_USBDN_CDC_SW 7 |
165 | #define CPCAP_BIT_ALEFT_HS_CDC_SW 6 |
166 | #define CPCAP_BIT_ARIGHT_HS_CDC_SW 5 |
167 | #define CPCAP_BIT_A4_LINEOUT_L_CDC_SW 4 |
168 | #define CPCAP_BIT_A4_LINEOUT_R_CDC_SW 3 |
169 | #define CPCAP_BIT_A2_LDSP_L_CDC_SW 2 |
170 | #define CPCAP_BIT_A2_LDSP_R_CDC_SW 1 |
171 | #define CPCAP_BIT_A1_EAR_CDC_SW 0 |
172 | |
173 | /* Register 522 CPCAP_REG_RXSDOA --- RX Stereo DAC to Output Amp Switches */ |
174 | #define CPCAP_BIT_PGA_DAC_EN 12 |
175 | #define CPCAP_BIT_ST_DAC_SW 11 |
176 | #define CPCAP_BIT_MONO_DAC1 10 |
177 | #define CPCAP_BIT_MONO_DAC0 9 |
178 | #define CPCAP_BIT_PGA_OUTR_USBDP_DAC_SW 8 |
179 | #define CPCAP_BIT_PGA_OUTL_USBDN_DAC_SW 7 |
180 | #define CPCAP_BIT_ALEFT_HS_DAC_SW 6 |
181 | #define CPCAP_BIT_ARIGHT_HS_DAC_SW 5 |
182 | #define CPCAP_BIT_A4_LINEOUT_L_DAC_SW 4 |
183 | #define CPCAP_BIT_A4_LINEOUT_R_DAC_SW 3 |
184 | #define CPCAP_BIT_A2_LDSP_L_DAC_SW 2 |
185 | #define CPCAP_BIT_A2_LDSP_R_DAC_SW 1 |
186 | #define CPCAP_BIT_A1_EAR_DAC_SW 0 |
187 | |
188 | /* Register 523 CPCAP_REG_RXEPOA --- RX External PGA to Output Amp Switches */ |
189 | #define CPCAP_BIT_PGA_EXT_L_EN 14 |
190 | #define CPCAP_BIT_PGA_EXT_R_EN 13 |
191 | #define CPCAP_BIT_PGA_IN_L_SW 12 |
192 | #define CPCAP_BIT_PGA_IN_R_SW 11 |
193 | #define CPCAP_BIT_MONO_EXT1 10 |
194 | #define CPCAP_BIT_MONO_EXT0 9 |
195 | #define CPCAP_BIT_PGA_OUTR_USBDP_EXT_SW 8 |
196 | #define CPCAP_BIT_PGA_OUTL_USBDN_EXT_SW 7 |
197 | #define CPCAP_BIT_ALEFT_HS_EXT_SW 6 |
198 | #define CPCAP_BIT_ARIGHT_HS_EXT_SW 5 |
199 | #define CPCAP_BIT_A4_LINEOUT_L_EXT_SW 4 |
200 | #define CPCAP_BIT_A4_LINEOUT_R_EXT_SW 3 |
201 | #define CPCAP_BIT_A2_LDSP_L_EXT_SW 2 |
202 | #define CPCAP_BIT_A2_LDSP_R_EXT_SW 1 |
203 | #define CPCAP_BIT_A1_EAR_EXT_SW 0 |
204 | |
205 | /* Register 525 CPCAP_REG_A2LA --- SPK Amplifier and Clock Config for Headset */ |
206 | #define CPCAP_BIT_NCP_CLK_SYNC 7 |
207 | #define CPCAP_BIT_A2_CLK_SYNC 6 |
208 | #define CPCAP_BIT_A2_FREE_RUN 5 |
209 | #define CPCAP_BIT_A2_CLK2 4 |
210 | #define CPCAP_BIT_A2_CLK1 3 |
211 | #define CPCAP_BIT_A2_CLK0 2 |
212 | #define CPCAP_BIT_A2_CLK_IN 1 |
213 | #define CPCAP_BIT_A2_CONFIG 0 |
214 | |
215 | #define SLEEP_ACTIVATE_POWER 2 |
216 | #define CLOCK_TREE_RESET_TIME 1 |
217 | |
218 | /* constants for ST delay workaround */ |
219 | #define STM_STDAC_ACTIVATE_RAMP_TIME 1 |
220 | #define STM_STDAC_EN_TEST_PRE 0x090C |
221 | #define STM_STDAC_EN_TEST_POST 0x0000 |
222 | #define STM_STDAC_EN_ST_TEST1_PRE 0x2400 |
223 | #define STM_STDAC_EN_ST_TEST1_POST 0x0400 |
224 | |
225 | struct cpcap_reg_info { |
226 | u16 reg; |
227 | u16 mask; |
228 | u16 val; |
229 | }; |
230 | |
231 | static const struct cpcap_reg_info cpcap_default_regs[] = { |
232 | { CPCAP_REG_VAUDIOC, 0x003F, 0x0000 }, |
233 | { CPCAP_REG_CC, 0xFFFF, 0x0000 }, |
234 | { CPCAP_REG_CC, 0xFFFF, 0x0000 }, |
235 | { CPCAP_REG_CDI, 0xBFFF, 0x0000 }, |
236 | { CPCAP_REG_SDAC, 0x0FFF, 0x0000 }, |
237 | { CPCAP_REG_SDACDI, 0x3FFF, 0x0000 }, |
238 | { CPCAP_REG_TXI, 0x0FDF, 0x0000 }, |
239 | { CPCAP_REG_TXMP, 0x0FFF, 0x0400 }, |
240 | { CPCAP_REG_RXOA, 0x01FF, 0x0000 }, |
241 | { CPCAP_REG_RXVC, 0xFF3C, 0x0000 }, |
242 | { CPCAP_REG_RXCOA, 0x07FF, 0x0000 }, |
243 | { CPCAP_REG_RXSDOA, 0x1FFF, 0x0000 }, |
244 | { CPCAP_REG_RXEPOA, 0x7FFF, 0x0000 }, |
245 | { CPCAP_REG_A2LA, BIT(CPCAP_BIT_A2_FREE_RUN), |
246 | BIT(CPCAP_BIT_A2_FREE_RUN) }, |
247 | }; |
248 | |
249 | enum cpcap_dai { |
250 | CPCAP_DAI_HIFI, |
251 | CPCAP_DAI_VOICE, |
252 | }; |
253 | |
254 | struct cpcap_audio { |
255 | struct snd_soc_component *component; |
256 | struct regmap *regmap; |
257 | |
258 | u16 vendor; |
259 | |
260 | int codec_clk_id; |
261 | int codec_freq; |
262 | int codec_format; |
263 | }; |
264 | |
265 | static int cpcap_st_workaround(struct snd_soc_dapm_widget *w, |
266 | struct snd_kcontrol *kcontrol, int event) |
267 | { |
268 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
269 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
270 | int err = 0; |
271 | |
272 | /* Only CPCAP from ST requires workaround */ |
273 | if (cpcap->vendor != CPCAP_VENDOR_ST) |
274 | return 0; |
275 | |
276 | switch (event) { |
277 | case SND_SOC_DAPM_PRE_PMU: |
278 | err = regmap_write(map: cpcap->regmap, CPCAP_REG_TEST, |
279 | STM_STDAC_EN_TEST_PRE); |
280 | if (err) |
281 | return err; |
282 | err = regmap_write(map: cpcap->regmap, CPCAP_REG_ST_TEST1, |
283 | STM_STDAC_EN_ST_TEST1_PRE); |
284 | break; |
285 | case SND_SOC_DAPM_POST_PMU: |
286 | msleep(STM_STDAC_ACTIVATE_RAMP_TIME); |
287 | |
288 | err = regmap_write(map: cpcap->regmap, CPCAP_REG_ST_TEST1, |
289 | STM_STDAC_EN_ST_TEST1_POST); |
290 | if (err) |
291 | return err; |
292 | err = regmap_write(map: cpcap->regmap, CPCAP_REG_TEST, |
293 | STM_STDAC_EN_TEST_POST); |
294 | break; |
295 | default: |
296 | break; |
297 | } |
298 | |
299 | return err; |
300 | } |
301 | |
302 | /* Capture Gain Control: 0dB to 31dB in 1dB steps */ |
303 | static const DECLARE_TLV_DB_SCALE(mic_gain_tlv, 0, 100, 0); |
304 | |
305 | /* Playback Gain Control: -33dB to 12dB in 3dB steps */ |
306 | static const DECLARE_TLV_DB_SCALE(vol_tlv, -3300, 300, 0); |
307 | |
308 | static const struct snd_kcontrol_new cpcap_snd_controls[] = { |
309 | /* Playback Gain */ |
310 | SOC_SINGLE_TLV("HiFi Playback Volume" , |
311 | CPCAP_REG_RXVC, CPCAP_BIT_VOL_DAC0, 0xF, 0, vol_tlv), |
312 | SOC_SINGLE_TLV("Voice Playback Volume" , |
313 | CPCAP_REG_RXVC, CPCAP_BIT_VOL_CDC0, 0xF, 0, vol_tlv), |
314 | SOC_SINGLE_TLV("Ext Playback Volume" , |
315 | CPCAP_REG_RXVC, CPCAP_BIT_VOL_EXT0, 0xF, 0, vol_tlv), |
316 | |
317 | /* Capture Gain */ |
318 | SOC_SINGLE_TLV("Mic1 Capture Volume" , |
319 | CPCAP_REG_TXMP, CPCAP_BIT_MIC1_GAIN_0, 0x1F, 0, mic_gain_tlv), |
320 | SOC_SINGLE_TLV("Mic2 Capture Volume" , |
321 | CPCAP_REG_TXMP, CPCAP_BIT_MIC2_GAIN_0, 0x1F, 0, mic_gain_tlv), |
322 | |
323 | /* Phase Invert */ |
324 | SOC_SINGLE("Hifi Left Phase Invert Switch" , |
325 | CPCAP_REG_RXSDOA, CPCAP_BIT_MONO_DAC0, 1, 0), |
326 | SOC_SINGLE("Ext Left Phase Invert Switch" , |
327 | CPCAP_REG_RXEPOA, CPCAP_BIT_MONO_EXT0, 1, 0), |
328 | }; |
329 | |
330 | static const char * const cpcap_out_mux_texts[] = { |
331 | "Off" , "Voice" , "HiFi" , "Ext" |
332 | }; |
333 | |
334 | static const char * const cpcap_in_right_mux_texts[] = { |
335 | "Off" , "Mic 1" , "Headset Mic" , "EMU Mic" , "Ext Right" |
336 | }; |
337 | |
338 | static const char * const cpcap_in_left_mux_texts[] = { |
339 | "Off" , "Mic 2" , "Ext Left" |
340 | }; |
341 | |
342 | /* |
343 | * input muxes use unusual register layout, so that we need to use custom |
344 | * getter/setter methods |
345 | */ |
346 | static SOC_ENUM_SINGLE_EXT_DECL(cpcap_input_left_mux_enum, |
347 | cpcap_in_left_mux_texts); |
348 | static SOC_ENUM_SINGLE_EXT_DECL(cpcap_input_right_mux_enum, |
349 | cpcap_in_right_mux_texts); |
350 | |
351 | /* |
352 | * mux uses same bit in CPCAP_REG_RXCOA, CPCAP_REG_RXSDOA & CPCAP_REG_RXEPOA; |
353 | * even though the register layout makes it look like a mixer, this is a mux. |
354 | * Enabling multiple inputs will result in no audio being forwarded. |
355 | */ |
356 | static SOC_ENUM_SINGLE_DECL(cpcap_earpiece_mux_enum, 0, 0, cpcap_out_mux_texts); |
357 | static SOC_ENUM_SINGLE_DECL(cpcap_spkr_r_mux_enum, 0, 1, cpcap_out_mux_texts); |
358 | static SOC_ENUM_SINGLE_DECL(cpcap_spkr_l_mux_enum, 0, 2, cpcap_out_mux_texts); |
359 | static SOC_ENUM_SINGLE_DECL(cpcap_line_r_mux_enum, 0, 3, cpcap_out_mux_texts); |
360 | static SOC_ENUM_SINGLE_DECL(cpcap_line_l_mux_enum, 0, 4, cpcap_out_mux_texts); |
361 | static SOC_ENUM_SINGLE_DECL(cpcap_hs_r_mux_enum, 0, 5, cpcap_out_mux_texts); |
362 | static SOC_ENUM_SINGLE_DECL(cpcap_hs_l_mux_enum, 0, 6, cpcap_out_mux_texts); |
363 | static SOC_ENUM_SINGLE_DECL(cpcap_emu_l_mux_enum, 0, 7, cpcap_out_mux_texts); |
364 | static SOC_ENUM_SINGLE_DECL(cpcap_emu_r_mux_enum, 0, 8, cpcap_out_mux_texts); |
365 | |
366 | static int cpcap_output_mux_get_enum(struct snd_kcontrol *kcontrol, |
367 | struct snd_ctl_elem_value *ucontrol) |
368 | { |
369 | struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); |
370 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
371 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
372 | unsigned int shift = e->shift_l; |
373 | int reg_voice, reg_hifi, reg_ext, status; |
374 | int err; |
375 | |
376 | err = regmap_read(map: cpcap->regmap, CPCAP_REG_RXCOA, val: ®_voice); |
377 | if (err) |
378 | return err; |
379 | err = regmap_read(map: cpcap->regmap, CPCAP_REG_RXSDOA, val: ®_hifi); |
380 | if (err) |
381 | return err; |
382 | err = regmap_read(map: cpcap->regmap, CPCAP_REG_RXEPOA, val: ®_ext); |
383 | if (err) |
384 | return err; |
385 | |
386 | reg_voice = (reg_voice >> shift) & 1; |
387 | reg_hifi = (reg_hifi >> shift) & 1; |
388 | reg_ext = (reg_ext >> shift) & 1; |
389 | status = reg_ext << 2 | reg_hifi << 1 | reg_voice; |
390 | |
391 | switch (status) { |
392 | case 0x04: |
393 | ucontrol->value.enumerated.item[0] = 3; |
394 | break; |
395 | case 0x02: |
396 | ucontrol->value.enumerated.item[0] = 2; |
397 | break; |
398 | case 0x01: |
399 | ucontrol->value.enumerated.item[0] = 1; |
400 | break; |
401 | default: |
402 | ucontrol->value.enumerated.item[0] = 0; |
403 | break; |
404 | } |
405 | |
406 | return 0; |
407 | } |
408 | |
409 | static int cpcap_output_mux_put_enum(struct snd_kcontrol *kcontrol, |
410 | struct snd_ctl_elem_value *ucontrol) |
411 | { |
412 | struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); |
413 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
414 | struct snd_soc_dapm_context *dapm = |
415 | snd_soc_dapm_kcontrol_dapm(kcontrol); |
416 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
417 | unsigned int muxval = ucontrol->value.enumerated.item[0]; |
418 | unsigned int mask = BIT(e->shift_l); |
419 | u16 reg_voice = 0x00, reg_hifi = 0x00, reg_ext = 0x00; |
420 | int err; |
421 | |
422 | switch (muxval) { |
423 | case 1: |
424 | reg_voice = mask; |
425 | break; |
426 | case 2: |
427 | reg_hifi = mask; |
428 | break; |
429 | case 3: |
430 | reg_ext = mask; |
431 | break; |
432 | default: |
433 | break; |
434 | } |
435 | |
436 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_RXCOA, |
437 | mask, val: reg_voice); |
438 | if (err) |
439 | return err; |
440 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_RXSDOA, |
441 | mask, val: reg_hifi); |
442 | if (err) |
443 | return err; |
444 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_RXEPOA, |
445 | mask, val: reg_ext); |
446 | if (err) |
447 | return err; |
448 | |
449 | snd_soc_dapm_mux_update_power(dapm, kcontrol, mux: muxval, e, NULL); |
450 | |
451 | return 0; |
452 | } |
453 | |
454 | static int cpcap_input_right_mux_get_enum(struct snd_kcontrol *kcontrol, |
455 | struct snd_ctl_elem_value *ucontrol) |
456 | { |
457 | struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); |
458 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
459 | int regval, mask; |
460 | int err; |
461 | |
462 | err = regmap_read(map: cpcap->regmap, CPCAP_REG_TXI, val: ®val); |
463 | if (err) |
464 | return err; |
465 | |
466 | mask = 0; |
467 | mask |= BIT(CPCAP_BIT_MIC1_MUX); |
468 | mask |= BIT(CPCAP_BIT_HS_MIC_MUX); |
469 | mask |= BIT(CPCAP_BIT_EMU_MIC_MUX); |
470 | mask |= BIT(CPCAP_BIT_RX_R_ENCODE); |
471 | |
472 | switch (regval & mask) { |
473 | case BIT(CPCAP_BIT_RX_R_ENCODE): |
474 | ucontrol->value.enumerated.item[0] = 4; |
475 | break; |
476 | case BIT(CPCAP_BIT_EMU_MIC_MUX): |
477 | ucontrol->value.enumerated.item[0] = 3; |
478 | break; |
479 | case BIT(CPCAP_BIT_HS_MIC_MUX): |
480 | ucontrol->value.enumerated.item[0] = 2; |
481 | break; |
482 | case BIT(CPCAP_BIT_MIC1_MUX): |
483 | ucontrol->value.enumerated.item[0] = 1; |
484 | break; |
485 | default: |
486 | ucontrol->value.enumerated.item[0] = 0; |
487 | break; |
488 | } |
489 | |
490 | return 0; |
491 | } |
492 | |
493 | static int cpcap_input_right_mux_put_enum(struct snd_kcontrol *kcontrol, |
494 | struct snd_ctl_elem_value *ucontrol) |
495 | { |
496 | struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); |
497 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
498 | struct snd_soc_dapm_context *dapm = |
499 | snd_soc_dapm_kcontrol_dapm(kcontrol); |
500 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
501 | unsigned int muxval = ucontrol->value.enumerated.item[0]; |
502 | int regval = 0, mask; |
503 | int err; |
504 | |
505 | mask = 0; |
506 | mask |= BIT(CPCAP_BIT_MIC1_MUX); |
507 | mask |= BIT(CPCAP_BIT_HS_MIC_MUX); |
508 | mask |= BIT(CPCAP_BIT_EMU_MIC_MUX); |
509 | mask |= BIT(CPCAP_BIT_RX_R_ENCODE); |
510 | |
511 | switch (muxval) { |
512 | case 1: |
513 | regval = BIT(CPCAP_BIT_MIC1_MUX); |
514 | break; |
515 | case 2: |
516 | regval = BIT(CPCAP_BIT_HS_MIC_MUX); |
517 | break; |
518 | case 3: |
519 | regval = BIT(CPCAP_BIT_EMU_MIC_MUX); |
520 | break; |
521 | case 4: |
522 | regval = BIT(CPCAP_BIT_RX_R_ENCODE); |
523 | break; |
524 | default: |
525 | break; |
526 | } |
527 | |
528 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_TXI, |
529 | mask, val: regval); |
530 | if (err) |
531 | return err; |
532 | |
533 | snd_soc_dapm_mux_update_power(dapm, kcontrol, mux: muxval, e, NULL); |
534 | |
535 | return 0; |
536 | } |
537 | |
538 | static int cpcap_input_left_mux_get_enum(struct snd_kcontrol *kcontrol, |
539 | struct snd_ctl_elem_value *ucontrol) |
540 | { |
541 | struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); |
542 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
543 | int regval, mask; |
544 | int err; |
545 | |
546 | err = regmap_read(map: cpcap->regmap, CPCAP_REG_TXI, val: ®val); |
547 | if (err) |
548 | return err; |
549 | |
550 | mask = 0; |
551 | mask |= BIT(CPCAP_BIT_MIC2_MUX); |
552 | mask |= BIT(CPCAP_BIT_RX_L_ENCODE); |
553 | |
554 | switch (regval & mask) { |
555 | case BIT(CPCAP_BIT_RX_L_ENCODE): |
556 | ucontrol->value.enumerated.item[0] = 2; |
557 | break; |
558 | case BIT(CPCAP_BIT_MIC2_MUX): |
559 | ucontrol->value.enumerated.item[0] = 1; |
560 | break; |
561 | default: |
562 | ucontrol->value.enumerated.item[0] = 0; |
563 | break; |
564 | } |
565 | |
566 | return 0; |
567 | } |
568 | |
569 | static int cpcap_input_left_mux_put_enum(struct snd_kcontrol *kcontrol, |
570 | struct snd_ctl_elem_value *ucontrol) |
571 | { |
572 | struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); |
573 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
574 | struct snd_soc_dapm_context *dapm = |
575 | snd_soc_dapm_kcontrol_dapm(kcontrol); |
576 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
577 | unsigned int muxval = ucontrol->value.enumerated.item[0]; |
578 | int regval = 0, mask; |
579 | int err; |
580 | |
581 | mask = 0; |
582 | mask |= BIT(CPCAP_BIT_MIC2_MUX); |
583 | mask |= BIT(CPCAP_BIT_RX_L_ENCODE); |
584 | |
585 | switch (muxval) { |
586 | case 1: |
587 | regval = BIT(CPCAP_BIT_MIC2_MUX); |
588 | break; |
589 | case 2: |
590 | regval = BIT(CPCAP_BIT_RX_L_ENCODE); |
591 | break; |
592 | default: |
593 | break; |
594 | } |
595 | |
596 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_TXI, |
597 | mask, val: regval); |
598 | if (err) |
599 | return err; |
600 | |
601 | snd_soc_dapm_mux_update_power(dapm, kcontrol, mux: muxval, e, NULL); |
602 | |
603 | return 0; |
604 | } |
605 | |
606 | static const struct snd_kcontrol_new cpcap_input_left_mux = |
607 | SOC_DAPM_ENUM_EXT("Input Left" , cpcap_input_left_mux_enum, |
608 | cpcap_input_left_mux_get_enum, |
609 | cpcap_input_left_mux_put_enum); |
610 | static const struct snd_kcontrol_new cpcap_input_right_mux = |
611 | SOC_DAPM_ENUM_EXT("Input Right" , cpcap_input_right_mux_enum, |
612 | cpcap_input_right_mux_get_enum, |
613 | cpcap_input_right_mux_put_enum); |
614 | static const struct snd_kcontrol_new cpcap_emu_left_mux = |
615 | SOC_DAPM_ENUM_EXT("EMU Left" , cpcap_emu_l_mux_enum, |
616 | cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); |
617 | static const struct snd_kcontrol_new cpcap_emu_right_mux = |
618 | SOC_DAPM_ENUM_EXT("EMU Right" , cpcap_emu_r_mux_enum, |
619 | cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); |
620 | static const struct snd_kcontrol_new cpcap_hs_left_mux = |
621 | SOC_DAPM_ENUM_EXT("Headset Left" , cpcap_hs_l_mux_enum, |
622 | cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); |
623 | static const struct snd_kcontrol_new cpcap_hs_right_mux = |
624 | SOC_DAPM_ENUM_EXT("Headset Right" , cpcap_hs_r_mux_enum, |
625 | cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); |
626 | static const struct snd_kcontrol_new cpcap_line_left_mux = |
627 | SOC_DAPM_ENUM_EXT("Line Left" , cpcap_line_l_mux_enum, |
628 | cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); |
629 | static const struct snd_kcontrol_new cpcap_line_right_mux = |
630 | SOC_DAPM_ENUM_EXT("Line Right" , cpcap_line_r_mux_enum, |
631 | cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); |
632 | static const struct snd_kcontrol_new cpcap_speaker_left_mux = |
633 | SOC_DAPM_ENUM_EXT("Speaker Left" , cpcap_spkr_l_mux_enum, |
634 | cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); |
635 | static const struct snd_kcontrol_new cpcap_speaker_right_mux = |
636 | SOC_DAPM_ENUM_EXT("Speaker Right" , cpcap_spkr_r_mux_enum, |
637 | cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); |
638 | static const struct snd_kcontrol_new cpcap_earpiece_mux = |
639 | SOC_DAPM_ENUM_EXT("Earpiece" , cpcap_earpiece_mux_enum, |
640 | cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); |
641 | |
642 | static const struct snd_kcontrol_new cpcap_hifi_mono_mixer_controls[] = { |
643 | SOC_DAPM_SINGLE("HiFi Mono Playback Switch" , |
644 | CPCAP_REG_RXSDOA, CPCAP_BIT_MONO_DAC1, 1, 0), |
645 | }; |
646 | static const struct snd_kcontrol_new cpcap_ext_mono_mixer_controls[] = { |
647 | SOC_DAPM_SINGLE("Ext Mono Playback Switch" , |
648 | CPCAP_REG_RXEPOA, CPCAP_BIT_MONO_EXT0, 1, 0), |
649 | }; |
650 | |
651 | static const struct snd_kcontrol_new cpcap_extr_mute_control = |
652 | SOC_DAPM_SINGLE("Switch" , |
653 | CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_IN_R_SW, 1, 0); |
654 | static const struct snd_kcontrol_new cpcap_extl_mute_control = |
655 | SOC_DAPM_SINGLE("Switch" , |
656 | CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_IN_L_SW, 1, 0); |
657 | |
658 | static const struct snd_kcontrol_new cpcap_voice_loopback = |
659 | SOC_DAPM_SINGLE("Switch" , |
660 | CPCAP_REG_TXI, CPCAP_BIT_DLM, 1, 0); |
661 | |
662 | static const struct snd_soc_dapm_widget cpcap_dapm_widgets[] = { |
663 | /* DAIs */ |
664 | SND_SOC_DAPM_AIF_IN("HiFi RX" , NULL, 0, SND_SOC_NOPM, 0, 0), |
665 | SND_SOC_DAPM_AIF_IN("Voice RX" , NULL, 0, SND_SOC_NOPM, 0, 0), |
666 | SND_SOC_DAPM_AIF_OUT("Voice TX" , NULL, 0, SND_SOC_NOPM, 0, 0), |
667 | |
668 | /* Power Supply */ |
669 | SND_SOC_DAPM_REGULATOR_SUPPLY("VAUDIO" , SLEEP_ACTIVATE_POWER, 0), |
670 | |
671 | /* Highpass Filters */ |
672 | SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Highpass Filter RX" , |
673 | CPCAP_REG_CC, CPCAP_BIT_AUDIHPF_0, 0x3, 0x3, 0x0), |
674 | SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Highpass Filter TX" , |
675 | CPCAP_REG_CC, CPCAP_BIT_AUDOHPF_0, 0x3, 0x3, 0x0), |
676 | |
677 | /* Clocks */ |
678 | SND_SOC_DAPM_SUPPLY("HiFi DAI Clock" , |
679 | CPCAP_REG_SDACDI, CPCAP_BIT_ST_CLK_EN, 0, NULL, 0), |
680 | SND_SOC_DAPM_SUPPLY("Voice DAI Clock" , |
681 | CPCAP_REG_CDI, CPCAP_BIT_CDC_CLK_EN, 0, NULL, 0), |
682 | |
683 | /* Microphone Bias */ |
684 | SND_SOC_DAPM_SUPPLY("MIC1R Bias" , |
685 | CPCAP_REG_TXI, CPCAP_BIT_MB_ON1R, 0, NULL, 0), |
686 | SND_SOC_DAPM_SUPPLY("MIC1L Bias" , |
687 | CPCAP_REG_TXI, CPCAP_BIT_MB_ON1L, 0, NULL, 0), |
688 | SND_SOC_DAPM_SUPPLY("MIC2 Bias" , |
689 | CPCAP_REG_TXI, CPCAP_BIT_MB_ON2, 0, NULL, 0), |
690 | |
691 | /* Inputs */ |
692 | SND_SOC_DAPM_INPUT("MICR" ), |
693 | SND_SOC_DAPM_INPUT("HSMIC" ), |
694 | SND_SOC_DAPM_INPUT("EMUMIC" ), |
695 | SND_SOC_DAPM_INPUT("MICL" ), |
696 | SND_SOC_DAPM_INPUT("EXTR" ), |
697 | SND_SOC_DAPM_INPUT("EXTL" ), |
698 | |
699 | /* Capture Route */ |
700 | SND_SOC_DAPM_MUX("Right Capture Route" , |
701 | SND_SOC_NOPM, 0, 0, &cpcap_input_right_mux), |
702 | SND_SOC_DAPM_MUX("Left Capture Route" , |
703 | SND_SOC_NOPM, 0, 0, &cpcap_input_left_mux), |
704 | |
705 | /* Capture PGAs */ |
706 | SND_SOC_DAPM_PGA("Microphone 1 PGA" , |
707 | CPCAP_REG_TXI, CPCAP_BIT_MIC1_PGA_EN, 0, NULL, 0), |
708 | SND_SOC_DAPM_PGA("Microphone 2 PGA" , |
709 | CPCAP_REG_TXI, CPCAP_BIT_MIC2_PGA_EN, 0, NULL, 0), |
710 | |
711 | /* ADC */ |
712 | SND_SOC_DAPM_ADC("ADC Right" , NULL, |
713 | CPCAP_REG_CC, CPCAP_BIT_MIC1_CDC_EN, 0), |
714 | SND_SOC_DAPM_ADC("ADC Left" , NULL, |
715 | CPCAP_REG_CC, CPCAP_BIT_MIC2_CDC_EN, 0), |
716 | |
717 | /* DAC */ |
718 | SND_SOC_DAPM_DAC_E("DAC HiFi" , NULL, |
719 | CPCAP_REG_SDAC, CPCAP_BIT_ST_DAC_EN, 0, |
720 | cpcap_st_workaround, |
721 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), |
722 | SND_SOC_DAPM_DAC_E("DAC Voice" , NULL, |
723 | CPCAP_REG_CC, CPCAP_BIT_CDC_EN_RX, 0, |
724 | cpcap_st_workaround, |
725 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), |
726 | |
727 | /* Playback PGA */ |
728 | SND_SOC_DAPM_PGA("HiFi PGA" , |
729 | CPCAP_REG_RXSDOA, CPCAP_BIT_PGA_DAC_EN, 0, NULL, 0), |
730 | SND_SOC_DAPM_PGA("Voice PGA" , |
731 | CPCAP_REG_RXCOA, CPCAP_BIT_PGA_CDC_EN, 0, NULL, 0), |
732 | SND_SOC_DAPM_PGA_E("Ext Right PGA" , |
733 | CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_EXT_R_EN, 0, |
734 | NULL, 0, |
735 | cpcap_st_workaround, |
736 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), |
737 | SND_SOC_DAPM_PGA_E("Ext Left PGA" , |
738 | CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_EXT_L_EN, 0, |
739 | NULL, 0, |
740 | cpcap_st_workaround, |
741 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), |
742 | |
743 | /* Playback Switch */ |
744 | SND_SOC_DAPM_SWITCH("Ext Right Enable" , SND_SOC_NOPM, 0, 0, |
745 | &cpcap_extr_mute_control), |
746 | SND_SOC_DAPM_SWITCH("Ext Left Enable" , SND_SOC_NOPM, 0, 0, |
747 | &cpcap_extl_mute_control), |
748 | |
749 | /* Loopback Switch */ |
750 | SND_SOC_DAPM_SWITCH("Voice Loopback" , SND_SOC_NOPM, 0, 0, |
751 | &cpcap_voice_loopback), |
752 | |
753 | /* Mono Mixer */ |
754 | SOC_MIXER_ARRAY("HiFi Mono Left Mixer" , SND_SOC_NOPM, 0, 0, |
755 | cpcap_hifi_mono_mixer_controls), |
756 | SOC_MIXER_ARRAY("HiFi Mono Right Mixer" , SND_SOC_NOPM, 0, 0, |
757 | cpcap_hifi_mono_mixer_controls), |
758 | SOC_MIXER_ARRAY("Ext Mono Left Mixer" , SND_SOC_NOPM, 0, 0, |
759 | cpcap_ext_mono_mixer_controls), |
760 | SOC_MIXER_ARRAY("Ext Mono Right Mixer" , SND_SOC_NOPM, 0, 0, |
761 | cpcap_ext_mono_mixer_controls), |
762 | |
763 | /* Output Routes */ |
764 | SND_SOC_DAPM_MUX("Earpiece Playback Route" , SND_SOC_NOPM, 0, 0, |
765 | &cpcap_earpiece_mux), |
766 | SND_SOC_DAPM_MUX("Speaker Right Playback Route" , SND_SOC_NOPM, 0, 0, |
767 | &cpcap_speaker_right_mux), |
768 | SND_SOC_DAPM_MUX("Speaker Left Playback Route" , SND_SOC_NOPM, 0, 0, |
769 | &cpcap_speaker_left_mux), |
770 | SND_SOC_DAPM_MUX("Lineout Right Playback Route" , SND_SOC_NOPM, 0, 0, |
771 | &cpcap_line_right_mux), |
772 | SND_SOC_DAPM_MUX("Lineout Left Playback Route" , SND_SOC_NOPM, 0, 0, |
773 | &cpcap_line_left_mux), |
774 | SND_SOC_DAPM_MUX("Headset Right Playback Route" , SND_SOC_NOPM, 0, 0, |
775 | &cpcap_hs_right_mux), |
776 | SND_SOC_DAPM_MUX("Headset Left Playback Route" , SND_SOC_NOPM, 0, 0, |
777 | &cpcap_hs_left_mux), |
778 | SND_SOC_DAPM_MUX("EMU Right Playback Route" , SND_SOC_NOPM, 0, 0, |
779 | &cpcap_emu_right_mux), |
780 | SND_SOC_DAPM_MUX("EMU Left Playback Route" , SND_SOC_NOPM, 0, 0, |
781 | &cpcap_emu_left_mux), |
782 | |
783 | /* Output Amplifier */ |
784 | SND_SOC_DAPM_PGA("Earpiece PGA" , |
785 | CPCAP_REG_RXOA, CPCAP_BIT_A1_EAR_EN, 0, NULL, 0), |
786 | SND_SOC_DAPM_PGA("Speaker Right PGA" , |
787 | CPCAP_REG_RXOA, CPCAP_BIT_A2_LDSP_R_EN, 0, NULL, 0), |
788 | SND_SOC_DAPM_PGA("Speaker Left PGA" , |
789 | CPCAP_REG_RXOA, CPCAP_BIT_A2_LDSP_L_EN, 0, NULL, 0), |
790 | SND_SOC_DAPM_PGA("Lineout Right PGA" , |
791 | CPCAP_REG_RXOA, CPCAP_BIT_A4_LINEOUT_R_EN, 0, NULL, 0), |
792 | SND_SOC_DAPM_PGA("Lineout Left PGA" , |
793 | CPCAP_REG_RXOA, CPCAP_BIT_A4_LINEOUT_L_EN, 0, NULL, 0), |
794 | SND_SOC_DAPM_PGA("Headset Right PGA" , |
795 | CPCAP_REG_RXOA, CPCAP_BIT_HS_R_EN, 0, NULL, 0), |
796 | SND_SOC_DAPM_PGA("Headset Left PGA" , |
797 | CPCAP_REG_RXOA, CPCAP_BIT_HS_L_EN, 0, NULL, 0), |
798 | SND_SOC_DAPM_PGA("EMU Right PGA" , |
799 | CPCAP_REG_RXOA, CPCAP_BIT_EMU_SPKR_R_EN, 0, NULL, 0), |
800 | SND_SOC_DAPM_PGA("EMU Left PGA" , |
801 | CPCAP_REG_RXOA, CPCAP_BIT_EMU_SPKR_L_EN, 0, NULL, 0), |
802 | |
803 | /* Headet Charge Pump */ |
804 | SND_SOC_DAPM_SUPPLY("Headset Charge Pump" , |
805 | CPCAP_REG_RXOA, CPCAP_BIT_ST_HS_CP_EN, 0, NULL, 0), |
806 | |
807 | /* Outputs */ |
808 | SND_SOC_DAPM_OUTPUT("EP" ), |
809 | SND_SOC_DAPM_OUTPUT("SPKR" ), |
810 | SND_SOC_DAPM_OUTPUT("SPKL" ), |
811 | SND_SOC_DAPM_OUTPUT("LINER" ), |
812 | SND_SOC_DAPM_OUTPUT("LINEL" ), |
813 | SND_SOC_DAPM_OUTPUT("HSR" ), |
814 | SND_SOC_DAPM_OUTPUT("HSL" ), |
815 | SND_SOC_DAPM_OUTPUT("EMUR" ), |
816 | SND_SOC_DAPM_OUTPUT("EMUL" ), |
817 | }; |
818 | |
819 | static const struct snd_soc_dapm_route intercon[] = { |
820 | /* Power Supply */ |
821 | {"HiFi PGA" , NULL, "VAUDIO" }, |
822 | {"Voice PGA" , NULL, "VAUDIO" }, |
823 | {"Ext Right PGA" , NULL, "VAUDIO" }, |
824 | {"Ext Left PGA" , NULL, "VAUDIO" }, |
825 | {"Microphone 1 PGA" , NULL, "VAUDIO" }, |
826 | {"Microphone 2 PGA" , NULL, "VAUDIO" }, |
827 | |
828 | /* Stream -> AIF */ |
829 | {"HiFi RX" , NULL, "HiFi Playback" }, |
830 | {"Voice RX" , NULL, "Voice Playback" }, |
831 | {"Voice Capture" , NULL, "Voice TX" }, |
832 | |
833 | /* AIF clocks */ |
834 | {"HiFi RX" , NULL, "HiFi DAI Clock" }, |
835 | {"Voice RX" , NULL, "Voice DAI Clock" }, |
836 | {"Voice TX" , NULL, "Voice DAI Clock" }, |
837 | |
838 | /* Digital Loopback */ |
839 | {"Voice Loopback" , "Switch" , "Voice TX" }, |
840 | {"Voice RX" , NULL, "Voice Loopback" }, |
841 | |
842 | /* Highpass Filters */ |
843 | {"Highpass Filter RX" , NULL, "Voice RX" }, |
844 | {"Voice TX" , NULL, "Highpass Filter TX" }, |
845 | |
846 | /* AIF -> DAC mapping */ |
847 | {"DAC HiFi" , NULL, "HiFi RX" }, |
848 | {"DAC Voice" , NULL, "Highpass Filter RX" }, |
849 | |
850 | /* DAC -> PGA */ |
851 | {"HiFi PGA" , NULL, "DAC HiFi" }, |
852 | {"Voice PGA" , NULL, "DAC Voice" }, |
853 | |
854 | /* Ext Input -> PGA */ |
855 | {"Ext Right PGA" , NULL, "EXTR" }, |
856 | {"Ext Left PGA" , NULL, "EXTL" }, |
857 | |
858 | /* Ext PGA -> Ext Playback Switch */ |
859 | {"Ext Right Enable" , "Switch" , "Ext Right PGA" }, |
860 | {"Ext Left Enable" , "Switch" , "Ext Left PGA" }, |
861 | |
862 | /* HiFi PGA -> Mono Mixer */ |
863 | {"HiFi Mono Left Mixer" , NULL, "HiFi PGA" }, |
864 | {"HiFi Mono Left Mixer" , "HiFi Mono Playback Switch" , "HiFi PGA" }, |
865 | {"HiFi Mono Right Mixer" , NULL, "HiFi PGA" }, |
866 | {"HiFi Mono Right Mixer" , "HiFi Mono Playback Switch" , "HiFi PGA" }, |
867 | |
868 | /* Ext Playback Switch -> Ext Mono Mixer */ |
869 | {"Ext Mono Right Mixer" , NULL, "Ext Right Enable" }, |
870 | {"Ext Mono Right Mixer" , "Ext Mono Playback Switch" , "Ext Left Enable" }, |
871 | {"Ext Mono Left Mixer" , NULL, "Ext Left Enable" }, |
872 | {"Ext Mono Left Mixer" , "Ext Mono Playback Switch" , "Ext Right Enable" }, |
873 | |
874 | /* HiFi Mono Mixer -> Output Route */ |
875 | {"Earpiece Playback Route" , "HiFi" , "HiFi Mono Right Mixer" }, |
876 | {"Speaker Right Playback Route" , "HiFi" , "HiFi Mono Right Mixer" }, |
877 | {"Speaker Left Playback Route" , "HiFi" , "HiFi Mono Left Mixer" }, |
878 | {"Lineout Right Playback Route" , "HiFi" , "HiFi Mono Right Mixer" }, |
879 | {"Lineout Left Playback Route" , "HiFi" , "HiFi Mono Left Mixer" }, |
880 | {"Headset Right Playback Route" , "HiFi" , "HiFi Mono Right Mixer" }, |
881 | {"Headset Left Playback Route" , "HiFi" , "HiFi Mono Left Mixer" }, |
882 | {"EMU Right Playback Route" , "HiFi" , "HiFi Mono Right Mixer" }, |
883 | {"EMU Left Playback Route" , "HiFi" , "HiFi Mono Left Mixer" }, |
884 | |
885 | /* Voice PGA -> Output Route */ |
886 | {"Earpiece Playback Route" , "Voice" , "Voice PGA" }, |
887 | {"Speaker Right Playback Route" , "Voice" , "Voice PGA" }, |
888 | {"Speaker Left Playback Route" , "Voice" , "Voice PGA" }, |
889 | {"Lineout Right Playback Route" , "Voice" , "Voice PGA" }, |
890 | {"Lineout Left Playback Route" , "Voice" , "Voice PGA" }, |
891 | {"Headset Right Playback Route" , "Voice" , "Voice PGA" }, |
892 | {"Headset Left Playback Route" , "Voice" , "Voice PGA" }, |
893 | {"EMU Right Playback Route" , "Voice" , "Voice PGA" }, |
894 | {"EMU Left Playback Route" , "Voice" , "Voice PGA" }, |
895 | |
896 | /* Ext Mono Mixer -> Output Route */ |
897 | {"Earpiece Playback Route" , "Ext" , "Ext Mono Right Mixer" }, |
898 | {"Speaker Right Playback Route" , "Ext" , "Ext Mono Right Mixer" }, |
899 | {"Speaker Left Playback Route" , "Ext" , "Ext Mono Left Mixer" }, |
900 | {"Lineout Right Playback Route" , "Ext" , "Ext Mono Right Mixer" }, |
901 | {"Lineout Left Playback Route" , "Ext" , "Ext Mono Left Mixer" }, |
902 | {"Headset Right Playback Route" , "Ext" , "Ext Mono Right Mixer" }, |
903 | {"Headset Left Playback Route" , "Ext" , "Ext Mono Left Mixer" }, |
904 | {"EMU Right Playback Route" , "Ext" , "Ext Mono Right Mixer" }, |
905 | {"EMU Left Playback Route" , "Ext" , "Ext Mono Left Mixer" }, |
906 | |
907 | /* Output Route -> Output Amplifier */ |
908 | {"Earpiece PGA" , NULL, "Earpiece Playback Route" }, |
909 | {"Speaker Right PGA" , NULL, "Speaker Right Playback Route" }, |
910 | {"Speaker Left PGA" , NULL, "Speaker Left Playback Route" }, |
911 | {"Lineout Right PGA" , NULL, "Lineout Right Playback Route" }, |
912 | {"Lineout Left PGA" , NULL, "Lineout Left Playback Route" }, |
913 | {"Headset Right PGA" , NULL, "Headset Right Playback Route" }, |
914 | {"Headset Left PGA" , NULL, "Headset Left Playback Route" }, |
915 | {"EMU Right PGA" , NULL, "EMU Right Playback Route" }, |
916 | {"EMU Left PGA" , NULL, "EMU Left Playback Route" }, |
917 | |
918 | /* Output Amplifier -> Output */ |
919 | {"EP" , NULL, "Earpiece PGA" }, |
920 | {"SPKR" , NULL, "Speaker Right PGA" }, |
921 | {"SPKL" , NULL, "Speaker Left PGA" }, |
922 | {"LINER" , NULL, "Lineout Right PGA" }, |
923 | {"LINEL" , NULL, "Lineout Left PGA" }, |
924 | {"HSR" , NULL, "Headset Right PGA" }, |
925 | {"HSL" , NULL, "Headset Left PGA" }, |
926 | {"EMUR" , NULL, "EMU Right PGA" }, |
927 | {"EMUL" , NULL, "EMU Left PGA" }, |
928 | |
929 | /* Headset Charge Pump -> Headset */ |
930 | {"HSR" , NULL, "Headset Charge Pump" }, |
931 | {"HSL" , NULL, "Headset Charge Pump" }, |
932 | |
933 | /* Mic -> Mic Route */ |
934 | {"Right Capture Route" , "Mic 1" , "MICR" }, |
935 | {"Right Capture Route" , "Headset Mic" , "HSMIC" }, |
936 | {"Right Capture Route" , "EMU Mic" , "EMUMIC" }, |
937 | {"Right Capture Route" , "Ext Right" , "EXTR" }, |
938 | {"Left Capture Route" , "Mic 2" , "MICL" }, |
939 | {"Left Capture Route" , "Ext Left" , "EXTL" }, |
940 | |
941 | /* Input Route -> Microphone PGA */ |
942 | {"Microphone 1 PGA" , NULL, "Right Capture Route" }, |
943 | {"Microphone 2 PGA" , NULL, "Left Capture Route" }, |
944 | |
945 | /* Microphone PGA -> ADC */ |
946 | {"ADC Right" , NULL, "Microphone 1 PGA" }, |
947 | {"ADC Left" , NULL, "Microphone 2 PGA" }, |
948 | |
949 | /* ADC -> Stream */ |
950 | {"Highpass Filter TX" , NULL, "ADC Right" }, |
951 | {"Highpass Filter TX" , NULL, "ADC Left" }, |
952 | |
953 | /* Mic Bias */ |
954 | {"MICL" , NULL, "MIC1L Bias" }, |
955 | {"MICR" , NULL, "MIC1R Bias" }, |
956 | }; |
957 | |
958 | static int cpcap_set_sysclk(struct cpcap_audio *cpcap, enum cpcap_dai dai, |
959 | int clk_id, int freq) |
960 | { |
961 | u16 clkfreqreg, clkfreqshift; |
962 | u16 clkfreqmask, clkfreqval; |
963 | u16 clkidreg, clkidshift; |
964 | u16 mask, val; |
965 | int err; |
966 | |
967 | switch (dai) { |
968 | case CPCAP_DAI_HIFI: |
969 | clkfreqreg = CPCAP_REG_SDAC; |
970 | clkfreqshift = CPCAP_BIT_ST_DAC_CLK0; |
971 | clkidreg = CPCAP_REG_SDACDI; |
972 | clkidshift = CPCAP_BIT_ST_DAC_CLK_IN_SEL; |
973 | break; |
974 | case CPCAP_DAI_VOICE: |
975 | clkfreqreg = CPCAP_REG_CC; |
976 | clkfreqshift = CPCAP_BIT_CDC_CLK0; |
977 | clkidreg = CPCAP_REG_CDI; |
978 | clkidshift = CPCAP_BIT_CLK_IN_SEL; |
979 | break; |
980 | default: |
981 | dev_err(cpcap->component->dev, "invalid DAI: %d" , dai); |
982 | return -EINVAL; |
983 | } |
984 | |
985 | /* setup clk id */ |
986 | if (clk_id < 0 || clk_id > 1) { |
987 | dev_err(cpcap->component->dev, "invalid clk id %d" , clk_id); |
988 | return -EINVAL; |
989 | } |
990 | err = regmap_update_bits(map: cpcap->regmap, reg: clkidreg, BIT(clkidshift), |
991 | val: clk_id ? BIT(clkidshift) : 0); |
992 | if (err) |
993 | return err; |
994 | |
995 | /* enable PLL for Voice DAI */ |
996 | if (dai == CPCAP_DAI_VOICE) { |
997 | mask = BIT(CPCAP_BIT_CDC_PLL_SEL); |
998 | val = BIT(CPCAP_BIT_CDC_PLL_SEL); |
999 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_CDI, |
1000 | mask, val); |
1001 | if (err) |
1002 | return err; |
1003 | } |
1004 | |
1005 | /* setup frequency */ |
1006 | clkfreqmask = 0x7 << clkfreqshift; |
1007 | switch (freq) { |
1008 | case 15360000: |
1009 | clkfreqval = 0x01 << clkfreqshift; |
1010 | break; |
1011 | case 16800000: |
1012 | clkfreqval = 0x02 << clkfreqshift; |
1013 | break; |
1014 | case 19200000: |
1015 | clkfreqval = 0x03 << clkfreqshift; |
1016 | break; |
1017 | case 26000000: |
1018 | clkfreqval = 0x04 << clkfreqshift; |
1019 | break; |
1020 | case 33600000: |
1021 | clkfreqval = 0x05 << clkfreqshift; |
1022 | break; |
1023 | case 38400000: |
1024 | clkfreqval = 0x06 << clkfreqshift; |
1025 | break; |
1026 | default: |
1027 | dev_err(cpcap->component->dev, "unsupported freq %u" , freq); |
1028 | return -EINVAL; |
1029 | } |
1030 | |
1031 | err = regmap_update_bits(map: cpcap->regmap, reg: clkfreqreg, |
1032 | mask: clkfreqmask, val: clkfreqval); |
1033 | if (err) |
1034 | return err; |
1035 | |
1036 | if (dai == CPCAP_DAI_VOICE) { |
1037 | cpcap->codec_clk_id = clk_id; |
1038 | cpcap->codec_freq = freq; |
1039 | } |
1040 | |
1041 | return 0; |
1042 | } |
1043 | |
1044 | static int cpcap_set_samprate(struct cpcap_audio *cpcap, enum cpcap_dai dai, |
1045 | int samplerate) |
1046 | { |
1047 | struct snd_soc_component *component = cpcap->component; |
1048 | u16 sampreg, sampmask, sampshift, sampval, sampreset; |
1049 | int err, sampreadval; |
1050 | |
1051 | switch (dai) { |
1052 | case CPCAP_DAI_HIFI: |
1053 | sampreg = CPCAP_REG_SDAC; |
1054 | sampshift = CPCAP_BIT_ST_SR0; |
1055 | sampreset = BIT(CPCAP_BIT_DF_RESET_ST_DAC) | |
1056 | BIT(CPCAP_BIT_ST_CLOCK_TREE_RESET); |
1057 | break; |
1058 | case CPCAP_DAI_VOICE: |
1059 | sampreg = CPCAP_REG_CC; |
1060 | sampshift = CPCAP_BIT_CDC_SR0; |
1061 | sampreset = BIT(CPCAP_BIT_DF_RESET) | |
1062 | BIT(CPCAP_BIT_CDC_CLOCK_TREE_RESET); |
1063 | break; |
1064 | default: |
1065 | dev_err(component->dev, "invalid DAI: %d" , dai); |
1066 | return -EINVAL; |
1067 | } |
1068 | |
1069 | sampmask = 0xF << sampshift | sampreset; |
1070 | switch (samplerate) { |
1071 | case 48000: |
1072 | sampval = 0x8 << sampshift; |
1073 | break; |
1074 | case 44100: |
1075 | sampval = 0x7 << sampshift; |
1076 | break; |
1077 | case 32000: |
1078 | sampval = 0x6 << sampshift; |
1079 | break; |
1080 | case 24000: |
1081 | sampval = 0x5 << sampshift; |
1082 | break; |
1083 | case 22050: |
1084 | sampval = 0x4 << sampshift; |
1085 | break; |
1086 | case 16000: |
1087 | sampval = 0x3 << sampshift; |
1088 | break; |
1089 | case 12000: |
1090 | sampval = 0x2 << sampshift; |
1091 | break; |
1092 | case 11025: |
1093 | sampval = 0x1 << sampshift; |
1094 | break; |
1095 | case 8000: |
1096 | sampval = 0x0 << sampshift; |
1097 | break; |
1098 | default: |
1099 | dev_err(component->dev, "unsupported samplerate %d" , samplerate); |
1100 | return -EINVAL; |
1101 | } |
1102 | err = regmap_update_bits(map: cpcap->regmap, reg: sampreg, |
1103 | mask: sampmask, val: sampval | sampreset); |
1104 | if (err) |
1105 | return err; |
1106 | |
1107 | /* Wait for clock tree reset to complete */ |
1108 | mdelay(CLOCK_TREE_RESET_TIME); |
1109 | |
1110 | err = regmap_read(map: cpcap->regmap, reg: sampreg, val: &sampreadval); |
1111 | if (err) |
1112 | return err; |
1113 | |
1114 | if (sampreadval & sampreset) { |
1115 | dev_err(component->dev, "reset self-clear failed: %04x" , |
1116 | sampreadval); |
1117 | return -EIO; |
1118 | } |
1119 | |
1120 | return 0; |
1121 | } |
1122 | |
1123 | static int cpcap_hifi_hw_params(struct snd_pcm_substream *substream, |
1124 | struct snd_pcm_hw_params *params, |
1125 | struct snd_soc_dai *dai) |
1126 | { |
1127 | struct snd_soc_component *component = dai->component; |
1128 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
1129 | int rate = params_rate(p: params); |
1130 | |
1131 | dev_dbg(component->dev, "HiFi setup HW params: rate=%d" , rate); |
1132 | return cpcap_set_samprate(cpcap, dai: CPCAP_DAI_HIFI, samplerate: rate); |
1133 | } |
1134 | |
1135 | static int cpcap_hifi_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, |
1136 | unsigned int freq, int dir) |
1137 | { |
1138 | struct snd_soc_component *component = codec_dai->component; |
1139 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
1140 | struct device *dev = component->dev; |
1141 | |
1142 | dev_dbg(dev, "HiFi setup sysclk: clk_id=%u, freq=%u" , clk_id, freq); |
1143 | return cpcap_set_sysclk(cpcap, dai: CPCAP_DAI_HIFI, clk_id, freq); |
1144 | } |
1145 | |
1146 | static int cpcap_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai, |
1147 | unsigned int fmt) |
1148 | { |
1149 | struct snd_soc_component *component = codec_dai->component; |
1150 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
1151 | struct device *dev = component->dev; |
1152 | static const u16 reg = CPCAP_REG_SDACDI; |
1153 | static const u16 mask = |
1154 | BIT(CPCAP_BIT_SMB_ST_DAC) | |
1155 | BIT(CPCAP_BIT_ST_CLK_INV) | |
1156 | BIT(CPCAP_BIT_ST_FS_INV) | |
1157 | BIT(CPCAP_BIT_ST_DIG_AUD_FS0) | |
1158 | BIT(CPCAP_BIT_ST_DIG_AUD_FS1) | |
1159 | BIT(CPCAP_BIT_ST_L_TIMESLOT0) | |
1160 | BIT(CPCAP_BIT_ST_L_TIMESLOT1) | |
1161 | BIT(CPCAP_BIT_ST_L_TIMESLOT2) | |
1162 | BIT(CPCAP_BIT_ST_R_TIMESLOT0) | |
1163 | BIT(CPCAP_BIT_ST_R_TIMESLOT1) | |
1164 | BIT(CPCAP_BIT_ST_R_TIMESLOT2); |
1165 | u16 val = 0x0000; |
1166 | |
1167 | dev_dbg(dev, "HiFi setup dai format (%08x)" , fmt); |
1168 | |
1169 | /* |
1170 | * "HiFi Playback" should always be configured as |
1171 | * SND_SOC_DAIFMT_CBP_CFP - codec clk & frm provider |
1172 | * SND_SOC_DAIFMT_I2S - I2S mode |
1173 | */ |
1174 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
1175 | case SND_SOC_DAIFMT_CBP_CFP: |
1176 | val &= ~BIT(CPCAP_BIT_SMB_ST_DAC); |
1177 | break; |
1178 | default: |
1179 | dev_err(dev, "HiFi dai fmt failed: CPCAP should be provider" ); |
1180 | return -EINVAL; |
1181 | } |
1182 | |
1183 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
1184 | case SND_SOC_DAIFMT_IB_IF: |
1185 | val |= BIT(CPCAP_BIT_ST_FS_INV); |
1186 | val |= BIT(CPCAP_BIT_ST_CLK_INV); |
1187 | break; |
1188 | case SND_SOC_DAIFMT_IB_NF: |
1189 | val &= ~BIT(CPCAP_BIT_ST_FS_INV); |
1190 | val |= BIT(CPCAP_BIT_ST_CLK_INV); |
1191 | break; |
1192 | case SND_SOC_DAIFMT_NB_IF: |
1193 | val |= BIT(CPCAP_BIT_ST_FS_INV); |
1194 | val &= ~BIT(CPCAP_BIT_ST_CLK_INV); |
1195 | break; |
1196 | case SND_SOC_DAIFMT_NB_NF: |
1197 | val &= ~BIT(CPCAP_BIT_ST_FS_INV); |
1198 | val &= ~BIT(CPCAP_BIT_ST_CLK_INV); |
1199 | break; |
1200 | default: |
1201 | dev_err(dev, "HiFi dai fmt failed: unsupported clock invert mode" ); |
1202 | return -EINVAL; |
1203 | } |
1204 | |
1205 | if (val & BIT(CPCAP_BIT_ST_CLK_INV)) |
1206 | val &= ~BIT(CPCAP_BIT_ST_CLK_INV); |
1207 | else |
1208 | val |= BIT(CPCAP_BIT_ST_CLK_INV); |
1209 | |
1210 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
1211 | case SND_SOC_DAIFMT_I2S: |
1212 | val |= BIT(CPCAP_BIT_ST_DIG_AUD_FS0); |
1213 | val |= BIT(CPCAP_BIT_ST_DIG_AUD_FS1); |
1214 | break; |
1215 | default: |
1216 | /* 01 - 4 slots network mode */ |
1217 | val |= BIT(CPCAP_BIT_ST_DIG_AUD_FS0); |
1218 | val &= ~BIT(CPCAP_BIT_ST_DIG_AUD_FS1); |
1219 | /* L on slot 1 */ |
1220 | val |= BIT(CPCAP_BIT_ST_L_TIMESLOT0); |
1221 | break; |
1222 | } |
1223 | |
1224 | dev_dbg(dev, "HiFi dai format: val=%04x" , val); |
1225 | return regmap_update_bits(map: cpcap->regmap, reg, mask, val); |
1226 | } |
1227 | |
1228 | static int cpcap_hifi_set_mute(struct snd_soc_dai *dai, int mute, int direction) |
1229 | { |
1230 | struct snd_soc_component *component = dai->component; |
1231 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
1232 | static const u16 reg = CPCAP_REG_RXSDOA; |
1233 | static const u16 mask = BIT(CPCAP_BIT_ST_DAC_SW); |
1234 | u16 val; |
1235 | |
1236 | if (mute) |
1237 | val = 0; |
1238 | else |
1239 | val = BIT(CPCAP_BIT_ST_DAC_SW); |
1240 | |
1241 | dev_dbg(component->dev, "HiFi mute: %d" , mute); |
1242 | return regmap_update_bits(map: cpcap->regmap, reg, mask, val); |
1243 | } |
1244 | |
1245 | static const struct snd_soc_dai_ops cpcap_dai_hifi_ops = { |
1246 | .hw_params = cpcap_hifi_hw_params, |
1247 | .set_sysclk = cpcap_hifi_set_dai_sysclk, |
1248 | .set_fmt = cpcap_hifi_set_dai_fmt, |
1249 | .mute_stream = cpcap_hifi_set_mute, |
1250 | .no_capture_mute = 1, |
1251 | }; |
1252 | |
1253 | static int cpcap_voice_hw_params(struct snd_pcm_substream *substream, |
1254 | struct snd_pcm_hw_params *params, |
1255 | struct snd_soc_dai *dai) |
1256 | { |
1257 | struct snd_soc_component *component = dai->component; |
1258 | struct device *dev = component->dev; |
1259 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
1260 | static const u16 reg_cdi = CPCAP_REG_CDI; |
1261 | int rate = params_rate(p: params); |
1262 | int channels = params_channels(p: params); |
1263 | int direction = substream->stream; |
1264 | u16 val, mask; |
1265 | int err; |
1266 | |
1267 | dev_dbg(dev, "Voice setup HW params: rate=%d, direction=%d, chan=%d" , |
1268 | rate, direction, channels); |
1269 | |
1270 | err = cpcap_set_samprate(cpcap, dai: CPCAP_DAI_VOICE, samplerate: rate); |
1271 | if (err) |
1272 | return err; |
1273 | |
1274 | if (direction == SNDRV_PCM_STREAM_CAPTURE) { |
1275 | mask = 0x0000; |
1276 | mask |= BIT(CPCAP_BIT_MIC1_RX_TIMESLOT0); |
1277 | mask |= BIT(CPCAP_BIT_MIC1_RX_TIMESLOT1); |
1278 | mask |= BIT(CPCAP_BIT_MIC1_RX_TIMESLOT2); |
1279 | mask |= BIT(CPCAP_BIT_MIC2_TIMESLOT0); |
1280 | mask |= BIT(CPCAP_BIT_MIC2_TIMESLOT1); |
1281 | mask |= BIT(CPCAP_BIT_MIC2_TIMESLOT2); |
1282 | val = 0x0000; |
1283 | if (channels >= 2) |
1284 | val = BIT(CPCAP_BIT_MIC1_RX_TIMESLOT0); |
1285 | err = regmap_update_bits(map: cpcap->regmap, reg: reg_cdi, mask, val); |
1286 | if (err) |
1287 | return err; |
1288 | } |
1289 | |
1290 | return 0; |
1291 | } |
1292 | |
1293 | static int cpcap_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, |
1294 | unsigned int freq, int dir) |
1295 | { |
1296 | struct snd_soc_component *component = codec_dai->component; |
1297 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
1298 | |
1299 | dev_dbg(component->dev, "Voice setup sysclk: clk_id=%u, freq=%u" , |
1300 | clk_id, freq); |
1301 | return cpcap_set_sysclk(cpcap, dai: CPCAP_DAI_VOICE, clk_id, freq); |
1302 | } |
1303 | |
1304 | static int cpcap_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, |
1305 | unsigned int fmt) |
1306 | { |
1307 | struct snd_soc_component *component = codec_dai->component; |
1308 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
1309 | static const u16 mask = BIT(CPCAP_BIT_SMB_CDC) | |
1310 | BIT(CPCAP_BIT_CLK_INV) | |
1311 | BIT(CPCAP_BIT_FS_INV) | |
1312 | BIT(CPCAP_BIT_CDC_DIG_AUD_FS0) | |
1313 | BIT(CPCAP_BIT_CDC_DIG_AUD_FS1); |
1314 | u16 val = 0x0000; |
1315 | int err; |
1316 | |
1317 | dev_dbg(component->dev, "Voice setup dai format (%08x)" , fmt); |
1318 | |
1319 | /* |
1320 | * "Voice Playback" and "Voice Capture" should always be |
1321 | * configured as SND_SOC_DAIFMT_CBP_CFP - codec clk & frm |
1322 | * provider |
1323 | */ |
1324 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
1325 | case SND_SOC_DAIFMT_CBP_CFP: |
1326 | val &= ~BIT(CPCAP_BIT_SMB_CDC); |
1327 | break; |
1328 | default: |
1329 | dev_err(component->dev, "Voice dai fmt failed: CPCAP should be the provider" ); |
1330 | val &= ~BIT(CPCAP_BIT_SMB_CDC); |
1331 | break; |
1332 | } |
1333 | |
1334 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
1335 | case SND_SOC_DAIFMT_IB_IF: |
1336 | val |= BIT(CPCAP_BIT_CLK_INV); |
1337 | val |= BIT(CPCAP_BIT_FS_INV); |
1338 | break; |
1339 | case SND_SOC_DAIFMT_IB_NF: |
1340 | val |= BIT(CPCAP_BIT_CLK_INV); |
1341 | val &= ~BIT(CPCAP_BIT_FS_INV); |
1342 | break; |
1343 | case SND_SOC_DAIFMT_NB_IF: |
1344 | val &= ~BIT(CPCAP_BIT_CLK_INV); |
1345 | val |= BIT(CPCAP_BIT_FS_INV); |
1346 | break; |
1347 | case SND_SOC_DAIFMT_NB_NF: |
1348 | val &= ~BIT(CPCAP_BIT_CLK_INV); |
1349 | val &= ~BIT(CPCAP_BIT_FS_INV); |
1350 | break; |
1351 | default: |
1352 | dev_err(component->dev, "Voice dai fmt failed: unsupported clock invert mode" ); |
1353 | break; |
1354 | } |
1355 | |
1356 | if (val & BIT(CPCAP_BIT_CLK_INV)) |
1357 | val &= ~BIT(CPCAP_BIT_CLK_INV); |
1358 | else |
1359 | val |= BIT(CPCAP_BIT_CLK_INV); |
1360 | |
1361 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
1362 | case SND_SOC_DAIFMT_I2S: |
1363 | /* 11 - true I2S mode */ |
1364 | val |= BIT(CPCAP_BIT_CDC_DIG_AUD_FS0); |
1365 | val |= BIT(CPCAP_BIT_CDC_DIG_AUD_FS1); |
1366 | break; |
1367 | default: |
1368 | /* 4 timeslots network mode */ |
1369 | val |= BIT(CPCAP_BIT_CDC_DIG_AUD_FS0); |
1370 | val &= ~BIT(CPCAP_BIT_CDC_DIG_AUD_FS1); |
1371 | break; |
1372 | } |
1373 | |
1374 | dev_dbg(component->dev, "Voice dai format: val=%04x" , val); |
1375 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_CDI, mask, val); |
1376 | if (err) |
1377 | return err; |
1378 | |
1379 | cpcap->codec_format = val; |
1380 | return 0; |
1381 | } |
1382 | |
1383 | |
1384 | /* |
1385 | * Configure codec for voice call if requested. |
1386 | * |
1387 | * We can configure most with snd_soc_dai_set_sysclk(), snd_soc_dai_set_fmt() |
1388 | * and snd_soc_dai_set_tdm_slot(). This function configures the rest of the |
1389 | * cpcap related hardware as CPU is not involved in the voice call. |
1390 | */ |
1391 | static int cpcap_voice_call(struct cpcap_audio *cpcap, struct snd_soc_dai *dai, |
1392 | bool voice_call) |
1393 | { |
1394 | int mask, err; |
1395 | |
1396 | /* Modem to codec VAUDIO_MODE1 */ |
1397 | mask = BIT(CPCAP_BIT_VAUDIO_MODE1); |
1398 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_VAUDIOC, |
1399 | mask, val: voice_call ? mask : 0); |
1400 | if (err) |
1401 | return err; |
1402 | |
1403 | /* Clear MIC1_MUX for call */ |
1404 | mask = BIT(CPCAP_BIT_MIC1_MUX); |
1405 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_TXI, |
1406 | mask, val: voice_call ? 0 : mask); |
1407 | if (err) |
1408 | return err; |
1409 | |
1410 | /* Set MIC2_MUX for call */ |
1411 | mask = BIT(CPCAP_BIT_MB_ON1L) | BIT(CPCAP_BIT_MB_ON1R) | |
1412 | BIT(CPCAP_BIT_MIC2_MUX) | BIT(CPCAP_BIT_MIC2_PGA_EN); |
1413 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_TXI, |
1414 | mask, val: voice_call ? mask : 0); |
1415 | if (err) |
1416 | return err; |
1417 | |
1418 | /* Enable LDSP for call */ |
1419 | mask = BIT(CPCAP_BIT_A2_LDSP_L_EN) | BIT(CPCAP_BIT_A2_LDSP_R_EN); |
1420 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_RXOA, |
1421 | mask, val: voice_call ? mask : 0); |
1422 | if (err) |
1423 | return err; |
1424 | |
1425 | /* Enable CPCAP_BIT_PGA_CDC_EN for call */ |
1426 | mask = BIT(CPCAP_BIT_PGA_CDC_EN); |
1427 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_RXCOA, |
1428 | mask, val: voice_call ? mask : 0); |
1429 | if (err) |
1430 | return err; |
1431 | |
1432 | /* Unmute voice for call */ |
1433 | if (dai) { |
1434 | err = snd_soc_dai_digital_mute(dai, mute: !voice_call, |
1435 | direction: SNDRV_PCM_STREAM_PLAYBACK); |
1436 | if (err) |
1437 | return err; |
1438 | } |
1439 | |
1440 | /* Set modem to codec mic CDC and HPF for call */ |
1441 | mask = BIT(CPCAP_BIT_MIC2_CDC_EN) | BIT(CPCAP_BIT_CDC_EN_RX) | |
1442 | BIT(CPCAP_BIT_AUDOHPF_1) | BIT(CPCAP_BIT_AUDOHPF_0) | |
1443 | BIT(CPCAP_BIT_AUDIHPF_1) | BIT(CPCAP_BIT_AUDIHPF_0); |
1444 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_CC, |
1445 | mask, val: voice_call ? mask : 0); |
1446 | if (err) |
1447 | return err; |
1448 | |
1449 | /* Enable modem to codec CDC for call*/ |
1450 | mask = BIT(CPCAP_BIT_CDC_CLK_EN); |
1451 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_CDI, |
1452 | mask, val: voice_call ? mask : 0); |
1453 | |
1454 | return err; |
1455 | } |
1456 | |
1457 | static int cpcap_voice_set_tdm_slot(struct snd_soc_dai *dai, |
1458 | unsigned int tx_mask, unsigned int rx_mask, |
1459 | int slots, int slot_width) |
1460 | { |
1461 | struct snd_soc_component *component = dai->component; |
1462 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
1463 | int err, ts_mask, mask; |
1464 | bool voice_call; |
1465 | |
1466 | /* |
1467 | * Primitive test for voice call, probably needs more checks |
1468 | * later on for 16-bit calls detected, Bluetooth headset etc. |
1469 | */ |
1470 | if (tx_mask == 0 && rx_mask == 1 && slot_width == 8) |
1471 | voice_call = true; |
1472 | else |
1473 | voice_call = false; |
1474 | |
1475 | ts_mask = 0x7 << CPCAP_BIT_MIC2_TIMESLOT0; |
1476 | ts_mask |= 0x7 << CPCAP_BIT_MIC1_RX_TIMESLOT0; |
1477 | |
1478 | mask = (tx_mask & 0x7) << CPCAP_BIT_MIC2_TIMESLOT0; |
1479 | mask |= (rx_mask & 0x7) << CPCAP_BIT_MIC1_RX_TIMESLOT0; |
1480 | |
1481 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_CDI, |
1482 | mask: ts_mask, val: mask); |
1483 | if (err) |
1484 | return err; |
1485 | |
1486 | err = cpcap_set_samprate(cpcap, dai: CPCAP_DAI_VOICE, samplerate: slot_width * 1000); |
1487 | if (err) |
1488 | return err; |
1489 | |
1490 | err = cpcap_voice_call(cpcap, dai, voice_call); |
1491 | if (err) |
1492 | return err; |
1493 | |
1494 | return 0; |
1495 | } |
1496 | |
1497 | static int cpcap_voice_set_mute(struct snd_soc_dai *dai, int mute, int direction) |
1498 | { |
1499 | struct snd_soc_component *component = dai->component; |
1500 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
1501 | static const u16 reg = CPCAP_REG_RXCOA; |
1502 | static const u16 mask = BIT(CPCAP_BIT_CDC_SW); |
1503 | u16 val; |
1504 | |
1505 | if (mute) |
1506 | val = 0; |
1507 | else |
1508 | val = BIT(CPCAP_BIT_CDC_SW); |
1509 | |
1510 | dev_dbg(component->dev, "Voice mute: %d" , mute); |
1511 | return regmap_update_bits(map: cpcap->regmap, reg, mask, val); |
1512 | }; |
1513 | |
1514 | static const struct snd_soc_dai_ops cpcap_dai_voice_ops = { |
1515 | .hw_params = cpcap_voice_hw_params, |
1516 | .set_sysclk = cpcap_voice_set_dai_sysclk, |
1517 | .set_fmt = cpcap_voice_set_dai_fmt, |
1518 | .set_tdm_slot = cpcap_voice_set_tdm_slot, |
1519 | .mute_stream = cpcap_voice_set_mute, |
1520 | .no_capture_mute = 1, |
1521 | }; |
1522 | |
1523 | static struct snd_soc_dai_driver cpcap_dai[] = { |
1524 | { |
1525 | .id = 0, |
1526 | .name = "cpcap-hifi" , |
1527 | .playback = { |
1528 | .stream_name = "HiFi Playback" , |
1529 | .channels_min = 2, |
1530 | .channels_max = 2, |
1531 | .rates = SNDRV_PCM_RATE_8000_48000, |
1532 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE, |
1533 | }, |
1534 | .ops = &cpcap_dai_hifi_ops, |
1535 | }, |
1536 | { |
1537 | .id = 1, |
1538 | .name = "cpcap-voice" , |
1539 | .playback = { |
1540 | .stream_name = "Voice Playback" , |
1541 | .channels_min = 1, |
1542 | .channels_max = 1, |
1543 | .rates = SNDRV_PCM_RATE_8000_48000, |
1544 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
1545 | }, |
1546 | .capture = { |
1547 | .stream_name = "Voice Capture" , |
1548 | .channels_min = 1, |
1549 | .channels_max = 2, |
1550 | .rates = SNDRV_PCM_RATE_8000_48000, |
1551 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
1552 | }, |
1553 | .ops = &cpcap_dai_voice_ops, |
1554 | }, |
1555 | }; |
1556 | |
1557 | static int cpcap_dai_mux(struct cpcap_audio *cpcap, bool swap_dai_configuration) |
1558 | { |
1559 | u16 hifi_val, voice_val; |
1560 | u16 hifi_mask = BIT(CPCAP_BIT_DIG_AUD_IN_ST_DAC); |
1561 | u16 voice_mask = BIT(CPCAP_BIT_DIG_AUD_IN); |
1562 | int err; |
1563 | |
1564 | |
1565 | |
1566 | if (!swap_dai_configuration) { |
1567 | /* Codec on DAI0, HiFi on DAI1 */ |
1568 | voice_val = 0; |
1569 | hifi_val = hifi_mask; |
1570 | } else { |
1571 | /* Codec on DAI1, HiFi on DAI0 */ |
1572 | voice_val = voice_mask; |
1573 | hifi_val = 0; |
1574 | } |
1575 | |
1576 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_CDI, |
1577 | mask: voice_mask, val: voice_val); |
1578 | if (err) |
1579 | return err; |
1580 | |
1581 | err = regmap_update_bits(map: cpcap->regmap, CPCAP_REG_SDACDI, |
1582 | mask: hifi_mask, val: hifi_val); |
1583 | if (err) |
1584 | return err; |
1585 | |
1586 | return 0; |
1587 | } |
1588 | |
1589 | static int cpcap_audio_reset(struct snd_soc_component *component, |
1590 | bool swap_dai_configuration) |
1591 | { |
1592 | struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(c: component); |
1593 | int i, err = 0; |
1594 | |
1595 | dev_dbg(component->dev, "init audio codec" ); |
1596 | |
1597 | for (i = 0; i < ARRAY_SIZE(cpcap_default_regs); i++) { |
1598 | err = regmap_update_bits(map: cpcap->regmap, |
1599 | reg: cpcap_default_regs[i].reg, |
1600 | mask: cpcap_default_regs[i].mask, |
1601 | val: cpcap_default_regs[i].val); |
1602 | if (err) |
1603 | return err; |
1604 | } |
1605 | |
1606 | /* setup default settings */ |
1607 | err = cpcap_dai_mux(cpcap, swap_dai_configuration); |
1608 | if (err) |
1609 | return err; |
1610 | |
1611 | err = cpcap_set_sysclk(cpcap, dai: CPCAP_DAI_HIFI, clk_id: 0, freq: 26000000); |
1612 | if (err) |
1613 | return err; |
1614 | err = cpcap_set_sysclk(cpcap, dai: CPCAP_DAI_VOICE, clk_id: 0, freq: 26000000); |
1615 | if (err) |
1616 | return err; |
1617 | |
1618 | err = cpcap_set_samprate(cpcap, dai: CPCAP_DAI_HIFI, samplerate: 48000); |
1619 | if (err) |
1620 | return err; |
1621 | |
1622 | err = cpcap_set_samprate(cpcap, dai: CPCAP_DAI_VOICE, samplerate: 48000); |
1623 | if (err) |
1624 | return err; |
1625 | |
1626 | return 0; |
1627 | } |
1628 | |
1629 | static int cpcap_soc_probe(struct snd_soc_component *component) |
1630 | { |
1631 | struct cpcap_audio *cpcap; |
1632 | int err; |
1633 | |
1634 | cpcap = devm_kzalloc(dev: component->dev, size: sizeof(*cpcap), GFP_KERNEL); |
1635 | if (!cpcap) |
1636 | return -ENOMEM; |
1637 | snd_soc_component_set_drvdata(c: component, data: cpcap); |
1638 | cpcap->component = component; |
1639 | |
1640 | cpcap->regmap = dev_get_regmap(dev: component->dev->parent, NULL); |
1641 | if (!cpcap->regmap) |
1642 | return -ENODEV; |
1643 | snd_soc_component_init_regmap(component, regmap: cpcap->regmap); |
1644 | |
1645 | err = cpcap_get_vendor(dev: component->dev, regmap: cpcap->regmap, vendor: &cpcap->vendor); |
1646 | if (err) |
1647 | return err; |
1648 | |
1649 | return cpcap_audio_reset(component, swap_dai_configuration: false); |
1650 | } |
1651 | |
1652 | static struct snd_soc_component_driver soc_codec_dev_cpcap = { |
1653 | .probe = cpcap_soc_probe, |
1654 | .controls = cpcap_snd_controls, |
1655 | .num_controls = ARRAY_SIZE(cpcap_snd_controls), |
1656 | .dapm_widgets = cpcap_dapm_widgets, |
1657 | .num_dapm_widgets = ARRAY_SIZE(cpcap_dapm_widgets), |
1658 | .dapm_routes = intercon, |
1659 | .num_dapm_routes = ARRAY_SIZE(intercon), |
1660 | .idle_bias_on = 1, |
1661 | .use_pmdown_time = 1, |
1662 | .endianness = 1, |
1663 | }; |
1664 | |
1665 | static int cpcap_codec_probe(struct platform_device *pdev) |
1666 | { |
1667 | struct device_node *codec_node = |
1668 | of_get_child_by_name(node: pdev->dev.parent->of_node, name: "audio-codec" ); |
1669 | if (!codec_node) |
1670 | return -ENODEV; |
1671 | |
1672 | pdev->dev.of_node = codec_node; |
1673 | |
1674 | return devm_snd_soc_register_component(dev: &pdev->dev, component_driver: &soc_codec_dev_cpcap, |
1675 | dai_drv: cpcap_dai, ARRAY_SIZE(cpcap_dai)); |
1676 | } |
1677 | |
1678 | static struct platform_driver cpcap_codec_driver = { |
1679 | .probe = cpcap_codec_probe, |
1680 | .driver = { |
1681 | .name = "cpcap-codec" , |
1682 | }, |
1683 | }; |
1684 | module_platform_driver(cpcap_codec_driver); |
1685 | |
1686 | MODULE_ALIAS("platform:cpcap-codec" ); |
1687 | MODULE_DESCRIPTION("ASoC CPCAP codec driver" ); |
1688 | MODULE_AUTHOR("Sebastian Reichel" ); |
1689 | MODULE_LICENSE("GPL v2" ); |
1690 | |