diff options
-rw-r--r-- | sound/soc/codecs/Kconfig | 4 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/mc13783.c | 800 | ||||
-rw-r--r-- | sound/soc/codecs/mc13783.h | 28 |
4 files changed, 834 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f8035bd09af6..22c686444633 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -44,6 +44,7 @@ config SND_SOC_ALL_CODECS | |||
44 | select SND_SOC_MAX9850 if I2C | 44 | select SND_SOC_MAX9850 if I2C |
45 | select SND_SOC_MAX9768 if I2C | 45 | select SND_SOC_MAX9768 if I2C |
46 | select SND_SOC_MAX9877 if I2C | 46 | select SND_SOC_MAX9877 if I2C |
47 | select SND_SOC_MC13783 if MFD_MC13XXX | ||
47 | select SND_SOC_ML26124 if I2C | 48 | select SND_SOC_ML26124 if I2C |
48 | select SND_SOC_PCM3008 | 49 | select SND_SOC_PCM3008 |
49 | select SND_SOC_RT5631 if I2C | 50 | select SND_SOC_RT5631 if I2C |
@@ -444,6 +445,9 @@ config SND_SOC_MAX9768 | |||
444 | config SND_SOC_MAX9877 | 445 | config SND_SOC_MAX9877 |
445 | tristate | 446 | tristate |
446 | 447 | ||
448 | config SND_SOC_MC13783 | ||
449 | tristate | ||
450 | |||
447 | config SND_SOC_ML26124 | 451 | config SND_SOC_ML26124 |
448 | tristate | 452 | tristate |
449 | 453 | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d11ab901b075..a9663e9c375b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -31,6 +31,7 @@ snd-soc-max9768-objs := max9768.o | |||
31 | snd-soc-max98088-objs := max98088.o | 31 | snd-soc-max98088-objs := max98088.o |
32 | snd-soc-max98095-objs := max98095.o | 32 | snd-soc-max98095-objs := max98095.o |
33 | snd-soc-max9850-objs := max9850.o | 33 | snd-soc-max9850-objs := max9850.o |
34 | snd-soc-mc13783-objs := mc13783.o | ||
34 | snd-soc-ml26124-objs := ml26124.o | 35 | snd-soc-ml26124-objs := ml26124.o |
35 | snd-soc-pcm3008-objs := pcm3008.o | 36 | snd-soc-pcm3008-objs := pcm3008.o |
36 | snd-soc-rt5631-objs := rt5631.o | 37 | snd-soc-rt5631-objs := rt5631.o |
@@ -140,6 +141,7 @@ obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o | |||
140 | obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o | 141 | obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o |
141 | obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o | 142 | obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o |
142 | obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o | 143 | obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o |
144 | obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o | ||
143 | obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o | 145 | obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o |
144 | obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o | 146 | obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o |
145 | obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o | 147 | obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o |
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c new file mode 100644 index 000000000000..50fa38b9d183 --- /dev/null +++ b/sound/soc/codecs/mc13783.c | |||
@@ -0,0 +1,800 @@ | |||
1 | /* | ||
2 | * Copyright 2008 Juergen Beisert, kernel@pengutronix.de | ||
3 | * Copyright 2009 Sascha Hauer, s.hauer@pengutronix.de | ||
4 | * Copyright 2012 Philippe Retornaz, philippe.retornaz@epfl.ch | ||
5 | * | ||
6 | * Initial development of this code was funded by | ||
7 | * Phytec Messtechnik GmbH, http://www.phytec.de | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version 2 | ||
12 | * of the License, or (at your option) any later version. | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
21 | * MA 02110-1301, USA. | ||
22 | */ | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/device.h> | ||
25 | #include <linux/mfd/mc13xxx.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/control.h> | ||
29 | #include <sound/pcm.h> | ||
30 | #include <sound/soc.h> | ||
31 | #include <sound/initval.h> | ||
32 | #include <sound/soc-dapm.h> | ||
33 | |||
34 | #include "mc13783.h" | ||
35 | |||
36 | #define MC13783_AUDIO_RX0 36 | ||
37 | #define MC13783_AUDIO_RX1 37 | ||
38 | #define MC13783_AUDIO_TX 38 | ||
39 | #define MC13783_SSI_NETWORK 39 | ||
40 | #define MC13783_AUDIO_CODEC 40 | ||
41 | #define MC13783_AUDIO_DAC 41 | ||
42 | |||
43 | #define AUDIO_RX0_ALSPEN (1 << 5) | ||
44 | #define AUDIO_RX0_ALSPSEL (1 << 7) | ||
45 | #define AUDIO_RX0_ADDCDC (1 << 21) | ||
46 | #define AUDIO_RX0_ADDSTDC (1 << 22) | ||
47 | #define AUDIO_RX0_ADDRXIN (1 << 23) | ||
48 | |||
49 | #define AUDIO_RX1_PGARXEN (1 << 0); | ||
50 | #define AUDIO_RX1_PGASTEN (1 << 5) | ||
51 | #define AUDIO_RX1_ARXINEN (1 << 10) | ||
52 | |||
53 | #define AUDIO_TX_AMC1REN (1 << 5) | ||
54 | #define AUDIO_TX_AMC1LEN (1 << 7) | ||
55 | #define AUDIO_TX_AMC2EN (1 << 9) | ||
56 | #define AUDIO_TX_ATXINEN (1 << 11) | ||
57 | #define AUDIO_TX_RXINREC (1 << 13) | ||
58 | |||
59 | #define SSI_NETWORK_CDCTXRXSLOT(x) (((x) & 0x3) << 2) | ||
60 | #define SSI_NETWORK_CDCTXSECSLOT(x) (((x) & 0x3) << 4) | ||
61 | #define SSI_NETWORK_CDCRXSECSLOT(x) (((x) & 0x3) << 6) | ||
62 | #define SSI_NETWORK_CDCRXSECGAIN(x) (((x) & 0x3) << 8) | ||
63 | #define SSI_NETWORK_CDCSUMGAIN(x) (1 << 10) | ||
64 | #define SSI_NETWORK_CDCFSDLY(x) (1 << 11) | ||
65 | #define SSI_NETWORK_DAC_SLOTS_8 (1 << 12) | ||
66 | #define SSI_NETWORK_DAC_SLOTS_4 (2 << 12) | ||
67 | #define SSI_NETWORK_DAC_SLOTS_2 (3 << 12) | ||
68 | #define SSI_NETWORK_DAC_SLOT_MASK (3 << 12) | ||
69 | #define SSI_NETWORK_DAC_RXSLOT_0_1 (0 << 14) | ||
70 | #define SSI_NETWORK_DAC_RXSLOT_2_3 (1 << 14) | ||
71 | #define SSI_NETWORK_DAC_RXSLOT_4_5 (2 << 14) | ||
72 | #define SSI_NETWORK_DAC_RXSLOT_6_7 (3 << 14) | ||
73 | #define SSI_NETWORK_DAC_RXSLOT_MASK (3 << 14) | ||
74 | #define SSI_NETWORK_STDCRXSECSLOT(x) (((x) & 0x3) << 16) | ||
75 | #define SSI_NETWORK_STDCRXSECGAIN(x) (((x) & 0x3) << 18) | ||
76 | #define SSI_NETWORK_STDCSUMGAIN (1 << 20) | ||
77 | |||
78 | /* | ||
79 | * MC13783_AUDIO_CODEC and MC13783_AUDIO_DAC mostly share the same | ||
80 | * register layout | ||
81 | */ | ||
82 | #define AUDIO_SSI_SEL (1 << 0) | ||
83 | #define AUDIO_CLK_SEL (1 << 1) | ||
84 | #define AUDIO_CSM (1 << 2) | ||
85 | #define AUDIO_BCL_INV (1 << 3) | ||
86 | #define AUDIO_CFS_INV (1 << 4) | ||
87 | #define AUDIO_CFS(x) (((x) & 0x3) << 5) | ||
88 | #define AUDIO_CLK(x) (((x) & 0x7) << 7) | ||
89 | #define AUDIO_C_EN (1 << 11) | ||
90 | #define AUDIO_C_CLK_EN (1 << 12) | ||
91 | #define AUDIO_C_RESET (1 << 15) | ||
92 | |||
93 | #define AUDIO_CODEC_CDCFS8K16K (1 << 10) | ||
94 | #define AUDIO_DAC_CFS_DLY_B (1 << 10) | ||
95 | |||
96 | struct mc13783_priv { | ||
97 | struct snd_soc_codec codec; | ||
98 | struct mc13xxx *mc13xxx; | ||
99 | |||
100 | enum mc13783_ssi_port adc_ssi_port; | ||
101 | enum mc13783_ssi_port dac_ssi_port; | ||
102 | }; | ||
103 | |||
104 | static unsigned int mc13783_read(struct snd_soc_codec *codec, | ||
105 | unsigned int reg) | ||
106 | { | ||
107 | struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
108 | unsigned int value = 0; | ||
109 | |||
110 | mc13xxx_lock(priv->mc13xxx); | ||
111 | |||
112 | mc13xxx_reg_read(priv->mc13xxx, reg, &value); | ||
113 | |||
114 | mc13xxx_unlock(priv->mc13xxx); | ||
115 | |||
116 | return value; | ||
117 | } | ||
118 | |||
119 | static int mc13783_write(struct snd_soc_codec *codec, | ||
120 | unsigned int reg, unsigned int value) | ||
121 | { | ||
122 | struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
123 | int ret; | ||
124 | |||
125 | mc13xxx_lock(priv->mc13xxx); | ||
126 | |||
127 | ret = mc13xxx_reg_write(priv->mc13xxx, reg, value); | ||
128 | |||
129 | mc13xxx_unlock(priv->mc13xxx); | ||
130 | |||
131 | return ret; | ||
132 | } | ||
133 | |||
134 | /* Mapping between sample rates and register value */ | ||
135 | static unsigned int mc13783_rates[] = { | ||
136 | 8000, 11025, 12000, 16000, | ||
137 | 22050, 24000, 32000, 44100, | ||
138 | 48000, 64000, 96000 | ||
139 | }; | ||
140 | |||
141 | static int mc13783_pcm_hw_params_dac(struct snd_pcm_substream *substream, | ||
142 | struct snd_pcm_hw_params *params, | ||
143 | struct snd_soc_dai *dai) | ||
144 | { | ||
145 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
146 | struct snd_soc_codec *codec = rtd->codec; | ||
147 | unsigned int rate = params_rate(params); | ||
148 | int i; | ||
149 | |||
150 | for (i = 0; i < ARRAY_SIZE(mc13783_rates); i++) { | ||
151 | if (rate == mc13783_rates[i]) { | ||
152 | snd_soc_update_bits(codec, MC13783_AUDIO_DAC, | ||
153 | 0xf << 17, i << 17); | ||
154 | return 0; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | return -EINVAL; | ||
159 | } | ||
160 | |||
161 | static int mc13783_pcm_hw_params_codec(struct snd_pcm_substream *substream, | ||
162 | struct snd_pcm_hw_params *params, | ||
163 | struct snd_soc_dai *dai) | ||
164 | { | ||
165 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
166 | struct snd_soc_codec *codec = rtd->codec; | ||
167 | unsigned int rate = params_rate(params); | ||
168 | unsigned int val; | ||
169 | |||
170 | switch (rate) { | ||
171 | case 8000: | ||
172 | val = 0; | ||
173 | break; | ||
174 | case 16000: | ||
175 | val = AUDIO_CODEC_CDCFS8K16K; | ||
176 | break; | ||
177 | default: | ||
178 | return -EINVAL; | ||
179 | } | ||
180 | |||
181 | snd_soc_update_bits(codec, MC13783_AUDIO_CODEC, AUDIO_CODEC_CDCFS8K16K, | ||
182 | val); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int mc13783_pcm_hw_params_sync(struct snd_pcm_substream *substream, | ||
188 | struct snd_pcm_hw_params *params, | ||
189 | struct snd_soc_dai *dai) | ||
190 | { | ||
191 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
192 | return mc13783_pcm_hw_params_dac(substream, params, dai); | ||
193 | else | ||
194 | return mc13783_pcm_hw_params_codec(substream, params, dai); | ||
195 | } | ||
196 | |||
197 | static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt, | ||
198 | unsigned int reg) | ||
199 | { | ||
200 | struct snd_soc_codec *codec = dai->codec; | ||
201 | unsigned int val = 0; | ||
202 | unsigned int mask = AUDIO_CFS(3) | AUDIO_BCL_INV | AUDIO_CFS_INV | | ||
203 | AUDIO_CSM | AUDIO_C_CLK_EN | AUDIO_C_RESET; | ||
204 | |||
205 | |||
206 | /* DAI mode */ | ||
207 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
208 | case SND_SOC_DAIFMT_I2S: | ||
209 | val |= AUDIO_CFS(2); | ||
210 | break; | ||
211 | case SND_SOC_DAIFMT_DSP_A: | ||
212 | val |= AUDIO_CFS(1); | ||
213 | break; | ||
214 | default: | ||
215 | return -EINVAL; | ||
216 | } | ||
217 | |||
218 | /* DAI clock inversion */ | ||
219 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
220 | case SND_SOC_DAIFMT_NB_NF: | ||
221 | val |= AUDIO_BCL_INV; | ||
222 | break; | ||
223 | case SND_SOC_DAIFMT_NB_IF: | ||
224 | val |= AUDIO_BCL_INV | AUDIO_CFS_INV; | ||
225 | break; | ||
226 | case SND_SOC_DAIFMT_IB_NF: | ||
227 | break; | ||
228 | case SND_SOC_DAIFMT_IB_IF: | ||
229 | val |= AUDIO_CFS_INV; | ||
230 | break; | ||
231 | } | ||
232 | |||
233 | /* DAI clock master masks */ | ||
234 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
235 | case SND_SOC_DAIFMT_CBM_CFM: | ||
236 | val |= AUDIO_C_CLK_EN; | ||
237 | break; | ||
238 | case SND_SOC_DAIFMT_CBS_CFS: | ||
239 | val |= AUDIO_CSM; | ||
240 | break; | ||
241 | case SND_SOC_DAIFMT_CBM_CFS: | ||
242 | case SND_SOC_DAIFMT_CBS_CFM: | ||
243 | return -EINVAL; | ||
244 | } | ||
245 | |||
246 | val |= AUDIO_C_RESET; | ||
247 | |||
248 | snd_soc_update_bits(codec, reg, mask, val); | ||
249 | |||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static int mc13783_set_fmt_async(struct snd_soc_dai *dai, unsigned int fmt) | ||
254 | { | ||
255 | if (dai->id == MC13783_ID_STEREO_DAC) | ||
256 | return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC); | ||
257 | else | ||
258 | return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC); | ||
259 | } | ||
260 | |||
261 | static int mc13783_set_fmt_sync(struct snd_soc_dai *dai, unsigned int fmt) | ||
262 | { | ||
263 | int ret; | ||
264 | |||
265 | ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC); | ||
266 | if (ret) | ||
267 | return ret; | ||
268 | |||
269 | /* | ||
270 | * In synchronous mode force the voice codec into slave mode | ||
271 | * so that the clock / framesync from the stereo DAC is used | ||
272 | */ | ||
273 | fmt &= ~SND_SOC_DAIFMT_MASTER_MASK; | ||
274 | fmt |= SND_SOC_DAIFMT_CBS_CFS; | ||
275 | ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC); | ||
276 | |||
277 | return ret; | ||
278 | } | ||
279 | |||
280 | static int mc13783_sysclk[] = { | ||
281 | 13000000, | ||
282 | 15360000, | ||
283 | 16800000, | ||
284 | -1, | ||
285 | 26000000, | ||
286 | -1, /* 12000000, invalid for voice codec */ | ||
287 | -1, /* 3686400, invalid for voice codec */ | ||
288 | 33600000, | ||
289 | }; | ||
290 | |||
291 | static int mc13783_set_sysclk(struct snd_soc_dai *dai, | ||
292 | int clk_id, unsigned int freq, int dir, | ||
293 | unsigned int reg) | ||
294 | { | ||
295 | struct snd_soc_codec *codec = dai->codec; | ||
296 | int clk; | ||
297 | unsigned int val = 0; | ||
298 | unsigned int mask = AUDIO_CLK(0x7) | AUDIO_CLK_SEL; | ||
299 | |||
300 | for (clk = 0; clk < ARRAY_SIZE(mc13783_sysclk); clk++) { | ||
301 | if (mc13783_sysclk[clk] < 0) | ||
302 | continue; | ||
303 | if (mc13783_sysclk[clk] == freq) | ||
304 | break; | ||
305 | } | ||
306 | |||
307 | if (clk == ARRAY_SIZE(mc13783_sysclk)) | ||
308 | return -EINVAL; | ||
309 | |||
310 | if (clk_id == MC13783_CLK_CLIB) | ||
311 | val |= AUDIO_CLK_SEL; | ||
312 | |||
313 | val |= AUDIO_CLK(clk); | ||
314 | |||
315 | snd_soc_update_bits(codec, reg, mask, val); | ||
316 | |||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | static int mc13783_set_sysclk_dac(struct snd_soc_dai *dai, | ||
321 | int clk_id, unsigned int freq, int dir) | ||
322 | { | ||
323 | return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC); | ||
324 | } | ||
325 | |||
326 | static int mc13783_set_sysclk_codec(struct snd_soc_dai *dai, | ||
327 | int clk_id, unsigned int freq, int dir) | ||
328 | { | ||
329 | return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC); | ||
330 | } | ||
331 | |||
332 | static int mc13783_set_sysclk_sync(struct snd_soc_dai *dai, | ||
333 | int clk_id, unsigned int freq, int dir) | ||
334 | { | ||
335 | int ret; | ||
336 | |||
337 | ret = mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC); | ||
338 | if (ret) | ||
339 | return ret; | ||
340 | |||
341 | return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC); | ||
342 | } | ||
343 | |||
344 | static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai, | ||
345 | unsigned int tx_mask, unsigned int rx_mask, int slots, | ||
346 | int slot_width) | ||
347 | { | ||
348 | struct snd_soc_codec *codec = dai->codec; | ||
349 | unsigned int val = 0; | ||
350 | unsigned int mask = SSI_NETWORK_DAC_SLOT_MASK | | ||
351 | SSI_NETWORK_DAC_RXSLOT_MASK; | ||
352 | |||
353 | switch (slots) { | ||
354 | case 2: | ||
355 | val |= SSI_NETWORK_DAC_SLOTS_2; | ||
356 | break; | ||
357 | case 4: | ||
358 | val |= SSI_NETWORK_DAC_SLOTS_4; | ||
359 | break; | ||
360 | case 8: | ||
361 | val |= SSI_NETWORK_DAC_SLOTS_8; | ||
362 | break; | ||
363 | default: | ||
364 | return -EINVAL; | ||
365 | } | ||
366 | |||
367 | switch (rx_mask) { | ||
368 | case 0xfffffffc: | ||
369 | val |= SSI_NETWORK_DAC_RXSLOT_0_1; | ||
370 | break; | ||
371 | case 0xfffffff3: | ||
372 | val |= SSI_NETWORK_DAC_RXSLOT_2_3; | ||
373 | break; | ||
374 | case 0xffffffcf: | ||
375 | val |= SSI_NETWORK_DAC_RXSLOT_4_5; | ||
376 | break; | ||
377 | case 0xffffff3f: | ||
378 | val |= SSI_NETWORK_DAC_RXSLOT_6_7; | ||
379 | break; | ||
380 | default: | ||
381 | return -EINVAL; | ||
382 | }; | ||
383 | |||
384 | snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val); | ||
385 | |||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai, | ||
390 | unsigned int tx_mask, unsigned int rx_mask, int slots, | ||
391 | int slot_width) | ||
392 | { | ||
393 | struct snd_soc_codec *codec = dai->codec; | ||
394 | unsigned int val = 0; | ||
395 | unsigned int mask = 0x3f; | ||
396 | |||
397 | if (slots != 4) | ||
398 | return -EINVAL; | ||
399 | |||
400 | if (tx_mask != 0xfffffffc) | ||
401 | return -EINVAL; | ||
402 | |||
403 | val |= (0x00 << 2); /* primary timeslot RX/TX(?) is 0 */ | ||
404 | val |= (0x01 << 4); /* secondary timeslot TX is 1 */ | ||
405 | |||
406 | snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val); | ||
407 | |||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | static int mc13783_set_tdm_slot_sync(struct snd_soc_dai *dai, | ||
412 | unsigned int tx_mask, unsigned int rx_mask, int slots, | ||
413 | int slot_width) | ||
414 | { | ||
415 | int ret; | ||
416 | |||
417 | ret = mc13783_set_tdm_slot_dac(dai, tx_mask, rx_mask, slots, | ||
418 | slot_width); | ||
419 | if (ret) | ||
420 | return ret; | ||
421 | |||
422 | ret = mc13783_set_tdm_slot_codec(dai, tx_mask, rx_mask, slots, | ||
423 | slot_width); | ||
424 | |||
425 | return ret; | ||
426 | } | ||
427 | |||
428 | static const struct snd_kcontrol_new mc1l_amp_ctl = | ||
429 | SOC_DAPM_SINGLE("Switch", 38, 7, 1, 0); | ||
430 | |||
431 | static const struct snd_kcontrol_new mc1r_amp_ctl = | ||
432 | SOC_DAPM_SINGLE("Switch", 38, 5, 1, 0); | ||
433 | |||
434 | static const struct snd_kcontrol_new mc2_amp_ctl = | ||
435 | SOC_DAPM_SINGLE("Switch", 38, 9, 1, 0); | ||
436 | |||
437 | static const struct snd_kcontrol_new atx_amp_ctl = | ||
438 | SOC_DAPM_SINGLE("Switch", 38, 11, 1, 0); | ||
439 | |||
440 | |||
441 | /* Virtual mux. The chip does the input selection automatically | ||
442 | * as soon as we enable one input. */ | ||
443 | static const char * const adcl_enum_text[] = { | ||
444 | "MC1L", "RXINL", | ||
445 | }; | ||
446 | |||
447 | static const struct soc_enum adcl_enum = | ||
448 | SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adcl_enum_text), adcl_enum_text); | ||
449 | |||
450 | static const struct snd_kcontrol_new left_input_mux = | ||
451 | SOC_DAPM_ENUM_VIRT("Route", adcl_enum); | ||
452 | |||
453 | static const char * const adcr_enum_text[] = { | ||
454 | "MC1R", "MC2", "RXINR", "TXIN", | ||
455 | }; | ||
456 | |||
457 | static const struct soc_enum adcr_enum = | ||
458 | SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adcr_enum_text), adcr_enum_text); | ||
459 | |||
460 | static const struct snd_kcontrol_new right_input_mux = | ||
461 | SOC_DAPM_ENUM_VIRT("Route", adcr_enum); | ||
462 | |||
463 | static const struct snd_kcontrol_new samp_ctl = | ||
464 | SOC_DAPM_SINGLE("Switch", 36, 3, 1, 0); | ||
465 | |||
466 | static const struct snd_kcontrol_new lamp_ctl = | ||
467 | SOC_DAPM_SINGLE("Switch", 36, 5, 1, 0); | ||
468 | |||
469 | static const struct snd_kcontrol_new hlamp_ctl = | ||
470 | SOC_DAPM_SINGLE("Switch", 36, 10, 1, 0); | ||
471 | |||
472 | static const struct snd_kcontrol_new hramp_ctl = | ||
473 | SOC_DAPM_SINGLE("Switch", 36, 9, 1, 0); | ||
474 | |||
475 | static const struct snd_kcontrol_new llamp_ctl = | ||
476 | SOC_DAPM_SINGLE("Switch", 36, 16, 1, 0); | ||
477 | |||
478 | static const struct snd_kcontrol_new lramp_ctl = | ||
479 | SOC_DAPM_SINGLE("Switch", 36, 15, 1, 0); | ||
480 | |||
481 | static const struct snd_soc_dapm_widget mc13783_dapm_widgets[] = { | ||
482 | /* Input */ | ||
483 | SND_SOC_DAPM_INPUT("MC1LIN"), | ||
484 | SND_SOC_DAPM_INPUT("MC1RIN"), | ||
485 | SND_SOC_DAPM_INPUT("MC2IN"), | ||
486 | SND_SOC_DAPM_INPUT("RXINR"), | ||
487 | SND_SOC_DAPM_INPUT("RXINL"), | ||
488 | SND_SOC_DAPM_INPUT("TXIN"), | ||
489 | |||
490 | SND_SOC_DAPM_SUPPLY("MC1 Bias", 38, 0, 0, NULL, 0), | ||
491 | SND_SOC_DAPM_SUPPLY("MC2 Bias", 38, 1, 0, NULL, 0), | ||
492 | |||
493 | SND_SOC_DAPM_SWITCH("MC1L Amp", 38, 7, 0, &mc1l_amp_ctl), | ||
494 | SND_SOC_DAPM_SWITCH("MC1R Amp", 38, 5, 0, &mc1r_amp_ctl), | ||
495 | SND_SOC_DAPM_SWITCH("MC2 Amp", 38, 9, 0, &mc2_amp_ctl), | ||
496 | SND_SOC_DAPM_SWITCH("TXIN Amp", 38, 11, 0, &atx_amp_ctl), | ||
497 | |||
498 | SND_SOC_DAPM_VIRT_MUX("PGA Left Input Mux", SND_SOC_NOPM, 0, 0, | ||
499 | &left_input_mux), | ||
500 | SND_SOC_DAPM_VIRT_MUX("PGA Right Input Mux", SND_SOC_NOPM, 0, 0, | ||
501 | &right_input_mux), | ||
502 | |||
503 | SND_SOC_DAPM_PGA("PGA Left Input", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
504 | SND_SOC_DAPM_PGA("PGA Right Input", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
505 | |||
506 | SND_SOC_DAPM_ADC("ADC", "Capture", 40, 11, 0), | ||
507 | SND_SOC_DAPM_SUPPLY("ADC_Reset", 40, 15, 0, NULL, 0), | ||
508 | |||
509 | /* Output */ | ||
510 | SND_SOC_DAPM_SUPPLY("DAC_E", 41, 11, 0, NULL, 0), | ||
511 | SND_SOC_DAPM_SUPPLY("DAC_Reset", 41, 15, 0, NULL, 0), | ||
512 | SND_SOC_DAPM_OUTPUT("RXOUTL"), | ||
513 | SND_SOC_DAPM_OUTPUT("RXOUTR"), | ||
514 | SND_SOC_DAPM_OUTPUT("HSL"), | ||
515 | SND_SOC_DAPM_OUTPUT("HSR"), | ||
516 | SND_SOC_DAPM_OUTPUT("LSP"), | ||
517 | SND_SOC_DAPM_OUTPUT("SP"), | ||
518 | |||
519 | SND_SOC_DAPM_SWITCH("Speaker Amp", 36, 3, 0, &samp_ctl), | ||
520 | SND_SOC_DAPM_SWITCH("Loudspeaker Amp", SND_SOC_NOPM, 0, 0, &lamp_ctl), | ||
521 | SND_SOC_DAPM_SWITCH("Headset Amp Left", 36, 10, 0, &hlamp_ctl), | ||
522 | SND_SOC_DAPM_SWITCH("Headset Amp Right", 36, 9, 0, &hramp_ctl), | ||
523 | SND_SOC_DAPM_SWITCH("Line out Amp Left", 36, 16, 0, &llamp_ctl), | ||
524 | SND_SOC_DAPM_SWITCH("Line out Amp Right", 36, 15, 0, &lramp_ctl), | ||
525 | SND_SOC_DAPM_DAC("DAC", "Playback", 36, 22, 0), | ||
526 | SND_SOC_DAPM_PGA("DAC PGA", 37, 5, 0, NULL, 0), | ||
527 | }; | ||
528 | |||
529 | static struct snd_soc_dapm_route mc13783_routes[] = { | ||
530 | /* Input */ | ||
531 | { "MC1L Amp", NULL, "MC1LIN"}, | ||
532 | { "MC1R Amp", NULL, "MC1RIN" }, | ||
533 | { "MC2 Amp", NULL, "MC2IN" }, | ||
534 | { "TXIN Amp", NULL, "TXIN"}, | ||
535 | |||
536 | { "PGA Left Input Mux", "MC1L", "MC1L Amp" }, | ||
537 | { "PGA Left Input Mux", "RXINL", "RXINL"}, | ||
538 | { "PGA Right Input Mux", "MC1R", "MC1R Amp" }, | ||
539 | { "PGA Right Input Mux", "MC2", "MC2 Amp"}, | ||
540 | { "PGA Right Input Mux", "TXIN", "TXIN Amp"}, | ||
541 | { "PGA Right Input Mux", "RXINR", "RXINR"}, | ||
542 | |||
543 | { "PGA Left Input", NULL, "PGA Left Input Mux"}, | ||
544 | { "PGA Right Input", NULL, "PGA Right Input Mux"}, | ||
545 | |||
546 | { "ADC", NULL, "PGA Left Input"}, | ||
547 | { "ADC", NULL, "PGA Right Input"}, | ||
548 | { "ADC", NULL, "ADC_Reset"}, | ||
549 | |||
550 | /* Output */ | ||
551 | { "HSL", NULL, "Headset Amp Left" }, | ||
552 | { "HSR", NULL, "Headset Amp Right"}, | ||
553 | { "RXOUTL", NULL, "Line out Amp Left"}, | ||
554 | { "RXOUTR", NULL, "Line out Amp Right"}, | ||
555 | { "SP", NULL, "Speaker Amp"}, | ||
556 | { "Speaker Amp", NULL, "DAC PGA"}, | ||
557 | { "LSP", NULL, "DAC PGA"}, | ||
558 | { "Headset Amp Left", NULL, "DAC PGA"}, | ||
559 | { "Headset Amp Right", NULL, "DAC PGA"}, | ||
560 | { "Line out Amp Left", NULL, "DAC PGA"}, | ||
561 | { "Line out Amp Right", NULL, "DAC PGA"}, | ||
562 | { "DAC PGA", NULL, "DAC"}, | ||
563 | { "DAC", NULL, "DAC_E"}, | ||
564 | }; | ||
565 | |||
566 | static const char * const mc13783_3d_mixer[] = {"Stereo", "Phase Mix", | ||
567 | "Mono", "Mono Mix"}; | ||
568 | |||
569 | static const struct soc_enum mc13783_enum_3d_mixer = | ||
570 | SOC_ENUM_SINGLE(MC13783_AUDIO_RX1, 16, ARRAY_SIZE(mc13783_3d_mixer), | ||
571 | mc13783_3d_mixer); | ||
572 | |||
573 | static struct snd_kcontrol_new mc13783_control_list[] = { | ||
574 | SOC_SINGLE("Loudspeaker enable", MC13783_AUDIO_RX0, 5, 1, 0), | ||
575 | SOC_SINGLE("PCM Playback Volume", MC13783_AUDIO_RX1, 6, 15, 0), | ||
576 | SOC_DOUBLE("PCM Capture Volume", MC13783_AUDIO_TX, 19, 14, 31, 0), | ||
577 | SOC_ENUM("3D Control", mc13783_enum_3d_mixer), | ||
578 | }; | ||
579 | |||
580 | static int mc13783_probe(struct snd_soc_codec *codec) | ||
581 | { | ||
582 | struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
583 | struct snd_soc_dapm_context *dapm = &codec->dapm; | ||
584 | |||
585 | mc13xxx_lock(priv->mc13xxx); | ||
586 | |||
587 | /* these are the reset values */ | ||
588 | mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX0, 0x25893); | ||
589 | mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX1, 0x00d35A); | ||
590 | mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_TX, 0x420000); | ||
591 | mc13xxx_reg_write(priv->mc13xxx, MC13783_SSI_NETWORK, 0x013060); | ||
592 | mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_CODEC, 0x180027); | ||
593 | mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_DAC, 0x0e0004); | ||
594 | |||
595 | snd_soc_add_codec_controls(codec, mc13783_control_list, | ||
596 | ARRAY_SIZE(mc13783_control_list)); | ||
597 | |||
598 | snd_soc_dapm_new_controls(dapm, mc13783_dapm_widgets, | ||
599 | ARRAY_SIZE(mc13783_dapm_widgets)); | ||
600 | snd_soc_dapm_add_routes(dapm, mc13783_routes, | ||
601 | ARRAY_SIZE(mc13783_routes)); | ||
602 | |||
603 | if (priv->adc_ssi_port == MC13783_SSI1_PORT) | ||
604 | mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC, | ||
605 | AUDIO_SSI_SEL, 0); | ||
606 | else | ||
607 | mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC, | ||
608 | 0, AUDIO_SSI_SEL); | ||
609 | |||
610 | if (priv->dac_ssi_port == MC13783_SSI1_PORT) | ||
611 | mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC, | ||
612 | AUDIO_SSI_SEL, 0); | ||
613 | else | ||
614 | mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC, | ||
615 | 0, AUDIO_SSI_SEL); | ||
616 | |||
617 | mc13xxx_unlock(priv->mc13xxx); | ||
618 | |||
619 | return 0; | ||
620 | } | ||
621 | |||
622 | static int mc13783_remove(struct snd_soc_codec *codec) | ||
623 | { | ||
624 | struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
625 | |||
626 | mc13xxx_lock(priv->mc13xxx); | ||
627 | |||
628 | /* Make sure VAUDIOON is off */ | ||
629 | mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_RX0, 0x3, 0); | ||
630 | |||
631 | mc13xxx_unlock(priv->mc13xxx); | ||
632 | |||
633 | return 0; | ||
634 | } | ||
635 | |||
636 | #define MC13783_RATES_RECORD (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000) | ||
637 | |||
638 | #define MC13783_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ | ||
639 | SNDRV_PCM_FMTBIT_S24_LE) | ||
640 | |||
641 | static struct snd_soc_dai_ops mc13783_ops_dac = { | ||
642 | .hw_params = mc13783_pcm_hw_params_dac, | ||
643 | .set_fmt = mc13783_set_fmt_async, | ||
644 | .set_sysclk = mc13783_set_sysclk_dac, | ||
645 | .set_tdm_slot = mc13783_set_tdm_slot_dac, | ||
646 | }; | ||
647 | |||
648 | static struct snd_soc_dai_ops mc13783_ops_codec = { | ||
649 | .hw_params = mc13783_pcm_hw_params_codec, | ||
650 | .set_fmt = mc13783_set_fmt_async, | ||
651 | .set_sysclk = mc13783_set_sysclk_codec, | ||
652 | .set_tdm_slot = mc13783_set_tdm_slot_codec, | ||
653 | }; | ||
654 | |||
655 | /* | ||
656 | * The mc13783 has two SSI ports, both of them can be routed either | ||
657 | * to the voice codec or the stereo DAC. When two different SSI ports | ||
658 | * are used for the voice codec and the stereo DAC we can do different | ||
659 | * formats and sysclock settings for playback and capture | ||
660 | * (mc13783-hifi-playback and mc13783-hifi-capture). Using the same port | ||
661 | * forces us to use symmetric rates (mc13783-hifi). | ||
662 | */ | ||
663 | static struct snd_soc_dai_driver mc13783_dai_async[] = { | ||
664 | { | ||
665 | .name = "mc13783-hifi-playback", | ||
666 | .id = MC13783_ID_STEREO_DAC, | ||
667 | .playback = { | ||
668 | .stream_name = "Playback", | ||
669 | .channels_min = 1, | ||
670 | .channels_max = 2, | ||
671 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
672 | .formats = MC13783_FORMATS, | ||
673 | }, | ||
674 | .ops = &mc13783_ops_dac, | ||
675 | }, { | ||
676 | .name = "mc13783-hifi-capture", | ||
677 | .id = MC13783_ID_STEREO_CODEC, | ||
678 | .capture = { | ||
679 | .stream_name = "Capture", | ||
680 | .channels_min = 1, | ||
681 | .channels_max = 2, | ||
682 | .rates = MC13783_RATES_RECORD, | ||
683 | .formats = MC13783_FORMATS, | ||
684 | }, | ||
685 | .ops = &mc13783_ops_codec, | ||
686 | }, | ||
687 | }; | ||
688 | |||
689 | static struct snd_soc_dai_ops mc13783_ops_sync = { | ||
690 | .hw_params = mc13783_pcm_hw_params_sync, | ||
691 | .set_fmt = mc13783_set_fmt_sync, | ||
692 | .set_sysclk = mc13783_set_sysclk_sync, | ||
693 | .set_tdm_slot = mc13783_set_tdm_slot_sync, | ||
694 | }; | ||
695 | |||
696 | static struct snd_soc_dai_driver mc13783_dai_sync[] = { | ||
697 | { | ||
698 | .name = "mc13783-hifi", | ||
699 | .id = MC13783_ID_SYNC, | ||
700 | .playback = { | ||
701 | .stream_name = "Playback", | ||
702 | .channels_min = 1, | ||
703 | .channels_max = 2, | ||
704 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
705 | .formats = MC13783_FORMATS, | ||
706 | }, | ||
707 | .capture = { | ||
708 | .stream_name = "Capture", | ||
709 | .channels_min = 1, | ||
710 | .channels_max = 2, | ||
711 | .rates = MC13783_RATES_RECORD, | ||
712 | .formats = MC13783_FORMATS, | ||
713 | }, | ||
714 | .ops = &mc13783_ops_sync, | ||
715 | .symmetric_rates = 1, | ||
716 | } | ||
717 | }; | ||
718 | |||
719 | static struct snd_soc_codec_driver soc_codec_dev_mc13783 = { | ||
720 | .probe = mc13783_probe, | ||
721 | .remove = mc13783_remove, | ||
722 | .read = mc13783_read, | ||
723 | .write = mc13783_write, | ||
724 | }; | ||
725 | |||
726 | static int mc13783_codec_probe(struct platform_device *pdev) | ||
727 | { | ||
728 | struct mc13xxx *mc13xxx; | ||
729 | struct mc13783_priv *priv; | ||
730 | struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data; | ||
731 | int ret; | ||
732 | |||
733 | mc13xxx = dev_get_drvdata(pdev->dev.parent); | ||
734 | |||
735 | |||
736 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | ||
737 | if (priv == NULL) | ||
738 | return -ENOMEM; | ||
739 | |||
740 | dev_set_drvdata(&pdev->dev, priv); | ||
741 | priv->mc13xxx = mc13xxx; | ||
742 | if (pdata) { | ||
743 | priv->adc_ssi_port = pdata->adc_ssi_port; | ||
744 | priv->dac_ssi_port = pdata->dac_ssi_port; | ||
745 | } else { | ||
746 | priv->adc_ssi_port = MC13783_SSI1_PORT; | ||
747 | priv->dac_ssi_port = MC13783_SSI2_PORT; | ||
748 | } | ||
749 | |||
750 | if (priv->adc_ssi_port == priv->dac_ssi_port) | ||
751 | ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783, | ||
752 | mc13783_dai_sync, ARRAY_SIZE(mc13783_dai_sync)); | ||
753 | else | ||
754 | ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783, | ||
755 | mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async)); | ||
756 | |||
757 | if (ret) | ||
758 | goto err_register_codec; | ||
759 | |||
760 | return 0; | ||
761 | |||
762 | err_register_codec: | ||
763 | dev_err(&pdev->dev, "register codec failed with %d\n", ret); | ||
764 | |||
765 | return ret; | ||
766 | } | ||
767 | |||
768 | static int mc13783_codec_remove(struct platform_device *pdev) | ||
769 | { | ||
770 | snd_soc_unregister_codec(&pdev->dev); | ||
771 | |||
772 | return 0; | ||
773 | } | ||
774 | |||
775 | static struct platform_driver mc13783_codec_driver = { | ||
776 | .driver = { | ||
777 | .name = "mc13783-codec", | ||
778 | .owner = THIS_MODULE, | ||
779 | }, | ||
780 | .probe = mc13783_codec_probe, | ||
781 | .remove = __devexit_p(mc13783_codec_remove), | ||
782 | }; | ||
783 | |||
784 | static __init int mc13783_init(void) | ||
785 | { | ||
786 | return platform_driver_register(&mc13783_codec_driver); | ||
787 | } | ||
788 | |||
789 | static __exit void mc13783_exit(void) | ||
790 | { | ||
791 | platform_driver_unregister(&mc13783_codec_driver); | ||
792 | } | ||
793 | |||
794 | module_init(mc13783_init); | ||
795 | module_exit(mc13783_exit); | ||
796 | |||
797 | MODULE_DESCRIPTION("ASoC MC13783 driver"); | ||
798 | MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>"); | ||
799 | MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>"); | ||
800 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/mc13783.h b/sound/soc/codecs/mc13783.h new file mode 100644 index 000000000000..3a6d1993a217 --- /dev/null +++ b/sound/soc/codecs/mc13783.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Copyright 2008 Juergen Beisert, kernel@pengutronix.de | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version 2 | ||
7 | * of the License, or (at your option) any later version. | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program; if not, write to the Free Software Foundation, Inc. | ||
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
16 | */ | ||
17 | |||
18 | #ifndef MC13783_MIXER_H | ||
19 | #define MC13783_MIXER_H | ||
20 | |||
21 | #define MC13783_CLK_CLIA 1 | ||
22 | #define MC13783_CLK_CLIB 2 | ||
23 | |||
24 | #define MC13783_ID_STEREO_DAC 1 | ||
25 | #define MC13783_ID_STEREO_CODEC 2 | ||
26 | #define MC13783_ID_SYNC 3 | ||
27 | |||
28 | #endif /* MC13783_MIXER_H */ | ||