1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * alc5623.c -- alc562[123] ALSA Soc Audio driver |
4 | * |
5 | * Copyright 2008 Realtek Microelectronics |
6 | * Author: flove <flove@realtek.com> Ethan <eku@marvell.com> |
7 | * |
8 | * Copyright 2010 Arnaud Patard <arnaud.patard@rtp-net.org> |
9 | * |
10 | * Based on WM8753.c |
11 | */ |
12 | |
13 | #include <linux/module.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/init.h> |
16 | #include <linux/delay.h> |
17 | #include <linux/pm.h> |
18 | #include <linux/i2c.h> |
19 | #include <linux/regmap.h> |
20 | #include <linux/slab.h> |
21 | #include <linux/of.h> |
22 | #include <sound/core.h> |
23 | #include <sound/pcm.h> |
24 | #include <sound/pcm_params.h> |
25 | #include <sound/tlv.h> |
26 | #include <sound/soc.h> |
27 | #include <sound/initval.h> |
28 | #include <sound/alc5623.h> |
29 | |
30 | #include "alc5623.h" |
31 | |
32 | static int caps_charge = 2000; |
33 | module_param(caps_charge, int, 0); |
34 | MODULE_PARM_DESC(caps_charge, "ALC5623 cap charge time (msecs)" ); |
35 | |
36 | /* codec private data */ |
37 | struct alc5623_priv { |
38 | struct regmap *regmap; |
39 | u8 id; |
40 | unsigned int sysclk; |
41 | unsigned int add_ctrl; |
42 | unsigned int jack_det_ctrl; |
43 | }; |
44 | |
45 | static inline int alc5623_reset(struct snd_soc_component *component) |
46 | { |
47 | return snd_soc_component_write(component, ALC5623_RESET, val: 0); |
48 | } |
49 | |
50 | static int amp_mixer_event(struct snd_soc_dapm_widget *w, |
51 | struct snd_kcontrol *kcontrol, int event) |
52 | { |
53 | struct snd_soc_component *component = snd_soc_dapm_to_component(dapm: w->dapm); |
54 | |
55 | /* to power-on/off class-d amp generators/speaker */ |
56 | /* need to write to 'index-46h' register : */ |
57 | /* so write index num (here 0x46) to reg 0x6a */ |
58 | /* and then 0xffff/0 to reg 0x6c */ |
59 | snd_soc_component_write(component, ALC5623_HID_CTRL_INDEX, val: 0x46); |
60 | |
61 | switch (event) { |
62 | case SND_SOC_DAPM_PRE_PMU: |
63 | snd_soc_component_write(component, ALC5623_HID_CTRL_DATA, val: 0xFFFF); |
64 | break; |
65 | case SND_SOC_DAPM_POST_PMD: |
66 | snd_soc_component_write(component, ALC5623_HID_CTRL_DATA, val: 0); |
67 | break; |
68 | } |
69 | |
70 | return 0; |
71 | } |
72 | |
73 | /* |
74 | * ALC5623 Controls |
75 | */ |
76 | |
77 | static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0); |
78 | static const DECLARE_TLV_DB_SCALE(hp_tlv, -4650, 150, 0); |
79 | static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -1650, 150, 0); |
80 | static const DECLARE_TLV_DB_RANGE(boost_tlv, |
81 | 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), |
82 | 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), |
83 | 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0) |
84 | ); |
85 | static const DECLARE_TLV_DB_SCALE(dig_tlv, 0, 600, 0); |
86 | |
87 | static const struct snd_kcontrol_new alc5621_vol_snd_controls[] = { |
88 | SOC_DOUBLE_TLV("Speaker Playback Volume" , |
89 | ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv), |
90 | SOC_DOUBLE("Speaker Playback Switch" , |
91 | ALC5623_SPK_OUT_VOL, 15, 7, 1, 1), |
92 | SOC_DOUBLE_TLV("Headphone Playback Volume" , |
93 | ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv), |
94 | SOC_DOUBLE("Headphone Playback Switch" , |
95 | ALC5623_HP_OUT_VOL, 15, 7, 1, 1), |
96 | }; |
97 | |
98 | static const struct snd_kcontrol_new alc5622_vol_snd_controls[] = { |
99 | SOC_DOUBLE_TLV("Speaker Playback Volume" , |
100 | ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv), |
101 | SOC_DOUBLE("Speaker Playback Switch" , |
102 | ALC5623_SPK_OUT_VOL, 15, 7, 1, 1), |
103 | SOC_DOUBLE_TLV("Line Playback Volume" , |
104 | ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv), |
105 | SOC_DOUBLE("Line Playback Switch" , |
106 | ALC5623_HP_OUT_VOL, 15, 7, 1, 1), |
107 | }; |
108 | |
109 | static const struct snd_kcontrol_new alc5623_vol_snd_controls[] = { |
110 | SOC_DOUBLE_TLV("Line Playback Volume" , |
111 | ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv), |
112 | SOC_DOUBLE("Line Playback Switch" , |
113 | ALC5623_SPK_OUT_VOL, 15, 7, 1, 1), |
114 | SOC_DOUBLE_TLV("Headphone Playback Volume" , |
115 | ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv), |
116 | SOC_DOUBLE("Headphone Playback Switch" , |
117 | ALC5623_HP_OUT_VOL, 15, 7, 1, 1), |
118 | }; |
119 | |
120 | static const struct snd_kcontrol_new alc5623_snd_controls[] = { |
121 | SOC_DOUBLE_TLV("Auxout Playback Volume" , |
122 | ALC5623_MONO_AUX_OUT_VOL, 8, 0, 31, 1, hp_tlv), |
123 | SOC_DOUBLE("Auxout Playback Switch" , |
124 | ALC5623_MONO_AUX_OUT_VOL, 15, 7, 1, 1), |
125 | SOC_DOUBLE_TLV("PCM Playback Volume" , |
126 | ALC5623_STEREO_DAC_VOL, 8, 0, 31, 1, vol_tlv), |
127 | SOC_DOUBLE_TLV("AuxI Capture Volume" , |
128 | ALC5623_AUXIN_VOL, 8, 0, 31, 1, vol_tlv), |
129 | SOC_DOUBLE_TLV("LineIn Capture Volume" , |
130 | ALC5623_LINE_IN_VOL, 8, 0, 31, 1, vol_tlv), |
131 | SOC_SINGLE_TLV("Mic1 Capture Volume" , |
132 | ALC5623_MIC_VOL, 8, 31, 1, vol_tlv), |
133 | SOC_SINGLE_TLV("Mic2 Capture Volume" , |
134 | ALC5623_MIC_VOL, 0, 31, 1, vol_tlv), |
135 | SOC_DOUBLE_TLV("Rec Capture Volume" , |
136 | ALC5623_ADC_REC_GAIN, 7, 0, 31, 0, adc_rec_tlv), |
137 | SOC_SINGLE_TLV("Mic 1 Boost Volume" , |
138 | ALC5623_MIC_CTRL, 10, 2, 0, boost_tlv), |
139 | SOC_SINGLE_TLV("Mic 2 Boost Volume" , |
140 | ALC5623_MIC_CTRL, 8, 2, 0, boost_tlv), |
141 | SOC_SINGLE_TLV("Digital Boost Volume" , |
142 | ALC5623_ADD_CTRL_REG, 4, 3, 0, dig_tlv), |
143 | }; |
144 | |
145 | /* |
146 | * DAPM Controls |
147 | */ |
148 | static const struct snd_kcontrol_new alc5623_hp_mixer_controls[] = { |
149 | SOC_DAPM_SINGLE("LI2HP Playback Switch" , ALC5623_LINE_IN_VOL, 15, 1, 1), |
150 | SOC_DAPM_SINGLE("AUXI2HP Playback Switch" , ALC5623_AUXIN_VOL, 15, 1, 1), |
151 | SOC_DAPM_SINGLE("MIC12HP Playback Switch" , ALC5623_MIC_ROUTING_CTRL, 15, 1, 1), |
152 | SOC_DAPM_SINGLE("MIC22HP Playback Switch" , ALC5623_MIC_ROUTING_CTRL, 7, 1, 1), |
153 | SOC_DAPM_SINGLE("DAC2HP Playback Switch" , ALC5623_STEREO_DAC_VOL, 15, 1, 1), |
154 | }; |
155 | |
156 | static const struct snd_kcontrol_new alc5623_hpl_mixer_controls[] = { |
157 | SOC_DAPM_SINGLE("ADC2HP_L Playback Switch" , ALC5623_ADC_REC_GAIN, 15, 1, 1), |
158 | }; |
159 | |
160 | static const struct snd_kcontrol_new alc5623_hpr_mixer_controls[] = { |
161 | SOC_DAPM_SINGLE("ADC2HP_R Playback Switch" , ALC5623_ADC_REC_GAIN, 14, 1, 1), |
162 | }; |
163 | |
164 | static const struct snd_kcontrol_new alc5623_mono_mixer_controls[] = { |
165 | SOC_DAPM_SINGLE("ADC2MONO_L Playback Switch" , ALC5623_ADC_REC_GAIN, 13, 1, 1), |
166 | SOC_DAPM_SINGLE("ADC2MONO_R Playback Switch" , ALC5623_ADC_REC_GAIN, 12, 1, 1), |
167 | SOC_DAPM_SINGLE("LI2MONO Playback Switch" , ALC5623_LINE_IN_VOL, 13, 1, 1), |
168 | SOC_DAPM_SINGLE("AUXI2MONO Playback Switch" , ALC5623_AUXIN_VOL, 13, 1, 1), |
169 | SOC_DAPM_SINGLE("MIC12MONO Playback Switch" , ALC5623_MIC_ROUTING_CTRL, 13, 1, 1), |
170 | SOC_DAPM_SINGLE("MIC22MONO Playback Switch" , ALC5623_MIC_ROUTING_CTRL, 5, 1, 1), |
171 | SOC_DAPM_SINGLE("DAC2MONO Playback Switch" , ALC5623_STEREO_DAC_VOL, 13, 1, 1), |
172 | }; |
173 | |
174 | static const struct snd_kcontrol_new alc5623_speaker_mixer_controls[] = { |
175 | SOC_DAPM_SINGLE("LI2SPK Playback Switch" , ALC5623_LINE_IN_VOL, 14, 1, 1), |
176 | SOC_DAPM_SINGLE("AUXI2SPK Playback Switch" , ALC5623_AUXIN_VOL, 14, 1, 1), |
177 | SOC_DAPM_SINGLE("MIC12SPK Playback Switch" , ALC5623_MIC_ROUTING_CTRL, 14, 1, 1), |
178 | SOC_DAPM_SINGLE("MIC22SPK Playback Switch" , ALC5623_MIC_ROUTING_CTRL, 6, 1, 1), |
179 | SOC_DAPM_SINGLE("DAC2SPK Playback Switch" , ALC5623_STEREO_DAC_VOL, 14, 1, 1), |
180 | }; |
181 | |
182 | /* Left Record Mixer */ |
183 | static const struct snd_kcontrol_new alc5623_captureL_mixer_controls[] = { |
184 | SOC_DAPM_SINGLE("Mic1 Capture Switch" , ALC5623_ADC_REC_MIXER, 14, 1, 1), |
185 | SOC_DAPM_SINGLE("Mic2 Capture Switch" , ALC5623_ADC_REC_MIXER, 13, 1, 1), |
186 | SOC_DAPM_SINGLE("LineInL Capture Switch" , ALC5623_ADC_REC_MIXER, 12, 1, 1), |
187 | SOC_DAPM_SINGLE("Left AuxI Capture Switch" , ALC5623_ADC_REC_MIXER, 11, 1, 1), |
188 | SOC_DAPM_SINGLE("HPMixerL Capture Switch" , ALC5623_ADC_REC_MIXER, 10, 1, 1), |
189 | SOC_DAPM_SINGLE("SPKMixer Capture Switch" , ALC5623_ADC_REC_MIXER, 9, 1, 1), |
190 | SOC_DAPM_SINGLE("MonoMixer Capture Switch" , ALC5623_ADC_REC_MIXER, 8, 1, 1), |
191 | }; |
192 | |
193 | /* Right Record Mixer */ |
194 | static const struct snd_kcontrol_new alc5623_captureR_mixer_controls[] = { |
195 | SOC_DAPM_SINGLE("Mic1 Capture Switch" , ALC5623_ADC_REC_MIXER, 6, 1, 1), |
196 | SOC_DAPM_SINGLE("Mic2 Capture Switch" , ALC5623_ADC_REC_MIXER, 5, 1, 1), |
197 | SOC_DAPM_SINGLE("LineInR Capture Switch" , ALC5623_ADC_REC_MIXER, 4, 1, 1), |
198 | SOC_DAPM_SINGLE("Right AuxI Capture Switch" , ALC5623_ADC_REC_MIXER, 3, 1, 1), |
199 | SOC_DAPM_SINGLE("HPMixerR Capture Switch" , ALC5623_ADC_REC_MIXER, 2, 1, 1), |
200 | SOC_DAPM_SINGLE("SPKMixer Capture Switch" , ALC5623_ADC_REC_MIXER, 1, 1, 1), |
201 | SOC_DAPM_SINGLE("MonoMixer Capture Switch" , ALC5623_ADC_REC_MIXER, 0, 1, 1), |
202 | }; |
203 | |
204 | static const char *alc5623_spk_n_sour_sel[] = { |
205 | "RN/-R" , "RP/+R" , "LN/-R" , "Vmid" }; |
206 | static const char *alc5623_hpl_out_input_sel[] = { |
207 | "Vmid" , "HP Left Mix" }; |
208 | static const char *alc5623_hpr_out_input_sel[] = { |
209 | "Vmid" , "HP Right Mix" }; |
210 | static const char *alc5623_spkout_input_sel[] = { |
211 | "Vmid" , "HPOut Mix" , "Speaker Mix" , "Mono Mix" }; |
212 | static const char *alc5623_aux_out_input_sel[] = { |
213 | "Vmid" , "HPOut Mix" , "Speaker Mix" , "Mono Mix" }; |
214 | |
215 | /* auxout output mux */ |
216 | static SOC_ENUM_SINGLE_DECL(alc5623_aux_out_input_enum, |
217 | ALC5623_OUTPUT_MIXER_CTRL, 6, |
218 | alc5623_aux_out_input_sel); |
219 | static const struct snd_kcontrol_new alc5623_auxout_mux_controls = |
220 | SOC_DAPM_ENUM("Route" , alc5623_aux_out_input_enum); |
221 | |
222 | /* speaker output mux */ |
223 | static SOC_ENUM_SINGLE_DECL(alc5623_spkout_input_enum, |
224 | ALC5623_OUTPUT_MIXER_CTRL, 10, |
225 | alc5623_spkout_input_sel); |
226 | static const struct snd_kcontrol_new alc5623_spkout_mux_controls = |
227 | SOC_DAPM_ENUM("Route" , alc5623_spkout_input_enum); |
228 | |
229 | /* headphone left output mux */ |
230 | static SOC_ENUM_SINGLE_DECL(alc5623_hpl_out_input_enum, |
231 | ALC5623_OUTPUT_MIXER_CTRL, 9, |
232 | alc5623_hpl_out_input_sel); |
233 | static const struct snd_kcontrol_new alc5623_hpl_out_mux_controls = |
234 | SOC_DAPM_ENUM("Route" , alc5623_hpl_out_input_enum); |
235 | |
236 | /* headphone right output mux */ |
237 | static SOC_ENUM_SINGLE_DECL(alc5623_hpr_out_input_enum, |
238 | ALC5623_OUTPUT_MIXER_CTRL, 8, |
239 | alc5623_hpr_out_input_sel); |
240 | static const struct snd_kcontrol_new alc5623_hpr_out_mux_controls = |
241 | SOC_DAPM_ENUM("Route" , alc5623_hpr_out_input_enum); |
242 | |
243 | /* speaker output N select */ |
244 | static SOC_ENUM_SINGLE_DECL(alc5623_spk_n_sour_enum, |
245 | ALC5623_OUTPUT_MIXER_CTRL, 14, |
246 | alc5623_spk_n_sour_sel); |
247 | static const struct snd_kcontrol_new alc5623_spkoutn_mux_controls = |
248 | SOC_DAPM_ENUM("Route" , alc5623_spk_n_sour_enum); |
249 | |
250 | static const struct snd_soc_dapm_widget alc5623_dapm_widgets[] = { |
251 | /* Muxes */ |
252 | SND_SOC_DAPM_MUX("AuxOut Mux" , SND_SOC_NOPM, 0, 0, |
253 | &alc5623_auxout_mux_controls), |
254 | SND_SOC_DAPM_MUX("SpeakerOut Mux" , SND_SOC_NOPM, 0, 0, |
255 | &alc5623_spkout_mux_controls), |
256 | SND_SOC_DAPM_MUX("Left Headphone Mux" , SND_SOC_NOPM, 0, 0, |
257 | &alc5623_hpl_out_mux_controls), |
258 | SND_SOC_DAPM_MUX("Right Headphone Mux" , SND_SOC_NOPM, 0, 0, |
259 | &alc5623_hpr_out_mux_controls), |
260 | SND_SOC_DAPM_MUX("SpeakerOut N Mux" , SND_SOC_NOPM, 0, 0, |
261 | &alc5623_spkoutn_mux_controls), |
262 | |
263 | /* output mixers */ |
264 | SND_SOC_DAPM_MIXER("HP Mix" , SND_SOC_NOPM, 0, 0, |
265 | &alc5623_hp_mixer_controls[0], |
266 | ARRAY_SIZE(alc5623_hp_mixer_controls)), |
267 | SND_SOC_DAPM_MIXER("HPR Mix" , ALC5623_PWR_MANAG_ADD2, 4, 0, |
268 | &alc5623_hpr_mixer_controls[0], |
269 | ARRAY_SIZE(alc5623_hpr_mixer_controls)), |
270 | SND_SOC_DAPM_MIXER("HPL Mix" , ALC5623_PWR_MANAG_ADD2, 5, 0, |
271 | &alc5623_hpl_mixer_controls[0], |
272 | ARRAY_SIZE(alc5623_hpl_mixer_controls)), |
273 | SND_SOC_DAPM_MIXER("HPOut Mix" , SND_SOC_NOPM, 0, 0, NULL, 0), |
274 | SND_SOC_DAPM_MIXER("Mono Mix" , ALC5623_PWR_MANAG_ADD2, 2, 0, |
275 | &alc5623_mono_mixer_controls[0], |
276 | ARRAY_SIZE(alc5623_mono_mixer_controls)), |
277 | SND_SOC_DAPM_MIXER("Speaker Mix" , ALC5623_PWR_MANAG_ADD2, 3, 0, |
278 | &alc5623_speaker_mixer_controls[0], |
279 | ARRAY_SIZE(alc5623_speaker_mixer_controls)), |
280 | |
281 | /* input mixers */ |
282 | SND_SOC_DAPM_MIXER("Left Capture Mix" , ALC5623_PWR_MANAG_ADD2, 1, 0, |
283 | &alc5623_captureL_mixer_controls[0], |
284 | ARRAY_SIZE(alc5623_captureL_mixer_controls)), |
285 | SND_SOC_DAPM_MIXER("Right Capture Mix" , ALC5623_PWR_MANAG_ADD2, 0, 0, |
286 | &alc5623_captureR_mixer_controls[0], |
287 | ARRAY_SIZE(alc5623_captureR_mixer_controls)), |
288 | |
289 | SND_SOC_DAPM_DAC("Left DAC" , "Left HiFi Playback" , |
290 | ALC5623_PWR_MANAG_ADD2, 9, 0), |
291 | SND_SOC_DAPM_DAC("Right DAC" , "Right HiFi Playback" , |
292 | ALC5623_PWR_MANAG_ADD2, 8, 0), |
293 | SND_SOC_DAPM_MIXER("I2S Mix" , ALC5623_PWR_MANAG_ADD1, 15, 0, NULL, 0), |
294 | SND_SOC_DAPM_MIXER("AuxI Mix" , SND_SOC_NOPM, 0, 0, NULL, 0), |
295 | SND_SOC_DAPM_MIXER("Line Mix" , SND_SOC_NOPM, 0, 0, NULL, 0), |
296 | SND_SOC_DAPM_ADC("Left ADC" , "Left HiFi Capture" , |
297 | ALC5623_PWR_MANAG_ADD2, 7, 0), |
298 | SND_SOC_DAPM_ADC("Right ADC" , "Right HiFi Capture" , |
299 | ALC5623_PWR_MANAG_ADD2, 6, 0), |
300 | SND_SOC_DAPM_PGA("Left Headphone" , ALC5623_PWR_MANAG_ADD3, 10, 0, NULL, 0), |
301 | SND_SOC_DAPM_PGA("Right Headphone" , ALC5623_PWR_MANAG_ADD3, 9, 0, NULL, 0), |
302 | SND_SOC_DAPM_PGA("SpeakerOut" , ALC5623_PWR_MANAG_ADD3, 12, 0, NULL, 0), |
303 | SND_SOC_DAPM_PGA("Left AuxOut" , ALC5623_PWR_MANAG_ADD3, 14, 0, NULL, 0), |
304 | SND_SOC_DAPM_PGA("Right AuxOut" , ALC5623_PWR_MANAG_ADD3, 13, 0, NULL, 0), |
305 | SND_SOC_DAPM_PGA("Left LineIn" , ALC5623_PWR_MANAG_ADD3, 7, 0, NULL, 0), |
306 | SND_SOC_DAPM_PGA("Right LineIn" , ALC5623_PWR_MANAG_ADD3, 6, 0, NULL, 0), |
307 | SND_SOC_DAPM_PGA("Left AuxI" , ALC5623_PWR_MANAG_ADD3, 5, 0, NULL, 0), |
308 | SND_SOC_DAPM_PGA("Right AuxI" , ALC5623_PWR_MANAG_ADD3, 4, 0, NULL, 0), |
309 | SND_SOC_DAPM_PGA("MIC1 PGA" , ALC5623_PWR_MANAG_ADD3, 3, 0, NULL, 0), |
310 | SND_SOC_DAPM_PGA("MIC2 PGA" , ALC5623_PWR_MANAG_ADD3, 2, 0, NULL, 0), |
311 | SND_SOC_DAPM_PGA("MIC1 Pre Amp" , ALC5623_PWR_MANAG_ADD3, 1, 0, NULL, 0), |
312 | SND_SOC_DAPM_PGA("MIC2 Pre Amp" , ALC5623_PWR_MANAG_ADD3, 0, 0, NULL, 0), |
313 | SND_SOC_DAPM_MICBIAS("Mic Bias1" , ALC5623_PWR_MANAG_ADD1, 11, 0), |
314 | |
315 | SND_SOC_DAPM_OUTPUT("AUXOUTL" ), |
316 | SND_SOC_DAPM_OUTPUT("AUXOUTR" ), |
317 | SND_SOC_DAPM_OUTPUT("HPL" ), |
318 | SND_SOC_DAPM_OUTPUT("HPR" ), |
319 | SND_SOC_DAPM_OUTPUT("SPKOUT" ), |
320 | SND_SOC_DAPM_OUTPUT("SPKOUTN" ), |
321 | SND_SOC_DAPM_INPUT("LINEINL" ), |
322 | SND_SOC_DAPM_INPUT("LINEINR" ), |
323 | SND_SOC_DAPM_INPUT("AUXINL" ), |
324 | SND_SOC_DAPM_INPUT("AUXINR" ), |
325 | SND_SOC_DAPM_INPUT("MIC1" ), |
326 | SND_SOC_DAPM_INPUT("MIC2" ), |
327 | SND_SOC_DAPM_VMID("Vmid" ), |
328 | }; |
329 | |
330 | static const char *alc5623_amp_names[] = {"AB Amp" , "D Amp" }; |
331 | static SOC_ENUM_SINGLE_DECL(alc5623_amp_enum, |
332 | ALC5623_OUTPUT_MIXER_CTRL, 13, |
333 | alc5623_amp_names); |
334 | static const struct snd_kcontrol_new alc5623_amp_mux_controls = |
335 | SOC_DAPM_ENUM("Route" , alc5623_amp_enum); |
336 | |
337 | static const struct snd_soc_dapm_widget alc5623_dapm_amp_widgets[] = { |
338 | SND_SOC_DAPM_PGA_E("D Amp" , ALC5623_PWR_MANAG_ADD2, 14, 0, NULL, 0, |
339 | amp_mixer_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
340 | SND_SOC_DAPM_PGA("AB Amp" , ALC5623_PWR_MANAG_ADD2, 15, 0, NULL, 0), |
341 | SND_SOC_DAPM_MUX("AB-D Amp Mux" , SND_SOC_NOPM, 0, 0, |
342 | &alc5623_amp_mux_controls), |
343 | }; |
344 | |
345 | static const struct snd_soc_dapm_route intercon[] = { |
346 | /* virtual mixer - mixes left & right channels */ |
347 | {"I2S Mix" , NULL, "Left DAC" }, |
348 | {"I2S Mix" , NULL, "Right DAC" }, |
349 | {"Line Mix" , NULL, "Right LineIn" }, |
350 | {"Line Mix" , NULL, "Left LineIn" }, |
351 | {"AuxI Mix" , NULL, "Left AuxI" }, |
352 | {"AuxI Mix" , NULL, "Right AuxI" }, |
353 | {"AUXOUTL" , NULL, "Left AuxOut" }, |
354 | {"AUXOUTR" , NULL, "Right AuxOut" }, |
355 | |
356 | /* HP mixer */ |
357 | {"HPL Mix" , "ADC2HP_L Playback Switch" , "Left Capture Mix" }, |
358 | {"HPL Mix" , NULL, "HP Mix" }, |
359 | {"HPR Mix" , "ADC2HP_R Playback Switch" , "Right Capture Mix" }, |
360 | {"HPR Mix" , NULL, "HP Mix" }, |
361 | {"HP Mix" , "LI2HP Playback Switch" , "Line Mix" }, |
362 | {"HP Mix" , "AUXI2HP Playback Switch" , "AuxI Mix" }, |
363 | {"HP Mix" , "MIC12HP Playback Switch" , "MIC1 PGA" }, |
364 | {"HP Mix" , "MIC22HP Playback Switch" , "MIC2 PGA" }, |
365 | {"HP Mix" , "DAC2HP Playback Switch" , "I2S Mix" }, |
366 | |
367 | /* speaker mixer */ |
368 | {"Speaker Mix" , "LI2SPK Playback Switch" , "Line Mix" }, |
369 | {"Speaker Mix" , "AUXI2SPK Playback Switch" , "AuxI Mix" }, |
370 | {"Speaker Mix" , "MIC12SPK Playback Switch" , "MIC1 PGA" }, |
371 | {"Speaker Mix" , "MIC22SPK Playback Switch" , "MIC2 PGA" }, |
372 | {"Speaker Mix" , "DAC2SPK Playback Switch" , "I2S Mix" }, |
373 | |
374 | /* mono mixer */ |
375 | {"Mono Mix" , "ADC2MONO_L Playback Switch" , "Left Capture Mix" }, |
376 | {"Mono Mix" , "ADC2MONO_R Playback Switch" , "Right Capture Mix" }, |
377 | {"Mono Mix" , "LI2MONO Playback Switch" , "Line Mix" }, |
378 | {"Mono Mix" , "AUXI2MONO Playback Switch" , "AuxI Mix" }, |
379 | {"Mono Mix" , "MIC12MONO Playback Switch" , "MIC1 PGA" }, |
380 | {"Mono Mix" , "MIC22MONO Playback Switch" , "MIC2 PGA" }, |
381 | {"Mono Mix" , "DAC2MONO Playback Switch" , "I2S Mix" }, |
382 | |
383 | /* Left record mixer */ |
384 | {"Left Capture Mix" , "LineInL Capture Switch" , "LINEINL" }, |
385 | {"Left Capture Mix" , "Left AuxI Capture Switch" , "AUXINL" }, |
386 | {"Left Capture Mix" , "Mic1 Capture Switch" , "MIC1 Pre Amp" }, |
387 | {"Left Capture Mix" , "Mic2 Capture Switch" , "MIC2 Pre Amp" }, |
388 | {"Left Capture Mix" , "HPMixerL Capture Switch" , "HPL Mix" }, |
389 | {"Left Capture Mix" , "SPKMixer Capture Switch" , "Speaker Mix" }, |
390 | {"Left Capture Mix" , "MonoMixer Capture Switch" , "Mono Mix" }, |
391 | |
392 | /*Right record mixer */ |
393 | {"Right Capture Mix" , "LineInR Capture Switch" , "LINEINR" }, |
394 | {"Right Capture Mix" , "Right AuxI Capture Switch" , "AUXINR" }, |
395 | {"Right Capture Mix" , "Mic1 Capture Switch" , "MIC1 Pre Amp" }, |
396 | {"Right Capture Mix" , "Mic2 Capture Switch" , "MIC2 Pre Amp" }, |
397 | {"Right Capture Mix" , "HPMixerR Capture Switch" , "HPR Mix" }, |
398 | {"Right Capture Mix" , "SPKMixer Capture Switch" , "Speaker Mix" }, |
399 | {"Right Capture Mix" , "MonoMixer Capture Switch" , "Mono Mix" }, |
400 | |
401 | /* headphone left mux */ |
402 | {"Left Headphone Mux" , "HP Left Mix" , "HPL Mix" }, |
403 | {"Left Headphone Mux" , "Vmid" , "Vmid" }, |
404 | |
405 | /* headphone right mux */ |
406 | {"Right Headphone Mux" , "HP Right Mix" , "HPR Mix" }, |
407 | {"Right Headphone Mux" , "Vmid" , "Vmid" }, |
408 | |
409 | /* speaker out mux */ |
410 | {"SpeakerOut Mux" , "Vmid" , "Vmid" }, |
411 | {"SpeakerOut Mux" , "HPOut Mix" , "HPOut Mix" }, |
412 | {"SpeakerOut Mux" , "Speaker Mix" , "Speaker Mix" }, |
413 | {"SpeakerOut Mux" , "Mono Mix" , "Mono Mix" }, |
414 | |
415 | /* Mono/Aux Out mux */ |
416 | {"AuxOut Mux" , "Vmid" , "Vmid" }, |
417 | {"AuxOut Mux" , "HPOut Mix" , "HPOut Mix" }, |
418 | {"AuxOut Mux" , "Speaker Mix" , "Speaker Mix" }, |
419 | {"AuxOut Mux" , "Mono Mix" , "Mono Mix" }, |
420 | |
421 | /* output pga */ |
422 | {"HPL" , NULL, "Left Headphone" }, |
423 | {"Left Headphone" , NULL, "Left Headphone Mux" }, |
424 | {"HPR" , NULL, "Right Headphone" }, |
425 | {"Right Headphone" , NULL, "Right Headphone Mux" }, |
426 | {"Left AuxOut" , NULL, "AuxOut Mux" }, |
427 | {"Right AuxOut" , NULL, "AuxOut Mux" }, |
428 | |
429 | /* input pga */ |
430 | {"Left LineIn" , NULL, "LINEINL" }, |
431 | {"Right LineIn" , NULL, "LINEINR" }, |
432 | {"Left AuxI" , NULL, "AUXINL" }, |
433 | {"Right AuxI" , NULL, "AUXINR" }, |
434 | {"MIC1 Pre Amp" , NULL, "MIC1" }, |
435 | {"MIC2 Pre Amp" , NULL, "MIC2" }, |
436 | {"MIC1 PGA" , NULL, "MIC1 Pre Amp" }, |
437 | {"MIC2 PGA" , NULL, "MIC2 Pre Amp" }, |
438 | |
439 | /* left ADC */ |
440 | {"Left ADC" , NULL, "Left Capture Mix" }, |
441 | |
442 | /* right ADC */ |
443 | {"Right ADC" , NULL, "Right Capture Mix" }, |
444 | |
445 | {"SpeakerOut N Mux" , "RN/-R" , "SpeakerOut" }, |
446 | {"SpeakerOut N Mux" , "RP/+R" , "SpeakerOut" }, |
447 | {"SpeakerOut N Mux" , "LN/-R" , "SpeakerOut" }, |
448 | {"SpeakerOut N Mux" , "Vmid" , "Vmid" }, |
449 | |
450 | {"SPKOUT" , NULL, "SpeakerOut" }, |
451 | {"SPKOUTN" , NULL, "SpeakerOut N Mux" }, |
452 | }; |
453 | |
454 | static const struct snd_soc_dapm_route intercon_spk[] = { |
455 | {"SpeakerOut" , NULL, "SpeakerOut Mux" }, |
456 | }; |
457 | |
458 | static const struct snd_soc_dapm_route intercon_amp_spk[] = { |
459 | {"AB Amp" , NULL, "SpeakerOut Mux" }, |
460 | {"D Amp" , NULL, "SpeakerOut Mux" }, |
461 | {"AB-D Amp Mux" , "AB Amp" , "AB Amp" }, |
462 | {"AB-D Amp Mux" , "D Amp" , "D Amp" }, |
463 | {"SpeakerOut" , NULL, "AB-D Amp Mux" }, |
464 | }; |
465 | |
466 | /* PLL divisors */ |
467 | struct _pll_div { |
468 | u32 pll_in; |
469 | u32 pll_out; |
470 | u16 regvalue; |
471 | }; |
472 | |
473 | /* Note : pll code from original alc5623 driver. Not sure of how good it is */ |
474 | /* useful only for master mode */ |
475 | static const struct _pll_div codec_master_pll_div[] = { |
476 | |
477 | { 2048000, 8192000, 0x0ea0}, |
478 | { 3686400, 8192000, 0x4e27}, |
479 | { 12000000, 8192000, 0x456b}, |
480 | { 13000000, 8192000, 0x495f}, |
481 | { 13100000, 8192000, 0x0320}, |
482 | { 2048000, 11289600, 0xf637}, |
483 | { 3686400, 11289600, 0x2f22}, |
484 | { 12000000, 11289600, 0x3e2f}, |
485 | { 13000000, 11289600, 0x4d5b}, |
486 | { 13100000, 11289600, 0x363b}, |
487 | { 2048000, 16384000, 0x1ea0}, |
488 | { 3686400, 16384000, 0x9e27}, |
489 | { 12000000, 16384000, 0x452b}, |
490 | { 13000000, 16384000, 0x542f}, |
491 | { 13100000, 16384000, 0x03a0}, |
492 | { 2048000, 16934400, 0xe625}, |
493 | { 3686400, 16934400, 0x9126}, |
494 | { 12000000, 16934400, 0x4d2c}, |
495 | { 13000000, 16934400, 0x742f}, |
496 | { 13100000, 16934400, 0x3c27}, |
497 | { 2048000, 22579200, 0x2aa0}, |
498 | { 3686400, 22579200, 0x2f20}, |
499 | { 12000000, 22579200, 0x7e2f}, |
500 | { 13000000, 22579200, 0x742f}, |
501 | { 13100000, 22579200, 0x3c27}, |
502 | { 2048000, 24576000, 0x2ea0}, |
503 | { 3686400, 24576000, 0xee27}, |
504 | { 12000000, 24576000, 0x2915}, |
505 | { 13000000, 24576000, 0x772e}, |
506 | { 13100000, 24576000, 0x0d20}, |
507 | }; |
508 | |
509 | static const struct _pll_div codec_slave_pll_div[] = { |
510 | |
511 | { 1024000, 16384000, 0x3ea0}, |
512 | { 1411200, 22579200, 0x3ea0}, |
513 | { 1536000, 24576000, 0x3ea0}, |
514 | { 2048000, 16384000, 0x1ea0}, |
515 | { 2822400, 22579200, 0x1ea0}, |
516 | { 3072000, 24576000, 0x1ea0}, |
517 | |
518 | }; |
519 | |
520 | static int alc5623_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, |
521 | int source, unsigned int freq_in, unsigned int freq_out) |
522 | { |
523 | int i; |
524 | struct snd_soc_component *component = codec_dai->component; |
525 | int gbl_clk = 0, pll_div = 0; |
526 | u16 reg; |
527 | |
528 | if (pll_id < ALC5623_PLL_FR_MCLK || pll_id > ALC5623_PLL_FR_BCK) |
529 | return -ENODEV; |
530 | |
531 | /* Disable PLL power */ |
532 | snd_soc_component_update_bits(component, ALC5623_PWR_MANAG_ADD2, |
533 | ALC5623_PWR_ADD2_PLL, |
534 | val: 0); |
535 | |
536 | /* pll is not used in slave mode */ |
537 | reg = snd_soc_component_read(component, ALC5623_DAI_CONTROL); |
538 | if (reg & ALC5623_DAI_SDP_SLAVE_MODE) |
539 | return 0; |
540 | |
541 | if (!freq_in || !freq_out) |
542 | return 0; |
543 | |
544 | switch (pll_id) { |
545 | case ALC5623_PLL_FR_MCLK: |
546 | for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i++) { |
547 | if (codec_master_pll_div[i].pll_in == freq_in |
548 | && codec_master_pll_div[i].pll_out == freq_out) { |
549 | /* PLL source from MCLK */ |
550 | pll_div = codec_master_pll_div[i].regvalue; |
551 | break; |
552 | } |
553 | } |
554 | break; |
555 | case ALC5623_PLL_FR_BCK: |
556 | for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) { |
557 | if (codec_slave_pll_div[i].pll_in == freq_in |
558 | && codec_slave_pll_div[i].pll_out == freq_out) { |
559 | /* PLL source from Bitclk */ |
560 | gbl_clk = ALC5623_GBL_CLK_PLL_SOUR_SEL_BITCLK; |
561 | pll_div = codec_slave_pll_div[i].regvalue; |
562 | break; |
563 | } |
564 | } |
565 | break; |
566 | default: |
567 | return -EINVAL; |
568 | } |
569 | |
570 | if (!pll_div) |
571 | return -EINVAL; |
572 | |
573 | snd_soc_component_write(component, ALC5623_GLOBAL_CLK_CTRL_REG, val: gbl_clk); |
574 | snd_soc_component_write(component, ALC5623_PLL_CTRL, val: pll_div); |
575 | snd_soc_component_update_bits(component, ALC5623_PWR_MANAG_ADD2, |
576 | ALC5623_PWR_ADD2_PLL, |
577 | ALC5623_PWR_ADD2_PLL); |
578 | gbl_clk |= ALC5623_GBL_CLK_SYS_SOUR_SEL_PLL; |
579 | snd_soc_component_write(component, ALC5623_GLOBAL_CLK_CTRL_REG, val: gbl_clk); |
580 | |
581 | return 0; |
582 | } |
583 | |
584 | struct _coeff_div { |
585 | u16 fs; |
586 | u16 regvalue; |
587 | }; |
588 | |
589 | /* codec hifi mclk (after PLL) clock divider coefficients */ |
590 | /* values inspired from column BCLK=32Fs of Appendix A table */ |
591 | static const struct _coeff_div coeff_div[] = { |
592 | {256*8, 0x3a69}, |
593 | {384*8, 0x3c6b}, |
594 | {256*4, 0x2a69}, |
595 | {384*4, 0x2c6b}, |
596 | {256*2, 0x1a69}, |
597 | {384*2, 0x1c6b}, |
598 | {256*1, 0x0a69}, |
599 | {384*1, 0x0c6b}, |
600 | }; |
601 | |
602 | static int get_coeff(struct snd_soc_component *component, int rate) |
603 | { |
604 | struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(c: component); |
605 | int i; |
606 | |
607 | for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { |
608 | if (coeff_div[i].fs * rate == alc5623->sysclk) |
609 | return i; |
610 | } |
611 | return -EINVAL; |
612 | } |
613 | |
614 | /* |
615 | * Clock after PLL and dividers |
616 | */ |
617 | static int alc5623_set_dai_sysclk(struct snd_soc_dai *codec_dai, |
618 | int clk_id, unsigned int freq, int dir) |
619 | { |
620 | struct snd_soc_component *component = codec_dai->component; |
621 | struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(c: component); |
622 | |
623 | switch (freq) { |
624 | case 8192000: |
625 | case 11289600: |
626 | case 12288000: |
627 | case 16384000: |
628 | case 16934400: |
629 | case 18432000: |
630 | case 22579200: |
631 | case 24576000: |
632 | alc5623->sysclk = freq; |
633 | return 0; |
634 | } |
635 | return -EINVAL; |
636 | } |
637 | |
638 | static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai, |
639 | unsigned int fmt) |
640 | { |
641 | struct snd_soc_component *component = codec_dai->component; |
642 | u16 iface = 0; |
643 | |
644 | /* set audio interface clocking */ |
645 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
646 | case SND_SOC_DAIFMT_CBP_CFP: |
647 | iface = ALC5623_DAI_SDP_MASTER_MODE; |
648 | break; |
649 | case SND_SOC_DAIFMT_CBC_CFC: |
650 | iface = ALC5623_DAI_SDP_SLAVE_MODE; |
651 | break; |
652 | default: |
653 | return -EINVAL; |
654 | } |
655 | |
656 | /* interface format */ |
657 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
658 | case SND_SOC_DAIFMT_I2S: |
659 | iface |= ALC5623_DAI_I2S_DF_I2S; |
660 | break; |
661 | case SND_SOC_DAIFMT_RIGHT_J: |
662 | iface |= ALC5623_DAI_I2S_DF_RIGHT; |
663 | break; |
664 | case SND_SOC_DAIFMT_LEFT_J: |
665 | iface |= ALC5623_DAI_I2S_DF_LEFT; |
666 | break; |
667 | case SND_SOC_DAIFMT_DSP_A: |
668 | iface |= ALC5623_DAI_I2S_DF_PCM; |
669 | break; |
670 | case SND_SOC_DAIFMT_DSP_B: |
671 | iface |= ALC5623_DAI_I2S_DF_PCM | ALC5623_DAI_I2S_PCM_MODE; |
672 | break; |
673 | default: |
674 | return -EINVAL; |
675 | } |
676 | |
677 | /* clock inversion */ |
678 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
679 | case SND_SOC_DAIFMT_NB_NF: |
680 | break; |
681 | case SND_SOC_DAIFMT_IB_IF: |
682 | iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL; |
683 | break; |
684 | case SND_SOC_DAIFMT_IB_NF: |
685 | iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL; |
686 | break; |
687 | case SND_SOC_DAIFMT_NB_IF: |
688 | break; |
689 | default: |
690 | return -EINVAL; |
691 | } |
692 | |
693 | return snd_soc_component_write(component, ALC5623_DAI_CONTROL, val: iface); |
694 | } |
695 | |
696 | static int alc5623_pcm_hw_params(struct snd_pcm_substream *substream, |
697 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) |
698 | { |
699 | struct snd_soc_component *component = dai->component; |
700 | struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(c: component); |
701 | int coeff, rate; |
702 | u16 iface; |
703 | |
704 | iface = snd_soc_component_read(component, ALC5623_DAI_CONTROL); |
705 | iface &= ~ALC5623_DAI_I2S_DL_MASK; |
706 | |
707 | /* bit size */ |
708 | switch (params_width(p: params)) { |
709 | case 16: |
710 | iface |= ALC5623_DAI_I2S_DL_16; |
711 | break; |
712 | case 20: |
713 | iface |= ALC5623_DAI_I2S_DL_20; |
714 | break; |
715 | case 24: |
716 | iface |= ALC5623_DAI_I2S_DL_24; |
717 | break; |
718 | case 32: |
719 | iface |= ALC5623_DAI_I2S_DL_32; |
720 | break; |
721 | default: |
722 | return -EINVAL; |
723 | } |
724 | |
725 | /* set iface & srate */ |
726 | snd_soc_component_write(component, ALC5623_DAI_CONTROL, val: iface); |
727 | rate = params_rate(p: params); |
728 | coeff = get_coeff(component, rate); |
729 | if (coeff < 0) |
730 | return -EINVAL; |
731 | |
732 | coeff = coeff_div[coeff].regvalue; |
733 | dev_dbg(component->dev, "%s: sysclk=%d,rate=%d,coeff=0x%04x\n" , |
734 | __func__, alc5623->sysclk, rate, coeff); |
735 | snd_soc_component_write(component, ALC5623_STEREO_AD_DA_CLK_CTRL, val: coeff); |
736 | |
737 | return 0; |
738 | } |
739 | |
740 | static int alc5623_mute(struct snd_soc_dai *dai, int mute, int direction) |
741 | { |
742 | struct snd_soc_component *component = dai->component; |
743 | u16 hp_mute = ALC5623_MISC_M_DAC_L_INPUT | ALC5623_MISC_M_DAC_R_INPUT; |
744 | u16 mute_reg = snd_soc_component_read(component, ALC5623_MISC_CTRL) & ~hp_mute; |
745 | |
746 | if (mute) |
747 | mute_reg |= hp_mute; |
748 | |
749 | return snd_soc_component_write(component, ALC5623_MISC_CTRL, val: mute_reg); |
750 | } |
751 | |
752 | #define ALC5623_ADD2_POWER_EN (ALC5623_PWR_ADD2_VREF \ |
753 | | ALC5623_PWR_ADD2_DAC_REF_CIR) |
754 | |
755 | #define ALC5623_ADD3_POWER_EN (ALC5623_PWR_ADD3_MAIN_BIAS \ |
756 | | ALC5623_PWR_ADD3_MIC1_BOOST_AD) |
757 | |
758 | #define ALC5623_ADD1_POWER_EN \ |
759 | (ALC5623_PWR_ADD1_SHORT_CURR_DET_EN | ALC5623_PWR_ADD1_SOFTGEN_EN \ |
760 | | ALC5623_PWR_ADD1_DEPOP_BUF_HP | ALC5623_PWR_ADD1_HP_OUT_AMP \ |
761 | | ALC5623_PWR_ADD1_HP_OUT_ENH_AMP) |
762 | |
763 | #define ALC5623_ADD1_POWER_EN_5622 \ |
764 | (ALC5623_PWR_ADD1_SHORT_CURR_DET_EN \ |
765 | | ALC5623_PWR_ADD1_HP_OUT_AMP) |
766 | |
767 | static void enable_power_depop(struct snd_soc_component *component) |
768 | { |
769 | struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(c: component); |
770 | |
771 | snd_soc_component_update_bits(component, ALC5623_PWR_MANAG_ADD1, |
772 | ALC5623_PWR_ADD1_SOFTGEN_EN, |
773 | ALC5623_PWR_ADD1_SOFTGEN_EN); |
774 | |
775 | snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD3, ALC5623_ADD3_POWER_EN); |
776 | |
777 | snd_soc_component_update_bits(component, ALC5623_MISC_CTRL, |
778 | ALC5623_MISC_HP_DEPOP_MODE2_EN, |
779 | ALC5623_MISC_HP_DEPOP_MODE2_EN); |
780 | |
781 | msleep(msecs: 500); |
782 | |
783 | snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD2, ALC5623_ADD2_POWER_EN); |
784 | |
785 | /* avoid writing '1' into 5622 reserved bits */ |
786 | if (alc5623->id == 0x22) |
787 | snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1, |
788 | ALC5623_ADD1_POWER_EN_5622); |
789 | else |
790 | snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1, |
791 | ALC5623_ADD1_POWER_EN); |
792 | |
793 | /* disable HP Depop2 */ |
794 | snd_soc_component_update_bits(component, ALC5623_MISC_CTRL, |
795 | ALC5623_MISC_HP_DEPOP_MODE2_EN, |
796 | val: 0); |
797 | |
798 | } |
799 | |
800 | static int alc5623_set_bias_level(struct snd_soc_component *component, |
801 | enum snd_soc_bias_level level) |
802 | { |
803 | switch (level) { |
804 | case SND_SOC_BIAS_ON: |
805 | enable_power_depop(component); |
806 | break; |
807 | case SND_SOC_BIAS_PREPARE: |
808 | break; |
809 | case SND_SOC_BIAS_STANDBY: |
810 | /* everything off except vref/vmid, */ |
811 | snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD2, |
812 | ALC5623_PWR_ADD2_VREF); |
813 | snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD3, |
814 | ALC5623_PWR_ADD3_MAIN_BIAS); |
815 | break; |
816 | case SND_SOC_BIAS_OFF: |
817 | /* everything off, dac mute, inactive */ |
818 | snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD2, val: 0); |
819 | snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD3, val: 0); |
820 | snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1, val: 0); |
821 | break; |
822 | } |
823 | return 0; |
824 | } |
825 | |
826 | #define ALC5623_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \ |
827 | | SNDRV_PCM_FMTBIT_S24_LE \ |
828 | | SNDRV_PCM_FMTBIT_S32_LE) |
829 | |
830 | static const struct snd_soc_dai_ops alc5623_dai_ops = { |
831 | .hw_params = alc5623_pcm_hw_params, |
832 | .mute_stream = alc5623_mute, |
833 | .set_fmt = alc5623_set_dai_fmt, |
834 | .set_sysclk = alc5623_set_dai_sysclk, |
835 | .set_pll = alc5623_set_dai_pll, |
836 | .no_capture_mute = 1, |
837 | }; |
838 | |
839 | static struct snd_soc_dai_driver alc5623_dai = { |
840 | .name = "alc5623-hifi" , |
841 | .playback = { |
842 | .stream_name = "Playback" , |
843 | .channels_min = 1, |
844 | .channels_max = 2, |
845 | .rate_min = 8000, |
846 | .rate_max = 48000, |
847 | .rates = SNDRV_PCM_RATE_8000_48000, |
848 | .formats = ALC5623_FORMATS,}, |
849 | .capture = { |
850 | .stream_name = "Capture" , |
851 | .channels_min = 1, |
852 | .channels_max = 2, |
853 | .rate_min = 8000, |
854 | .rate_max = 48000, |
855 | .rates = SNDRV_PCM_RATE_8000_48000, |
856 | .formats = ALC5623_FORMATS,}, |
857 | |
858 | .ops = &alc5623_dai_ops, |
859 | }; |
860 | |
861 | static int alc5623_suspend(struct snd_soc_component *component) |
862 | { |
863 | struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(c: component); |
864 | |
865 | regcache_cache_only(map: alc5623->regmap, enable: true); |
866 | |
867 | return 0; |
868 | } |
869 | |
870 | static int alc5623_resume(struct snd_soc_component *component) |
871 | { |
872 | struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(c: component); |
873 | int ret; |
874 | |
875 | /* Sync reg_cache with the hardware */ |
876 | regcache_cache_only(map: alc5623->regmap, enable: false); |
877 | ret = regcache_sync(map: alc5623->regmap); |
878 | if (ret != 0) { |
879 | dev_err(component->dev, "Failed to sync register cache: %d\n" , |
880 | ret); |
881 | regcache_cache_only(map: alc5623->regmap, enable: true); |
882 | return ret; |
883 | } |
884 | |
885 | return 0; |
886 | } |
887 | |
888 | static int alc5623_probe(struct snd_soc_component *component) |
889 | { |
890 | struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(c: component); |
891 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
892 | |
893 | alc5623_reset(component); |
894 | |
895 | if (alc5623->add_ctrl) { |
896 | snd_soc_component_write(component, ALC5623_ADD_CTRL_REG, |
897 | val: alc5623->add_ctrl); |
898 | } |
899 | |
900 | if (alc5623->jack_det_ctrl) { |
901 | snd_soc_component_write(component, ALC5623_JACK_DET_CTRL, |
902 | val: alc5623->jack_det_ctrl); |
903 | } |
904 | |
905 | switch (alc5623->id) { |
906 | case 0x21: |
907 | snd_soc_add_component_controls(component, controls: alc5621_vol_snd_controls, |
908 | ARRAY_SIZE(alc5621_vol_snd_controls)); |
909 | break; |
910 | case 0x22: |
911 | snd_soc_add_component_controls(component, controls: alc5622_vol_snd_controls, |
912 | ARRAY_SIZE(alc5622_vol_snd_controls)); |
913 | break; |
914 | case 0x23: |
915 | snd_soc_add_component_controls(component, controls: alc5623_vol_snd_controls, |
916 | ARRAY_SIZE(alc5623_vol_snd_controls)); |
917 | break; |
918 | default: |
919 | return -EINVAL; |
920 | } |
921 | |
922 | snd_soc_add_component_controls(component, controls: alc5623_snd_controls, |
923 | ARRAY_SIZE(alc5623_snd_controls)); |
924 | |
925 | snd_soc_dapm_new_controls(dapm, widget: alc5623_dapm_widgets, |
926 | ARRAY_SIZE(alc5623_dapm_widgets)); |
927 | |
928 | /* set up audio path interconnects */ |
929 | snd_soc_dapm_add_routes(dapm, route: intercon, ARRAY_SIZE(intercon)); |
930 | |
931 | switch (alc5623->id) { |
932 | case 0x21: |
933 | case 0x22: |
934 | snd_soc_dapm_new_controls(dapm, widget: alc5623_dapm_amp_widgets, |
935 | ARRAY_SIZE(alc5623_dapm_amp_widgets)); |
936 | snd_soc_dapm_add_routes(dapm, route: intercon_amp_spk, |
937 | ARRAY_SIZE(intercon_amp_spk)); |
938 | break; |
939 | case 0x23: |
940 | snd_soc_dapm_add_routes(dapm, route: intercon_spk, |
941 | ARRAY_SIZE(intercon_spk)); |
942 | break; |
943 | default: |
944 | return -EINVAL; |
945 | } |
946 | |
947 | return 0; |
948 | } |
949 | |
950 | static const struct snd_soc_component_driver soc_component_device_alc5623 = { |
951 | .probe = alc5623_probe, |
952 | .suspend = alc5623_suspend, |
953 | .resume = alc5623_resume, |
954 | .set_bias_level = alc5623_set_bias_level, |
955 | .suspend_bias_off = 1, |
956 | .idle_bias_on = 1, |
957 | .use_pmdown_time = 1, |
958 | .endianness = 1, |
959 | }; |
960 | |
961 | static const struct regmap_config alc5623_regmap = { |
962 | .reg_bits = 8, |
963 | .val_bits = 16, |
964 | .reg_stride = 2, |
965 | |
966 | .max_register = ALC5623_VENDOR_ID2, |
967 | .cache_type = REGCACHE_RBTREE, |
968 | }; |
969 | |
970 | static const struct i2c_device_id alc5623_i2c_table[] = { |
971 | {"alc5621" , 0x21}, |
972 | {"alc5622" , 0x22}, |
973 | {"alc5623" , 0x23}, |
974 | {} |
975 | }; |
976 | MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table); |
977 | |
978 | /* |
979 | * ALC5623 2 wire address is determined by A1 pin |
980 | * state during powerup. |
981 | * low = 0x1a |
982 | * high = 0x1b |
983 | */ |
984 | static int alc5623_i2c_probe(struct i2c_client *client) |
985 | { |
986 | struct alc5623_platform_data *pdata; |
987 | struct alc5623_priv *alc5623; |
988 | struct device_node *np; |
989 | unsigned int vid1, vid2; |
990 | int ret; |
991 | u32 val32; |
992 | const struct i2c_device_id *id; |
993 | |
994 | alc5623 = devm_kzalloc(dev: &client->dev, size: sizeof(struct alc5623_priv), |
995 | GFP_KERNEL); |
996 | if (alc5623 == NULL) |
997 | return -ENOMEM; |
998 | |
999 | alc5623->regmap = devm_regmap_init_i2c(client, &alc5623_regmap); |
1000 | if (IS_ERR(ptr: alc5623->regmap)) { |
1001 | ret = PTR_ERR(ptr: alc5623->regmap); |
1002 | dev_err(&client->dev, "Failed to initialise I/O: %d\n" , ret); |
1003 | return ret; |
1004 | } |
1005 | |
1006 | ret = regmap_read(map: alc5623->regmap, ALC5623_VENDOR_ID1, val: &vid1); |
1007 | if (ret < 0) { |
1008 | dev_err(&client->dev, "failed to read vendor ID1: %d\n" , ret); |
1009 | return ret; |
1010 | } |
1011 | |
1012 | ret = regmap_read(map: alc5623->regmap, ALC5623_VENDOR_ID2, val: &vid2); |
1013 | if (ret < 0) { |
1014 | dev_err(&client->dev, "failed to read vendor ID2: %d\n" , ret); |
1015 | return ret; |
1016 | } |
1017 | vid2 >>= 8; |
1018 | |
1019 | id = i2c_match_id(id: alc5623_i2c_table, client); |
1020 | |
1021 | if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) { |
1022 | dev_err(&client->dev, "unknown or wrong codec\n" ); |
1023 | dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n" , |
1024 | 0x10ec, id->driver_data, |
1025 | vid1, vid2); |
1026 | return -ENODEV; |
1027 | } |
1028 | |
1029 | dev_dbg(&client->dev, "Found codec id : alc56%02x\n" , vid2); |
1030 | |
1031 | pdata = client->dev.platform_data; |
1032 | if (pdata) { |
1033 | alc5623->add_ctrl = pdata->add_ctrl; |
1034 | alc5623->jack_det_ctrl = pdata->jack_det_ctrl; |
1035 | } else { |
1036 | if (client->dev.of_node) { |
1037 | np = client->dev.of_node; |
1038 | ret = of_property_read_u32(np, propname: "add-ctrl" , out_value: &val32); |
1039 | if (!ret) |
1040 | alc5623->add_ctrl = val32; |
1041 | ret = of_property_read_u32(np, propname: "jack-det-ctrl" , out_value: &val32); |
1042 | if (!ret) |
1043 | alc5623->jack_det_ctrl = val32; |
1044 | } |
1045 | } |
1046 | |
1047 | alc5623->id = vid2; |
1048 | switch (alc5623->id) { |
1049 | case 0x21: |
1050 | alc5623_dai.name = "alc5621-hifi" ; |
1051 | break; |
1052 | case 0x22: |
1053 | alc5623_dai.name = "alc5622-hifi" ; |
1054 | break; |
1055 | case 0x23: |
1056 | alc5623_dai.name = "alc5623-hifi" ; |
1057 | break; |
1058 | default: |
1059 | return -EINVAL; |
1060 | } |
1061 | |
1062 | i2c_set_clientdata(client, data: alc5623); |
1063 | |
1064 | ret = devm_snd_soc_register_component(dev: &client->dev, |
1065 | component_driver: &soc_component_device_alc5623, dai_drv: &alc5623_dai, num_dai: 1); |
1066 | if (ret != 0) |
1067 | dev_err(&client->dev, "Failed to register codec: %d\n" , ret); |
1068 | |
1069 | return ret; |
1070 | } |
1071 | |
1072 | #ifdef CONFIG_OF |
1073 | static const struct of_device_id alc5623_of_match[] = { |
1074 | { .compatible = "realtek,alc5623" , }, |
1075 | { } |
1076 | }; |
1077 | MODULE_DEVICE_TABLE(of, alc5623_of_match); |
1078 | #endif |
1079 | |
1080 | /* i2c codec control layer */ |
1081 | static struct i2c_driver alc5623_i2c_driver = { |
1082 | .driver = { |
1083 | .name = "alc562x-codec" , |
1084 | .of_match_table = of_match_ptr(alc5623_of_match), |
1085 | }, |
1086 | .probe = alc5623_i2c_probe, |
1087 | .id_table = alc5623_i2c_table, |
1088 | }; |
1089 | |
1090 | module_i2c_driver(alc5623_i2c_driver); |
1091 | |
1092 | MODULE_DESCRIPTION("ASoC alc5621/2/3 driver" ); |
1093 | MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>" ); |
1094 | MODULE_LICENSE("GPL" ); |
1095 | |