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