diff options
Diffstat (limited to 'sound/soc')
-rw-r--r-- | sound/soc/codecs/Kconfig | 3 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/ak4535.c | 696 | ||||
-rw-r--r-- | sound/soc/codecs/ak4535.h | 46 |
4 files changed, 747 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f439138db843..1db04a28a53d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -2,6 +2,9 @@ config SND_SOC_AC97_CODEC | |||
2 | tristate | 2 | tristate |
3 | select SND_AC97_CODEC | 3 | select SND_AC97_CODEC |
4 | 4 | ||
5 | config SND_SOC_AK4535 | ||
6 | tristate | ||
7 | |||
5 | config SND_SOC_UDA1380 | 8 | config SND_SOC_UDA1380 |
6 | tristate | 9 | tristate |
7 | 10 | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d9826f35d8c0..d7b97abcf729 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | snd-soc-ac97-objs := ac97.o | 1 | snd-soc-ac97-objs := ac97.o |
2 | snd-soc-ak4535-objs := ak4535.o | ||
2 | snd-soc-uda1380-objs := uda1380.o | 3 | snd-soc-uda1380-objs := uda1380.o |
3 | snd-soc-wm8510-objs := wm8510.o | 4 | snd-soc-wm8510-objs := wm8510.o |
4 | snd-soc-wm8731-objs := wm8731.o | 5 | snd-soc-wm8731-objs := wm8731.o |
@@ -11,6 +12,7 @@ snd-soc-cs4270-objs := cs4270.o | |||
11 | snd-soc-tlv320aic3x-objs := tlv320aic3x.o | 12 | snd-soc-tlv320aic3x-objs := tlv320aic3x.o |
12 | 13 | ||
13 | obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o | 14 | obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o |
15 | obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o | ||
14 | obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o | 16 | obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o |
15 | obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o | 17 | obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o |
16 | obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o | 18 | obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o |
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c new file mode 100644 index 000000000000..469266e881d8 --- /dev/null +++ b/sound/soc/codecs/ak4535.c | |||
@@ -0,0 +1,696 @@ | |||
1 | /* | ||
2 | * ak4535.c -- AK4535 ALSA Soc Audio driver | ||
3 | * | ||
4 | * Copyright 2005 Openedhand Ltd. | ||
5 | * | ||
6 | * Author: Richard Purdie <richard@openedhand.com> | ||
7 | * | ||
8 | * Based on wm8753.c by Liam Girdwood | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/moduleparam.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/pm.h> | ||
20 | #include <linux/i2c.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <sound/core.h> | ||
23 | #include <sound/pcm.h> | ||
24 | #include <sound/pcm_params.h> | ||
25 | #include <sound/soc.h> | ||
26 | #include <sound/soc-dapm.h> | ||
27 | #include <sound/initval.h> | ||
28 | |||
29 | #include "ak4535.h" | ||
30 | |||
31 | #define AUDIO_NAME "ak4535" | ||
32 | #define AK4535_VERSION "0.3" | ||
33 | |||
34 | struct snd_soc_codec_device soc_codec_dev_ak4535; | ||
35 | |||
36 | /* codec private data */ | ||
37 | struct ak4535_priv { | ||
38 | unsigned int sysclk; | ||
39 | }; | ||
40 | |||
41 | /* | ||
42 | * ak4535 register cache | ||
43 | */ | ||
44 | static const u16 ak4535_reg[AK4535_CACHEREGNUM] = { | ||
45 | 0x0000, 0x0080, 0x0000, 0x0003, | ||
46 | 0x0002, 0x0000, 0x0011, 0x0001, | ||
47 | 0x0000, 0x0040, 0x0036, 0x0010, | ||
48 | 0x0000, 0x0000, 0x0057, 0x0000, | ||
49 | }; | ||
50 | |||
51 | /* | ||
52 | * read ak4535 register cache | ||
53 | */ | ||
54 | static inline unsigned int ak4535_read_reg_cache(struct snd_soc_codec *codec, | ||
55 | unsigned int reg) | ||
56 | { | ||
57 | u16 *cache = codec->reg_cache; | ||
58 | if (reg >= AK4535_CACHEREGNUM) | ||
59 | return -1; | ||
60 | return cache[reg]; | ||
61 | } | ||
62 | |||
63 | static inline unsigned int ak4535_read(struct snd_soc_codec *codec, | ||
64 | unsigned int reg) | ||
65 | { | ||
66 | u8 data; | ||
67 | data = reg; | ||
68 | |||
69 | if (codec->hw_write(codec->control_data, &data, 1) != 1) | ||
70 | return -EIO; | ||
71 | |||
72 | if (codec->hw_read(codec->control_data, &data, 1) != 1) | ||
73 | return -EIO; | ||
74 | |||
75 | return data; | ||
76 | }; | ||
77 | |||
78 | /* | ||
79 | * write ak4535 register cache | ||
80 | */ | ||
81 | static inline void ak4535_write_reg_cache(struct snd_soc_codec *codec, | ||
82 | u16 reg, unsigned int value) | ||
83 | { | ||
84 | u16 *cache = codec->reg_cache; | ||
85 | if (reg >= AK4535_CACHEREGNUM) | ||
86 | return; | ||
87 | cache[reg] = value; | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * write to the AK4535 register space | ||
92 | */ | ||
93 | static int ak4535_write(struct snd_soc_codec *codec, unsigned int reg, | ||
94 | unsigned int value) | ||
95 | { | ||
96 | u8 data[2]; | ||
97 | |||
98 | /* data is | ||
99 | * D15..D8 AK4535 register offset | ||
100 | * D7...D0 register data | ||
101 | */ | ||
102 | data[0] = reg & 0xff; | ||
103 | data[1] = value & 0xff; | ||
104 | |||
105 | ak4535_write_reg_cache(codec, reg, value); | ||
106 | if (codec->hw_write(codec->control_data, data, 2) == 2) | ||
107 | return 0; | ||
108 | else | ||
109 | return -EIO; | ||
110 | } | ||
111 | |||
112 | static int ak4535_sync(struct snd_soc_codec *codec) | ||
113 | { | ||
114 | u16 *cache = codec->reg_cache; | ||
115 | int i, r = 0; | ||
116 | |||
117 | for (i = 0; i < AK4535_CACHEREGNUM; i++) | ||
118 | r |= ak4535_write(codec, i, cache[i]); | ||
119 | |||
120 | return r; | ||
121 | }; | ||
122 | |||
123 | static const char *ak4535_mono_gain[] = {"+6dB", "-17dB"}; | ||
124 | static const char *ak4535_mono_out[] = {"(L + R)/2", "Hi-Z"}; | ||
125 | static const char *ak4535_hp_out[] = {"Stereo", "Mono"}; | ||
126 | static const char *ak4535_deemp[] = {"44.1kHz", "Off", "48kHz", "32kHz"}; | ||
127 | static const char *ak4535_mic_select[] = {"Internal", "External"}; | ||
128 | |||
129 | static const struct soc_enum ak4535_enum[] = { | ||
130 | SOC_ENUM_SINGLE(AK4535_SIG1, 7, 2, ak4535_mono_gain), | ||
131 | SOC_ENUM_SINGLE(AK4535_SIG1, 6, 2, ak4535_mono_out), | ||
132 | SOC_ENUM_SINGLE(AK4535_MODE2, 2, 2, ak4535_hp_out), | ||
133 | SOC_ENUM_SINGLE(AK4535_DAC, 0, 4, ak4535_deemp), | ||
134 | SOC_ENUM_SINGLE(AK4535_MIC, 1, 2, ak4535_mic_select), | ||
135 | }; | ||
136 | |||
137 | static const struct snd_kcontrol_new ak4535_snd_controls[] = { | ||
138 | SOC_SINGLE("ALC2 Switch", AK4535_SIG1, 1, 1, 0), | ||
139 | SOC_ENUM("Mono 1 Output", ak4535_enum[1]), | ||
140 | SOC_ENUM("Mono 1 Gain", ak4535_enum[0]), | ||
141 | SOC_ENUM("Headphone Output", ak4535_enum[2]), | ||
142 | SOC_ENUM("Playback Deemphasis", ak4535_enum[3]), | ||
143 | SOC_SINGLE("Bass Volume", AK4535_DAC, 2, 3, 0), | ||
144 | SOC_SINGLE("Mic Boost (+20dB) Switch", AK4535_MIC, 0, 1, 0), | ||
145 | SOC_ENUM("Mic Select", ak4535_enum[4]), | ||
146 | SOC_SINGLE("ALC Operation Time", AK4535_TIMER, 0, 3, 0), | ||
147 | SOC_SINGLE("ALC Recovery Time", AK4535_TIMER, 2, 3, 0), | ||
148 | SOC_SINGLE("ALC ZC Time", AK4535_TIMER, 4, 3, 0), | ||
149 | SOC_SINGLE("ALC 1 Switch", AK4535_ALC1, 5, 1, 0), | ||
150 | SOC_SINGLE("ALC 2 Switch", AK4535_ALC1, 6, 1, 0), | ||
151 | SOC_SINGLE("ALC Volume", AK4535_ALC2, 0, 127, 0), | ||
152 | SOC_SINGLE("Capture Volume", AK4535_PGA, 0, 127, 0), | ||
153 | SOC_SINGLE("Left Playback Volume", AK4535_LATT, 0, 127, 1), | ||
154 | SOC_SINGLE("Right Playback Volume", AK4535_RATT, 0, 127, 1), | ||
155 | SOC_SINGLE("AUX Bypass Volume", AK4535_VOL, 0, 15, 0), | ||
156 | SOC_SINGLE("Mic Sidetone Volume", AK4535_VOL, 4, 7, 0), | ||
157 | }; | ||
158 | |||
159 | /* add non dapm controls */ | ||
160 | static int ak4535_add_controls(struct snd_soc_codec *codec) | ||
161 | { | ||
162 | int err, i; | ||
163 | |||
164 | for (i = 0; i < ARRAY_SIZE(ak4535_snd_controls); i++) { | ||
165 | err = snd_ctl_add(codec->card, | ||
166 | snd_soc_cnew(&ak4535_snd_controls[i], codec, NULL)); | ||
167 | if (err < 0) | ||
168 | return err; | ||
169 | } | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | /* Mono 1 Mixer */ | ||
175 | static const struct snd_kcontrol_new ak4535_mono1_mixer_controls[] = { | ||
176 | SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG1, 4, 1, 0), | ||
177 | SOC_DAPM_SINGLE("Mono Playback Switch", AK4535_SIG1, 5, 1, 0), | ||
178 | }; | ||
179 | |||
180 | /* Stereo Mixer */ | ||
181 | static const struct snd_kcontrol_new ak4535_stereo_mixer_controls[] = { | ||
182 | SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG2, 4, 1, 0), | ||
183 | SOC_DAPM_SINGLE("Playback Switch", AK4535_SIG2, 7, 1, 0), | ||
184 | SOC_DAPM_SINGLE("Aux Bypass Switch", AK4535_SIG2, 5, 1, 0), | ||
185 | }; | ||
186 | |||
187 | /* Input Mixer */ | ||
188 | static const struct snd_kcontrol_new ak4535_input_mixer_controls[] = { | ||
189 | SOC_DAPM_SINGLE("Mic Capture Switch", AK4535_MIC, 2, 1, 0), | ||
190 | SOC_DAPM_SINGLE("Aux Capture Switch", AK4535_MIC, 5, 1, 0), | ||
191 | }; | ||
192 | |||
193 | /* Input mux */ | ||
194 | static const struct snd_kcontrol_new ak4535_input_mux_control = | ||
195 | SOC_DAPM_ENUM("Input Select", ak4535_enum[4]); | ||
196 | |||
197 | /* HP L switch */ | ||
198 | static const struct snd_kcontrol_new ak4535_hpl_control = | ||
199 | SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 1, 1, 1); | ||
200 | |||
201 | /* HP R switch */ | ||
202 | static const struct snd_kcontrol_new ak4535_hpr_control = | ||
203 | SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 0, 1, 1); | ||
204 | |||
205 | /* mono 2 switch */ | ||
206 | static const struct snd_kcontrol_new ak4535_mono2_control = | ||
207 | SOC_DAPM_SINGLE("Switch", AK4535_SIG1, 0, 1, 0); | ||
208 | |||
209 | /* Line out switch */ | ||
210 | static const struct snd_kcontrol_new ak4535_line_control = | ||
211 | SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 6, 1, 0); | ||
212 | |||
213 | /* ak4535 dapm widgets */ | ||
214 | static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = { | ||
215 | SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0, | ||
216 | &ak4535_stereo_mixer_controls[0], | ||
217 | ARRAY_SIZE(ak4535_stereo_mixer_controls)), | ||
218 | SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0, | ||
219 | &ak4535_mono1_mixer_controls[0], | ||
220 | ARRAY_SIZE(ak4535_mono1_mixer_controls)), | ||
221 | SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, | ||
222 | &ak4535_input_mixer_controls[0], | ||
223 | ARRAY_SIZE(ak4535_input_mixer_controls)), | ||
224 | SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, | ||
225 | &ak4535_input_mux_control), | ||
226 | SND_SOC_DAPM_DAC("DAC", "Playback", AK4535_PM2, 0, 0), | ||
227 | SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0, | ||
228 | &ak4535_mono2_control), | ||
229 | /* speaker powersave bit */ | ||
230 | SND_SOC_DAPM_PGA("Speaker Enable", AK4535_MODE2, 0, 0, NULL, 0), | ||
231 | SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0, | ||
232 | &ak4535_line_control), | ||
233 | SND_SOC_DAPM_SWITCH("Left HP Enable", SND_SOC_NOPM, 0, 0, | ||
234 | &ak4535_hpl_control), | ||
235 | SND_SOC_DAPM_SWITCH("Right HP Enable", SND_SOC_NOPM, 0, 0, | ||
236 | &ak4535_hpr_control), | ||
237 | SND_SOC_DAPM_OUTPUT("LOUT"), | ||
238 | SND_SOC_DAPM_OUTPUT("HPL"), | ||
239 | SND_SOC_DAPM_OUTPUT("ROUT"), | ||
240 | SND_SOC_DAPM_OUTPUT("HPR"), | ||
241 | SND_SOC_DAPM_OUTPUT("SPP"), | ||
242 | SND_SOC_DAPM_OUTPUT("SPN"), | ||
243 | SND_SOC_DAPM_OUTPUT("MOUT1"), | ||
244 | SND_SOC_DAPM_OUTPUT("MOUT2"), | ||
245 | SND_SOC_DAPM_OUTPUT("MICOUT"), | ||
246 | SND_SOC_DAPM_ADC("ADC", "Capture", AK4535_PM1, 0, 0), | ||
247 | SND_SOC_DAPM_PGA("Spk Amp", AK4535_PM2, 3, 0, NULL, 0), | ||
248 | SND_SOC_DAPM_PGA("HP R Amp", AK4535_PM2, 1, 0, NULL, 0), | ||
249 | SND_SOC_DAPM_PGA("HP L Amp", AK4535_PM2, 2, 0, NULL, 0), | ||
250 | SND_SOC_DAPM_PGA("Mic", AK4535_PM1, 1, 0, NULL, 0), | ||
251 | SND_SOC_DAPM_PGA("Line Out", AK4535_PM1, 4, 0, NULL, 0), | ||
252 | SND_SOC_DAPM_PGA("Mono Out", AK4535_PM1, 3, 0, NULL, 0), | ||
253 | SND_SOC_DAPM_PGA("AUX In", AK4535_PM1, 2, 0, NULL, 0), | ||
254 | |||
255 | SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4535_MIC, 3, 0), | ||
256 | SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4535_MIC, 4, 0), | ||
257 | SND_SOC_DAPM_INPUT("MICIN"), | ||
258 | SND_SOC_DAPM_INPUT("MICEXT"), | ||
259 | SND_SOC_DAPM_INPUT("AUX"), | ||
260 | SND_SOC_DAPM_INPUT("MIN"), | ||
261 | SND_SOC_DAPM_INPUT("AIN"), | ||
262 | }; | ||
263 | |||
264 | static const struct snd_soc_dapm_route audio_map[] = { | ||
265 | /*stereo mixer */ | ||
266 | {"Stereo Mixer", "Playback Switch", "DAC"}, | ||
267 | {"Stereo Mixer", "Mic Sidetone Switch", "Mic"}, | ||
268 | {"Stereo Mixer", "Aux Bypass Switch", "AUX In"}, | ||
269 | |||
270 | /* mono1 mixer */ | ||
271 | {"Mono1 Mixer", "Mic Sidetone Switch", "Mic"}, | ||
272 | {"Mono1 Mixer", "Mono Playback Switch", "DAC"}, | ||
273 | |||
274 | /* Mic */ | ||
275 | {"Mic", NULL, "AIN"}, | ||
276 | {"Input Mux", "Internal", "Mic Int Bias"}, | ||
277 | {"Input Mux", "External", "Mic Ext Bias"}, | ||
278 | {"Mic Int Bias", NULL, "MICIN"}, | ||
279 | {"Mic Ext Bias", NULL, "MICEXT"}, | ||
280 | {"MICOUT", NULL, "Input Mux"}, | ||
281 | |||
282 | /* line out */ | ||
283 | {"LOUT", NULL, "Line Out Enable"}, | ||
284 | {"ROUT", NULL, "Line Out Enable"}, | ||
285 | {"Line Out Enable", "Switch", "Line Out"}, | ||
286 | {"Line Out", NULL, "Stereo Mixer"}, | ||
287 | |||
288 | /* mono1 out */ | ||
289 | {"MOUT1", NULL, "Mono Out"}, | ||
290 | {"Mono Out", NULL, "Mono1 Mixer"}, | ||
291 | |||
292 | /* left HP */ | ||
293 | {"HPL", NULL, "Left HP Enable"}, | ||
294 | {"Left HP Enable", "Switch", "HP L Amp"}, | ||
295 | {"HP L Amp", NULL, "Stereo Mixer"}, | ||
296 | |||
297 | /* right HP */ | ||
298 | {"HPR", NULL, "Right HP Enable"}, | ||
299 | {"Right HP Enable", "Switch", "HP R Amp"}, | ||
300 | {"HP R Amp", NULL, "Stereo Mixer"}, | ||
301 | |||
302 | /* speaker */ | ||
303 | {"SPP", NULL, "Speaker Enable"}, | ||
304 | {"SPN", NULL, "Speaker Enable"}, | ||
305 | {"Speaker Enable", "Switch", "Spk Amp"}, | ||
306 | {"Spk Amp", NULL, "MIN"}, | ||
307 | |||
308 | /* mono 2 */ | ||
309 | {"MOUT2", NULL, "Mono 2 Enable"}, | ||
310 | {"Mono 2 Enable", "Switch", "Stereo Mixer"}, | ||
311 | |||
312 | /* Aux In */ | ||
313 | {"Aux In", NULL, "AUX"}, | ||
314 | |||
315 | /* ADC */ | ||
316 | {"ADC", NULL, "Input Mixer"}, | ||
317 | {"Input Mixer", "Mic Capture Switch", "Mic"}, | ||
318 | {"Input Mixer", "Aux Capture Switch", "Aux In"}, | ||
319 | }; | ||
320 | |||
321 | static int ak4535_add_widgets(struct snd_soc_codec *codec) | ||
322 | { | ||
323 | snd_soc_dapm_new_controls(codec, ak4535_dapm_widgets, | ||
324 | ARRAY_SIZE(ak4535_dapm_widgets)); | ||
325 | |||
326 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
327 | |||
328 | snd_soc_dapm_new_widgets(codec); | ||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static int ak4535_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, | ||
333 | int clk_id, unsigned int freq, int dir) | ||
334 | { | ||
335 | struct snd_soc_codec *codec = codec_dai->codec; | ||
336 | struct ak4535_priv *ak4535 = codec->private_data; | ||
337 | |||
338 | ak4535->sysclk = freq; | ||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | static int ak4535_hw_params(struct snd_pcm_substream *substream, | ||
343 | struct snd_pcm_hw_params *params) | ||
344 | { | ||
345 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
346 | struct snd_soc_device *socdev = rtd->socdev; | ||
347 | struct snd_soc_codec *codec = socdev->codec; | ||
348 | struct ak4535_priv *ak4535 = codec->private_data; | ||
349 | u8 mode2 = ak4535_read_reg_cache(codec, AK4535_MODE2) & ~(0x3 << 5); | ||
350 | int rate = params_rate(params), fs = 256; | ||
351 | |||
352 | if (rate) | ||
353 | fs = ak4535->sysclk / rate; | ||
354 | |||
355 | /* set fs */ | ||
356 | switch (fs) { | ||
357 | case 1024: | ||
358 | mode2 |= (0x2 << 5); | ||
359 | break; | ||
360 | case 512: | ||
361 | mode2 |= (0x1 << 5); | ||
362 | break; | ||
363 | case 256: | ||
364 | break; | ||
365 | } | ||
366 | |||
367 | /* set rate */ | ||
368 | ak4535_write(codec, AK4535_MODE2, mode2); | ||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | static int ak4535_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, | ||
373 | unsigned int fmt) | ||
374 | { | ||
375 | struct snd_soc_codec *codec = codec_dai->codec; | ||
376 | u8 mode1 = 0; | ||
377 | |||
378 | /* interface format */ | ||
379 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
380 | case SND_SOC_DAIFMT_I2S: | ||
381 | mode1 = 0x0002; | ||
382 | break; | ||
383 | case SND_SOC_DAIFMT_LEFT_J: | ||
384 | mode1 = 0x0001; | ||
385 | break; | ||
386 | default: | ||
387 | return -EINVAL; | ||
388 | } | ||
389 | |||
390 | /* use 32 fs for BCLK to save power */ | ||
391 | mode1 |= 0x4; | ||
392 | |||
393 | ak4535_write(codec, AK4535_MODE1, mode1); | ||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | static int ak4535_mute(struct snd_soc_codec_dai *dai, int mute) | ||
398 | { | ||
399 | struct snd_soc_codec *codec = dai->codec; | ||
400 | u16 mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf; | ||
401 | if (!mute) | ||
402 | ak4535_write(codec, AK4535_DAC, mute_reg); | ||
403 | else | ||
404 | ak4535_write(codec, AK4535_DAC, mute_reg | 0x20); | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static int ak4535_set_bias_level(struct snd_soc_codec *codec, | ||
409 | enum snd_soc_bias_level level) | ||
410 | { | ||
411 | u16 i; | ||
412 | |||
413 | switch (level) { | ||
414 | case SND_SOC_BIAS_ON: | ||
415 | ak4535_mute(codec->dai, 0); | ||
416 | break; | ||
417 | case SND_SOC_BIAS_PREPARE: | ||
418 | ak4535_mute(codec->dai, 1); | ||
419 | break; | ||
420 | case SND_SOC_BIAS_STANDBY: | ||
421 | i = ak4535_read_reg_cache(codec, AK4535_PM1); | ||
422 | ak4535_write(codec, AK4535_PM1, i | 0x80); | ||
423 | i = ak4535_read_reg_cache(codec, AK4535_PM2); | ||
424 | ak4535_write(codec, AK4535_PM2, i & (~0x80)); | ||
425 | break; | ||
426 | case SND_SOC_BIAS_OFF: | ||
427 | i = ak4535_read_reg_cache(codec, AK4535_PM1); | ||
428 | ak4535_write(codec, AK4535_PM1, i & (~0x80)); | ||
429 | break; | ||
430 | } | ||
431 | codec->bias_level = level; | ||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | #define AK4535_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ | ||
436 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ | ||
437 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) | ||
438 | |||
439 | struct snd_soc_codec_dai ak4535_dai = { | ||
440 | .name = "AK4535", | ||
441 | .playback = { | ||
442 | .stream_name = "Playback", | ||
443 | .channels_min = 1, | ||
444 | .channels_max = 2, | ||
445 | .rates = AK4535_RATES, | ||
446 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | ||
447 | .capture = { | ||
448 | .stream_name = "Capture", | ||
449 | .channels_min = 1, | ||
450 | .channels_max = 2, | ||
451 | .rates = AK4535_RATES, | ||
452 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | ||
453 | .ops = { | ||
454 | .hw_params = ak4535_hw_params, | ||
455 | }, | ||
456 | .dai_ops = { | ||
457 | .set_fmt = ak4535_set_dai_fmt, | ||
458 | .digital_mute = ak4535_mute, | ||
459 | .set_sysclk = ak4535_set_dai_sysclk, | ||
460 | }, | ||
461 | }; | ||
462 | EXPORT_SYMBOL_GPL(ak4535_dai); | ||
463 | |||
464 | static int ak4535_suspend(struct platform_device *pdev, pm_message_t state) | ||
465 | { | ||
466 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
467 | struct snd_soc_codec *codec = socdev->codec; | ||
468 | |||
469 | ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | static int ak4535_resume(struct platform_device *pdev) | ||
474 | { | ||
475 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
476 | struct snd_soc_codec *codec = socdev->codec; | ||
477 | ak4535_sync(codec); | ||
478 | ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
479 | ak4535_set_bias_level(codec, codec->suspend_bias_level); | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | /* | ||
484 | * initialise the AK4535 driver | ||
485 | * register the mixer and dsp interfaces with the kernel | ||
486 | */ | ||
487 | static int ak4535_init(struct snd_soc_device *socdev) | ||
488 | { | ||
489 | struct snd_soc_codec *codec = socdev->codec; | ||
490 | int ret = 0; | ||
491 | |||
492 | codec->name = "AK4535"; | ||
493 | codec->owner = THIS_MODULE; | ||
494 | codec->read = ak4535_read_reg_cache; | ||
495 | codec->write = ak4535_write; | ||
496 | codec->set_bias_level = ak4535_set_bias_level; | ||
497 | codec->dai = &ak4535_dai; | ||
498 | codec->num_dai = 1; | ||
499 | codec->reg_cache_size = ARRAY_SIZE(ak4535_reg); | ||
500 | codec->reg_cache = kmemdup(ak4535_reg, sizeof(ak4535_reg), GFP_KERNEL); | ||
501 | |||
502 | if (codec->reg_cache == NULL) | ||
503 | return -ENOMEM; | ||
504 | |||
505 | /* register pcms */ | ||
506 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
507 | if (ret < 0) { | ||
508 | printk(KERN_ERR "ak4535: failed to create pcms\n"); | ||
509 | goto pcm_err; | ||
510 | } | ||
511 | |||
512 | /* power on device */ | ||
513 | ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
514 | |||
515 | ak4535_add_controls(codec); | ||
516 | ak4535_add_widgets(codec); | ||
517 | ret = snd_soc_register_card(socdev); | ||
518 | if (ret < 0) { | ||
519 | printk(KERN_ERR "ak4535: failed to register card\n"); | ||
520 | goto card_err; | ||
521 | } | ||
522 | |||
523 | return ret; | ||
524 | |||
525 | card_err: | ||
526 | snd_soc_free_pcms(socdev); | ||
527 | snd_soc_dapm_free(socdev); | ||
528 | pcm_err: | ||
529 | kfree(codec->reg_cache); | ||
530 | |||
531 | return ret; | ||
532 | } | ||
533 | |||
534 | static struct snd_soc_device *ak4535_socdev; | ||
535 | |||
536 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
537 | |||
538 | #define I2C_DRIVERID_AK4535 0xfefe /* liam - need a proper id */ | ||
539 | |||
540 | static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; | ||
541 | |||
542 | /* Magic definition of all other variables and things */ | ||
543 | I2C_CLIENT_INSMOD; | ||
544 | |||
545 | static struct i2c_driver ak4535_i2c_driver; | ||
546 | static struct i2c_client client_template; | ||
547 | |||
548 | /* If the i2c layer weren't so broken, we could pass this kind of data | ||
549 | around */ | ||
550 | static int ak4535_codec_probe(struct i2c_adapter *adap, int addr, int kind) | ||
551 | { | ||
552 | struct snd_soc_device *socdev = ak4535_socdev; | ||
553 | struct ak4535_setup_data *setup = socdev->codec_data; | ||
554 | struct snd_soc_codec *codec = socdev->codec; | ||
555 | struct i2c_client *i2c; | ||
556 | int ret; | ||
557 | |||
558 | if (addr != setup->i2c_address) | ||
559 | return -ENODEV; | ||
560 | |||
561 | client_template.adapter = adap; | ||
562 | client_template.addr = addr; | ||
563 | |||
564 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | ||
565 | if (i2c == NULL) { | ||
566 | kfree(codec); | ||
567 | return -ENOMEM; | ||
568 | } | ||
569 | i2c_set_clientdata(i2c, codec); | ||
570 | codec->control_data = i2c; | ||
571 | |||
572 | ret = i2c_attach_client(i2c); | ||
573 | if (ret < 0) { | ||
574 | printk(KERN_ERR "failed to attach codec at addr %x\n", addr); | ||
575 | goto err; | ||
576 | } | ||
577 | |||
578 | ret = ak4535_init(socdev); | ||
579 | if (ret < 0) { | ||
580 | printk(KERN_ERR "failed to initialise AK4535\n"); | ||
581 | goto err; | ||
582 | } | ||
583 | return ret; | ||
584 | |||
585 | err: | ||
586 | kfree(codec); | ||
587 | kfree(i2c); | ||
588 | return ret; | ||
589 | } | ||
590 | |||
591 | static int ak4535_i2c_detach(struct i2c_client *client) | ||
592 | { | ||
593 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | ||
594 | i2c_detach_client(client); | ||
595 | kfree(codec->reg_cache); | ||
596 | kfree(client); | ||
597 | return 0; | ||
598 | } | ||
599 | |||
600 | static int ak4535_i2c_attach(struct i2c_adapter *adap) | ||
601 | { | ||
602 | return i2c_probe(adap, &addr_data, ak4535_codec_probe); | ||
603 | } | ||
604 | |||
605 | /* corgi i2c codec control layer */ | ||
606 | static struct i2c_driver ak4535_i2c_driver = { | ||
607 | .driver = { | ||
608 | .name = "AK4535 I2C Codec", | ||
609 | .owner = THIS_MODULE, | ||
610 | }, | ||
611 | .id = I2C_DRIVERID_AK4535, | ||
612 | .attach_adapter = ak4535_i2c_attach, | ||
613 | .detach_client = ak4535_i2c_detach, | ||
614 | .command = NULL, | ||
615 | }; | ||
616 | |||
617 | static struct i2c_client client_template = { | ||
618 | .name = "AK4535", | ||
619 | .driver = &ak4535_i2c_driver, | ||
620 | }; | ||
621 | #endif | ||
622 | |||
623 | static int ak4535_probe(struct platform_device *pdev) | ||
624 | { | ||
625 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
626 | struct ak4535_setup_data *setup; | ||
627 | struct snd_soc_codec *codec; | ||
628 | struct ak4535_priv *ak4535; | ||
629 | int ret = 0; | ||
630 | |||
631 | printk(KERN_INFO "AK4535 Audio Codec %s", AK4535_VERSION); | ||
632 | |||
633 | setup = socdev->codec_data; | ||
634 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | ||
635 | if (codec == NULL) | ||
636 | return -ENOMEM; | ||
637 | |||
638 | ak4535 = kzalloc(sizeof(struct ak4535_priv), GFP_KERNEL); | ||
639 | if (ak4535 == NULL) { | ||
640 | kfree(codec); | ||
641 | return -ENOMEM; | ||
642 | } | ||
643 | |||
644 | codec->private_data = ak4535; | ||
645 | socdev->codec = codec; | ||
646 | mutex_init(&codec->mutex); | ||
647 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
648 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
649 | |||
650 | ak4535_socdev = socdev; | ||
651 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
652 | if (setup->i2c_address) { | ||
653 | normal_i2c[0] = setup->i2c_address; | ||
654 | codec->hw_write = (hw_write_t)i2c_master_send; | ||
655 | codec->hw_read = (hw_read_t)i2c_master_recv; | ||
656 | ret = i2c_add_driver(&ak4535_i2c_driver); | ||
657 | if (ret != 0) | ||
658 | printk(KERN_ERR "can't add i2c driver"); | ||
659 | } | ||
660 | #else | ||
661 | /* Add other interfaces here */ | ||
662 | #endif | ||
663 | return ret; | ||
664 | } | ||
665 | |||
666 | /* power down chip */ | ||
667 | static int ak4535_remove(struct platform_device *pdev) | ||
668 | { | ||
669 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
670 | struct snd_soc_codec *codec = socdev->codec; | ||
671 | |||
672 | if (codec->control_data) | ||
673 | ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
674 | |||
675 | snd_soc_free_pcms(socdev); | ||
676 | snd_soc_dapm_free(socdev); | ||
677 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
678 | i2c_del_driver(&ak4535_i2c_driver); | ||
679 | #endif | ||
680 | kfree(codec->private_data); | ||
681 | kfree(codec); | ||
682 | |||
683 | return 0; | ||
684 | } | ||
685 | |||
686 | struct snd_soc_codec_device soc_codec_dev_ak4535 = { | ||
687 | .probe = ak4535_probe, | ||
688 | .remove = ak4535_remove, | ||
689 | .suspend = ak4535_suspend, | ||
690 | .resume = ak4535_resume, | ||
691 | }; | ||
692 | EXPORT_SYMBOL_GPL(soc_codec_dev_ak4535); | ||
693 | |||
694 | MODULE_DESCRIPTION("Soc AK4535 driver"); | ||
695 | MODULE_AUTHOR("Richard Purdie"); | ||
696 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/ak4535.h b/sound/soc/codecs/ak4535.h new file mode 100644 index 000000000000..fc686ddf753a --- /dev/null +++ b/sound/soc/codecs/ak4535.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | * ak4535.h -- AK4535 Soc Audio driver | ||
3 | * | ||
4 | * Copyright 2005 Openedhand Ltd. | ||
5 | * | ||
6 | * Author: Richard Purdie <richard@openedhand.com> | ||
7 | * | ||
8 | * Based on wm8753.h | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #ifndef _AK4535_H | ||
16 | #define _AK4535_H | ||
17 | |||
18 | /* AK4535 register space */ | ||
19 | |||
20 | #define AK4535_PM1 0x0 | ||
21 | #define AK4535_PM2 0x1 | ||
22 | #define AK4535_SIG1 0x2 | ||
23 | #define AK4535_SIG2 0x3 | ||
24 | #define AK4535_MODE1 0x4 | ||
25 | #define AK4535_MODE2 0x5 | ||
26 | #define AK4535_DAC 0x6 | ||
27 | #define AK4535_MIC 0x7 | ||
28 | #define AK4535_TIMER 0x8 | ||
29 | #define AK4535_ALC1 0x9 | ||
30 | #define AK4535_ALC2 0xa | ||
31 | #define AK4535_PGA 0xb | ||
32 | #define AK4535_LATT 0xc | ||
33 | #define AK4535_RATT 0xd | ||
34 | #define AK4535_VOL 0xe | ||
35 | #define AK4535_STATUS 0xf | ||
36 | |||
37 | #define AK4535_CACHEREGNUM 0x10 | ||
38 | |||
39 | struct ak4535_setup_data { | ||
40 | unsigned short i2c_address; | ||
41 | }; | ||
42 | |||
43 | extern struct snd_soc_codec_dai ak4535_dai; | ||
44 | extern struct snd_soc_codec_device soc_codec_dev_ak4535; | ||
45 | |||
46 | #endif | ||