diff options
author | Barry Song <21cnbao@gmail.com> | 2009-07-16 04:00:05 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-07-16 06:14:39 -0400 |
commit | 1274738d85d0e25c4f82d83f50a6bcbe2397e9ea (patch) | |
tree | b28cefa53b26f1bfbe119a46962db39fec280337 /sound/soc | |
parent | 3e46a447396df99e2367fe1564651abaacc19c13 (diff) |
ASoC: new ad1938 codec driver based on asoc
Signed-off-by: Barry Song <21cnbao@gmail.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc')
-rw-r--r-- | sound/soc/codecs/Kconfig | 4 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/ad1938.c | 652 | ||||
-rw-r--r-- | sound/soc/codecs/ad1938.h | 100 |
4 files changed, 758 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 4219a41d3867..57f5b73f1bd3 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -12,6 +12,7 @@ config SND_SOC_ALL_CODECS | |||
12 | tristate "Build all ASoC CODEC drivers" | 12 | tristate "Build all ASoC CODEC drivers" |
13 | select SND_SOC_L3 | 13 | select SND_SOC_L3 |
14 | select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS | 14 | select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS |
15 | select SND_SOC_AD1938 if SPI_MASTER | ||
15 | select SND_SOC_AD1980 if SND_SOC_AC97_BUS | 16 | select SND_SOC_AD1980 if SND_SOC_AC97_BUS |
16 | select SND_SOC_AD73311 if I2C | 17 | select SND_SOC_AD73311 if I2C |
17 | select SND_SOC_AK4104 if SPI_MASTER | 18 | select SND_SOC_AK4104 if SPI_MASTER |
@@ -66,6 +67,9 @@ config SND_SOC_AC97_CODEC | |||
66 | tristate | 67 | tristate |
67 | select SND_AC97_CODEC | 68 | select SND_AC97_CODEC |
68 | 69 | ||
70 | config SND_SOC_AD1938 | ||
71 | tristate | ||
72 | |||
69 | config SND_SOC_AD1980 | 73 | config SND_SOC_AD1980 |
70 | tristate | 74 | tristate |
71 | 75 | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index cf111c978223..9a92f5539163 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-ad1938-objs := ad1938.o | ||
2 | snd-soc-ad1980-objs := ad1980.o | 3 | snd-soc-ad1980-objs := ad1980.o |
3 | snd-soc-ad73311-objs := ad73311.o | 4 | snd-soc-ad73311-objs := ad73311.o |
4 | snd-soc-ak4104-objs := ak4104.o | 5 | snd-soc-ak4104-objs := ak4104.o |
@@ -42,6 +43,7 @@ snd-soc-wm9713-objs := wm9713.o | |||
42 | snd-soc-max9877-objs := max9877.o | 43 | snd-soc-max9877-objs := max9877.o |
43 | 44 | ||
44 | obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o | 45 | obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o |
46 | obj-$(CONFIG_SND_SOC_AD1938) += snd-soc-ad1938.o | ||
45 | obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o | 47 | obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o |
46 | obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o | 48 | obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o |
47 | obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o | 49 | obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o |
diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c new file mode 100644 index 000000000000..3dc80910318a --- /dev/null +++ b/sound/soc/codecs/ad1938.c | |||
@@ -0,0 +1,652 @@ | |||
1 | /* | ||
2 | * File: sound/soc/codecs/ad1938.c | ||
3 | * Author: Barry Song <Barry.Song@analog.com> | ||
4 | * | ||
5 | * Created: June 04 2009 | ||
6 | * Description: Driver for AD1938 sound chip | ||
7 | * | ||
8 | * Modified: | ||
9 | * Copyright 2009 Analog Devices Inc. | ||
10 | * | ||
11 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or | ||
16 | * (at your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | * GNU General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with this program; if not, see the file COPYING, or write | ||
25 | * to the Free Software Foundation, Inc., | ||
26 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
27 | */ | ||
28 | |||
29 | #include <linux/init.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/version.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/device.h> | ||
34 | #include <sound/core.h> | ||
35 | #include <sound/pcm.h> | ||
36 | #include <sound/pcm_params.h> | ||
37 | #include <sound/initval.h> | ||
38 | #include <sound/soc.h> | ||
39 | #include <sound/tlv.h> | ||
40 | #include <sound/soc-dapm.h> | ||
41 | #include <linux/spi/spi.h> | ||
42 | #include "ad1938.h" | ||
43 | |||
44 | /* codec private data */ | ||
45 | struct ad1938_priv { | ||
46 | struct snd_soc_codec codec; | ||
47 | u8 reg_cache[AD1938_NUM_REGS]; | ||
48 | |||
49 | }; | ||
50 | |||
51 | static struct snd_soc_codec *ad1938_codec; | ||
52 | struct snd_soc_codec_device soc_codec_dev_ad1938; | ||
53 | static int ad1938_register(struct ad1938_priv *ad1938); | ||
54 | static void ad1938_unregister(struct ad1938_priv *ad1938); | ||
55 | |||
56 | /* | ||
57 | * AD1938 volume/mute/de-emphasis etc. controls | ||
58 | */ | ||
59 | static const char *ad1938_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"}; | ||
60 | |||
61 | static const struct soc_enum ad1938_deemp_enum = | ||
62 | SOC_ENUM_SINGLE(AD1938_DAC_CTRL2, 1, 4, ad1938_deemp); | ||
63 | |||
64 | static const struct snd_kcontrol_new ad1938_snd_controls[] = { | ||
65 | /* DAC volume control */ | ||
66 | SOC_DOUBLE_R("DAC1 Volume", AD1938_DAC_L1_VOL, | ||
67 | AD1938_DAC_R1_VOL, 0, 0xFF, 1), | ||
68 | SOC_DOUBLE_R("DAC2 Volume", AD1938_DAC_L2_VOL, | ||
69 | AD1938_DAC_R2_VOL, 0, 0xFF, 1), | ||
70 | SOC_DOUBLE_R("DAC3 Volume", AD1938_DAC_L3_VOL, | ||
71 | AD1938_DAC_R3_VOL, 0, 0xFF, 1), | ||
72 | SOC_DOUBLE_R("DAC4 Volume", AD1938_DAC_L4_VOL, | ||
73 | AD1938_DAC_R4_VOL, 0, 0xFF, 1), | ||
74 | |||
75 | /* ADC switch control */ | ||
76 | SOC_DOUBLE("ADC1 Switch", AD1938_ADC_CTRL0, AD1938_ADCL1_MUTE, AD1938_ADCR1_MUTE, 1, 1), | ||
77 | SOC_DOUBLE("ADC2 Switch", AD1938_ADC_CTRL0, AD1938_ADCL2_MUTE, AD1938_ADCR2_MUTE, 1, 1), | ||
78 | |||
79 | /* DAC switch control */ | ||
80 | SOC_DOUBLE("DAC1 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL1_MUTE, AD1938_DACR1_MUTE, 1, 1), | ||
81 | SOC_DOUBLE("DAC2 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL2_MUTE, AD1938_DACR2_MUTE, 1, 1), | ||
82 | SOC_DOUBLE("DAC3 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL3_MUTE, AD1938_DACR3_MUTE, 1, 1), | ||
83 | SOC_DOUBLE("DAC4 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL4_MUTE, AD1938_DACR4_MUTE, 1, 1), | ||
84 | |||
85 | /* ADC high-pass filter */ | ||
86 | SOC_SINGLE("ADC High Pass Filter Switch", AD1938_ADC_CTRL0, | ||
87 | AD1938_ADC_HIGHPASS_FILTER, 1, 0), | ||
88 | |||
89 | /* DAC de-emphasis */ | ||
90 | SOC_ENUM("Playback Deemphasis", ad1938_deemp_enum), | ||
91 | }; | ||
92 | |||
93 | static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = { | ||
94 | SND_SOC_DAPM_DAC("DAC", "Playback", AD1938_DAC_CTRL0, 0, 1), | ||
95 | SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), | ||
96 | SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1938_ADC_CTRL0, 0, 1, NULL, 0), | ||
97 | }; | ||
98 | |||
99 | static const struct snd_soc_dapm_route audio_paths[] = { | ||
100 | { "DAC", NULL, "ADC_PWR" }, | ||
101 | { "ADC", NULL, "ADC_PWR" }, | ||
102 | }; | ||
103 | |||
104 | /* | ||
105 | * DAI ops entries | ||
106 | */ | ||
107 | |||
108 | static int ad1938_mute(struct snd_soc_dai *dai, int mute) | ||
109 | { | ||
110 | struct snd_soc_codec *codec = dai->codec; | ||
111 | int reg; | ||
112 | |||
113 | reg = codec->read(codec, AD1938_DAC_CTRL2); | ||
114 | reg = (mute > 0) ? reg | AD1938_DAC_MASTER_MUTE : reg & (~AD1938_DAC_MASTER_MUTE); | ||
115 | codec->write(codec, AD1938_DAC_CTRL2, reg); | ||
116 | |||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static inline int ad1938_pll_powerctrl(struct snd_soc_codec *codec, int cmd) | ||
121 | { | ||
122 | int reg = codec->read(codec, AD1938_PLL_CLK_CTRL0); | ||
123 | reg = (cmd > 0) ? reg & (~AD1938_PLL_POWERDOWN) : reg | AD1938_PLL_POWERDOWN; | ||
124 | codec->write(codec, AD1938_PLL_CLK_CTRL0, reg); | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static int ad1938_set_tdm_slot(struct snd_soc_dai *dai, | ||
130 | unsigned int mask, int slots) | ||
131 | { | ||
132 | struct snd_soc_codec *codec = dai->codec; | ||
133 | int dac_reg = codec->read(codec, AD1938_DAC_CTRL1); | ||
134 | int adc_reg = codec->read(codec, AD1938_ADC_CTRL2); | ||
135 | |||
136 | dac_reg &= ~AD1938_DAC_CHAN_MASK; | ||
137 | adc_reg &= ~AD1938_ADC_CHAN_MASK; | ||
138 | |||
139 | switch(slots) { | ||
140 | case 2: | ||
141 | dac_reg |= AD1938_DAC_2_CHANNELS << AD1938_DAC_CHAN_SHFT; | ||
142 | adc_reg |= AD1938_ADC_2_CHANNELS << AD1938_ADC_CHAN_SHFT; | ||
143 | break; | ||
144 | case 4: | ||
145 | dac_reg |= AD1938_DAC_4_CHANNELS << AD1938_DAC_CHAN_SHFT; | ||
146 | adc_reg |= AD1938_ADC_4_CHANNELS << AD1938_ADC_CHAN_SHFT; | ||
147 | break; | ||
148 | case 8: | ||
149 | dac_reg |= AD1938_DAC_8_CHANNELS << AD1938_DAC_CHAN_SHFT; | ||
150 | adc_reg |= AD1938_ADC_8_CHANNELS << AD1938_ADC_CHAN_SHFT; | ||
151 | break; | ||
152 | case 16: | ||
153 | dac_reg |= AD1938_DAC_16_CHANNELS << AD1938_DAC_CHAN_SHFT; | ||
154 | adc_reg |= AD1938_ADC_16_CHANNELS << AD1938_ADC_CHAN_SHFT; | ||
155 | break; | ||
156 | default: | ||
157 | return -EINVAL; | ||
158 | } | ||
159 | |||
160 | codec->write(codec, AD1938_DAC_CTRL1, dac_reg); | ||
161 | codec->write(codec, AD1938_ADC_CTRL2, adc_reg); | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static int ad1938_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
167 | unsigned int fmt) | ||
168 | { | ||
169 | struct snd_soc_codec *codec = codec_dai->codec; | ||
170 | int adc_reg, dac_reg; | ||
171 | |||
172 | adc_reg = codec->read(codec, AD1938_ADC_CTRL2); | ||
173 | dac_reg = codec->read(codec, AD1938_DAC_CTRL1); | ||
174 | |||
175 | /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S with TDM) | ||
176 | * and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) | ||
177 | */ | ||
178 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
179 | case SND_SOC_DAIFMT_I2S: | ||
180 | adc_reg &= ~AD1938_ADC_SERFMT_MASK; | ||
181 | adc_reg |= AD1938_ADC_SERFMT_TDM; | ||
182 | break; | ||
183 | case SND_SOC_DAIFMT_DSP_A: | ||
184 | adc_reg &= ~AD1938_ADC_SERFMT_MASK; | ||
185 | adc_reg |= AD1938_ADC_SERFMT_AUX; | ||
186 | break; | ||
187 | default: | ||
188 | return -EINVAL; | ||
189 | } | ||
190 | |||
191 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
192 | case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */ | ||
193 | adc_reg &= ~AD1938_ADC_LEFT_HIGH; | ||
194 | adc_reg &= ~AD1938_ADC_BCLK_INV; | ||
195 | dac_reg &= ~AD1938_DAC_LEFT_HIGH; | ||
196 | dac_reg &= ~AD1938_DAC_BCLK_INV; | ||
197 | break; | ||
198 | case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */ | ||
199 | adc_reg |= AD1938_ADC_LEFT_HIGH; | ||
200 | adc_reg &= ~AD1938_ADC_BCLK_INV; | ||
201 | dac_reg |= AD1938_DAC_LEFT_HIGH; | ||
202 | dac_reg &= ~AD1938_DAC_BCLK_INV; | ||
203 | break; | ||
204 | case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */ | ||
205 | adc_reg &= ~AD1938_ADC_LEFT_HIGH; | ||
206 | adc_reg |= AD1938_ADC_BCLK_INV; | ||
207 | dac_reg &= ~AD1938_DAC_LEFT_HIGH; | ||
208 | dac_reg |= AD1938_DAC_BCLK_INV; | ||
209 | break; | ||
210 | |||
211 | case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */ | ||
212 | adc_reg |= AD1938_ADC_LEFT_HIGH; | ||
213 | adc_reg |= AD1938_ADC_BCLK_INV; | ||
214 | dac_reg |= AD1938_DAC_LEFT_HIGH; | ||
215 | dac_reg |= AD1938_DAC_BCLK_INV; | ||
216 | break; | ||
217 | default: | ||
218 | return -EINVAL; | ||
219 | } | ||
220 | |||
221 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
222 | case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */ | ||
223 | adc_reg |= AD1938_ADC_LCR_MASTER; | ||
224 | adc_reg |= AD1938_ADC_BCLK_MASTER; | ||
225 | dac_reg |= AD1938_DAC_LCR_MASTER; | ||
226 | dac_reg |= AD1938_DAC_BCLK_MASTER; | ||
227 | break; | ||
228 | case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */ | ||
229 | adc_reg |= AD1938_ADC_LCR_MASTER; | ||
230 | adc_reg &= ~AD1938_ADC_BCLK_MASTER; | ||
231 | dac_reg |= AD1938_DAC_LCR_MASTER; | ||
232 | dac_reg &= ~AD1938_DAC_BCLK_MASTER; | ||
233 | break; | ||
234 | case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */ | ||
235 | adc_reg &= ~AD1938_ADC_LCR_MASTER; | ||
236 | adc_reg |= AD1938_ADC_BCLK_MASTER; | ||
237 | dac_reg &= ~AD1938_DAC_LCR_MASTER; | ||
238 | dac_reg |= AD1938_DAC_BCLK_MASTER; | ||
239 | break; | ||
240 | case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */ | ||
241 | adc_reg &= ~AD1938_ADC_LCR_MASTER; | ||
242 | adc_reg &= ~AD1938_ADC_BCLK_MASTER; | ||
243 | dac_reg &= ~AD1938_DAC_LCR_MASTER; | ||
244 | dac_reg &= ~AD1938_DAC_BCLK_MASTER; | ||
245 | break; | ||
246 | default: | ||
247 | return -EINVAL; | ||
248 | } | ||
249 | |||
250 | codec->write(codec, AD1938_ADC_CTRL2, adc_reg); | ||
251 | codec->write(codec, AD1938_DAC_CTRL1, dac_reg); | ||
252 | |||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static int ad1938_hw_params(struct snd_pcm_substream *substream, | ||
257 | struct snd_pcm_hw_params *params, | ||
258 | struct snd_soc_dai *dai) | ||
259 | { | ||
260 | int word_len = 0, reg = 0; | ||
261 | |||
262 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
263 | struct snd_soc_device *socdev = rtd->socdev; | ||
264 | struct snd_soc_codec *codec = socdev->card->codec; | ||
265 | |||
266 | /* bit size */ | ||
267 | switch (params_format(params)) { | ||
268 | case SNDRV_PCM_FORMAT_S16_LE: | ||
269 | word_len = 3; | ||
270 | break; | ||
271 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
272 | word_len = 1; | ||
273 | break; | ||
274 | case SNDRV_PCM_FORMAT_S24_LE: | ||
275 | case SNDRV_PCM_FORMAT_S32_LE: | ||
276 | word_len = 0; | ||
277 | break; | ||
278 | } | ||
279 | |||
280 | reg = codec->read(codec, AD1938_DAC_CTRL2); | ||
281 | reg = (reg & (~AD1938_DAC_WORD_LEN_MASK)) | word_len; | ||
282 | codec->write(codec, AD1938_DAC_CTRL2, reg); | ||
283 | |||
284 | reg = codec->read(codec, AD1938_ADC_CTRL1); | ||
285 | reg = (reg & (~AD1938_ADC_WORD_LEN_MASK)) | word_len; | ||
286 | codec->write(codec, AD1938_ADC_CTRL1, reg); | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static int ad1938_set_bias_level(struct snd_soc_codec *codec, | ||
292 | enum snd_soc_bias_level level) | ||
293 | { | ||
294 | switch (level) { | ||
295 | case SND_SOC_BIAS_ON: | ||
296 | ad1938_pll_powerctrl(codec, 1); | ||
297 | break; | ||
298 | case SND_SOC_BIAS_PREPARE: | ||
299 | break; | ||
300 | case SND_SOC_BIAS_STANDBY: | ||
301 | case SND_SOC_BIAS_OFF: | ||
302 | ad1938_pll_powerctrl(codec, 0); | ||
303 | break; | ||
304 | } | ||
305 | codec->bias_level = level; | ||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * interface to read/write ad1938 register | ||
311 | */ | ||
312 | |||
313 | #define AD1938_SPI_ADDR 0x4 | ||
314 | #define AD1938_SPI_READ 0x1 | ||
315 | #define AD1938_SPI_BUFLEN 3 | ||
316 | |||
317 | /* | ||
318 | * write to the ad1938 register space | ||
319 | */ | ||
320 | |||
321 | static int ad1938_write_reg(struct snd_soc_codec *codec, unsigned int reg, | ||
322 | unsigned int value) | ||
323 | { | ||
324 | u8 *reg_cache = codec->reg_cache; | ||
325 | int ret = 0; | ||
326 | |||
327 | if(value != reg_cache[reg]) { | ||
328 | uint8_t buf[AD1938_SPI_BUFLEN]; | ||
329 | struct spi_transfer t = { | ||
330 | .tx_buf = buf, | ||
331 | .len = AD1938_SPI_BUFLEN, | ||
332 | }; | ||
333 | struct spi_message m; | ||
334 | |||
335 | buf[0] = AD1938_SPI_ADDR << 1; | ||
336 | buf[1] = reg; | ||
337 | buf[2] = value; | ||
338 | spi_message_init(&m); | ||
339 | spi_message_add_tail(&t, &m); | ||
340 | if((ret = spi_sync(codec->control_data, &m)) == 0) | ||
341 | reg_cache[reg] = value; | ||
342 | } | ||
343 | |||
344 | return ret; | ||
345 | } | ||
346 | |||
347 | /* | ||
348 | * read from the ad1938 register space cache | ||
349 | */ | ||
350 | |||
351 | static unsigned int ad1938_read_reg_cache(struct snd_soc_codec *codec, | ||
352 | unsigned int reg) | ||
353 | { | ||
354 | u8 *reg_cache = codec->reg_cache; | ||
355 | |||
356 | if (reg >= codec->reg_cache_size) | ||
357 | return -EINVAL; | ||
358 | |||
359 | return reg_cache[reg]; | ||
360 | } | ||
361 | |||
362 | /* | ||
363 | * read from the ad1938 register space | ||
364 | */ | ||
365 | |||
366 | static unsigned int ad1938_read_reg(struct snd_soc_codec *codec, unsigned int reg) | ||
367 | { | ||
368 | char w_buf[AD1938_SPI_BUFLEN]; | ||
369 | char r_buf[AD1938_SPI_BUFLEN]; | ||
370 | int ret; | ||
371 | |||
372 | struct spi_transfer t = { | ||
373 | .tx_buf = w_buf, | ||
374 | .rx_buf = r_buf, | ||
375 | .len = AD1938_SPI_BUFLEN, | ||
376 | }; | ||
377 | struct spi_message m; | ||
378 | |||
379 | w_buf[0] = (AD1938_SPI_ADDR << 1) | AD1938_SPI_READ; | ||
380 | w_buf[1] = reg; | ||
381 | w_buf[2] = 0; | ||
382 | |||
383 | spi_message_init(&m); | ||
384 | spi_message_add_tail(&t, &m); | ||
385 | ret = spi_sync(codec->control_data, &m); | ||
386 | if (ret == 0) | ||
387 | return r_buf[2]; | ||
388 | else | ||
389 | return -EIO; | ||
390 | } | ||
391 | |||
392 | static int ad1938_fill_cache(struct snd_soc_codec *codec) | ||
393 | { | ||
394 | int i; | ||
395 | u8 *reg_cache = codec->reg_cache; | ||
396 | struct spi_device *spi = codec->control_data; | ||
397 | |||
398 | for (i = 0; i < codec->reg_cache_size; i++) { | ||
399 | int ret = ad1938_read_reg(codec, i); | ||
400 | if (ret == -EIO) { | ||
401 | dev_err(&spi->dev, "AD1938 SPI read failure\n"); | ||
402 | return ret; | ||
403 | } | ||
404 | reg_cache[i] = ret; | ||
405 | } | ||
406 | |||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | static int __devinit ad1938_spi_probe(struct spi_device *spi) | ||
411 | { | ||
412 | struct snd_soc_codec *codec; | ||
413 | struct ad1938_priv *ad1938; | ||
414 | |||
415 | ad1938 = kzalloc(sizeof(struct ad1938_priv), GFP_KERNEL); | ||
416 | if (ad1938 == NULL) | ||
417 | return -ENOMEM; | ||
418 | |||
419 | codec = &ad1938->codec; | ||
420 | codec->control_data = spi; | ||
421 | codec->dev = &spi->dev; | ||
422 | |||
423 | spi->dev.driver_data = ad1938; | ||
424 | |||
425 | return ad1938_register(ad1938); | ||
426 | } | ||
427 | |||
428 | static int __devexit ad1938_spi_remove(struct spi_device *spi) | ||
429 | { | ||
430 | struct ad1938_priv *ad1938 = spi->dev.driver_data; | ||
431 | |||
432 | ad1938_unregister(ad1938); | ||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static struct spi_driver ad1938_spi_driver = { | ||
437 | .driver = { | ||
438 | .name = "ad1938-spi", | ||
439 | .bus = &spi_bus_type, | ||
440 | .owner = THIS_MODULE, | ||
441 | }, | ||
442 | .probe = ad1938_spi_probe, | ||
443 | .remove = __devexit_p(ad1938_spi_remove), | ||
444 | }; | ||
445 | |||
446 | static struct snd_soc_dai_ops ad1938_dai_ops = { | ||
447 | .hw_params = ad1938_hw_params, | ||
448 | .digital_mute = ad1938_mute, | ||
449 | .set_tdm_slot = ad1938_set_tdm_slot, | ||
450 | .set_fmt = ad1938_set_dai_fmt, | ||
451 | }; | ||
452 | |||
453 | /* codec DAI instance */ | ||
454 | struct snd_soc_dai ad1938_dai = { | ||
455 | .name = "AD1938", | ||
456 | .playback = { | ||
457 | .stream_name = "Playback", | ||
458 | .channels_min = 2, | ||
459 | .channels_max = 8, | ||
460 | .rates = SNDRV_PCM_RATE_48000, | ||
461 | .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | | ||
462 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, | ||
463 | }, | ||
464 | .capture = { | ||
465 | .stream_name = "Capture", | ||
466 | .channels_min = 2, | ||
467 | .channels_max = 4, | ||
468 | .rates = SNDRV_PCM_RATE_48000, | ||
469 | .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | | ||
470 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, | ||
471 | }, | ||
472 | .ops = &ad1938_dai_ops, | ||
473 | }; | ||
474 | EXPORT_SYMBOL_GPL(ad1938_dai); | ||
475 | |||
476 | static int ad1938_register(struct ad1938_priv *ad1938) | ||
477 | { | ||
478 | int ret; | ||
479 | struct snd_soc_codec *codec = &ad1938->codec; | ||
480 | |||
481 | if (ad1938_codec) { | ||
482 | dev_err(codec->dev, "Another ad1938 is registered\n"); | ||
483 | return -EINVAL; | ||
484 | } | ||
485 | |||
486 | mutex_init(&codec->mutex); | ||
487 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
488 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
489 | codec->private_data = ad1938; | ||
490 | codec->reg_cache = ad1938->reg_cache; | ||
491 | codec->reg_cache_size = AD1938_NUM_REGS; | ||
492 | codec->name = "AD1938"; | ||
493 | codec->owner = THIS_MODULE; | ||
494 | codec->dai = &ad1938_dai; | ||
495 | codec->num_dai = 1; | ||
496 | codec->write = ad1938_write_reg; | ||
497 | codec->read = ad1938_read_reg_cache; | ||
498 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
499 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
500 | |||
501 | ad1938_dai.dev = codec->dev; | ||
502 | ad1938_codec = codec; | ||
503 | |||
504 | /* default setting for ad1938 */ | ||
505 | codec->write(codec, AD1938_DAC_CHNL_MUTE, 0x0); /* unmute dac channels */ | ||
506 | codec->write(codec, AD1938_DAC_CTRL2, 0x1A); /* de-emphasis: 48kHz, powedown dac */ | ||
507 | codec->write(codec, AD1938_DAC_CTRL0, 0x21); /* powerdown dac, dac tdm mode */ | ||
508 | codec->write(codec, AD1938_ADC_CTRL0, 0x3); /* high-pass filter enable */ | ||
509 | codec->write(codec, AD1938_ADC_CTRL1, 0x43); /* sata delay=1, adc aux mode */ | ||
510 | codec->write(codec, AD1938_PLL_CLK_CTRL0, 0x9D); /* pll input:mclki/xi */ | ||
511 | codec->write(codec, AD1938_PLL_CLK_CTRL1, 0x04); | ||
512 | |||
513 | ad1938_fill_cache(codec); | ||
514 | |||
515 | ret = snd_soc_register_codec(codec); | ||
516 | if (ret != 0) { | ||
517 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); | ||
518 | return ret; | ||
519 | } | ||
520 | |||
521 | ret = snd_soc_register_dai(&ad1938_dai); | ||
522 | if (ret != 0) { | ||
523 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); | ||
524 | snd_soc_unregister_codec(codec); | ||
525 | return ret; | ||
526 | } | ||
527 | |||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | static void ad1938_unregister(struct ad1938_priv *ad1938) | ||
532 | { | ||
533 | ad1938_set_bias_level(&ad1938->codec, SND_SOC_BIAS_OFF); | ||
534 | snd_soc_unregister_dai(&ad1938_dai); | ||
535 | snd_soc_unregister_codec(&ad1938->codec); | ||
536 | kfree(ad1938); | ||
537 | ad1938_codec = NULL; | ||
538 | } | ||
539 | |||
540 | static int ad1938_probe(struct platform_device *pdev) | ||
541 | { | ||
542 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
543 | struct snd_soc_codec *codec; | ||
544 | int ret = 0; | ||
545 | |||
546 | if (ad1938_codec == NULL) { | ||
547 | dev_err(&pdev->dev, "Codec device not registered\n"); | ||
548 | return -ENODEV; | ||
549 | } | ||
550 | |||
551 | socdev->card->codec = ad1938_codec; | ||
552 | codec = ad1938_codec; | ||
553 | |||
554 | /* register pcms */ | ||
555 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
556 | if (ret < 0) { | ||
557 | dev_err(codec->dev, "failed to create pcms: %d\n", ret); | ||
558 | goto pcm_err; | ||
559 | } | ||
560 | |||
561 | snd_soc_add_controls(codec, ad1938_snd_controls, | ||
562 | ARRAY_SIZE(ad1938_snd_controls)); | ||
563 | snd_soc_dapm_new_controls(codec, ad1938_dapm_widgets, | ||
564 | ARRAY_SIZE(ad1938_dapm_widgets)); | ||
565 | snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); | ||
566 | snd_soc_dapm_new_widgets(codec); | ||
567 | |||
568 | ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
569 | |||
570 | ret = snd_soc_init_card(socdev); | ||
571 | if (ret < 0) { | ||
572 | dev_err(codec->dev, "failed to register card: %d\n", ret); | ||
573 | goto card_err; | ||
574 | } | ||
575 | |||
576 | return ret; | ||
577 | |||
578 | card_err: | ||
579 | snd_soc_free_pcms(socdev); | ||
580 | snd_soc_dapm_free(socdev); | ||
581 | pcm_err: | ||
582 | return ret; | ||
583 | } | ||
584 | |||
585 | /* power down chip */ | ||
586 | static int ad1938_remove(struct platform_device *pdev) | ||
587 | { | ||
588 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
589 | |||
590 | snd_soc_free_pcms(socdev); | ||
591 | snd_soc_dapm_free(socdev); | ||
592 | |||
593 | return 0; | ||
594 | } | ||
595 | |||
596 | #ifdef CONFIG_PM | ||
597 | static int ad1938_suspend(struct platform_device *pdev, | ||
598 | pm_message_t state) | ||
599 | { | ||
600 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
601 | struct snd_soc_codec *codec = socdev->card->codec; | ||
602 | |||
603 | ad1938_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
604 | return 0; | ||
605 | } | ||
606 | |||
607 | static int ad1938_resume(struct platform_device *pdev) | ||
608 | { | ||
609 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
610 | struct snd_soc_codec *codec = socdev->card->codec; | ||
611 | |||
612 | if (codec->suspend_bias_level == SND_SOC_BIAS_ON) | ||
613 | ad1938_set_bias_level(codec, SND_SOC_BIAS_ON); | ||
614 | |||
615 | return 0; | ||
616 | } | ||
617 | #else | ||
618 | #define ad1938_suspend NULL | ||
619 | #define ad1938_resume NULL | ||
620 | #endif | ||
621 | |||
622 | struct snd_soc_codec_device soc_codec_dev_ad1938 = { | ||
623 | .probe = ad1938_probe, | ||
624 | .remove = ad1938_remove, | ||
625 | .suspend = ad1938_suspend, | ||
626 | .resume = ad1938_resume, | ||
627 | }; | ||
628 | EXPORT_SYMBOL_GPL(soc_codec_dev_ad1938); | ||
629 | |||
630 | static int __init ad1938_init(void) | ||
631 | { | ||
632 | int ret; | ||
633 | |||
634 | ret = spi_register_driver(&ad1938_spi_driver); | ||
635 | if (ret != 0) { | ||
636 | printk(KERN_ERR "Failed to register ad1938 SPI driver: %d\n", | ||
637 | ret); | ||
638 | } | ||
639 | |||
640 | return ret; | ||
641 | } | ||
642 | module_init(ad1938_init); | ||
643 | |||
644 | static void __exit ad1938_exit(void) | ||
645 | { | ||
646 | spi_unregister_driver(&ad1938_spi_driver); | ||
647 | } | ||
648 | module_exit(ad1938_exit); | ||
649 | |||
650 | MODULE_DESCRIPTION("ASoC ad1938 driver"); | ||
651 | MODULE_AUTHOR("Barry Song "); | ||
652 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/ad1938.h b/sound/soc/codecs/ad1938.h new file mode 100644 index 000000000000..fe3c48cd2d5b --- /dev/null +++ b/sound/soc/codecs/ad1938.h | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * File: sound/soc/codecs/ad1836.h | ||
3 | * Based on: | ||
4 | * Author: Barry Song <Barry.Song@analog.com> | ||
5 | * | ||
6 | * Created: May 25, 2009 | ||
7 | * Description: definitions for AD1938 registers | ||
8 | * | ||
9 | * Modified: | ||
10 | * | ||
11 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or | ||
16 | * (at your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | * GNU General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with this program; if not, see the file COPYING, or write | ||
25 | * to the Free Software Foundation, Inc., | ||
26 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
27 | */ | ||
28 | |||
29 | #ifndef __AD1938_H__ | ||
30 | #define __AD1938_H__ | ||
31 | |||
32 | #define AD1938_PLL_CLK_CTRL0 0 | ||
33 | #define AD1938_PLL_POWERDOWN 0x01 | ||
34 | #define AD1938_PLL_CLK_CTRL1 1 | ||
35 | #define AD1938_DAC_CTRL0 2 | ||
36 | #define AD1938_DAC_POWERDOWN 0x01 | ||
37 | #define AD1938_DAC_SERFMT_MASK 0xC0 | ||
38 | #define AD1938_DAC_SERFMT_STEREO (0 << 6) | ||
39 | #define AD1938_DAC_SERFMT_TDM (1 << 6) | ||
40 | #define AD1938_DAC_CTRL1 3 | ||
41 | #define AD1938_DAC_2_CHANNELS 0 | ||
42 | #define AD1938_DAC_4_CHANNELS 1 | ||
43 | #define AD1938_DAC_8_CHANNELS 2 | ||
44 | #define AD1938_DAC_16_CHANNELS 3 | ||
45 | #define AD1938_DAC_CHAN_SHFT 1 | ||
46 | #define AD1938_DAC_CHAN_MASK (3 << AD1938_DAC_CHAN_SHFT) | ||
47 | #define AD1938_DAC_LCR_MASTER (1 << 4) | ||
48 | #define AD1938_DAC_BCLK_MASTER (1 << 5) | ||
49 | #define AD1938_DAC_LEFT_HIGH (1 << 3) | ||
50 | #define AD1938_DAC_BCLK_INV (1 << 7) | ||
51 | #define AD1938_DAC_CTRL2 4 | ||
52 | #define AD1938_DAC_WORD_LEN_MASK 0xC | ||
53 | #define AD1938_DAC_MASTER_MUTE 1 | ||
54 | #define AD1938_DAC_CHNL_MUTE 5 | ||
55 | #define AD1938_DACL1_MUTE 0 | ||
56 | #define AD1938_DACR1_MUTE 1 | ||
57 | #define AD1938_DACL2_MUTE 2 | ||
58 | #define AD1938_DACR2_MUTE 3 | ||
59 | #define AD1938_DACL3_MUTE 4 | ||
60 | #define AD1938_DACR3_MUTE 5 | ||
61 | #define AD1938_DACL4_MUTE 6 | ||
62 | #define AD1938_DACR4_MUTE 7 | ||
63 | #define AD1938_DAC_L1_VOL 6 | ||
64 | #define AD1938_DAC_R1_VOL 7 | ||
65 | #define AD1938_DAC_L2_VOL 8 | ||
66 | #define AD1938_DAC_R2_VOL 9 | ||
67 | #define AD1938_DAC_L3_VOL 10 | ||
68 | #define AD1938_DAC_R3_VOL 11 | ||
69 | #define AD1938_DAC_L4_VOL 12 | ||
70 | #define AD1938_DAC_R4_VOL 13 | ||
71 | #define AD1938_ADC_CTRL0 14 | ||
72 | #define AD1938_ADC_POWERDOWN 0x01 | ||
73 | #define AD1938_ADC_HIGHPASS_FILTER 1 | ||
74 | #define AD1938_ADCL1_MUTE 2 | ||
75 | #define AD1938_ADCR1_MUTE 3 | ||
76 | #define AD1938_ADCL2_MUTE 4 | ||
77 | #define AD1938_ADCR2_MUTE 5 | ||
78 | #define AD1938_ADC_CTRL1 15 | ||
79 | #define AD1938_ADC_SERFMT_MASK 0x60 | ||
80 | #define AD1938_ADC_SERFMT_STEREO (0 << 5) | ||
81 | #define AD1938_ADC_SERFMT_TDM (1 << 2) | ||
82 | #define AD1938_ADC_SERFMT_AUX (2 << 5) | ||
83 | #define AD1938_ADC_WORD_LEN_MASK 0x3 | ||
84 | #define AD1938_ADC_CTRL2 16 | ||
85 | #define AD1938_ADC_2_CHANNELS 0 | ||
86 | #define AD1938_ADC_4_CHANNELS 1 | ||
87 | #define AD1938_ADC_8_CHANNELS 2 | ||
88 | #define AD1938_ADC_16_CHANNELS 3 | ||
89 | #define AD1938_ADC_CHAN_SHFT 4 | ||
90 | #define AD1938_ADC_CHAN_MASK (3 << AD1938_ADC_CHAN_SHFT) | ||
91 | #define AD1938_ADC_LCR_MASTER (1 << 3) | ||
92 | #define AD1938_ADC_BCLK_MASTER (1 << 6) | ||
93 | #define AD1938_ADC_LEFT_HIGH (1 << 2) | ||
94 | #define AD1938_ADC_BCLK_INV (1 << 1) | ||
95 | |||
96 | #define AD1938_NUM_REGS 17 | ||
97 | |||
98 | extern struct snd_soc_dai ad1938_dai; | ||
99 | extern struct snd_soc_codec_device soc_codec_dev_ad1938; | ||
100 | #endif | ||