diff options
Diffstat (limited to 'sound/soc')
93 files changed, 16078 insertions, 1840 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index f743530add8f..4dfda6674bec 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig | |||
@@ -5,6 +5,7 @@ | |||
5 | menuconfig SND_SOC | 5 | menuconfig SND_SOC |
6 | tristate "ALSA for SoC audio support" | 6 | tristate "ALSA for SoC audio support" |
7 | select SND_PCM | 7 | select SND_PCM |
8 | select AC97_BUS if SND_SOC_AC97_BUS | ||
8 | ---help--- | 9 | ---help--- |
9 | 10 | ||
10 | If you want ASoC support, you should say Y here and also to the | 11 | If you want ASoC support, you should say Y here and also to the |
@@ -31,6 +32,7 @@ source "sound/soc/sh/Kconfig" | |||
31 | source "sound/soc/fsl/Kconfig" | 32 | source "sound/soc/fsl/Kconfig" |
32 | source "sound/soc/davinci/Kconfig" | 33 | source "sound/soc/davinci/Kconfig" |
33 | source "sound/soc/omap/Kconfig" | 34 | source "sound/soc/omap/Kconfig" |
35 | source "sound/soc/blackfin/Kconfig" | ||
34 | 36 | ||
35 | # Supported codecs | 37 | # Supported codecs |
36 | source "sound/soc/codecs/Kconfig" | 38 | source "sound/soc/codecs/Kconfig" |
diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 933a66d30804..d849349f2c66 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile | |||
@@ -2,4 +2,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o | |||
2 | 2 | ||
3 | obj-$(CONFIG_SND_SOC) += snd-soc-core.o | 3 | obj-$(CONFIG_SND_SOC) += snd-soc-core.o |
4 | obj-$(CONFIG_SND_SOC) += codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ | 4 | obj-$(CONFIG_SND_SOC) += codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ |
5 | obj-$(CONFIG_SND_SOC) += omap/ au1x/ | 5 | obj-$(CONFIG_SND_SOC) += omap/ au1x/ blackfin/ |
diff --git a/sound/soc/at32/at32-pcm.c b/sound/soc/at32/at32-pcm.c index 435f1daf177c..c83584f989a9 100644 --- a/sound/soc/at32/at32-pcm.c +++ b/sound/soc/at32/at32-pcm.c | |||
@@ -434,7 +434,8 @@ static int at32_pcm_suspend(struct platform_device *pdev, | |||
434 | params = prtd->params; | 434 | params = prtd->params; |
435 | 435 | ||
436 | /* Disable the PDC and save the PDC registers */ | 436 | /* Disable the PDC and save the PDC registers */ |
437 | ssc_writex(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable); | 437 | ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, |
438 | params->mask->pdc_disable); | ||
438 | 439 | ||
439 | prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr); | 440 | prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr); |
440 | prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr); | 441 | prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr); |
@@ -464,7 +465,7 @@ static int at32_pcm_resume(struct platform_device *pdev, | |||
464 | ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save); | 465 | ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save); |
465 | ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save); | 466 | ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save); |
466 | 467 | ||
467 | ssc_writex(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable); | 468 | ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_enable); |
468 | return 0; | 469 | return 0; |
469 | } | 470 | } |
470 | #else /* CONFIG_PM */ | 471 | #else /* CONFIG_PM */ |
diff --git a/sound/soc/at32/playpaq_wm8510.c b/sound/soc/at32/playpaq_wm8510.c index 3f326219f1ec..98a2d5826a85 100644 --- a/sound/soc/at32/playpaq_wm8510.c +++ b/sound/soc/at32/playpaq_wm8510.c | |||
@@ -377,6 +377,7 @@ static struct snd_soc_machine snd_soc_machine_playpaq = { | |||
377 | 377 | ||
378 | 378 | ||
379 | static struct wm8510_setup_data playpaq_wm8510_setup = { | 379 | static struct wm8510_setup_data playpaq_wm8510_setup = { |
380 | .i2c_bus = 0, | ||
380 | .i2c_address = 0x1a, | 381 | .i2c_address = 0x1a, |
381 | }; | 382 | }; |
382 | 383 | ||
@@ -405,7 +406,6 @@ static int __init playpaq_asoc_init(void) | |||
405 | ssc = ssc_request(0); | 406 | ssc = ssc_request(0); |
406 | if (IS_ERR(ssc)) { | 407 | if (IS_ERR(ssc)) { |
407 | ret = PTR_ERR(ssc); | 408 | ret = PTR_ERR(ssc); |
408 | ssc = NULL; | ||
409 | goto err_ssc; | 409 | goto err_ssc; |
410 | } | 410 | } |
411 | ssc_p->ssc = ssc; | 411 | ssc_p->ssc = ssc; |
@@ -476,10 +476,7 @@ err_pll0: | |||
476 | _gclk0 = NULL; | 476 | _gclk0 = NULL; |
477 | } | 477 | } |
478 | err_gclk0: | 478 | err_gclk0: |
479 | if (ssc != NULL) { | 479 | ssc_free(ssc); |
480 | ssc_free(ssc); | ||
481 | ssc = NULL; | ||
482 | } | ||
483 | err_ssc: | 480 | err_ssc: |
484 | return ret; | 481 | return ret; |
485 | } | 482 | } |
diff --git a/sound/soc/at91/Kconfig b/sound/soc/at91/Kconfig index 905186502e00..85a883299c2e 100644 --- a/sound/soc/at91/Kconfig +++ b/sound/soc/at91/Kconfig | |||
@@ -8,20 +8,3 @@ config SND_AT91_SOC | |||
8 | 8 | ||
9 | config SND_AT91_SOC_SSC | 9 | config SND_AT91_SOC_SSC |
10 | tristate | 10 | tristate |
11 | |||
12 | config SND_AT91_SOC_ETI_B1_WM8731 | ||
13 | tristate "SoC Audio support for WM8731-based Endrelia ETI-B1 boards" | ||
14 | depends on SND_AT91_SOC && (MACH_ETI_B1 || MACH_ETI_C1) | ||
15 | select SND_AT91_SOC_SSC | ||
16 | select SND_SOC_WM8731 | ||
17 | help | ||
18 | Say Y if you want to add support for SoC audio on WM8731-based | ||
19 | Endrelia Technologies Inc ETI-B1 or ETI-C1 boards. | ||
20 | |||
21 | config SND_AT91_SOC_ETI_SLAVE | ||
22 | bool "Run codec in slave Mode on Endrelia boards" | ||
23 | depends on SND_AT91_SOC_ETI_B1_WM8731 | ||
24 | default n | ||
25 | help | ||
26 | Say Y if you want to run with the AT91 SSC generating the BCLK | ||
27 | and LRC signals on Endrelia boards. | ||
diff --git a/sound/soc/at91/Makefile b/sound/soc/at91/Makefile index f23da17cc328..b817f11df286 100644 --- a/sound/soc/at91/Makefile +++ b/sound/soc/at91/Makefile | |||
@@ -4,8 +4,3 @@ snd-soc-at91-ssc-objs := at91-ssc.o | |||
4 | 4 | ||
5 | obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o | 5 | obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o |
6 | obj-$(CONFIG_SND_AT91_SOC_SSC) += snd-soc-at91-ssc.o | 6 | obj-$(CONFIG_SND_AT91_SOC_SSC) += snd-soc-at91-ssc.o |
7 | |||
8 | # AT91 Machine Support | ||
9 | snd-soc-eti-b1-wm8731-objs := eti_b1_wm8731.o | ||
10 | |||
11 | obj-$(CONFIG_SND_AT91_SOC_ETI_B1_WM8731) += snd-soc-eti-b1-wm8731.o | ||
diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c index 5d44515e62e0..1b61cc461261 100644 --- a/sound/soc/at91/at91-ssc.c +++ b/sound/soc/at91/at91-ssc.c | |||
@@ -5,7 +5,7 @@ | |||
5 | * Endrelia Technologies Inc. | 5 | * Endrelia Technologies Inc. |
6 | * | 6 | * |
7 | * Based on pxa2xx Platform drivers by | 7 | * Based on pxa2xx Platform drivers by |
8 | * Liam Girdwood <liam.girdwood@wolfsonmicro.com> | 8 | * Liam Girdwood <lrg@slimlogic.co.uk> |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify it | 10 | * This program is free software; you can redistribute it and/or modify it |
11 | * under the terms of the GNU General Public License as published by the | 11 | * under the terms of the GNU General Public License as published by the |
@@ -408,7 +408,7 @@ static int at91_ssc_hw_params(struct snd_pcm_substream *substream, | |||
408 | dma_params->pdc_xfer_size = 4; | 408 | dma_params->pdc_xfer_size = 4; |
409 | break; | 409 | break; |
410 | default: | 410 | default: |
411 | printk(KERN_WARNING "at91-ssc: unsupported PCM format"); | 411 | printk(KERN_WARNING "at91-ssc: unsupported PCM format\n"); |
412 | return -EINVAL; | 412 | return -EINVAL; |
413 | } | 413 | } |
414 | 414 | ||
diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c deleted file mode 100644 index b81d6b2cfa1d..000000000000 --- a/sound/soc/at91/eti_b1_wm8731.c +++ /dev/null | |||
@@ -1,348 +0,0 @@ | |||
1 | /* | ||
2 | * eti_b1_wm8731 -- SoC audio for AT91RM9200-based Endrelia ETI_B1 board. | ||
3 | * | ||
4 | * Author: Frank Mandarino <fmandarino@endrelia.com> | ||
5 | * Endrelia Technologies Inc. | ||
6 | * Created: Mar 29, 2006 | ||
7 | * | ||
8 | * Based on corgi.c by: | ||
9 | * | ||
10 | * Copyright 2005 Wolfson Microelectronics PLC. | ||
11 | * Copyright 2005 Openedhand Ltd. | ||
12 | * | ||
13 | * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> | ||
14 | * Richard Purdie <richard@openedhand.com> | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or modify it | ||
17 | * under the terms of the GNU General Public License as published by the | ||
18 | * Free Software Foundation; either version 2 of the License, or (at your | ||
19 | * option) any later version. | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <linux/module.h> | ||
24 | #include <linux/moduleparam.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/clk.h> | ||
27 | #include <linux/timer.h> | ||
28 | #include <linux/interrupt.h> | ||
29 | #include <linux/platform_device.h> | ||
30 | #include <sound/core.h> | ||
31 | #include <sound/pcm.h> | ||
32 | #include <sound/soc.h> | ||
33 | #include <sound/soc-dapm.h> | ||
34 | |||
35 | #include <mach/hardware.h> | ||
36 | #include <mach/gpio.h> | ||
37 | |||
38 | #include "../codecs/wm8731.h" | ||
39 | #include "at91-pcm.h" | ||
40 | #include "at91-ssc.h" | ||
41 | |||
42 | #if 0 | ||
43 | #define DBG(x...) printk(KERN_INFO "eti_b1_wm8731: " x) | ||
44 | #else | ||
45 | #define DBG(x...) | ||
46 | #endif | ||
47 | |||
48 | static struct clk *pck1_clk; | ||
49 | static struct clk *pllb_clk; | ||
50 | |||
51 | |||
52 | static int eti_b1_startup(struct snd_pcm_substream *substream) | ||
53 | { | ||
54 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
55 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
56 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
57 | int ret; | ||
58 | |||
59 | /* cpu clock is the AT91 master clock sent to the SSC */ | ||
60 | ret = snd_soc_dai_set_sysclk(cpu_dai, AT91_SYSCLK_MCK, | ||
61 | 60000000, SND_SOC_CLOCK_IN); | ||
62 | if (ret < 0) | ||
63 | return ret; | ||
64 | |||
65 | /* codec system clock is supplied by PCK1, set to 12MHz */ | ||
66 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, | ||
67 | 12000000, SND_SOC_CLOCK_IN); | ||
68 | if (ret < 0) | ||
69 | return ret; | ||
70 | |||
71 | /* Start PCK1 clock. */ | ||
72 | clk_enable(pck1_clk); | ||
73 | DBG("pck1 started\n"); | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static void eti_b1_shutdown(struct snd_pcm_substream *substream) | ||
79 | { | ||
80 | /* Stop PCK1 clock. */ | ||
81 | clk_disable(pck1_clk); | ||
82 | DBG("pck1 stopped\n"); | ||
83 | } | ||
84 | |||
85 | static int eti_b1_hw_params(struct snd_pcm_substream *substream, | ||
86 | struct snd_pcm_hw_params *params) | ||
87 | { | ||
88 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
89 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
90 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
91 | int ret; | ||
92 | |||
93 | #ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE | ||
94 | unsigned int rate; | ||
95 | int cmr_div, period; | ||
96 | |||
97 | /* set codec DAI configuration */ | ||
98 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
99 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | ||
100 | if (ret < 0) | ||
101 | return ret; | ||
102 | |||
103 | /* set cpu DAI configuration */ | ||
104 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
105 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | ||
106 | if (ret < 0) | ||
107 | return ret; | ||
108 | |||
109 | /* | ||
110 | * The SSC clock dividers depend on the sample rate. The CMR.DIV | ||
111 | * field divides the system master clock MCK to drive the SSC TK | ||
112 | * signal which provides the codec BCLK. The TCMR.PERIOD and | ||
113 | * RCMR.PERIOD fields further divide the BCLK signal to drive | ||
114 | * the SSC TF and RF signals which provide the codec DACLRC and | ||
115 | * ADCLRC clocks. | ||
116 | * | ||
117 | * The dividers were determined through trial and error, where a | ||
118 | * CMR.DIV value is chosen such that the resulting BCLK value is | ||
119 | * divisible, or almost divisible, by (2 * sample rate), and then | ||
120 | * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1. | ||
121 | */ | ||
122 | rate = params_rate(params); | ||
123 | |||
124 | switch (rate) { | ||
125 | case 8000: | ||
126 | cmr_div = 25; /* BCLK = 60MHz/(2*25) = 1.2MHz */ | ||
127 | period = 74; /* LRC = BCLK/(2*(74+1)) = 8000Hz */ | ||
128 | break; | ||
129 | case 32000: | ||
130 | cmr_div = 7; /* BCLK = 60MHz/(2*7) ~= 4.28571428MHz */ | ||
131 | period = 66; /* LRC = BCLK/(2*(66+1)) = 31982.942Hz */ | ||
132 | break; | ||
133 | case 48000: | ||
134 | cmr_div = 13; /* BCLK = 60MHz/(2*13) ~= 2.3076923MHz */ | ||
135 | period = 23; /* LRC = BCLK/(2*(23+1)) = 48076.923Hz */ | ||
136 | break; | ||
137 | default: | ||
138 | printk(KERN_WARNING "unsupported rate %d on ETI-B1 board\n", rate); | ||
139 | return -EINVAL; | ||
140 | } | ||
141 | |||
142 | /* set the MCK divider for BCLK */ | ||
143 | ret = snd_soc_dai_set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div); | ||
144 | if (ret < 0) | ||
145 | return ret; | ||
146 | |||
147 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
148 | /* set the BCLK divider for DACLRC */ | ||
149 | ret = snd_soc_dai_set_clkdiv(cpu_dai, | ||
150 | AT91SSC_TCMR_PERIOD, period); | ||
151 | } else { | ||
152 | /* set the BCLK divider for ADCLRC */ | ||
153 | ret = snd_soc_dai_set_clkdiv(cpu_dai, | ||
154 | AT91SSC_RCMR_PERIOD, period); | ||
155 | } | ||
156 | if (ret < 0) | ||
157 | return ret; | ||
158 | |||
159 | #else /* CONFIG_SND_AT91_SOC_ETI_SLAVE */ | ||
160 | /* | ||
161 | * Codec in Master Mode. | ||
162 | */ | ||
163 | |||
164 | /* set codec DAI configuration */ | ||
165 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
166 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||
167 | if (ret < 0) | ||
168 | return ret; | ||
169 | |||
170 | /* set cpu DAI configuration */ | ||
171 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
172 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||
173 | if (ret < 0) | ||
174 | return ret; | ||
175 | |||
176 | #endif /* CONFIG_SND_AT91_SOC_ETI_SLAVE */ | ||
177 | |||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static struct snd_soc_ops eti_b1_ops = { | ||
182 | .startup = eti_b1_startup, | ||
183 | .hw_params = eti_b1_hw_params, | ||
184 | .shutdown = eti_b1_shutdown, | ||
185 | }; | ||
186 | |||
187 | |||
188 | static const struct snd_soc_dapm_widget eti_b1_dapm_widgets[] = { | ||
189 | SND_SOC_DAPM_MIC("Int Mic", NULL), | ||
190 | SND_SOC_DAPM_SPK("Ext Spk", NULL), | ||
191 | }; | ||
192 | |||
193 | static const struct snd_soc_dapm_route intercon[] = { | ||
194 | |||
195 | /* speaker connected to LHPOUT */ | ||
196 | {"Ext Spk", NULL, "LHPOUT"}, | ||
197 | |||
198 | /* mic is connected to Mic Jack, with WM8731 Mic Bias */ | ||
199 | {"MICIN", NULL, "Mic Bias"}, | ||
200 | {"Mic Bias", NULL, "Int Mic"}, | ||
201 | }; | ||
202 | |||
203 | /* | ||
204 | * Logic for a wm8731 as connected on a Endrelia ETI-B1 board. | ||
205 | */ | ||
206 | static int eti_b1_wm8731_init(struct snd_soc_codec *codec) | ||
207 | { | ||
208 | DBG("eti_b1_wm8731_init() called\n"); | ||
209 | |||
210 | /* Add specific widgets */ | ||
211 | snd_soc_dapm_new_controls(codec, eti_b1_dapm_widgets, | ||
212 | ARRAY_SIZE(eti_b1_dapm_widgets)); | ||
213 | |||
214 | /* Set up specific audio path interconnects */ | ||
215 | snd_soc_dapm_add_route(codec, intercon, ARRAY_SIZE(intercon)); | ||
216 | |||
217 | /* not connected */ | ||
218 | snd_soc_dapm_disable_pin(codec, "RLINEIN"); | ||
219 | snd_soc_dapm_disable_pin(codec, "LLINEIN"); | ||
220 | |||
221 | /* always connected */ | ||
222 | snd_soc_dapm_enable_pin(codec, "Int Mic"); | ||
223 | snd_soc_dapm_enable_pin(codec, "Ext Spk"); | ||
224 | |||
225 | snd_soc_dapm_sync(codec); | ||
226 | |||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | static struct snd_soc_dai_link eti_b1_dai = { | ||
231 | .name = "WM8731", | ||
232 | .stream_name = "WM8731 PCM", | ||
233 | .cpu_dai = &at91_ssc_dai[1], | ||
234 | .codec_dai = &wm8731_dai, | ||
235 | .init = eti_b1_wm8731_init, | ||
236 | .ops = &eti_b1_ops, | ||
237 | }; | ||
238 | |||
239 | static struct snd_soc_machine snd_soc_machine_eti_b1 = { | ||
240 | .name = "ETI_B1_WM8731", | ||
241 | .dai_link = &eti_b1_dai, | ||
242 | .num_links = 1, | ||
243 | }; | ||
244 | |||
245 | static struct wm8731_setup_data eti_b1_wm8731_setup = { | ||
246 | .i2c_address = 0x1a, | ||
247 | }; | ||
248 | |||
249 | static struct snd_soc_device eti_b1_snd_devdata = { | ||
250 | .machine = &snd_soc_machine_eti_b1, | ||
251 | .platform = &at91_soc_platform, | ||
252 | .codec_dev = &soc_codec_dev_wm8731, | ||
253 | .codec_data = &eti_b1_wm8731_setup, | ||
254 | }; | ||
255 | |||
256 | static struct platform_device *eti_b1_snd_device; | ||
257 | |||
258 | static int __init eti_b1_init(void) | ||
259 | { | ||
260 | int ret; | ||
261 | struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data; | ||
262 | |||
263 | if (!request_mem_region(AT91RM9200_BASE_SSC1, SZ_16K, "soc-audio")) { | ||
264 | DBG("SSC1 memory region is busy\n"); | ||
265 | return -EBUSY; | ||
266 | } | ||
267 | |||
268 | ssc->base = ioremap(AT91RM9200_BASE_SSC1, SZ_16K); | ||
269 | if (!ssc->base) { | ||
270 | DBG("SSC1 memory ioremap failed\n"); | ||
271 | ret = -ENOMEM; | ||
272 | goto fail_release_mem; | ||
273 | } | ||
274 | |||
275 | ssc->pid = AT91RM9200_ID_SSC1; | ||
276 | |||
277 | eti_b1_snd_device = platform_device_alloc("soc-audio", -1); | ||
278 | if (!eti_b1_snd_device) { | ||
279 | DBG("platform device allocation failed\n"); | ||
280 | ret = -ENOMEM; | ||
281 | goto fail_io_unmap; | ||
282 | } | ||
283 | |||
284 | platform_set_drvdata(eti_b1_snd_device, &eti_b1_snd_devdata); | ||
285 | eti_b1_snd_devdata.dev = &eti_b1_snd_device->dev; | ||
286 | |||
287 | ret = platform_device_add(eti_b1_snd_device); | ||
288 | if (ret) { | ||
289 | DBG("platform device add failed\n"); | ||
290 | platform_device_put(eti_b1_snd_device); | ||
291 | goto fail_io_unmap; | ||
292 | } | ||
293 | |||
294 | at91_set_A_periph(AT91_PIN_PB6, 0); /* TF1 */ | ||
295 | at91_set_A_periph(AT91_PIN_PB7, 0); /* TK1 */ | ||
296 | at91_set_A_periph(AT91_PIN_PB8, 0); /* TD1 */ | ||
297 | at91_set_A_periph(AT91_PIN_PB9, 0); /* RD1 */ | ||
298 | /* at91_set_A_periph(AT91_PIN_PB10, 0);*/ /* RK1 */ | ||
299 | at91_set_A_periph(AT91_PIN_PB11, 0); /* RF1 */ | ||
300 | |||
301 | /* | ||
302 | * Set PCK1 parent to PLLB and its rate to 12 Mhz. | ||
303 | */ | ||
304 | pllb_clk = clk_get(NULL, "pllb"); | ||
305 | pck1_clk = clk_get(NULL, "pck1"); | ||
306 | |||
307 | clk_set_parent(pck1_clk, pllb_clk); | ||
308 | clk_set_rate(pck1_clk, 12000000); | ||
309 | |||
310 | DBG("MCLK rate %luHz\n", clk_get_rate(pck1_clk)); | ||
311 | |||
312 | /* assign the GPIO pin to PCK1 */ | ||
313 | at91_set_B_periph(AT91_PIN_PA24, 0); | ||
314 | |||
315 | #ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE | ||
316 | printk(KERN_INFO "eti_b1_wm8731: Codec in Slave Mode\n"); | ||
317 | #else | ||
318 | printk(KERN_INFO "eti_b1_wm8731: Codec in Master Mode\n"); | ||
319 | #endif | ||
320 | return ret; | ||
321 | |||
322 | fail_io_unmap: | ||
323 | iounmap(ssc->base); | ||
324 | fail_release_mem: | ||
325 | release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K); | ||
326 | return ret; | ||
327 | } | ||
328 | |||
329 | static void __exit eti_b1_exit(void) | ||
330 | { | ||
331 | struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data; | ||
332 | |||
333 | clk_put(pck1_clk); | ||
334 | clk_put(pllb_clk); | ||
335 | |||
336 | platform_device_unregister(eti_b1_snd_device); | ||
337 | |||
338 | iounmap(ssc->base); | ||
339 | release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K); | ||
340 | } | ||
341 | |||
342 | module_init(eti_b1_init); | ||
343 | module_exit(eti_b1_exit); | ||
344 | |||
345 | /* Module information */ | ||
346 | MODULE_AUTHOR("Frank Mandarino <fmandarino@endrelia.com>"); | ||
347 | MODULE_DESCRIPTION("ALSA SoC ETI-B1-WM8731"); | ||
348 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig new file mode 100644 index 000000000000..dc006206f622 --- /dev/null +++ b/sound/soc/blackfin/Kconfig | |||
@@ -0,0 +1,101 @@ | |||
1 | config SND_BF5XX_I2S | ||
2 | tristate "SoC I2S Audio for the ADI BF5xx chip" | ||
3 | depends on BLACKFIN && SND_SOC | ||
4 | help | ||
5 | Say Y or M if you want to add support for codecs attached to | ||
6 | the Blackfin SPORT (synchronous serial ports) interface in I2S | ||
7 | mode (supports single stereo In/Out). | ||
8 | You will also need to select the audio interfaces to support below. | ||
9 | |||
10 | config SND_BF5XX_SOC_SSM2602 | ||
11 | tristate "SoC SSM2602 Audio support for BF52x ezkit" | ||
12 | depends on SND_BF5XX_I2S | ||
13 | select SND_BF5XX_SOC_I2S | ||
14 | select SND_SOC_SSM2602 | ||
15 | select I2C | ||
16 | select I2C_BLACKFIN_TWI | ||
17 | help | ||
18 | Say Y if you want to add support for SoC audio on BF527-EZKIT. | ||
19 | |||
20 | config SND_BF5XX_SOC_AD73311 | ||
21 | tristate "SoC AD73311 Audio support for Blackfin" | ||
22 | depends on SND_BF5XX_I2S | ||
23 | select SND_BF5XX_SOC_I2S | ||
24 | select SND_SOC_AD73311 | ||
25 | help | ||
26 | Say Y if you want to add support for AD73311 codec on Blackfin. | ||
27 | |||
28 | config SND_BFIN_AD73311_SE | ||
29 | int "PF pin for AD73311L Chip Select" | ||
30 | depends on SND_BF5XX_SOC_AD73311 | ||
31 | default 4 | ||
32 | help | ||
33 | Enter the GPIO used to control AD73311's SE pin. Acceptable | ||
34 | values are 0 to 7 | ||
35 | |||
36 | config SND_BF5XX_AC97 | ||
37 | tristate "SoC AC97 Audio for the ADI BF5xx chip" | ||
38 | depends on BLACKFIN && SND_SOC | ||
39 | help | ||
40 | Say Y or M if you want to add support for codecs attached to | ||
41 | the Blackfin SPORT (synchronous serial ports) interface in slot 16 | ||
42 | mode (pseudo AC97 interface). | ||
43 | You will also need to select the audio interfaces to support below. | ||
44 | |||
45 | Note: | ||
46 | AC97 codecs which do not implment the slot-16 mode will not function | ||
47 | properly with this driver. This driver is known to work with the | ||
48 | Analog Devices line of AC97 codecs. | ||
49 | |||
50 | config SND_MMAP_SUPPORT | ||
51 | bool "Enable MMAP Support" | ||
52 | depends on SND_BF5XX_AC97 | ||
53 | default y | ||
54 | help | ||
55 | Say y if you want AC97 driver to support mmap mode. | ||
56 | We introduce an intermediate buffer to simulate mmap. | ||
57 | |||
58 | config SND_BF5XX_SOC_SPORT | ||
59 | tristate | ||
60 | |||
61 | config SND_BF5XX_SOC_I2S | ||
62 | tristate | ||
63 | select SND_BF5XX_SOC_SPORT | ||
64 | |||
65 | config SND_BF5XX_SOC_AC97 | ||
66 | tristate | ||
67 | select AC97_BUS | ||
68 | select SND_SOC_AC97_BUS | ||
69 | select SND_BF5XX_SOC_SPORT | ||
70 | |||
71 | config SND_BF5XX_SOC_AD1980 | ||
72 | tristate "SoC AD1980/1 Audio support for BF5xx" | ||
73 | depends on SND_BF5XX_AC97 | ||
74 | select SND_BF5XX_SOC_AC97 | ||
75 | select SND_SOC_AD1980 | ||
76 | help | ||
77 | Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT. | ||
78 | |||
79 | config SND_BF5XX_SPORT_NUM | ||
80 | int "Set a SPORT for Sound chip" | ||
81 | depends on (SND_BF5XX_I2S || SND_BF5XX_AC97) | ||
82 | range 0 3 if BF54x | ||
83 | range 0 1 if (BF53x || BF561) | ||
84 | default 0 | ||
85 | help | ||
86 | Set the correct SPORT for sound chip. | ||
87 | |||
88 | config SND_BF5XX_HAVE_COLD_RESET | ||
89 | bool "BOARD has COLD Reset GPIO" | ||
90 | depends on SND_BF5XX_AC97 | ||
91 | default y if BFIN548_EZKIT | ||
92 | default n if !BFIN548_EZKIT | ||
93 | |||
94 | config SND_BF5XX_RESET_GPIO_NUM | ||
95 | int "Set a GPIO for cold reset" | ||
96 | depends on SND_BF5XX_HAVE_COLD_RESET | ||
97 | range 0 159 | ||
98 | default 19 if BFIN548_EZKIT | ||
99 | default 5 if BFIN537_STAMP | ||
100 | help | ||
101 | Set the correct GPIO for RESET the sound chip. | ||
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile new file mode 100644 index 000000000000..97bb37a6359c --- /dev/null +++ b/sound/soc/blackfin/Makefile | |||
@@ -0,0 +1,21 @@ | |||
1 | # Blackfin Platform Support | ||
2 | snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o | ||
3 | snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o | ||
4 | snd-soc-bf5xx-sport-objs := bf5xx-sport.o | ||
5 | snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o | ||
6 | snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o | ||
7 | |||
8 | obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o | ||
9 | obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o | ||
10 | obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o | ||
11 | obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o | ||
12 | obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o | ||
13 | |||
14 | # Blackfin Machine Support | ||
15 | snd-ad1980-objs := bf5xx-ad1980.o | ||
16 | snd-ssm2602-objs := bf5xx-ssm2602.o | ||
17 | snd-ad73311-objs := bf5xx-ad73311.o | ||
18 | |||
19 | obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o | ||
20 | obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o | ||
21 | obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o | ||
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c new file mode 100644 index 000000000000..25e50d2ea1ec --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c | |||
@@ -0,0 +1,457 @@ | |||
1 | /* | ||
2 | * File: sound/soc/blackfin/bf5xx-ac97-pcm.c | ||
3 | * Author: Cliff Cai <Cliff.Cai@analog.com> | ||
4 | * | ||
5 | * Created: Tue June 06 2008 | ||
6 | * Description: DMA Driver for AC97 sound chip | ||
7 | * | ||
8 | * Modified: | ||
9 | * Copyright 2008 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/module.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/platform_device.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <linux/dma-mapping.h> | ||
34 | |||
35 | #include <sound/core.h> | ||
36 | #include <sound/pcm.h> | ||
37 | #include <sound/pcm_params.h> | ||
38 | #include <sound/soc.h> | ||
39 | |||
40 | #include <asm/dma.h> | ||
41 | |||
42 | #include "bf5xx-ac97-pcm.h" | ||
43 | #include "bf5xx-ac97.h" | ||
44 | #include "bf5xx-sport.h" | ||
45 | |||
46 | #if defined(CONFIG_SND_MMAP_SUPPORT) | ||
47 | static void bf5xx_mmap_copy(struct snd_pcm_substream *substream, | ||
48 | snd_pcm_uframes_t count) | ||
49 | { | ||
50 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
51 | struct sport_device *sport = runtime->private_data; | ||
52 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
53 | bf5xx_pcm_to_ac97( | ||
54 | (struct ac97_frame *)sport->tx_dma_buf + sport->tx_pos, | ||
55 | (__u32 *)runtime->dma_area + sport->tx_pos, count); | ||
56 | sport->tx_pos += runtime->period_size; | ||
57 | if (sport->tx_pos >= runtime->buffer_size) | ||
58 | sport->tx_pos %= runtime->buffer_size; | ||
59 | sport->tx_delay_pos = sport->tx_pos; | ||
60 | } else { | ||
61 | bf5xx_ac97_to_pcm( | ||
62 | (struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos, | ||
63 | (__u32 *)runtime->dma_area + sport->rx_pos, count); | ||
64 | sport->rx_pos += runtime->period_size; | ||
65 | if (sport->rx_pos >= runtime->buffer_size) | ||
66 | sport->rx_pos %= runtime->buffer_size; | ||
67 | } | ||
68 | } | ||
69 | #endif | ||
70 | |||
71 | static void bf5xx_dma_irq(void *data) | ||
72 | { | ||
73 | struct snd_pcm_substream *pcm = data; | ||
74 | #if defined(CONFIG_SND_MMAP_SUPPORT) | ||
75 | struct snd_pcm_runtime *runtime = pcm->runtime; | ||
76 | struct sport_device *sport = runtime->private_data; | ||
77 | bf5xx_mmap_copy(pcm, runtime->period_size); | ||
78 | if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
79 | if (sport->once == 0) { | ||
80 | snd_pcm_period_elapsed(pcm); | ||
81 | bf5xx_mmap_copy(pcm, runtime->period_size); | ||
82 | sport->once = 1; | ||
83 | } | ||
84 | } | ||
85 | #endif | ||
86 | snd_pcm_period_elapsed(pcm); | ||
87 | } | ||
88 | |||
89 | /* The memory size for pure pcm data is 128*1024 = 0x20000 bytes. | ||
90 | * The total rx/tx buffer is for ac97 frame to hold all pcm data | ||
91 | * is 0x20000 * sizeof(struct ac97_frame) / 4. | ||
92 | */ | ||
93 | #ifdef CONFIG_SND_MMAP_SUPPORT | ||
94 | static const struct snd_pcm_hardware bf5xx_pcm_hardware = { | ||
95 | .info = SNDRV_PCM_INFO_INTERLEAVED | | ||
96 | SNDRV_PCM_INFO_MMAP | | ||
97 | SNDRV_PCM_INFO_MMAP_VALID | | ||
98 | SNDRV_PCM_INFO_BLOCK_TRANSFER, | ||
99 | #else | ||
100 | static const struct snd_pcm_hardware bf5xx_pcm_hardware = { | ||
101 | .info = SNDRV_PCM_INFO_INTERLEAVED | | ||
102 | SNDRV_PCM_INFO_BLOCK_TRANSFER, | ||
103 | #endif | ||
104 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
105 | .period_bytes_min = 32, | ||
106 | .period_bytes_max = 0x10000, | ||
107 | .periods_min = 1, | ||
108 | .periods_max = PAGE_SIZE/32, | ||
109 | .buffer_bytes_max = 0x20000, /* 128 kbytes */ | ||
110 | .fifo_size = 16, | ||
111 | }; | ||
112 | |||
113 | static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, | ||
114 | struct snd_pcm_hw_params *params) | ||
115 | { | ||
116 | size_t size = bf5xx_pcm_hardware.buffer_bytes_max | ||
117 | * sizeof(struct ac97_frame) / 4; | ||
118 | |||
119 | snd_pcm_lib_malloc_pages(substream, size); | ||
120 | |||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) | ||
125 | { | ||
126 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
127 | |||
128 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
129 | memset(runtime->dma_area, 0, runtime->buffer_size); | ||
130 | snd_pcm_lib_free_pages(substream); | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) | ||
135 | { | ||
136 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
137 | struct sport_device *sport = runtime->private_data; | ||
138 | |||
139 | /* An intermediate buffer is introduced for implementing mmap for | ||
140 | * SPORT working in TMD mode(include AC97). | ||
141 | */ | ||
142 | #if defined(CONFIG_SND_MMAP_SUPPORT) | ||
143 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
144 | sport_set_tx_callback(sport, bf5xx_dma_irq, substream); | ||
145 | sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods, | ||
146 | runtime->period_size * sizeof(struct ac97_frame)); | ||
147 | } else { | ||
148 | sport_set_rx_callback(sport, bf5xx_dma_irq, substream); | ||
149 | sport_config_rx_dma(sport, sport->rx_dma_buf, runtime->periods, | ||
150 | runtime->period_size * sizeof(struct ac97_frame)); | ||
151 | } | ||
152 | #else | ||
153 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
154 | sport_set_tx_callback(sport, bf5xx_dma_irq, substream); | ||
155 | sport_config_tx_dma(sport, runtime->dma_area, runtime->periods, | ||
156 | runtime->period_size * sizeof(struct ac97_frame)); | ||
157 | } else { | ||
158 | sport_set_rx_callback(sport, bf5xx_dma_irq, substream); | ||
159 | sport_config_rx_dma(sport, runtime->dma_area, runtime->periods, | ||
160 | runtime->period_size * sizeof(struct ac97_frame)); | ||
161 | } | ||
162 | #endif | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
167 | { | ||
168 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
169 | struct sport_device *sport = runtime->private_data; | ||
170 | int ret = 0; | ||
171 | |||
172 | pr_debug("%s enter\n", __func__); | ||
173 | switch (cmd) { | ||
174 | case SNDRV_PCM_TRIGGER_START: | ||
175 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
176 | bf5xx_mmap_copy(substream, runtime->period_size); | ||
177 | snd_pcm_period_elapsed(substream); | ||
178 | sport->tx_delay_pos = 0; | ||
179 | sport_tx_start(sport); | ||
180 | } | ||
181 | else | ||
182 | sport_rx_start(sport); | ||
183 | break; | ||
184 | case SNDRV_PCM_TRIGGER_STOP: | ||
185 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
186 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
187 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
188 | #if defined(CONFIG_SND_MMAP_SUPPORT) | ||
189 | sport->tx_pos = 0; | ||
190 | #endif | ||
191 | sport_tx_stop(sport); | ||
192 | } else { | ||
193 | #if defined(CONFIG_SND_MMAP_SUPPORT) | ||
194 | sport->rx_pos = 0; | ||
195 | #endif | ||
196 | sport_rx_stop(sport); | ||
197 | } | ||
198 | break; | ||
199 | default: | ||
200 | ret = -EINVAL; | ||
201 | } | ||
202 | return ret; | ||
203 | } | ||
204 | |||
205 | static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) | ||
206 | { | ||
207 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
208 | struct sport_device *sport = runtime->private_data; | ||
209 | unsigned int curr; | ||
210 | |||
211 | #if defined(CONFIG_SND_MMAP_SUPPORT) | ||
212 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
213 | curr = sport->tx_delay_pos; | ||
214 | else | ||
215 | curr = sport->rx_pos; | ||
216 | #else | ||
217 | |||
218 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
219 | curr = sport_curr_offset_tx(sport) / sizeof(struct ac97_frame); | ||
220 | else | ||
221 | curr = sport_curr_offset_rx(sport) / sizeof(struct ac97_frame); | ||
222 | |||
223 | #endif | ||
224 | return curr; | ||
225 | } | ||
226 | |||
227 | static int bf5xx_pcm_open(struct snd_pcm_substream *substream) | ||
228 | { | ||
229 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
230 | int ret; | ||
231 | |||
232 | pr_debug("%s enter\n", __func__); | ||
233 | snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); | ||
234 | |||
235 | ret = snd_pcm_hw_constraint_integer(runtime, | ||
236 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
237 | if (ret < 0) | ||
238 | goto out; | ||
239 | |||
240 | if (sport_handle != NULL) | ||
241 | runtime->private_data = sport_handle; | ||
242 | else { | ||
243 | pr_err("sport_handle is NULL\n"); | ||
244 | return -1; | ||
245 | } | ||
246 | return 0; | ||
247 | |||
248 | out: | ||
249 | return ret; | ||
250 | } | ||
251 | |||
252 | static int bf5xx_pcm_close(struct snd_pcm_substream *substream) | ||
253 | { | ||
254 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
255 | struct sport_device *sport = runtime->private_data; | ||
256 | |||
257 | pr_debug("%s enter\n", __func__); | ||
258 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
259 | sport->once = 0; | ||
260 | memset(sport->tx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame)); | ||
261 | } else | ||
262 | memset(sport->rx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame)); | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | #ifdef CONFIG_SND_MMAP_SUPPORT | ||
268 | static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, | ||
269 | struct vm_area_struct *vma) | ||
270 | { | ||
271 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
272 | size_t size = vma->vm_end - vma->vm_start; | ||
273 | vma->vm_start = (unsigned long)runtime->dma_area; | ||
274 | vma->vm_end = vma->vm_start + size; | ||
275 | vma->vm_flags |= VM_SHARED; | ||
276 | return 0 ; | ||
277 | } | ||
278 | #else | ||
279 | static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, | ||
280 | snd_pcm_uframes_t pos, | ||
281 | void __user *buf, snd_pcm_uframes_t count) | ||
282 | { | ||
283 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
284 | |||
285 | pr_debug("%s copy pos:0x%lx count:0x%lx\n", | ||
286 | substream->stream ? "Capture" : "Playback", pos, count); | ||
287 | |||
288 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
289 | bf5xx_pcm_to_ac97( | ||
290 | (struct ac97_frame *)runtime->dma_area + pos, | ||
291 | buf, count); | ||
292 | else | ||
293 | bf5xx_ac97_to_pcm( | ||
294 | (struct ac97_frame *)runtime->dma_area + pos, | ||
295 | buf, count); | ||
296 | return 0; | ||
297 | } | ||
298 | #endif | ||
299 | |||
300 | struct snd_pcm_ops bf5xx_pcm_ac97_ops = { | ||
301 | .open = bf5xx_pcm_open, | ||
302 | .close = bf5xx_pcm_close, | ||
303 | .ioctl = snd_pcm_lib_ioctl, | ||
304 | .hw_params = bf5xx_pcm_hw_params, | ||
305 | .hw_free = bf5xx_pcm_hw_free, | ||
306 | .prepare = bf5xx_pcm_prepare, | ||
307 | .trigger = bf5xx_pcm_trigger, | ||
308 | .pointer = bf5xx_pcm_pointer, | ||
309 | #ifdef CONFIG_SND_MMAP_SUPPORT | ||
310 | .mmap = bf5xx_pcm_mmap, | ||
311 | #else | ||
312 | .copy = bf5xx_pcm_copy, | ||
313 | #endif | ||
314 | }; | ||
315 | |||
316 | static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | ||
317 | { | ||
318 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | ||
319 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
320 | size_t size = bf5xx_pcm_hardware.buffer_bytes_max | ||
321 | * sizeof(struct ac97_frame) / 4; | ||
322 | |||
323 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
324 | buf->dev.dev = pcm->card->dev; | ||
325 | buf->private_data = NULL; | ||
326 | buf->area = dma_alloc_coherent(pcm->card->dev, size, | ||
327 | &buf->addr, GFP_KERNEL); | ||
328 | if (!buf->area) { | ||
329 | pr_err("Failed to allocate dma memory\n"); | ||
330 | pr_err("Please increase uncached DMA memory region\n"); | ||
331 | return -ENOMEM; | ||
332 | } | ||
333 | buf->bytes = size; | ||
334 | |||
335 | pr_debug("%s, area:%p, size:0x%08lx\n", __func__, | ||
336 | buf->area, buf->bytes); | ||
337 | |||
338 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
339 | sport_handle->tx_buf = buf->area; | ||
340 | else | ||
341 | sport_handle->rx_buf = buf->area; | ||
342 | |||
343 | /* | ||
344 | * Need to allocate local buffer when enable | ||
345 | * MMAP for SPORT working in TMD mode (include AC97). | ||
346 | */ | ||
347 | #if defined(CONFIG_SND_MMAP_SUPPORT) | ||
348 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
349 | if (!sport_handle->tx_dma_buf) { | ||
350 | sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \ | ||
351 | size, &sport_handle->tx_dma_phy, GFP_KERNEL); | ||
352 | if (!sport_handle->tx_dma_buf) { | ||
353 | pr_err("Failed to allocate memory for tx dma \ | ||
354 | buf - Please increase uncached DMA \ | ||
355 | memory region\n"); | ||
356 | return -ENOMEM; | ||
357 | } else | ||
358 | memset(sport_handle->tx_dma_buf, 0, size); | ||
359 | } else | ||
360 | memset(sport_handle->tx_dma_buf, 0, size); | ||
361 | } else { | ||
362 | if (!sport_handle->rx_dma_buf) { | ||
363 | sport_handle->rx_dma_buf = dma_alloc_coherent(NULL, \ | ||
364 | size, &sport_handle->rx_dma_phy, GFP_KERNEL); | ||
365 | if (!sport_handle->rx_dma_buf) { | ||
366 | pr_err("Failed to allocate memory for rx dma \ | ||
367 | buf - Please increase uncached DMA \ | ||
368 | memory region\n"); | ||
369 | return -ENOMEM; | ||
370 | } else | ||
371 | memset(sport_handle->rx_dma_buf, 0, size); | ||
372 | } else | ||
373 | memset(sport_handle->rx_dma_buf, 0, size); | ||
374 | } | ||
375 | #endif | ||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) | ||
380 | { | ||
381 | struct snd_pcm_substream *substream; | ||
382 | struct snd_dma_buffer *buf; | ||
383 | int stream; | ||
384 | #if defined(CONFIG_SND_MMAP_SUPPORT) | ||
385 | size_t size = bf5xx_pcm_hardware.buffer_bytes_max * | ||
386 | sizeof(struct ac97_frame) / 4; | ||
387 | #endif | ||
388 | for (stream = 0; stream < 2; stream++) { | ||
389 | substream = pcm->streams[stream].substream; | ||
390 | if (!substream) | ||
391 | continue; | ||
392 | |||
393 | buf = &substream->dma_buffer; | ||
394 | if (!buf->area) | ||
395 | continue; | ||
396 | dma_free_coherent(NULL, buf->bytes, buf->area, 0); | ||
397 | buf->area = NULL; | ||
398 | #if defined(CONFIG_SND_MMAP_SUPPORT) | ||
399 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
400 | if (sport_handle->tx_dma_buf) | ||
401 | dma_free_coherent(NULL, size, \ | ||
402 | sport_handle->tx_dma_buf, 0); | ||
403 | sport_handle->tx_dma_buf = NULL; | ||
404 | } else { | ||
405 | |||
406 | if (sport_handle->rx_dma_buf) | ||
407 | dma_free_coherent(NULL, size, \ | ||
408 | sport_handle->rx_dma_buf, 0); | ||
409 | sport_handle->rx_dma_buf = NULL; | ||
410 | } | ||
411 | #endif | ||
412 | } | ||
413 | if (sport_handle) | ||
414 | sport_done(sport_handle); | ||
415 | } | ||
416 | |||
417 | static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK; | ||
418 | |||
419 | int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai, | ||
420 | struct snd_pcm *pcm) | ||
421 | { | ||
422 | int ret = 0; | ||
423 | |||
424 | pr_debug("%s enter\n", __func__); | ||
425 | if (!card->dev->dma_mask) | ||
426 | card->dev->dma_mask = &bf5xx_pcm_dmamask; | ||
427 | if (!card->dev->coherent_dma_mask) | ||
428 | card->dev->coherent_dma_mask = DMA_32BIT_MASK; | ||
429 | |||
430 | if (dai->playback.channels_min) { | ||
431 | ret = bf5xx_pcm_preallocate_dma_buffer(pcm, | ||
432 | SNDRV_PCM_STREAM_PLAYBACK); | ||
433 | if (ret) | ||
434 | goto out; | ||
435 | } | ||
436 | |||
437 | if (dai->capture.channels_min) { | ||
438 | ret = bf5xx_pcm_preallocate_dma_buffer(pcm, | ||
439 | SNDRV_PCM_STREAM_CAPTURE); | ||
440 | if (ret) | ||
441 | goto out; | ||
442 | } | ||
443 | out: | ||
444 | return ret; | ||
445 | } | ||
446 | |||
447 | struct snd_soc_platform bf5xx_ac97_soc_platform = { | ||
448 | .name = "bf5xx-audio", | ||
449 | .pcm_ops = &bf5xx_pcm_ac97_ops, | ||
450 | .pcm_new = bf5xx_pcm_ac97_new, | ||
451 | .pcm_free = bf5xx_pcm_free_dma_buffers, | ||
452 | }; | ||
453 | EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform); | ||
454 | |||
455 | MODULE_AUTHOR("Cliff Cai"); | ||
456 | MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module"); | ||
457 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.h b/sound/soc/blackfin/bf5xx-ac97-pcm.h new file mode 100644 index 000000000000..350125a0ae21 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * linux/sound/arm/bf5xx-ac97-pcm.h -- ALSA PCM interface for the Blackfin | ||
3 | * | ||
4 | * Copyright 2007 Analog Device Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #ifndef _BF5XX_AC97_PCM_H | ||
12 | #define _BF5XX_AC97_PCM_H | ||
13 | |||
14 | struct bf5xx_pcm_dma_params { | ||
15 | char *name; /* stream identifier */ | ||
16 | }; | ||
17 | |||
18 | struct bf5xx_gpio { | ||
19 | u32 sys; | ||
20 | u32 rx; | ||
21 | u32 tx; | ||
22 | u32 clk; | ||
23 | u32 frm; | ||
24 | }; | ||
25 | |||
26 | /* platform data */ | ||
27 | extern struct snd_soc_platform bf5xx_ac97_soc_platform; | ||
28 | |||
29 | #endif | ||
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c new file mode 100644 index 000000000000..5e5aafb6485f --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ac97.c | |||
@@ -0,0 +1,406 @@ | |||
1 | /* | ||
2 | * bf5xx-ac97.c -- AC97 support for the ADI blackfin chip. | ||
3 | * | ||
4 | * Author: Roy Huang | ||
5 | * Created: 11th. June 2007 | ||
6 | * Copyright: Analog Device Inc. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/init.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/wait.h> | ||
18 | #include <linux/delay.h> | ||
19 | |||
20 | #include <sound/core.h> | ||
21 | #include <sound/pcm.h> | ||
22 | #include <sound/ac97_codec.h> | ||
23 | #include <sound/initval.h> | ||
24 | #include <sound/soc.h> | ||
25 | |||
26 | #include <asm/irq.h> | ||
27 | #include <asm/portmux.h> | ||
28 | #include <linux/mutex.h> | ||
29 | #include <linux/gpio.h> | ||
30 | |||
31 | #include "bf5xx-sport.h" | ||
32 | #include "bf5xx-ac97.h" | ||
33 | |||
34 | #if defined(CONFIG_BF54x) | ||
35 | #define PIN_REQ_SPORT_0 {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, \ | ||
36 | P_SPORT0_RFS, P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0} | ||
37 | |||
38 | #define PIN_REQ_SPORT_1 {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, \ | ||
39 | P_SPORT1_RFS, P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0} | ||
40 | |||
41 | #define PIN_REQ_SPORT_2 {P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, \ | ||
42 | P_SPORT2_RFS, P_SPORT2_DRPRI, P_SPORT2_RSCLK, 0} | ||
43 | |||
44 | #define PIN_REQ_SPORT_3 {P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, \ | ||
45 | P_SPORT3_RFS, P_SPORT3_DRPRI, P_SPORT3_RSCLK, 0} | ||
46 | #else | ||
47 | #define PIN_REQ_SPORT_0 {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \ | ||
48 | P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0} | ||
49 | |||
50 | #define PIN_REQ_SPORT_1 {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \ | ||
51 | P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0} | ||
52 | #endif | ||
53 | |||
54 | static int *cmd_count; | ||
55 | static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; | ||
56 | |||
57 | #if defined(CONFIG_BF54x) | ||
58 | static struct sport_param sport_params[4] = { | ||
59 | { | ||
60 | .dma_rx_chan = CH_SPORT0_RX, | ||
61 | .dma_tx_chan = CH_SPORT0_TX, | ||
62 | .err_irq = IRQ_SPORT0_ERR, | ||
63 | .regs = (struct sport_register *)SPORT0_TCR1, | ||
64 | }, | ||
65 | { | ||
66 | .dma_rx_chan = CH_SPORT1_RX, | ||
67 | .dma_tx_chan = CH_SPORT1_TX, | ||
68 | .err_irq = IRQ_SPORT1_ERR, | ||
69 | .regs = (struct sport_register *)SPORT1_TCR1, | ||
70 | }, | ||
71 | { | ||
72 | .dma_rx_chan = CH_SPORT2_RX, | ||
73 | .dma_tx_chan = CH_SPORT2_TX, | ||
74 | .err_irq = IRQ_SPORT2_ERR, | ||
75 | .regs = (struct sport_register *)SPORT2_TCR1, | ||
76 | }, | ||
77 | { | ||
78 | .dma_rx_chan = CH_SPORT3_RX, | ||
79 | .dma_tx_chan = CH_SPORT3_TX, | ||
80 | .err_irq = IRQ_SPORT3_ERR, | ||
81 | .regs = (struct sport_register *)SPORT3_TCR1, | ||
82 | } | ||
83 | }; | ||
84 | #else | ||
85 | static struct sport_param sport_params[2] = { | ||
86 | { | ||
87 | .dma_rx_chan = CH_SPORT0_RX, | ||
88 | .dma_tx_chan = CH_SPORT0_TX, | ||
89 | .err_irq = IRQ_SPORT0_ERROR, | ||
90 | .regs = (struct sport_register *)SPORT0_TCR1, | ||
91 | }, | ||
92 | { | ||
93 | .dma_rx_chan = CH_SPORT1_RX, | ||
94 | .dma_tx_chan = CH_SPORT1_TX, | ||
95 | .err_irq = IRQ_SPORT1_ERROR, | ||
96 | .regs = (struct sport_register *)SPORT1_TCR1, | ||
97 | } | ||
98 | }; | ||
99 | #endif | ||
100 | |||
101 | void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \ | ||
102 | size_t count) | ||
103 | { | ||
104 | while (count--) { | ||
105 | dst->ac97_tag = TAG_VALID | TAG_PCM; | ||
106 | (dst++)->ac97_pcm = *src++; | ||
107 | } | ||
108 | } | ||
109 | EXPORT_SYMBOL(bf5xx_pcm_to_ac97); | ||
110 | |||
111 | void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \ | ||
112 | size_t count) | ||
113 | { | ||
114 | while (count--) | ||
115 | *(dst++) = (src++)->ac97_pcm; | ||
116 | } | ||
117 | EXPORT_SYMBOL(bf5xx_ac97_to_pcm); | ||
118 | |||
119 | static unsigned int sport_tx_curr_frag(struct sport_device *sport) | ||
120 | { | ||
121 | return sport->tx_curr_frag = sport_curr_offset_tx(sport) / \ | ||
122 | sport->tx_fragsize; | ||
123 | } | ||
124 | |||
125 | static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data) | ||
126 | { | ||
127 | struct sport_device *sport = sport_handle; | ||
128 | int nextfrag = sport_tx_curr_frag(sport); | ||
129 | struct ac97_frame *nextwrite; | ||
130 | |||
131 | sport_incfrag(sport, &nextfrag, 1); | ||
132 | |||
133 | nextwrite = (struct ac97_frame *)(sport->tx_buf + \ | ||
134 | nextfrag * sport->tx_fragsize); | ||
135 | pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n", | ||
136 | sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]); | ||
137 | nextwrite[cmd_count[nextfrag]].ac97_tag |= TAG_CMD; | ||
138 | nextwrite[cmd_count[nextfrag]].ac97_addr = addr; | ||
139 | nextwrite[cmd_count[nextfrag]].ac97_data = data; | ||
140 | ++cmd_count[nextfrag]; | ||
141 | pr_debug("ac97_sport: Inserting %02x/%04x into fragment %d\n", | ||
142 | addr >> 8, data, nextfrag); | ||
143 | } | ||
144 | |||
145 | static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97, | ||
146 | unsigned short reg) | ||
147 | { | ||
148 | struct ac97_frame out_frame[2], in_frame[2]; | ||
149 | |||
150 | pr_debug("%s enter 0x%x\n", __func__, reg); | ||
151 | |||
152 | /* When dma descriptor is enabled, the register should not be read */ | ||
153 | if (sport_handle->tx_run || sport_handle->rx_run) { | ||
154 | pr_err("Could you send a mail to cliff.cai@analog.com " | ||
155 | "to report this?\n"); | ||
156 | return -EFAULT; | ||
157 | } | ||
158 | |||
159 | memset(&out_frame, 0, 2 * sizeof(struct ac97_frame)); | ||
160 | memset(&in_frame, 0, 2 * sizeof(struct ac97_frame)); | ||
161 | out_frame[0].ac97_tag = TAG_VALID | TAG_CMD; | ||
162 | out_frame[0].ac97_addr = ((reg << 8) | 0x8000); | ||
163 | sport_send_and_recv(sport_handle, (unsigned char *)&out_frame, | ||
164 | (unsigned char *)&in_frame, | ||
165 | 2 * sizeof(struct ac97_frame)); | ||
166 | return in_frame[1].ac97_data; | ||
167 | } | ||
168 | |||
169 | void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, | ||
170 | unsigned short val) | ||
171 | { | ||
172 | pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val); | ||
173 | |||
174 | if (sport_handle->tx_run) { | ||
175 | enqueue_cmd(ac97, (reg << 8), val); /* write */ | ||
176 | enqueue_cmd(ac97, (reg << 8) | 0x8000, 0); /* read back */ | ||
177 | } else { | ||
178 | struct ac97_frame frame; | ||
179 | memset(&frame, 0, sizeof(struct ac97_frame)); | ||
180 | frame.ac97_tag = TAG_VALID | TAG_CMD; | ||
181 | frame.ac97_addr = (reg << 8); | ||
182 | frame.ac97_data = val; | ||
183 | sport_send_and_recv(sport_handle, (unsigned char *)&frame, \ | ||
184 | NULL, sizeof(struct ac97_frame)); | ||
185 | } | ||
186 | } | ||
187 | |||
188 | static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97) | ||
189 | { | ||
190 | #if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \ | ||
191 | (defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1)) | ||
192 | |||
193 | #define CONCAT(a, b, c) a ## b ## c | ||
194 | #define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS) | ||
195 | |||
196 | u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM); | ||
197 | u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM)); | ||
198 | |||
199 | pr_debug("%s enter\n", __func__); | ||
200 | |||
201 | peripheral_free(per); | ||
202 | gpio_request(gpio, "bf5xx-ac97"); | ||
203 | gpio_direction_output(gpio, 1); | ||
204 | udelay(2); | ||
205 | gpio_set_value(gpio, 0); | ||
206 | udelay(1); | ||
207 | gpio_free(gpio); | ||
208 | peripheral_request(per, "soc-audio"); | ||
209 | #else | ||
210 | pr_info("%s: Not implemented\n", __func__); | ||
211 | #endif | ||
212 | } | ||
213 | |||
214 | static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97) | ||
215 | { | ||
216 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | ||
217 | pr_debug("%s enter\n", __func__); | ||
218 | |||
219 | /* It is specified for bf548-ezkit */ | ||
220 | gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 0); | ||
221 | /* Keep reset pin low for 1 ms */ | ||
222 | mdelay(1); | ||
223 | gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1); | ||
224 | /* Wait for bit clock recover */ | ||
225 | mdelay(1); | ||
226 | #else | ||
227 | pr_info("%s: Not implemented\n", __func__); | ||
228 | #endif | ||
229 | } | ||
230 | |||
231 | struct snd_ac97_bus_ops soc_ac97_ops = { | ||
232 | .read = bf5xx_ac97_read, | ||
233 | .write = bf5xx_ac97_write, | ||
234 | .warm_reset = bf5xx_ac97_warm_reset, | ||
235 | .reset = bf5xx_ac97_cold_reset, | ||
236 | }; | ||
237 | EXPORT_SYMBOL_GPL(soc_ac97_ops); | ||
238 | |||
239 | #ifdef CONFIG_PM | ||
240 | static int bf5xx_ac97_suspend(struct platform_device *pdev, | ||
241 | struct snd_soc_dai *dai) | ||
242 | { | ||
243 | struct sport_device *sport = | ||
244 | (struct sport_device *)dai->private_data; | ||
245 | |||
246 | pr_debug("%s : sport %d\n", __func__, dai->id); | ||
247 | if (!dai->active) | ||
248 | return 0; | ||
249 | if (dai->capture.active) | ||
250 | sport_rx_stop(sport); | ||
251 | if (dai->playback.active) | ||
252 | sport_tx_stop(sport); | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static int bf5xx_ac97_resume(struct platform_device *pdev, | ||
257 | struct snd_soc_dai *dai) | ||
258 | { | ||
259 | int ret; | ||
260 | struct sport_device *sport = | ||
261 | (struct sport_device *)dai->private_data; | ||
262 | |||
263 | pr_debug("%s : sport %d\n", __func__, dai->id); | ||
264 | if (!dai->active) | ||
265 | return 0; | ||
266 | |||
267 | ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1); | ||
268 | if (ret) { | ||
269 | pr_err("SPORT is busy!\n"); | ||
270 | return -EBUSY; | ||
271 | } | ||
272 | |||
273 | ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1)); | ||
274 | if (ret) { | ||
275 | pr_err("SPORT is busy!\n"); | ||
276 | return -EBUSY; | ||
277 | } | ||
278 | |||
279 | ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1)); | ||
280 | if (ret) { | ||
281 | pr_err("SPORT is busy!\n"); | ||
282 | return -EBUSY; | ||
283 | } | ||
284 | |||
285 | if (dai->capture.active) | ||
286 | sport_rx_start(sport); | ||
287 | if (dai->playback.active) | ||
288 | sport_tx_start(sport); | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | #else | ||
293 | #define bf5xx_ac97_suspend NULL | ||
294 | #define bf5xx_ac97_resume NULL | ||
295 | #endif | ||
296 | |||
297 | static int bf5xx_ac97_probe(struct platform_device *pdev, | ||
298 | struct snd_soc_dai *dai) | ||
299 | { | ||
300 | int ret; | ||
301 | #if defined(CONFIG_BF54x) | ||
302 | u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1, | ||
303 | PIN_REQ_SPORT_2, PIN_REQ_SPORT_3}; | ||
304 | #else | ||
305 | u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1}; | ||
306 | #endif | ||
307 | cmd_count = (int *)get_zeroed_page(GFP_KERNEL); | ||
308 | if (cmd_count == NULL) | ||
309 | return -ENOMEM; | ||
310 | |||
311 | if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { | ||
312 | pr_err("Requesting Peripherals failed\n"); | ||
313 | return -EFAULT; | ||
314 | } | ||
315 | |||
316 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | ||
317 | /* Request PB3 as reset pin */ | ||
318 | if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) { | ||
319 | pr_err("Failed to request GPIO_%d for reset\n", | ||
320 | CONFIG_SND_BF5XX_RESET_GPIO_NUM); | ||
321 | peripheral_free_list(&sport_req[sport_num][0]); | ||
322 | return -1; | ||
323 | } | ||
324 | gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1); | ||
325 | #endif | ||
326 | sport_handle = sport_init(&sport_params[sport_num], 2, \ | ||
327 | sizeof(struct ac97_frame), NULL); | ||
328 | if (!sport_handle) { | ||
329 | peripheral_free_list(&sport_req[sport_num][0]); | ||
330 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | ||
331 | gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); | ||
332 | #endif | ||
333 | return -ENODEV; | ||
334 | } | ||
335 | /*SPORT works in TDM mode to simulate AC97 transfers*/ | ||
336 | ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1); | ||
337 | if (ret) { | ||
338 | pr_err("SPORT is busy!\n"); | ||
339 | kfree(sport_handle); | ||
340 | peripheral_free_list(&sport_req[sport_num][0]); | ||
341 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | ||
342 | gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); | ||
343 | #endif | ||
344 | return -EBUSY; | ||
345 | } | ||
346 | |||
347 | ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1)); | ||
348 | if (ret) { | ||
349 | pr_err("SPORT is busy!\n"); | ||
350 | kfree(sport_handle); | ||
351 | peripheral_free_list(&sport_req[sport_num][0]); | ||
352 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | ||
353 | gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); | ||
354 | #endif | ||
355 | return -EBUSY; | ||
356 | } | ||
357 | |||
358 | ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1)); | ||
359 | if (ret) { | ||
360 | pr_err("SPORT is busy!\n"); | ||
361 | kfree(sport_handle); | ||
362 | peripheral_free_list(&sport_req[sport_num][0]); | ||
363 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | ||
364 | gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); | ||
365 | #endif | ||
366 | return -EBUSY; | ||
367 | } | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static void bf5xx_ac97_remove(struct platform_device *pdev, | ||
372 | struct snd_soc_dai *dai) | ||
373 | { | ||
374 | free_page((unsigned long)cmd_count); | ||
375 | cmd_count = NULL; | ||
376 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | ||
377 | gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); | ||
378 | #endif | ||
379 | } | ||
380 | |||
381 | struct snd_soc_dai bfin_ac97_dai = { | ||
382 | .name = "bf5xx-ac97", | ||
383 | .id = 0, | ||
384 | .type = SND_SOC_DAI_AC97, | ||
385 | .probe = bf5xx_ac97_probe, | ||
386 | .remove = bf5xx_ac97_remove, | ||
387 | .suspend = bf5xx_ac97_suspend, | ||
388 | .resume = bf5xx_ac97_resume, | ||
389 | .playback = { | ||
390 | .stream_name = "AC97 Playback", | ||
391 | .channels_min = 2, | ||
392 | .channels_max = 2, | ||
393 | .rates = SNDRV_PCM_RATE_48000, | ||
394 | .formats = SNDRV_PCM_FMTBIT_S16_LE, }, | ||
395 | .capture = { | ||
396 | .stream_name = "AC97 Capture", | ||
397 | .channels_min = 2, | ||
398 | .channels_max = 2, | ||
399 | .rates = SNDRV_PCM_RATE_48000, | ||
400 | .formats = SNDRV_PCM_FMTBIT_S16_LE, }, | ||
401 | }; | ||
402 | EXPORT_SYMBOL_GPL(bfin_ac97_dai); | ||
403 | |||
404 | MODULE_AUTHOR("Roy Huang"); | ||
405 | MODULE_DESCRIPTION("AC97 driver for ADI Blackfin"); | ||
406 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/blackfin/bf5xx-ac97.h b/sound/soc/blackfin/bf5xx-ac97.h new file mode 100644 index 000000000000..3f77cc558dc0 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ac97.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * linux/sound/arm/bf5xx-ac97.h | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef _BF5XX_AC97_H | ||
10 | #define _BF5XX_AC97_H | ||
11 | |||
12 | extern struct snd_ac97_bus_ops bf5xx_ac97_ops; | ||
13 | extern struct snd_ac97 *ac97; | ||
14 | /* Frame format in memory, only support stereo currently */ | ||
15 | struct ac97_frame { | ||
16 | u16 ac97_tag; /* slot 0 */ | ||
17 | u16 ac97_addr; /* slot 1 */ | ||
18 | u16 ac97_data; /* slot 2 */ | ||
19 | u32 ac97_pcm; /* slot 3 and 4: left and right pcm data */ | ||
20 | } __attribute__ ((packed)); | ||
21 | |||
22 | #define TAG_VALID 0x8000 | ||
23 | #define TAG_CMD 0x6000 | ||
24 | #define TAG_PCM_LEFT 0x1000 | ||
25 | #define TAG_PCM_RIGHT 0x0800 | ||
26 | #define TAG_PCM (TAG_PCM_LEFT | TAG_PCM_RIGHT) | ||
27 | |||
28 | extern struct snd_soc_dai bfin_ac97_dai; | ||
29 | |||
30 | void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \ | ||
31 | size_t count); | ||
32 | |||
33 | void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \ | ||
34 | size_t count); | ||
35 | |||
36 | #endif | ||
diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c new file mode 100644 index 000000000000..124425d22320 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ad1980.c | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * File: sound/soc/blackfin/bf5xx-ad1980.c | ||
3 | * Author: Cliff Cai <Cliff.Cai@analog.com> | ||
4 | * | ||
5 | * Created: Tue June 06 2008 | ||
6 | * Description: Board driver for AD1980/1 audio codec | ||
7 | * | ||
8 | * Modified: | ||
9 | * Copyright 2008 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/module.h> | ||
30 | #include <linux/moduleparam.h> | ||
31 | #include <linux/device.h> | ||
32 | #include <asm/dma.h> | ||
33 | |||
34 | #include <sound/core.h> | ||
35 | #include <sound/pcm.h> | ||
36 | #include <sound/soc.h> | ||
37 | |||
38 | #include <linux/gpio.h> | ||
39 | #include <asm/portmux.h> | ||
40 | |||
41 | #include "../codecs/ad1980.h" | ||
42 | #include "bf5xx-sport.h" | ||
43 | #include "bf5xx-ac97-pcm.h" | ||
44 | #include "bf5xx-ac97.h" | ||
45 | |||
46 | static struct snd_soc_machine bf5xx_board; | ||
47 | |||
48 | static int bf5xx_board_startup(struct snd_pcm_substream *substream) | ||
49 | { | ||
50 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
51 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
52 | |||
53 | pr_debug("%s enter\n", __func__); | ||
54 | cpu_dai->private_data = sport_handle; | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static struct snd_soc_ops bf5xx_board_ops = { | ||
59 | .startup = bf5xx_board_startup, | ||
60 | }; | ||
61 | |||
62 | static struct snd_soc_dai_link bf5xx_board_dai = { | ||
63 | .name = "AC97", | ||
64 | .stream_name = "AC97 HiFi", | ||
65 | .cpu_dai = &bfin_ac97_dai, | ||
66 | .codec_dai = &ad1980_dai, | ||
67 | .ops = &bf5xx_board_ops, | ||
68 | }; | ||
69 | |||
70 | static struct snd_soc_machine bf5xx_board = { | ||
71 | .name = "bf5xx-board", | ||
72 | .dai_link = &bf5xx_board_dai, | ||
73 | .num_links = 1, | ||
74 | }; | ||
75 | |||
76 | static struct snd_soc_device bf5xx_board_snd_devdata = { | ||
77 | .machine = &bf5xx_board, | ||
78 | .platform = &bf5xx_ac97_soc_platform, | ||
79 | .codec_dev = &soc_codec_dev_ad1980, | ||
80 | }; | ||
81 | |||
82 | static struct platform_device *bf5xx_board_snd_device; | ||
83 | |||
84 | static int __init bf5xx_board_init(void) | ||
85 | { | ||
86 | int ret; | ||
87 | |||
88 | bf5xx_board_snd_device = platform_device_alloc("soc-audio", -1); | ||
89 | if (!bf5xx_board_snd_device) | ||
90 | return -ENOMEM; | ||
91 | |||
92 | platform_set_drvdata(bf5xx_board_snd_device, &bf5xx_board_snd_devdata); | ||
93 | bf5xx_board_snd_devdata.dev = &bf5xx_board_snd_device->dev; | ||
94 | ret = platform_device_add(bf5xx_board_snd_device); | ||
95 | |||
96 | if (ret) | ||
97 | platform_device_put(bf5xx_board_snd_device); | ||
98 | |||
99 | return ret; | ||
100 | } | ||
101 | |||
102 | static void __exit bf5xx_board_exit(void) | ||
103 | { | ||
104 | platform_device_unregister(bf5xx_board_snd_device); | ||
105 | } | ||
106 | |||
107 | module_init(bf5xx_board_init); | ||
108 | module_exit(bf5xx_board_exit); | ||
109 | |||
110 | /* Module information */ | ||
111 | MODULE_AUTHOR("Cliff Cai"); | ||
112 | MODULE_DESCRIPTION("ALSA SoC AD1980/1 BF5xx board"); | ||
113 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c new file mode 100644 index 000000000000..622c9b909532 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ad73311.c | |||
@@ -0,0 +1,240 @@ | |||
1 | /* | ||
2 | * File: sound/soc/blackfin/bf5xx-ad73311.c | ||
3 | * Author: Cliff Cai <Cliff.Cai@analog.com> | ||
4 | * | ||
5 | * Created: Thur Sep 25 2008 | ||
6 | * Description: Board driver for ad73311 sound chip | ||
7 | * | ||
8 | * Modified: | ||
9 | * Copyright 2008 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/module.h> | ||
30 | #include <linux/moduleparam.h> | ||
31 | #include <linux/device.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/gpio.h> | ||
34 | |||
35 | #include <sound/core.h> | ||
36 | #include <sound/pcm.h> | ||
37 | #include <sound/soc.h> | ||
38 | #include <sound/soc-dapm.h> | ||
39 | #include <sound/pcm_params.h> | ||
40 | |||
41 | #include <asm/blackfin.h> | ||
42 | #include <asm/cacheflush.h> | ||
43 | #include <asm/irq.h> | ||
44 | #include <asm/dma.h> | ||
45 | #include <asm/portmux.h> | ||
46 | |||
47 | #include "../codecs/ad73311.h" | ||
48 | #include "bf5xx-sport.h" | ||
49 | #include "bf5xx-i2s-pcm.h" | ||
50 | #include "bf5xx-i2s.h" | ||
51 | |||
52 | #if CONFIG_SND_BF5XX_SPORT_NUM == 0 | ||
53 | #define bfin_write_SPORT_TCR1 bfin_write_SPORT0_TCR1 | ||
54 | #define bfin_read_SPORT_TCR1 bfin_read_SPORT0_TCR1 | ||
55 | #define bfin_write_SPORT_TCR2 bfin_write_SPORT0_TCR2 | ||
56 | #define bfin_write_SPORT_TX16 bfin_write_SPORT0_TX16 | ||
57 | #define bfin_read_SPORT_STAT bfin_read_SPORT0_STAT | ||
58 | #else | ||
59 | #define bfin_write_SPORT_TCR1 bfin_write_SPORT1_TCR1 | ||
60 | #define bfin_read_SPORT_TCR1 bfin_read_SPORT1_TCR1 | ||
61 | #define bfin_write_SPORT_TCR2 bfin_write_SPORT1_TCR2 | ||
62 | #define bfin_write_SPORT_TX16 bfin_write_SPORT1_TX16 | ||
63 | #define bfin_read_SPORT_STAT bfin_read_SPORT1_STAT | ||
64 | #endif | ||
65 | |||
66 | #define GPIO_SE CONFIG_SND_BFIN_AD73311_SE | ||
67 | |||
68 | static struct snd_soc_machine bf5xx_ad73311; | ||
69 | |||
70 | static int snd_ad73311_startup(void) | ||
71 | { | ||
72 | pr_debug("%s enter\n", __func__); | ||
73 | |||
74 | /* Pull up SE pin on AD73311L */ | ||
75 | gpio_set_value(GPIO_SE, 1); | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static int snd_ad73311_configure(void) | ||
80 | { | ||
81 | unsigned short ctrl_regs[6]; | ||
82 | unsigned short status = 0; | ||
83 | int count = 0; | ||
84 | |||
85 | /* DMCLK = MCLK = 16.384 MHz | ||
86 | * SCLK = DMCLK/8 = 2.048 MHz | ||
87 | * Sample Rate = DMCLK/2048 = 8 KHz | ||
88 | */ | ||
89 | ctrl_regs[0] = AD_CONTROL | AD_WRITE | CTRL_REG_B | REGB_MCDIV(0) | \ | ||
90 | REGB_SCDIV(0) | REGB_DIRATE(0); | ||
91 | ctrl_regs[1] = AD_CONTROL | AD_WRITE | CTRL_REG_C | REGC_PUDEV | \ | ||
92 | REGC_PUADC | REGC_PUDAC | REGC_PUREF | REGC_REFUSE ; | ||
93 | ctrl_regs[2] = AD_CONTROL | AD_WRITE | CTRL_REG_D | REGD_OGS(2) | \ | ||
94 | REGD_IGS(2); | ||
95 | ctrl_regs[3] = AD_CONTROL | AD_WRITE | CTRL_REG_E | REGE_DA(0x1f); | ||
96 | ctrl_regs[4] = AD_CONTROL | AD_WRITE | CTRL_REG_F | REGF_SEEN ; | ||
97 | ctrl_regs[5] = AD_CONTROL | AD_WRITE | CTRL_REG_A | REGA_MODE_DATA; | ||
98 | |||
99 | local_irq_disable(); | ||
100 | snd_ad73311_startup(); | ||
101 | udelay(1); | ||
102 | |||
103 | bfin_write_SPORT_TCR1(TFSR); | ||
104 | bfin_write_SPORT_TCR2(0xF); | ||
105 | SSYNC(); | ||
106 | |||
107 | /* SPORT Tx Register is a 8 x 16 FIFO, all the data can be put to | ||
108 | * FIFO before enable SPORT to transfer the data | ||
109 | */ | ||
110 | for (count = 0; count < 6; count++) | ||
111 | bfin_write_SPORT_TX16(ctrl_regs[count]); | ||
112 | SSYNC(); | ||
113 | bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() | TSPEN); | ||
114 | SSYNC(); | ||
115 | |||
116 | /* When TUVF is set, the data is already send out */ | ||
117 | while (!(status & TUVF) && count++ < 10000) { | ||
118 | udelay(1); | ||
119 | status = bfin_read_SPORT_STAT(); | ||
120 | SSYNC(); | ||
121 | } | ||
122 | bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() & ~TSPEN); | ||
123 | SSYNC(); | ||
124 | local_irq_enable(); | ||
125 | |||
126 | if (count == 10000) { | ||
127 | printk(KERN_ERR "ad73311: failed to configure codec\n"); | ||
128 | return -1; | ||
129 | } | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static int bf5xx_probe(struct platform_device *pdev) | ||
134 | { | ||
135 | int err; | ||
136 | if (gpio_request(GPIO_SE, "AD73311_SE")) { | ||
137 | printk(KERN_ERR "%s: Failed ro request GPIO_%d\n", __func__, GPIO_SE); | ||
138 | return -EBUSY; | ||
139 | } | ||
140 | |||
141 | gpio_direction_output(GPIO_SE, 0); | ||
142 | |||
143 | err = snd_ad73311_configure(); | ||
144 | if (err < 0) | ||
145 | return -EFAULT; | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | static int bf5xx_ad73311_startup(struct snd_pcm_substream *substream) | ||
151 | { | ||
152 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
153 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
154 | |||
155 | pr_debug("%s enter\n", __func__); | ||
156 | cpu_dai->private_data = sport_handle; | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream, | ||
161 | struct snd_pcm_hw_params *params) | ||
162 | { | ||
163 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
164 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
165 | int ret = 0; | ||
166 | |||
167 | pr_debug("%s rate %d format %x\n", __func__, params_rate(params), | ||
168 | params_format(params)); | ||
169 | |||
170 | /* set cpu DAI configuration */ | ||
171 | ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | | ||
172 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||
173 | if (ret < 0) | ||
174 | return ret; | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | |||
180 | static struct snd_soc_ops bf5xx_ad73311_ops = { | ||
181 | .startup = bf5xx_ad73311_startup, | ||
182 | .hw_params = bf5xx_ad73311_hw_params, | ||
183 | }; | ||
184 | |||
185 | static struct snd_soc_dai_link bf5xx_ad73311_dai = { | ||
186 | .name = "ad73311", | ||
187 | .stream_name = "AD73311", | ||
188 | .cpu_dai = &bf5xx_i2s_dai, | ||
189 | .codec_dai = &ad73311_dai, | ||
190 | .ops = &bf5xx_ad73311_ops, | ||
191 | }; | ||
192 | |||
193 | static struct snd_soc_machine bf5xx_ad73311 = { | ||
194 | .name = "bf5xx_ad73311", | ||
195 | .probe = bf5xx_probe, | ||
196 | .dai_link = &bf5xx_ad73311_dai, | ||
197 | .num_links = 1, | ||
198 | }; | ||
199 | |||
200 | static struct snd_soc_device bf5xx_ad73311_snd_devdata = { | ||
201 | .machine = &bf5xx_ad73311, | ||
202 | .platform = &bf5xx_i2s_soc_platform, | ||
203 | .codec_dev = &soc_codec_dev_ad73311, | ||
204 | }; | ||
205 | |||
206 | static struct platform_device *bf52x_ad73311_snd_device; | ||
207 | |||
208 | static int __init bf5xx_ad73311_init(void) | ||
209 | { | ||
210 | int ret; | ||
211 | |||
212 | pr_debug("%s enter\n", __func__); | ||
213 | bf52x_ad73311_snd_device = platform_device_alloc("soc-audio", -1); | ||
214 | if (!bf52x_ad73311_snd_device) | ||
215 | return -ENOMEM; | ||
216 | |||
217 | platform_set_drvdata(bf52x_ad73311_snd_device, &bf5xx_ad73311_snd_devdata); | ||
218 | bf5xx_ad73311_snd_devdata.dev = &bf52x_ad73311_snd_device->dev; | ||
219 | ret = platform_device_add(bf52x_ad73311_snd_device); | ||
220 | |||
221 | if (ret) | ||
222 | platform_device_put(bf52x_ad73311_snd_device); | ||
223 | |||
224 | return ret; | ||
225 | } | ||
226 | |||
227 | static void __exit bf5xx_ad73311_exit(void) | ||
228 | { | ||
229 | pr_debug("%s enter\n", __func__); | ||
230 | platform_device_unregister(bf52x_ad73311_snd_device); | ||
231 | } | ||
232 | |||
233 | module_init(bf5xx_ad73311_init); | ||
234 | module_exit(bf5xx_ad73311_exit); | ||
235 | |||
236 | /* Module information */ | ||
237 | MODULE_AUTHOR("Cliff Cai"); | ||
238 | MODULE_DESCRIPTION("ALSA SoC AD73311 Blackfin"); | ||
239 | MODULE_LICENSE("GPL"); | ||
240 | |||
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c new file mode 100644 index 000000000000..61fccf925192 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c | |||
@@ -0,0 +1,288 @@ | |||
1 | /* | ||
2 | * File: sound/soc/blackfin/bf5xx-i2s-pcm.c | ||
3 | * Author: Cliff Cai <Cliff.Cai@analog.com> | ||
4 | * | ||
5 | * Created: Tue June 06 2008 | ||
6 | * Description: DMA driver for i2s codec | ||
7 | * | ||
8 | * Modified: | ||
9 | * Copyright 2008 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/module.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/platform_device.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <linux/dma-mapping.h> | ||
34 | |||
35 | #include <sound/core.h> | ||
36 | #include <sound/pcm.h> | ||
37 | #include <sound/pcm_params.h> | ||
38 | #include <sound/soc.h> | ||
39 | |||
40 | #include <asm/dma.h> | ||
41 | |||
42 | #include "bf5xx-i2s-pcm.h" | ||
43 | #include "bf5xx-i2s.h" | ||
44 | #include "bf5xx-sport.h" | ||
45 | |||
46 | static void bf5xx_dma_irq(void *data) | ||
47 | { | ||
48 | struct snd_pcm_substream *pcm = data; | ||
49 | snd_pcm_period_elapsed(pcm); | ||
50 | } | ||
51 | |||
52 | static const struct snd_pcm_hardware bf5xx_pcm_hardware = { | ||
53 | .info = SNDRV_PCM_INFO_INTERLEAVED | | ||
54 | SNDRV_PCM_INFO_MMAP | | ||
55 | SNDRV_PCM_INFO_MMAP_VALID | | ||
56 | SNDRV_PCM_INFO_BLOCK_TRANSFER, | ||
57 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | ||
58 | SNDRV_PCM_FMTBIT_S24_LE | | ||
59 | SNDRV_PCM_FMTBIT_S32_LE, | ||
60 | .period_bytes_min = 32, | ||
61 | .period_bytes_max = 0x10000, | ||
62 | .periods_min = 1, | ||
63 | .periods_max = PAGE_SIZE/32, | ||
64 | .buffer_bytes_max = 0x20000, /* 128 kbytes */ | ||
65 | .fifo_size = 16, | ||
66 | }; | ||
67 | |||
68 | static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, | ||
69 | struct snd_pcm_hw_params *params) | ||
70 | { | ||
71 | size_t size = bf5xx_pcm_hardware.buffer_bytes_max; | ||
72 | snd_pcm_lib_malloc_pages(substream, size); | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) | ||
78 | { | ||
79 | snd_pcm_lib_free_pages(substream); | ||
80 | |||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) | ||
85 | { | ||
86 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
87 | struct sport_device *sport = runtime->private_data; | ||
88 | int period_bytes = frames_to_bytes(runtime, runtime->period_size); | ||
89 | |||
90 | pr_debug("%s enter\n", __func__); | ||
91 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
92 | sport_set_tx_callback(sport, bf5xx_dma_irq, substream); | ||
93 | sport_config_tx_dma(sport, runtime->dma_area, | ||
94 | runtime->periods, period_bytes); | ||
95 | } else { | ||
96 | sport_set_rx_callback(sport, bf5xx_dma_irq, substream); | ||
97 | sport_config_rx_dma(sport, runtime->dma_area, | ||
98 | runtime->periods, period_bytes); | ||
99 | } | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
105 | { | ||
106 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
107 | struct sport_device *sport = runtime->private_data; | ||
108 | int ret = 0; | ||
109 | |||
110 | pr_debug("%s enter\n", __func__); | ||
111 | switch (cmd) { | ||
112 | case SNDRV_PCM_TRIGGER_START: | ||
113 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
114 | sport_tx_start(sport); | ||
115 | else | ||
116 | sport_rx_start(sport); | ||
117 | break; | ||
118 | case SNDRV_PCM_TRIGGER_STOP: | ||
119 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
120 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
121 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
122 | sport_tx_stop(sport); | ||
123 | else | ||
124 | sport_rx_stop(sport); | ||
125 | break; | ||
126 | default: | ||
127 | ret = -EINVAL; | ||
128 | } | ||
129 | |||
130 | return ret; | ||
131 | } | ||
132 | |||
133 | static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) | ||
134 | { | ||
135 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
136 | struct sport_device *sport = runtime->private_data; | ||
137 | unsigned int diff; | ||
138 | snd_pcm_uframes_t frames; | ||
139 | pr_debug("%s enter\n", __func__); | ||
140 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
141 | diff = sport_curr_offset_tx(sport); | ||
142 | frames = bytes_to_frames(substream->runtime, diff); | ||
143 | } else { | ||
144 | diff = sport_curr_offset_rx(sport); | ||
145 | frames = bytes_to_frames(substream->runtime, diff); | ||
146 | } | ||
147 | return frames; | ||
148 | } | ||
149 | |||
150 | static int bf5xx_pcm_open(struct snd_pcm_substream *substream) | ||
151 | { | ||
152 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
153 | int ret; | ||
154 | |||
155 | pr_debug("%s enter\n", __func__); | ||
156 | snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); | ||
157 | |||
158 | ret = snd_pcm_hw_constraint_integer(runtime, \ | ||
159 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
160 | if (ret < 0) | ||
161 | goto out; | ||
162 | |||
163 | if (sport_handle != NULL) | ||
164 | runtime->private_data = sport_handle; | ||
165 | else { | ||
166 | pr_err("sport_handle is NULL\n"); | ||
167 | return -1; | ||
168 | } | ||
169 | return 0; | ||
170 | |||
171 | out: | ||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, | ||
176 | struct vm_area_struct *vma) | ||
177 | { | ||
178 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
179 | size_t size = vma->vm_end - vma->vm_start; | ||
180 | vma->vm_start = (unsigned long)runtime->dma_area; | ||
181 | vma->vm_end = vma->vm_start + size; | ||
182 | vma->vm_flags |= VM_SHARED; | ||
183 | |||
184 | return 0 ; | ||
185 | } | ||
186 | |||
187 | struct snd_pcm_ops bf5xx_pcm_i2s_ops = { | ||
188 | .open = bf5xx_pcm_open, | ||
189 | .ioctl = snd_pcm_lib_ioctl, | ||
190 | .hw_params = bf5xx_pcm_hw_params, | ||
191 | .hw_free = bf5xx_pcm_hw_free, | ||
192 | .prepare = bf5xx_pcm_prepare, | ||
193 | .trigger = bf5xx_pcm_trigger, | ||
194 | .pointer = bf5xx_pcm_pointer, | ||
195 | .mmap = bf5xx_pcm_mmap, | ||
196 | }; | ||
197 | |||
198 | static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | ||
199 | { | ||
200 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | ||
201 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
202 | size_t size = bf5xx_pcm_hardware.buffer_bytes_max; | ||
203 | |||
204 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
205 | buf->dev.dev = pcm->card->dev; | ||
206 | buf->private_data = NULL; | ||
207 | buf->area = dma_alloc_coherent(pcm->card->dev, size, | ||
208 | &buf->addr, GFP_KERNEL); | ||
209 | if (!buf->area) { | ||
210 | pr_err("Failed to allocate dma memory \ | ||
211 | Please increase uncached DMA memory region\n"); | ||
212 | return -ENOMEM; | ||
213 | } | ||
214 | buf->bytes = size; | ||
215 | |||
216 | pr_debug("%s, area:%p, size:0x%08lx\n", __func__, | ||
217 | buf->area, buf->bytes); | ||
218 | |||
219 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
220 | sport_handle->tx_buf = buf->area; | ||
221 | else | ||
222 | sport_handle->rx_buf = buf->area; | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) | ||
228 | { | ||
229 | struct snd_pcm_substream *substream; | ||
230 | struct snd_dma_buffer *buf; | ||
231 | int stream; | ||
232 | |||
233 | for (stream = 0; stream < 2; stream++) { | ||
234 | substream = pcm->streams[stream].substream; | ||
235 | if (!substream) | ||
236 | continue; | ||
237 | |||
238 | buf = &substream->dma_buffer; | ||
239 | if (!buf->area) | ||
240 | continue; | ||
241 | dma_free_coherent(NULL, buf->bytes, buf->area, 0); | ||
242 | buf->area = NULL; | ||
243 | } | ||
244 | if (sport_handle) | ||
245 | sport_done(sport_handle); | ||
246 | } | ||
247 | |||
248 | static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK; | ||
249 | |||
250 | int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai, | ||
251 | struct snd_pcm *pcm) | ||
252 | { | ||
253 | int ret = 0; | ||
254 | |||
255 | pr_debug("%s enter\n", __func__); | ||
256 | if (!card->dev->dma_mask) | ||
257 | card->dev->dma_mask = &bf5xx_pcm_dmamask; | ||
258 | if (!card->dev->coherent_dma_mask) | ||
259 | card->dev->coherent_dma_mask = DMA_32BIT_MASK; | ||
260 | |||
261 | if (dai->playback.channels_min) { | ||
262 | ret = bf5xx_pcm_preallocate_dma_buffer(pcm, | ||
263 | SNDRV_PCM_STREAM_PLAYBACK); | ||
264 | if (ret) | ||
265 | goto out; | ||
266 | } | ||
267 | |||
268 | if (dai->capture.channels_min) { | ||
269 | ret = bf5xx_pcm_preallocate_dma_buffer(pcm, | ||
270 | SNDRV_PCM_STREAM_CAPTURE); | ||
271 | if (ret) | ||
272 | goto out; | ||
273 | } | ||
274 | out: | ||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | struct snd_soc_platform bf5xx_i2s_soc_platform = { | ||
279 | .name = "bf5xx-audio", | ||
280 | .pcm_ops = &bf5xx_pcm_i2s_ops, | ||
281 | .pcm_new = bf5xx_pcm_i2s_new, | ||
282 | .pcm_free = bf5xx_pcm_free_dma_buffers, | ||
283 | }; | ||
284 | EXPORT_SYMBOL_GPL(bf5xx_i2s_soc_platform); | ||
285 | |||
286 | MODULE_AUTHOR("Cliff Cai"); | ||
287 | MODULE_DESCRIPTION("ADI Blackfin I2S PCM DMA module"); | ||
288 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.h b/sound/soc/blackfin/bf5xx-i2s-pcm.h new file mode 100644 index 000000000000..4d4609a97c59 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * linux/sound/arm/bf5xx-i2s-pcm.h -- ALSA PCM interface for the Blackfin | ||
3 | * | ||
4 | * Copyright 2007 Analog Device Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #ifndef _BF5XX_I2S_PCM_H | ||
12 | #define _BF5XX_I2S_PCM_H | ||
13 | |||
14 | struct bf5xx_pcm_dma_params { | ||
15 | char *name; /* stream identifier */ | ||
16 | }; | ||
17 | |||
18 | struct bf5xx_gpio { | ||
19 | u32 sys; | ||
20 | u32 rx; | ||
21 | u32 tx; | ||
22 | u32 clk; | ||
23 | u32 frm; | ||
24 | }; | ||
25 | |||
26 | /* platform data */ | ||
27 | extern struct snd_soc_platform bf5xx_i2s_soc_platform; | ||
28 | |||
29 | #endif | ||
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c new file mode 100644 index 000000000000..827587f08180 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-i2s.c | |||
@@ -0,0 +1,311 @@ | |||
1 | /* | ||
2 | * File: sound/soc/blackfin/bf5xx-i2s.c | ||
3 | * Author: Cliff Cai <Cliff.Cai@analog.com> | ||
4 | * | ||
5 | * Created: Tue June 06 2008 | ||
6 | * Description: Blackfin I2S CPU DAI driver | ||
7 | * | ||
8 | * Modified: | ||
9 | * Copyright 2008 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/device.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <sound/core.h> | ||
34 | #include <sound/pcm.h> | ||
35 | #include <sound/pcm_params.h> | ||
36 | #include <sound/initval.h> | ||
37 | #include <sound/soc.h> | ||
38 | |||
39 | #include <asm/irq.h> | ||
40 | #include <asm/portmux.h> | ||
41 | #include <linux/mutex.h> | ||
42 | #include <linux/gpio.h> | ||
43 | |||
44 | #include "bf5xx-sport.h" | ||
45 | #include "bf5xx-i2s.h" | ||
46 | |||
47 | struct bf5xx_i2s_port { | ||
48 | u16 tcr1; | ||
49 | u16 rcr1; | ||
50 | u16 tcr2; | ||
51 | u16 rcr2; | ||
52 | int counter; | ||
53 | }; | ||
54 | |||
55 | static struct bf5xx_i2s_port bf5xx_i2s; | ||
56 | static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; | ||
57 | |||
58 | static struct sport_param sport_params[2] = { | ||
59 | { | ||
60 | .dma_rx_chan = CH_SPORT0_RX, | ||
61 | .dma_tx_chan = CH_SPORT0_TX, | ||
62 | .err_irq = IRQ_SPORT0_ERROR, | ||
63 | .regs = (struct sport_register *)SPORT0_TCR1, | ||
64 | }, | ||
65 | { | ||
66 | .dma_rx_chan = CH_SPORT1_RX, | ||
67 | .dma_tx_chan = CH_SPORT1_TX, | ||
68 | .err_irq = IRQ_SPORT1_ERROR, | ||
69 | .regs = (struct sport_register *)SPORT1_TCR1, | ||
70 | } | ||
71 | }; | ||
72 | |||
73 | static u16 sport_req[][7] = { | ||
74 | { P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, | ||
75 | P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}, | ||
76 | { P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, | ||
77 | P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}, | ||
78 | }; | ||
79 | |||
80 | static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, | ||
81 | unsigned int fmt) | ||
82 | { | ||
83 | int ret = 0; | ||
84 | |||
85 | /* interface format:support I2S,slave mode */ | ||
86 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
87 | case SND_SOC_DAIFMT_I2S: | ||
88 | bf5xx_i2s.tcr1 |= TFSR | TCKFE; | ||
89 | bf5xx_i2s.rcr1 |= RFSR | RCKFE; | ||
90 | bf5xx_i2s.tcr2 |= TSFSE; | ||
91 | bf5xx_i2s.rcr2 |= RSFSE; | ||
92 | break; | ||
93 | case SND_SOC_DAIFMT_DSP_A: | ||
94 | bf5xx_i2s.tcr1 |= TFSR; | ||
95 | bf5xx_i2s.rcr1 |= RFSR; | ||
96 | break; | ||
97 | case SND_SOC_DAIFMT_LEFT_J: | ||
98 | ret = -EINVAL; | ||
99 | break; | ||
100 | default: | ||
101 | ret = -EINVAL; | ||
102 | break; | ||
103 | } | ||
104 | |||
105 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
106 | case SND_SOC_DAIFMT_CBS_CFS: | ||
107 | ret = -EINVAL; | ||
108 | break; | ||
109 | case SND_SOC_DAIFMT_CBM_CFS: | ||
110 | ret = -EINVAL; | ||
111 | break; | ||
112 | case SND_SOC_DAIFMT_CBM_CFM: | ||
113 | break; | ||
114 | case SND_SOC_DAIFMT_CBS_CFM: | ||
115 | ret = -EINVAL; | ||
116 | break; | ||
117 | default: | ||
118 | ret = -EINVAL; | ||
119 | break; | ||
120 | } | ||
121 | |||
122 | return ret; | ||
123 | } | ||
124 | |||
125 | static int bf5xx_i2s_startup(struct snd_pcm_substream *substream) | ||
126 | { | ||
127 | pr_debug("%s enter\n", __func__); | ||
128 | |||
129 | /*this counter is used for counting how many pcm streams are opened*/ | ||
130 | bf5xx_i2s.counter++; | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream, | ||
135 | struct snd_pcm_hw_params *params) | ||
136 | { | ||
137 | int ret = 0; | ||
138 | |||
139 | bf5xx_i2s.tcr2 &= ~0x1f; | ||
140 | bf5xx_i2s.rcr2 &= ~0x1f; | ||
141 | switch (params_format(params)) { | ||
142 | case SNDRV_PCM_FORMAT_S16_LE: | ||
143 | bf5xx_i2s.tcr2 |= 15; | ||
144 | bf5xx_i2s.rcr2 |= 15; | ||
145 | sport_handle->wdsize = 2; | ||
146 | break; | ||
147 | case SNDRV_PCM_FORMAT_S24_LE: | ||
148 | bf5xx_i2s.tcr2 |= 23; | ||
149 | bf5xx_i2s.rcr2 |= 23; | ||
150 | sport_handle->wdsize = 3; | ||
151 | break; | ||
152 | case SNDRV_PCM_FORMAT_S32_LE: | ||
153 | bf5xx_i2s.tcr2 |= 31; | ||
154 | bf5xx_i2s.rcr2 |= 31; | ||
155 | sport_handle->wdsize = 4; | ||
156 | break; | ||
157 | } | ||
158 | |||
159 | if (bf5xx_i2s.counter == 1) { | ||
160 | /* | ||
161 | * TX and RX are not independent,they are enabled at the | ||
162 | * same time, even if only one side is running. So, we | ||
163 | * need to configure both of them at the time when the first | ||
164 | * stream is opened. | ||
165 | * | ||
166 | * CPU DAI:slave mode. | ||
167 | */ | ||
168 | ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1, | ||
169 | bf5xx_i2s.rcr2, 0, 0); | ||
170 | if (ret) { | ||
171 | pr_err("SPORT is busy!\n"); | ||
172 | return -EBUSY; | ||
173 | } | ||
174 | |||
175 | ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1, | ||
176 | bf5xx_i2s.tcr2, 0, 0); | ||
177 | if (ret) { | ||
178 | pr_err("SPORT is busy!\n"); | ||
179 | return -EBUSY; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream) | ||
187 | { | ||
188 | pr_debug("%s enter\n", __func__); | ||
189 | bf5xx_i2s.counter--; | ||
190 | } | ||
191 | |||
192 | static int bf5xx_i2s_probe(struct platform_device *pdev, | ||
193 | struct snd_soc_dai *dai) | ||
194 | { | ||
195 | pr_debug("%s enter\n", __func__); | ||
196 | if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { | ||
197 | pr_err("Requesting Peripherals failed\n"); | ||
198 | return -EFAULT; | ||
199 | } | ||
200 | |||
201 | /* request DMA for SPORT */ | ||
202 | sport_handle = sport_init(&sport_params[sport_num], 4, \ | ||
203 | 2 * sizeof(u32), NULL); | ||
204 | if (!sport_handle) { | ||
205 | peripheral_free_list(&sport_req[sport_num][0]); | ||
206 | return -ENODEV; | ||
207 | } | ||
208 | |||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static void bf5xx_i2s_remove(struct platform_device *pdev, | ||
213 | struct snd_soc_dai *dai) | ||
214 | { | ||
215 | pr_debug("%s enter\n", __func__); | ||
216 | peripheral_free_list(&sport_req[sport_num][0]); | ||
217 | } | ||
218 | |||
219 | #ifdef CONFIG_PM | ||
220 | static int bf5xx_i2s_suspend(struct platform_device *dev, | ||
221 | struct snd_soc_dai *dai) | ||
222 | { | ||
223 | struct sport_device *sport = | ||
224 | (struct sport_device *)dai->private_data; | ||
225 | |||
226 | pr_debug("%s : sport %d\n", __func__, dai->id); | ||
227 | if (!dai->active) | ||
228 | return 0; | ||
229 | if (dai->capture.active) | ||
230 | sport_rx_stop(sport); | ||
231 | if (dai->playback.active) | ||
232 | sport_tx_stop(sport); | ||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | static int bf5xx_i2s_resume(struct platform_device *pdev, | ||
237 | struct snd_soc_dai *dai) | ||
238 | { | ||
239 | int ret; | ||
240 | struct sport_device *sport = | ||
241 | (struct sport_device *)dai->private_data; | ||
242 | |||
243 | pr_debug("%s : sport %d\n", __func__, dai->id); | ||
244 | if (!dai->active) | ||
245 | return 0; | ||
246 | |||
247 | ret = sport_config_rx(sport_handle, RFSR | RCKFE, RSFSE|0x1f, 0, 0); | ||
248 | if (ret) { | ||
249 | pr_err("SPORT is busy!\n"); | ||
250 | return -EBUSY; | ||
251 | } | ||
252 | |||
253 | ret = sport_config_tx(sport_handle, TFSR | TCKFE, TSFSE|0x1f, 0, 0); | ||
254 | if (ret) { | ||
255 | pr_err("SPORT is busy!\n"); | ||
256 | return -EBUSY; | ||
257 | } | ||
258 | |||
259 | if (dai->capture.active) | ||
260 | sport_rx_start(sport); | ||
261 | if (dai->playback.active) | ||
262 | sport_tx_start(sport); | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | #else | ||
267 | #define bf5xx_i2s_suspend NULL | ||
268 | #define bf5xx_i2s_resume NULL | ||
269 | #endif | ||
270 | |||
271 | #define BF5XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ | ||
272 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ | ||
273 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ | ||
274 | SNDRV_PCM_RATE_96000) | ||
275 | |||
276 | #define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ | ||
277 | SNDRV_PCM_FMTBIT_S32_LE) | ||
278 | |||
279 | struct snd_soc_dai bf5xx_i2s_dai = { | ||
280 | .name = "bf5xx-i2s", | ||
281 | .id = 0, | ||
282 | .type = SND_SOC_DAI_I2S, | ||
283 | .probe = bf5xx_i2s_probe, | ||
284 | .remove = bf5xx_i2s_remove, | ||
285 | .suspend = bf5xx_i2s_suspend, | ||
286 | .resume = bf5xx_i2s_resume, | ||
287 | .playback = { | ||
288 | .channels_min = 1, | ||
289 | .channels_max = 2, | ||
290 | .rates = BF5XX_I2S_RATES, | ||
291 | .formats = BF5XX_I2S_FORMATS,}, | ||
292 | .capture = { | ||
293 | .channels_min = 1, | ||
294 | .channels_max = 2, | ||
295 | .rates = BF5XX_I2S_RATES, | ||
296 | .formats = BF5XX_I2S_FORMATS,}, | ||
297 | .ops = { | ||
298 | .startup = bf5xx_i2s_startup, | ||
299 | .shutdown = bf5xx_i2s_shutdown, | ||
300 | .hw_params = bf5xx_i2s_hw_params,}, | ||
301 | .dai_ops = { | ||
302 | .set_fmt = bf5xx_i2s_set_dai_fmt, | ||
303 | }, | ||
304 | }; | ||
305 | EXPORT_SYMBOL_GPL(bf5xx_i2s_dai); | ||
306 | |||
307 | /* Module information */ | ||
308 | MODULE_AUTHOR("Cliff Cai"); | ||
309 | MODULE_DESCRIPTION("I2S driver for ADI Blackfin"); | ||
310 | MODULE_LICENSE("GPL"); | ||
311 | |||
diff --git a/sound/soc/blackfin/bf5xx-i2s.h b/sound/soc/blackfin/bf5xx-i2s.h new file mode 100644 index 000000000000..7107d1a0b06b --- /dev/null +++ b/sound/soc/blackfin/bf5xx-i2s.h | |||
@@ -0,0 +1,14 @@ | |||
1 | /* | ||
2 | * linux/sound/arm/bf5xx-i2s.h | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef _BF5XX_I2S_H | ||
10 | #define _BF5XX_I2S_H | ||
11 | |||
12 | extern struct snd_soc_dai bf5xx_i2s_dai; | ||
13 | |||
14 | #endif | ||
diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c new file mode 100644 index 000000000000..3b99e484d555 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-sport.c | |||
@@ -0,0 +1,1032 @@ | |||
1 | /* | ||
2 | * File: bf5xx_sport.c | ||
3 | * Based on: | ||
4 | * Author: Roy Huang <roy.huang@analog.com> | ||
5 | * | ||
6 | * Created: Tue Sep 21 10:52:42 CEST 2004 | ||
7 | * Description: | ||
8 | * Blackfin SPORT Driver | ||
9 | * | ||
10 | * Copyright 2004-2007 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #include <linux/kernel.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/dma-mapping.h> | ||
34 | #include <linux/gpio.h> | ||
35 | #include <linux/bug.h> | ||
36 | #include <asm/portmux.h> | ||
37 | #include <asm/dma.h> | ||
38 | #include <asm/blackfin.h> | ||
39 | #include <asm/cacheflush.h> | ||
40 | |||
41 | #include "bf5xx-sport.h" | ||
42 | /* delay between frame sync pulse and first data bit in multichannel mode */ | ||
43 | #define FRAME_DELAY (1<<12) | ||
44 | |||
45 | struct sport_device *sport_handle; | ||
46 | EXPORT_SYMBOL(sport_handle); | ||
47 | /* note: multichannel is in units of 8 channels, | ||
48 | * tdm_count is # channels NOT / 8 ! */ | ||
49 | int sport_set_multichannel(struct sport_device *sport, | ||
50 | int tdm_count, u32 mask, int packed) | ||
51 | { | ||
52 | pr_debug("%s tdm_count=%d mask:0x%08x packed=%d\n", __func__, | ||
53 | tdm_count, mask, packed); | ||
54 | |||
55 | if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN)) | ||
56 | return -EBUSY; | ||
57 | |||
58 | if (tdm_count & 0x7) | ||
59 | return -EINVAL; | ||
60 | |||
61 | if (tdm_count > 32) | ||
62 | return -EINVAL; /* Only support less than 32 channels now */ | ||
63 | |||
64 | if (tdm_count) { | ||
65 | sport->regs->mcmc1 = ((tdm_count>>3)-1) << 12; | ||
66 | sport->regs->mcmc2 = FRAME_DELAY | MCMEN | \ | ||
67 | (packed ? (MCDTXPE|MCDRXPE) : 0); | ||
68 | |||
69 | sport->regs->mtcs0 = mask; | ||
70 | sport->regs->mrcs0 = mask; | ||
71 | sport->regs->mtcs1 = 0; | ||
72 | sport->regs->mrcs1 = 0; | ||
73 | sport->regs->mtcs2 = 0; | ||
74 | sport->regs->mrcs2 = 0; | ||
75 | sport->regs->mtcs3 = 0; | ||
76 | sport->regs->mrcs3 = 0; | ||
77 | } else { | ||
78 | sport->regs->mcmc1 = 0; | ||
79 | sport->regs->mcmc2 = 0; | ||
80 | |||
81 | sport->regs->mtcs0 = 0; | ||
82 | sport->regs->mrcs0 = 0; | ||
83 | } | ||
84 | |||
85 | sport->regs->mtcs1 = 0; sport->regs->mtcs2 = 0; sport->regs->mtcs3 = 0; | ||
86 | sport->regs->mrcs1 = 0; sport->regs->mrcs2 = 0; sport->regs->mrcs3 = 0; | ||
87 | |||
88 | SSYNC(); | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | EXPORT_SYMBOL(sport_set_multichannel); | ||
93 | |||
94 | int sport_config_rx(struct sport_device *sport, unsigned int rcr1, | ||
95 | unsigned int rcr2, unsigned int clkdiv, unsigned int fsdiv) | ||
96 | { | ||
97 | if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN)) | ||
98 | return -EBUSY; | ||
99 | |||
100 | sport->regs->rcr1 = rcr1; | ||
101 | sport->regs->rcr2 = rcr2; | ||
102 | sport->regs->rclkdiv = clkdiv; | ||
103 | sport->regs->rfsdiv = fsdiv; | ||
104 | |||
105 | SSYNC(); | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | EXPORT_SYMBOL(sport_config_rx); | ||
110 | |||
111 | int sport_config_tx(struct sport_device *sport, unsigned int tcr1, | ||
112 | unsigned int tcr2, unsigned int clkdiv, unsigned int fsdiv) | ||
113 | { | ||
114 | if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN)) | ||
115 | return -EBUSY; | ||
116 | |||
117 | sport->regs->tcr1 = tcr1; | ||
118 | sport->regs->tcr2 = tcr2; | ||
119 | sport->regs->tclkdiv = clkdiv; | ||
120 | sport->regs->tfsdiv = fsdiv; | ||
121 | |||
122 | SSYNC(); | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | EXPORT_SYMBOL(sport_config_tx); | ||
127 | |||
128 | static void setup_desc(struct dmasg *desc, void *buf, int fragcount, | ||
129 | size_t fragsize, unsigned int cfg, | ||
130 | unsigned int x_count, unsigned int ycount, size_t wdsize) | ||
131 | { | ||
132 | |||
133 | int i; | ||
134 | |||
135 | for (i = 0; i < fragcount; ++i) { | ||
136 | desc[i].next_desc_addr = (unsigned long)&(desc[i + 1]); | ||
137 | desc[i].start_addr = (unsigned long)buf + i*fragsize; | ||
138 | desc[i].cfg = cfg; | ||
139 | desc[i].x_count = x_count; | ||
140 | desc[i].x_modify = wdsize; | ||
141 | desc[i].y_count = ycount; | ||
142 | desc[i].y_modify = wdsize; | ||
143 | } | ||
144 | |||
145 | /* make circular */ | ||
146 | desc[fragcount-1].next_desc_addr = (unsigned long)desc; | ||
147 | |||
148 | pr_debug("setup desc: desc0=%p, next0=%lx, desc1=%p," | ||
149 | "next1=%lx\nx_count=%x,y_count=%x,addr=0x%lx,cfs=0x%x\n", | ||
150 | &(desc[0]), desc[0].next_desc_addr, | ||
151 | &(desc[1]), desc[1].next_desc_addr, | ||
152 | desc[0].x_count, desc[0].y_count, | ||
153 | desc[0].start_addr, desc[0].cfg); | ||
154 | } | ||
155 | |||
156 | static int sport_start(struct sport_device *sport) | ||
157 | { | ||
158 | enable_dma(sport->dma_rx_chan); | ||
159 | enable_dma(sport->dma_tx_chan); | ||
160 | sport->regs->rcr1 |= RSPEN; | ||
161 | sport->regs->tcr1 |= TSPEN; | ||
162 | SSYNC(); | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static int sport_stop(struct sport_device *sport) | ||
168 | { | ||
169 | sport->regs->tcr1 &= ~TSPEN; | ||
170 | sport->regs->rcr1 &= ~RSPEN; | ||
171 | SSYNC(); | ||
172 | |||
173 | disable_dma(sport->dma_rx_chan); | ||
174 | disable_dma(sport->dma_tx_chan); | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static inline int sport_hook_rx_dummy(struct sport_device *sport) | ||
179 | { | ||
180 | struct dmasg *desc, temp_desc; | ||
181 | unsigned long flags; | ||
182 | |||
183 | BUG_ON(sport->dummy_rx_desc == NULL); | ||
184 | BUG_ON(sport->curr_rx_desc == sport->dummy_rx_desc); | ||
185 | |||
186 | /* Maybe the dummy buffer descriptor ring is damaged */ | ||
187 | sport->dummy_rx_desc->next_desc_addr = \ | ||
188 | (unsigned long)(sport->dummy_rx_desc+1); | ||
189 | |||
190 | local_irq_save(flags); | ||
191 | desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_rx_chan); | ||
192 | /* Copy the descriptor which will be damaged to backup */ | ||
193 | temp_desc = *desc; | ||
194 | desc->x_count = 0xa; | ||
195 | desc->y_count = 0; | ||
196 | desc->next_desc_addr = (unsigned long)(sport->dummy_rx_desc); | ||
197 | local_irq_restore(flags); | ||
198 | /* Waiting for dummy buffer descriptor is already hooked*/ | ||
199 | while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) - | ||
200 | sizeof(struct dmasg)) != | ||
201 | (unsigned long)sport->dummy_rx_desc) | ||
202 | ; | ||
203 | sport->curr_rx_desc = sport->dummy_rx_desc; | ||
204 | /* Restore the damaged descriptor */ | ||
205 | *desc = temp_desc; | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static inline int sport_rx_dma_start(struct sport_device *sport, int dummy) | ||
211 | { | ||
212 | if (dummy) { | ||
213 | sport->dummy_rx_desc->next_desc_addr = \ | ||
214 | (unsigned long) sport->dummy_rx_desc; | ||
215 | sport->curr_rx_desc = sport->dummy_rx_desc; | ||
216 | } else | ||
217 | sport->curr_rx_desc = sport->dma_rx_desc; | ||
218 | |||
219 | set_dma_next_desc_addr(sport->dma_rx_chan, \ | ||
220 | (unsigned long)(sport->curr_rx_desc)); | ||
221 | set_dma_x_count(sport->dma_rx_chan, 0); | ||
222 | set_dma_x_modify(sport->dma_rx_chan, 0); | ||
223 | set_dma_config(sport->dma_rx_chan, (DMAFLOW_LARGE | NDSIZE_9 | \ | ||
224 | WDSIZE_32 | WNR)); | ||
225 | set_dma_curr_addr(sport->dma_rx_chan, sport->curr_rx_desc->start_addr); | ||
226 | SSYNC(); | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static inline int sport_tx_dma_start(struct sport_device *sport, int dummy) | ||
232 | { | ||
233 | if (dummy) { | ||
234 | sport->dummy_tx_desc->next_desc_addr = \ | ||
235 | (unsigned long) sport->dummy_tx_desc; | ||
236 | sport->curr_tx_desc = sport->dummy_tx_desc; | ||
237 | } else | ||
238 | sport->curr_tx_desc = sport->dma_tx_desc; | ||
239 | |||
240 | set_dma_next_desc_addr(sport->dma_tx_chan, \ | ||
241 | (unsigned long)(sport->curr_tx_desc)); | ||
242 | set_dma_x_count(sport->dma_tx_chan, 0); | ||
243 | set_dma_x_modify(sport->dma_tx_chan, 0); | ||
244 | set_dma_config(sport->dma_tx_chan, | ||
245 | (DMAFLOW_LARGE | NDSIZE_9 | WDSIZE_32)); | ||
246 | set_dma_curr_addr(sport->dma_tx_chan, sport->curr_tx_desc->start_addr); | ||
247 | SSYNC(); | ||
248 | |||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | int sport_rx_start(struct sport_device *sport) | ||
253 | { | ||
254 | unsigned long flags; | ||
255 | pr_debug("%s enter\n", __func__); | ||
256 | if (sport->rx_run) | ||
257 | return -EBUSY; | ||
258 | if (sport->tx_run) { | ||
259 | /* tx is running, rx is not running */ | ||
260 | BUG_ON(sport->dma_rx_desc == NULL); | ||
261 | BUG_ON(sport->curr_rx_desc != sport->dummy_rx_desc); | ||
262 | local_irq_save(flags); | ||
263 | while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) - | ||
264 | sizeof(struct dmasg)) != | ||
265 | (unsigned long)sport->dummy_rx_desc) | ||
266 | ; | ||
267 | sport->dummy_rx_desc->next_desc_addr = | ||
268 | (unsigned long)(sport->dma_rx_desc); | ||
269 | local_irq_restore(flags); | ||
270 | sport->curr_rx_desc = sport->dma_rx_desc; | ||
271 | } else { | ||
272 | sport_tx_dma_start(sport, 1); | ||
273 | sport_rx_dma_start(sport, 0); | ||
274 | sport_start(sport); | ||
275 | } | ||
276 | |||
277 | sport->rx_run = 1; | ||
278 | |||
279 | return 0; | ||
280 | } | ||
281 | EXPORT_SYMBOL(sport_rx_start); | ||
282 | |||
283 | int sport_rx_stop(struct sport_device *sport) | ||
284 | { | ||
285 | pr_debug("%s enter\n", __func__); | ||
286 | |||
287 | if (!sport->rx_run) | ||
288 | return 0; | ||
289 | if (sport->tx_run) { | ||
290 | /* TX dma is still running, hook the dummy buffer */ | ||
291 | sport_hook_rx_dummy(sport); | ||
292 | } else { | ||
293 | /* Both rx and tx dma will be stopped */ | ||
294 | sport_stop(sport); | ||
295 | sport->curr_rx_desc = NULL; | ||
296 | sport->curr_tx_desc = NULL; | ||
297 | } | ||
298 | |||
299 | sport->rx_run = 0; | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | EXPORT_SYMBOL(sport_rx_stop); | ||
304 | |||
305 | static inline int sport_hook_tx_dummy(struct sport_device *sport) | ||
306 | { | ||
307 | struct dmasg *desc, temp_desc; | ||
308 | unsigned long flags; | ||
309 | |||
310 | BUG_ON(sport->dummy_tx_desc == NULL); | ||
311 | BUG_ON(sport->curr_tx_desc == sport->dummy_tx_desc); | ||
312 | |||
313 | sport->dummy_tx_desc->next_desc_addr = \ | ||
314 | (unsigned long)(sport->dummy_tx_desc+1); | ||
315 | |||
316 | /* Shorten the time on last normal descriptor */ | ||
317 | local_irq_save(flags); | ||
318 | desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_tx_chan); | ||
319 | /* Store the descriptor which will be damaged */ | ||
320 | temp_desc = *desc; | ||
321 | desc->x_count = 0xa; | ||
322 | desc->y_count = 0; | ||
323 | desc->next_desc_addr = (unsigned long)(sport->dummy_tx_desc); | ||
324 | local_irq_restore(flags); | ||
325 | /* Waiting for dummy buffer descriptor is already hooked*/ | ||
326 | while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) - \ | ||
327 | sizeof(struct dmasg)) != \ | ||
328 | (unsigned long)sport->dummy_tx_desc) | ||
329 | ; | ||
330 | sport->curr_tx_desc = sport->dummy_tx_desc; | ||
331 | /* Restore the damaged descriptor */ | ||
332 | *desc = temp_desc; | ||
333 | |||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | int sport_tx_start(struct sport_device *sport) | ||
338 | { | ||
339 | unsigned flags; | ||
340 | pr_debug("%s: tx_run:%d, rx_run:%d\n", __func__, | ||
341 | sport->tx_run, sport->rx_run); | ||
342 | if (sport->tx_run) | ||
343 | return -EBUSY; | ||
344 | if (sport->rx_run) { | ||
345 | BUG_ON(sport->dma_tx_desc == NULL); | ||
346 | BUG_ON(sport->curr_tx_desc != sport->dummy_tx_desc); | ||
347 | /* Hook the normal buffer descriptor */ | ||
348 | local_irq_save(flags); | ||
349 | while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) - | ||
350 | sizeof(struct dmasg)) != | ||
351 | (unsigned long)sport->dummy_tx_desc) | ||
352 | ; | ||
353 | sport->dummy_tx_desc->next_desc_addr = | ||
354 | (unsigned long)(sport->dma_tx_desc); | ||
355 | local_irq_restore(flags); | ||
356 | sport->curr_tx_desc = sport->dma_tx_desc; | ||
357 | } else { | ||
358 | |||
359 | sport_tx_dma_start(sport, 0); | ||
360 | /* Let rx dma run the dummy buffer */ | ||
361 | sport_rx_dma_start(sport, 1); | ||
362 | sport_start(sport); | ||
363 | } | ||
364 | sport->tx_run = 1; | ||
365 | return 0; | ||
366 | } | ||
367 | EXPORT_SYMBOL(sport_tx_start); | ||
368 | |||
369 | int sport_tx_stop(struct sport_device *sport) | ||
370 | { | ||
371 | if (!sport->tx_run) | ||
372 | return 0; | ||
373 | if (sport->rx_run) { | ||
374 | /* RX is still running, hook the dummy buffer */ | ||
375 | sport_hook_tx_dummy(sport); | ||
376 | } else { | ||
377 | /* Both rx and tx dma stopped */ | ||
378 | sport_stop(sport); | ||
379 | sport->curr_rx_desc = NULL; | ||
380 | sport->curr_tx_desc = NULL; | ||
381 | } | ||
382 | |||
383 | sport->tx_run = 0; | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | EXPORT_SYMBOL(sport_tx_stop); | ||
388 | |||
389 | static inline int compute_wdsize(size_t wdsize) | ||
390 | { | ||
391 | switch (wdsize) { | ||
392 | case 1: | ||
393 | return WDSIZE_8; | ||
394 | case 2: | ||
395 | return WDSIZE_16; | ||
396 | case 4: | ||
397 | default: | ||
398 | return WDSIZE_32; | ||
399 | } | ||
400 | } | ||
401 | |||
402 | int sport_config_rx_dma(struct sport_device *sport, void *buf, | ||
403 | int fragcount, size_t fragsize) | ||
404 | { | ||
405 | unsigned int x_count; | ||
406 | unsigned int y_count; | ||
407 | unsigned int cfg; | ||
408 | dma_addr_t addr; | ||
409 | |||
410 | pr_debug("%s buf:%p, frag:%d, fragsize:0x%lx\n", __func__, \ | ||
411 | buf, fragcount, fragsize); | ||
412 | |||
413 | x_count = fragsize / sport->wdsize; | ||
414 | y_count = 0; | ||
415 | |||
416 | /* for fragments larger than 64k words we use 2d dma, | ||
417 | * denote fragecount as two numbers' mutliply and both of them | ||
418 | * are less than 64k.*/ | ||
419 | if (x_count >= 0x10000) { | ||
420 | int i, count = x_count; | ||
421 | |||
422 | for (i = 16; i > 0; i--) { | ||
423 | x_count = 1 << i; | ||
424 | if ((count & (x_count - 1)) == 0) { | ||
425 | y_count = count >> i; | ||
426 | if (y_count < 0x10000) | ||
427 | break; | ||
428 | } | ||
429 | } | ||
430 | if (i == 0) | ||
431 | return -EINVAL; | ||
432 | } | ||
433 | pr_debug("%s(x_count:0x%x, y_count:0x%x)\n", __func__, | ||
434 | x_count, y_count); | ||
435 | |||
436 | if (sport->dma_rx_desc) | ||
437 | dma_free_coherent(NULL, sport->rx_desc_bytes, | ||
438 | sport->dma_rx_desc, 0); | ||
439 | |||
440 | /* Allocate a new descritor ring as current one. */ | ||
441 | sport->dma_rx_desc = dma_alloc_coherent(NULL, \ | ||
442 | fragcount * sizeof(struct dmasg), &addr, 0); | ||
443 | sport->rx_desc_bytes = fragcount * sizeof(struct dmasg); | ||
444 | |||
445 | if (!sport->dma_rx_desc) { | ||
446 | pr_err("Failed to allocate memory for rx desc\n"); | ||
447 | return -ENOMEM; | ||
448 | } | ||
449 | |||
450 | sport->rx_buf = buf; | ||
451 | sport->rx_fragsize = fragsize; | ||
452 | sport->rx_frags = fragcount; | ||
453 | |||
454 | cfg = 0x7000 | DI_EN | compute_wdsize(sport->wdsize) | WNR | \ | ||
455 | (DESC_ELEMENT_COUNT << 8); /* large descriptor mode */ | ||
456 | |||
457 | if (y_count != 0) | ||
458 | cfg |= DMA2D; | ||
459 | |||
460 | setup_desc(sport->dma_rx_desc, buf, fragcount, fragsize, | ||
461 | cfg|DMAEN, x_count, y_count, sport->wdsize); | ||
462 | |||
463 | return 0; | ||
464 | } | ||
465 | EXPORT_SYMBOL(sport_config_rx_dma); | ||
466 | |||
467 | int sport_config_tx_dma(struct sport_device *sport, void *buf, \ | ||
468 | int fragcount, size_t fragsize) | ||
469 | { | ||
470 | unsigned int x_count; | ||
471 | unsigned int y_count; | ||
472 | unsigned int cfg; | ||
473 | dma_addr_t addr; | ||
474 | |||
475 | pr_debug("%s buf:%p, fragcount:%d, fragsize:0x%lx\n", | ||
476 | __func__, buf, fragcount, fragsize); | ||
477 | |||
478 | x_count = fragsize/sport->wdsize; | ||
479 | y_count = 0; | ||
480 | |||
481 | /* for fragments larger than 64k words we use 2d dma, | ||
482 | * denote fragecount as two numbers' mutliply and both of them | ||
483 | * are less than 64k.*/ | ||
484 | if (x_count >= 0x10000) { | ||
485 | int i, count = x_count; | ||
486 | |||
487 | for (i = 16; i > 0; i--) { | ||
488 | x_count = 1 << i; | ||
489 | if ((count & (x_count - 1)) == 0) { | ||
490 | y_count = count >> i; | ||
491 | if (y_count < 0x10000) | ||
492 | break; | ||
493 | } | ||
494 | } | ||
495 | if (i == 0) | ||
496 | return -EINVAL; | ||
497 | } | ||
498 | pr_debug("%s x_count:0x%x, y_count:0x%x\n", __func__, | ||
499 | x_count, y_count); | ||
500 | |||
501 | |||
502 | if (sport->dma_tx_desc) { | ||
503 | dma_free_coherent(NULL, sport->tx_desc_bytes, \ | ||
504 | sport->dma_tx_desc, 0); | ||
505 | } | ||
506 | |||
507 | sport->dma_tx_desc = dma_alloc_coherent(NULL, \ | ||
508 | fragcount * sizeof(struct dmasg), &addr, 0); | ||
509 | sport->tx_desc_bytes = fragcount * sizeof(struct dmasg); | ||
510 | if (!sport->dma_tx_desc) { | ||
511 | pr_err("Failed to allocate memory for tx desc\n"); | ||
512 | return -ENOMEM; | ||
513 | } | ||
514 | |||
515 | sport->tx_buf = buf; | ||
516 | sport->tx_fragsize = fragsize; | ||
517 | sport->tx_frags = fragcount; | ||
518 | cfg = 0x7000 | DI_EN | compute_wdsize(sport->wdsize) | \ | ||
519 | (DESC_ELEMENT_COUNT << 8); /* large descriptor mode */ | ||
520 | |||
521 | if (y_count != 0) | ||
522 | cfg |= DMA2D; | ||
523 | |||
524 | setup_desc(sport->dma_tx_desc, buf, fragcount, fragsize, | ||
525 | cfg|DMAEN, x_count, y_count, sport->wdsize); | ||
526 | |||
527 | return 0; | ||
528 | } | ||
529 | EXPORT_SYMBOL(sport_config_tx_dma); | ||
530 | |||
531 | /* setup dummy dma descriptor ring, which don't generate interrupts, | ||
532 | * the x_modify is set to 0 */ | ||
533 | static int sport_config_rx_dummy(struct sport_device *sport) | ||
534 | { | ||
535 | struct dmasg *desc; | ||
536 | unsigned config; | ||
537 | |||
538 | pr_debug("%s entered\n", __func__); | ||
539 | #if L1_DATA_A_LENGTH != 0 | ||
540 | desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc)); | ||
541 | #else | ||
542 | { | ||
543 | dma_addr_t addr; | ||
544 | desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0); | ||
545 | } | ||
546 | #endif | ||
547 | if (desc == NULL) { | ||
548 | pr_err("Failed to allocate memory for dummy rx desc\n"); | ||
549 | return -ENOMEM; | ||
550 | } | ||
551 | memset(desc, 0, 2 * sizeof(*desc)); | ||
552 | sport->dummy_rx_desc = desc; | ||
553 | desc->start_addr = (unsigned long)sport->dummy_buf; | ||
554 | config = DMAFLOW_LARGE | NDSIZE_9 | compute_wdsize(sport->wdsize) | ||
555 | | WNR | DMAEN; | ||
556 | desc->cfg = config; | ||
557 | desc->x_count = sport->dummy_count/sport->wdsize; | ||
558 | desc->x_modify = sport->wdsize; | ||
559 | desc->y_count = 0; | ||
560 | desc->y_modify = 0; | ||
561 | memcpy(desc+1, desc, sizeof(*desc)); | ||
562 | desc->next_desc_addr = (unsigned long)(desc+1); | ||
563 | desc[1].next_desc_addr = (unsigned long)desc; | ||
564 | return 0; | ||
565 | } | ||
566 | |||
567 | static int sport_config_tx_dummy(struct sport_device *sport) | ||
568 | { | ||
569 | struct dmasg *desc; | ||
570 | unsigned int config; | ||
571 | |||
572 | pr_debug("%s entered\n", __func__); | ||
573 | |||
574 | #if L1_DATA_A_LENGTH != 0 | ||
575 | desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc)); | ||
576 | #else | ||
577 | { | ||
578 | dma_addr_t addr; | ||
579 | desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0); | ||
580 | } | ||
581 | #endif | ||
582 | if (!desc) { | ||
583 | pr_err("Failed to allocate memory for dummy tx desc\n"); | ||
584 | return -ENOMEM; | ||
585 | } | ||
586 | memset(desc, 0, 2 * sizeof(*desc)); | ||
587 | sport->dummy_tx_desc = desc; | ||
588 | desc->start_addr = (unsigned long)sport->dummy_buf + \ | ||
589 | sport->dummy_count; | ||
590 | config = DMAFLOW_LARGE | NDSIZE_9 | | ||
591 | compute_wdsize(sport->wdsize) | DMAEN; | ||
592 | desc->cfg = config; | ||
593 | desc->x_count = sport->dummy_count/sport->wdsize; | ||
594 | desc->x_modify = sport->wdsize; | ||
595 | desc->y_count = 0; | ||
596 | desc->y_modify = 0; | ||
597 | memcpy(desc+1, desc, sizeof(*desc)); | ||
598 | desc->next_desc_addr = (unsigned long)(desc+1); | ||
599 | desc[1].next_desc_addr = (unsigned long)desc; | ||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | unsigned long sport_curr_offset_rx(struct sport_device *sport) | ||
604 | { | ||
605 | unsigned long curr = get_dma_curr_addr(sport->dma_rx_chan); | ||
606 | |||
607 | return (unsigned char *)curr - sport->rx_buf; | ||
608 | } | ||
609 | EXPORT_SYMBOL(sport_curr_offset_rx); | ||
610 | |||
611 | unsigned long sport_curr_offset_tx(struct sport_device *sport) | ||
612 | { | ||
613 | unsigned long curr = get_dma_curr_addr(sport->dma_tx_chan); | ||
614 | |||
615 | return (unsigned char *)curr - sport->tx_buf; | ||
616 | } | ||
617 | EXPORT_SYMBOL(sport_curr_offset_tx); | ||
618 | |||
619 | void sport_incfrag(struct sport_device *sport, int *frag, int tx) | ||
620 | { | ||
621 | ++(*frag); | ||
622 | if (tx == 1 && *frag == sport->tx_frags) | ||
623 | *frag = 0; | ||
624 | |||
625 | if (tx == 0 && *frag == sport->rx_frags) | ||
626 | *frag = 0; | ||
627 | } | ||
628 | EXPORT_SYMBOL(sport_incfrag); | ||
629 | |||
630 | void sport_decfrag(struct sport_device *sport, int *frag, int tx) | ||
631 | { | ||
632 | --(*frag); | ||
633 | if (tx == 1 && *frag == 0) | ||
634 | *frag = sport->tx_frags; | ||
635 | |||
636 | if (tx == 0 && *frag == 0) | ||
637 | *frag = sport->rx_frags; | ||
638 | } | ||
639 | EXPORT_SYMBOL(sport_decfrag); | ||
640 | |||
641 | static int sport_check_status(struct sport_device *sport, | ||
642 | unsigned int *sport_stat, | ||
643 | unsigned int *rx_stat, | ||
644 | unsigned int *tx_stat) | ||
645 | { | ||
646 | int status = 0; | ||
647 | |||
648 | if (sport_stat) { | ||
649 | SSYNC(); | ||
650 | status = sport->regs->stat; | ||
651 | if (status & (TOVF|TUVF|ROVF|RUVF)) | ||
652 | sport->regs->stat = (status & (TOVF|TUVF|ROVF|RUVF)); | ||
653 | SSYNC(); | ||
654 | *sport_stat = status; | ||
655 | } | ||
656 | |||
657 | if (rx_stat) { | ||
658 | SSYNC(); | ||
659 | status = get_dma_curr_irqstat(sport->dma_rx_chan); | ||
660 | if (status & (DMA_DONE|DMA_ERR)) | ||
661 | clear_dma_irqstat(sport->dma_rx_chan); | ||
662 | SSYNC(); | ||
663 | *rx_stat = status; | ||
664 | } | ||
665 | |||
666 | if (tx_stat) { | ||
667 | SSYNC(); | ||
668 | status = get_dma_curr_irqstat(sport->dma_tx_chan); | ||
669 | if (status & (DMA_DONE|DMA_ERR)) | ||
670 | clear_dma_irqstat(sport->dma_tx_chan); | ||
671 | SSYNC(); | ||
672 | *tx_stat = status; | ||
673 | } | ||
674 | |||
675 | return 0; | ||
676 | } | ||
677 | |||
678 | int sport_dump_stat(struct sport_device *sport, char *buf, size_t len) | ||
679 | { | ||
680 | int ret; | ||
681 | |||
682 | ret = snprintf(buf, len, | ||
683 | "sts: 0x%04x\n" | ||
684 | "rx dma %d sts: 0x%04x tx dma %d sts: 0x%04x\n", | ||
685 | sport->regs->stat, | ||
686 | sport->dma_rx_chan, | ||
687 | get_dma_curr_irqstat(sport->dma_rx_chan), | ||
688 | sport->dma_tx_chan, | ||
689 | get_dma_curr_irqstat(sport->dma_tx_chan)); | ||
690 | buf += ret; | ||
691 | len -= ret; | ||
692 | |||
693 | ret += snprintf(buf, len, | ||
694 | "curr_rx_desc:0x%p, curr_tx_desc:0x%p\n" | ||
695 | "dma_rx_desc:0x%p, dma_tx_desc:0x%p\n" | ||
696 | "dummy_rx_desc:0x%p, dummy_tx_desc:0x%p\n", | ||
697 | sport->curr_rx_desc, sport->curr_tx_desc, | ||
698 | sport->dma_rx_desc, sport->dma_tx_desc, | ||
699 | sport->dummy_rx_desc, sport->dummy_tx_desc); | ||
700 | |||
701 | return ret; | ||
702 | } | ||
703 | |||
704 | static irqreturn_t rx_handler(int irq, void *dev_id) | ||
705 | { | ||
706 | unsigned int rx_stat; | ||
707 | struct sport_device *sport = dev_id; | ||
708 | |||
709 | pr_debug("%s enter\n", __func__); | ||
710 | sport_check_status(sport, NULL, &rx_stat, NULL); | ||
711 | if (!(rx_stat & DMA_DONE)) | ||
712 | pr_err("rx dma is already stopped\n"); | ||
713 | |||
714 | if (sport->rx_callback) { | ||
715 | sport->rx_callback(sport->rx_data); | ||
716 | return IRQ_HANDLED; | ||
717 | } | ||
718 | |||
719 | return IRQ_NONE; | ||
720 | } | ||
721 | |||
722 | static irqreturn_t tx_handler(int irq, void *dev_id) | ||
723 | { | ||
724 | unsigned int tx_stat; | ||
725 | struct sport_device *sport = dev_id; | ||
726 | pr_debug("%s enter\n", __func__); | ||
727 | sport_check_status(sport, NULL, NULL, &tx_stat); | ||
728 | if (!(tx_stat & DMA_DONE)) { | ||
729 | pr_err("tx dma is already stopped\n"); | ||
730 | return IRQ_HANDLED; | ||
731 | } | ||
732 | if (sport->tx_callback) { | ||
733 | sport->tx_callback(sport->tx_data); | ||
734 | return IRQ_HANDLED; | ||
735 | } | ||
736 | |||
737 | return IRQ_NONE; | ||
738 | } | ||
739 | |||
740 | static irqreturn_t err_handler(int irq, void *dev_id) | ||
741 | { | ||
742 | unsigned int status = 0; | ||
743 | struct sport_device *sport = dev_id; | ||
744 | |||
745 | pr_debug("%s\n", __func__); | ||
746 | if (sport_check_status(sport, &status, NULL, NULL)) { | ||
747 | pr_err("error checking status ??"); | ||
748 | return IRQ_NONE; | ||
749 | } | ||
750 | |||
751 | if (status & (TOVF|TUVF|ROVF|RUVF)) { | ||
752 | pr_info("sport status error:%s%s%s%s\n", | ||
753 | status & TOVF ? " TOVF" : "", | ||
754 | status & TUVF ? " TUVF" : "", | ||
755 | status & ROVF ? " ROVF" : "", | ||
756 | status & RUVF ? " RUVF" : ""); | ||
757 | if (status & TOVF || status & TUVF) { | ||
758 | disable_dma(sport->dma_tx_chan); | ||
759 | if (sport->tx_run) | ||
760 | sport_tx_dma_start(sport, 0); | ||
761 | else | ||
762 | sport_tx_dma_start(sport, 1); | ||
763 | enable_dma(sport->dma_tx_chan); | ||
764 | } else { | ||
765 | disable_dma(sport->dma_rx_chan); | ||
766 | if (sport->rx_run) | ||
767 | sport_rx_dma_start(sport, 0); | ||
768 | else | ||
769 | sport_rx_dma_start(sport, 1); | ||
770 | enable_dma(sport->dma_rx_chan); | ||
771 | } | ||
772 | } | ||
773 | status = sport->regs->stat; | ||
774 | if (status & (TOVF|TUVF|ROVF|RUVF)) | ||
775 | sport->regs->stat = (status & (TOVF|TUVF|ROVF|RUVF)); | ||
776 | SSYNC(); | ||
777 | |||
778 | if (sport->err_callback) | ||
779 | sport->err_callback(sport->err_data); | ||
780 | |||
781 | return IRQ_HANDLED; | ||
782 | } | ||
783 | |||
784 | int sport_set_rx_callback(struct sport_device *sport, | ||
785 | void (*rx_callback)(void *), void *rx_data) | ||
786 | { | ||
787 | BUG_ON(rx_callback == NULL); | ||
788 | sport->rx_callback = rx_callback; | ||
789 | sport->rx_data = rx_data; | ||
790 | |||
791 | return 0; | ||
792 | } | ||
793 | EXPORT_SYMBOL(sport_set_rx_callback); | ||
794 | |||
795 | int sport_set_tx_callback(struct sport_device *sport, | ||
796 | void (*tx_callback)(void *), void *tx_data) | ||
797 | { | ||
798 | BUG_ON(tx_callback == NULL); | ||
799 | sport->tx_callback = tx_callback; | ||
800 | sport->tx_data = tx_data; | ||
801 | |||
802 | return 0; | ||
803 | } | ||
804 | EXPORT_SYMBOL(sport_set_tx_callback); | ||
805 | |||
806 | int sport_set_err_callback(struct sport_device *sport, | ||
807 | void (*err_callback)(void *), void *err_data) | ||
808 | { | ||
809 | BUG_ON(err_callback == NULL); | ||
810 | sport->err_callback = err_callback; | ||
811 | sport->err_data = err_data; | ||
812 | |||
813 | return 0; | ||
814 | } | ||
815 | EXPORT_SYMBOL(sport_set_err_callback); | ||
816 | |||
817 | struct sport_device *sport_init(struct sport_param *param, unsigned wdsize, | ||
818 | unsigned dummy_count, void *private_data) | ||
819 | { | ||
820 | int ret; | ||
821 | struct sport_device *sport; | ||
822 | pr_debug("%s enter\n", __func__); | ||
823 | BUG_ON(param == NULL); | ||
824 | BUG_ON(wdsize == 0 || dummy_count == 0); | ||
825 | sport = kmalloc(sizeof(struct sport_device), GFP_KERNEL); | ||
826 | if (!sport) { | ||
827 | pr_err("Failed to allocate for sport device\n"); | ||
828 | return NULL; | ||
829 | } | ||
830 | |||
831 | memset(sport, 0, sizeof(struct sport_device)); | ||
832 | sport->dma_rx_chan = param->dma_rx_chan; | ||
833 | sport->dma_tx_chan = param->dma_tx_chan; | ||
834 | sport->err_irq = param->err_irq; | ||
835 | sport->regs = param->regs; | ||
836 | sport->private_data = private_data; | ||
837 | |||
838 | if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) { | ||
839 | pr_err("Failed to request RX dma %d\n", \ | ||
840 | sport->dma_rx_chan); | ||
841 | goto __init_err1; | ||
842 | } | ||
843 | if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) { | ||
844 | pr_err("Failed to request RX irq %d\n", \ | ||
845 | sport->dma_rx_chan); | ||
846 | goto __init_err2; | ||
847 | } | ||
848 | |||
849 | if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) { | ||
850 | pr_err("Failed to request TX dma %d\n", \ | ||
851 | sport->dma_tx_chan); | ||
852 | goto __init_err2; | ||
853 | } | ||
854 | |||
855 | if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) { | ||
856 | pr_err("Failed to request TX irq %d\n", \ | ||
857 | sport->dma_tx_chan); | ||
858 | goto __init_err3; | ||
859 | } | ||
860 | |||
861 | if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err", | ||
862 | sport) < 0) { | ||
863 | pr_err("Failed to request err irq:%d\n", \ | ||
864 | sport->err_irq); | ||
865 | goto __init_err3; | ||
866 | } | ||
867 | |||
868 | pr_err("dma rx:%d tx:%d, err irq:%d, regs:%p\n", | ||
869 | sport->dma_rx_chan, sport->dma_tx_chan, | ||
870 | sport->err_irq, sport->regs); | ||
871 | |||
872 | sport->wdsize = wdsize; | ||
873 | sport->dummy_count = dummy_count; | ||
874 | |||
875 | #if L1_DATA_A_LENGTH != 0 | ||
876 | sport->dummy_buf = l1_data_sram_alloc(dummy_count * 2); | ||
877 | #else | ||
878 | sport->dummy_buf = kmalloc(dummy_count * 2, GFP_KERNEL); | ||
879 | #endif | ||
880 | if (sport->dummy_buf == NULL) { | ||
881 | pr_err("Failed to allocate dummy buffer\n"); | ||
882 | goto __error; | ||
883 | } | ||
884 | |||
885 | memset(sport->dummy_buf, 0, dummy_count * 2); | ||
886 | ret = sport_config_rx_dummy(sport); | ||
887 | if (ret) { | ||
888 | pr_err("Failed to config rx dummy ring\n"); | ||
889 | goto __error; | ||
890 | } | ||
891 | ret = sport_config_tx_dummy(sport); | ||
892 | if (ret) { | ||
893 | pr_err("Failed to config tx dummy ring\n"); | ||
894 | goto __error; | ||
895 | } | ||
896 | |||
897 | return sport; | ||
898 | __error: | ||
899 | free_irq(sport->err_irq, sport); | ||
900 | __init_err3: | ||
901 | free_dma(sport->dma_tx_chan); | ||
902 | __init_err2: | ||
903 | free_dma(sport->dma_rx_chan); | ||
904 | __init_err1: | ||
905 | kfree(sport); | ||
906 | return NULL; | ||
907 | } | ||
908 | EXPORT_SYMBOL(sport_init); | ||
909 | |||
910 | void sport_done(struct sport_device *sport) | ||
911 | { | ||
912 | if (sport == NULL) | ||
913 | return; | ||
914 | |||
915 | sport_stop(sport); | ||
916 | if (sport->dma_rx_desc) | ||
917 | dma_free_coherent(NULL, sport->rx_desc_bytes, | ||
918 | sport->dma_rx_desc, 0); | ||
919 | if (sport->dma_tx_desc) | ||
920 | dma_free_coherent(NULL, sport->tx_desc_bytes, | ||
921 | sport->dma_tx_desc, 0); | ||
922 | |||
923 | #if L1_DATA_A_LENGTH != 0 | ||
924 | l1_data_sram_free(sport->dummy_rx_desc); | ||
925 | l1_data_sram_free(sport->dummy_tx_desc); | ||
926 | l1_data_sram_free(sport->dummy_buf); | ||
927 | #else | ||
928 | dma_free_coherent(NULL, 2*sizeof(struct dmasg), | ||
929 | sport->dummy_rx_desc, 0); | ||
930 | dma_free_coherent(NULL, 2*sizeof(struct dmasg), | ||
931 | sport->dummy_tx_desc, 0); | ||
932 | kfree(sport->dummy_buf); | ||
933 | #endif | ||
934 | free_dma(sport->dma_rx_chan); | ||
935 | free_dma(sport->dma_tx_chan); | ||
936 | free_irq(sport->err_irq, sport); | ||
937 | |||
938 | kfree(sport); | ||
939 | sport = NULL; | ||
940 | } | ||
941 | EXPORT_SYMBOL(sport_done); | ||
942 | /* | ||
943 | * It is only used to send several bytes when dma is not enabled | ||
944 | * sport controller is configured but not enabled. | ||
945 | * Multichannel cannot works with pio mode */ | ||
946 | /* Used by ac97 to write and read codec register */ | ||
947 | int sport_send_and_recv(struct sport_device *sport, u8 *out_data, \ | ||
948 | u8 *in_data, int len) | ||
949 | { | ||
950 | unsigned short dma_config; | ||
951 | unsigned short status; | ||
952 | unsigned long flags; | ||
953 | unsigned long wait = 0; | ||
954 | |||
955 | pr_debug("%s enter, out_data:%p, in_data:%p len:%d\n", \ | ||
956 | __func__, out_data, in_data, len); | ||
957 | pr_debug("tcr1:0x%04x, tcr2:0x%04x, tclkdiv:0x%04x, tfsdiv:0x%04x\n" | ||
958 | "mcmc1:0x%04x, mcmc2:0x%04x\n", | ||
959 | sport->regs->tcr1, sport->regs->tcr2, | ||
960 | sport->regs->tclkdiv, sport->regs->tfsdiv, | ||
961 | sport->regs->mcmc1, sport->regs->mcmc2); | ||
962 | flush_dcache_range((unsigned)out_data, (unsigned)(out_data + len)); | ||
963 | |||
964 | /* Enable tx dma */ | ||
965 | dma_config = (RESTART | WDSIZE_16 | DI_EN); | ||
966 | set_dma_start_addr(sport->dma_tx_chan, (unsigned long)out_data); | ||
967 | set_dma_x_count(sport->dma_tx_chan, len/2); | ||
968 | set_dma_x_modify(sport->dma_tx_chan, 2); | ||
969 | set_dma_config(sport->dma_tx_chan, dma_config); | ||
970 | enable_dma(sport->dma_tx_chan); | ||
971 | |||
972 | if (in_data != NULL) { | ||
973 | invalidate_dcache_range((unsigned)in_data, \ | ||
974 | (unsigned)(in_data + len)); | ||
975 | /* Enable rx dma */ | ||
976 | dma_config = (RESTART | WDSIZE_16 | WNR | DI_EN); | ||
977 | set_dma_start_addr(sport->dma_rx_chan, (unsigned long)in_data); | ||
978 | set_dma_x_count(sport->dma_rx_chan, len/2); | ||
979 | set_dma_x_modify(sport->dma_rx_chan, 2); | ||
980 | set_dma_config(sport->dma_rx_chan, dma_config); | ||
981 | enable_dma(sport->dma_rx_chan); | ||
982 | } | ||
983 | |||
984 | local_irq_save(flags); | ||
985 | sport->regs->tcr1 |= TSPEN; | ||
986 | sport->regs->rcr1 |= RSPEN; | ||
987 | SSYNC(); | ||
988 | |||
989 | status = get_dma_curr_irqstat(sport->dma_tx_chan); | ||
990 | while (status & DMA_RUN) { | ||
991 | udelay(1); | ||
992 | status = get_dma_curr_irqstat(sport->dma_tx_chan); | ||
993 | pr_debug("DMA status:0x%04x\n", status); | ||
994 | if (wait++ > 100) | ||
995 | goto __over; | ||
996 | } | ||
997 | status = sport->regs->stat; | ||
998 | wait = 0; | ||
999 | |||
1000 | while (!(status & TXHRE)) { | ||
1001 | pr_debug("sport status:0x%04x\n", status); | ||
1002 | udelay(1); | ||
1003 | status = *(unsigned short *)&sport->regs->stat; | ||
1004 | if (wait++ > 1000) | ||
1005 | goto __over; | ||
1006 | } | ||
1007 | /* Wait for the last byte sent out */ | ||
1008 | udelay(20); | ||
1009 | pr_debug("sport status:0x%04x\n", status); | ||
1010 | |||
1011 | __over: | ||
1012 | sport->regs->tcr1 &= ~TSPEN; | ||
1013 | sport->regs->rcr1 &= ~RSPEN; | ||
1014 | SSYNC(); | ||
1015 | disable_dma(sport->dma_tx_chan); | ||
1016 | /* Clear the status */ | ||
1017 | clear_dma_irqstat(sport->dma_tx_chan); | ||
1018 | if (in_data != NULL) { | ||
1019 | disable_dma(sport->dma_rx_chan); | ||
1020 | clear_dma_irqstat(sport->dma_rx_chan); | ||
1021 | } | ||
1022 | SSYNC(); | ||
1023 | local_irq_restore(flags); | ||
1024 | |||
1025 | return 0; | ||
1026 | } | ||
1027 | EXPORT_SYMBOL(sport_send_and_recv); | ||
1028 | |||
1029 | MODULE_AUTHOR("Roy Huang"); | ||
1030 | MODULE_DESCRIPTION("SPORT driver for ADI Blackfin"); | ||
1031 | MODULE_LICENSE("GPL"); | ||
1032 | |||
diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h new file mode 100644 index 000000000000..fcadcc081f7f --- /dev/null +++ b/sound/soc/blackfin/bf5xx-sport.h | |||
@@ -0,0 +1,194 @@ | |||
1 | /* | ||
2 | * File: bf5xx_ac97_sport.h | ||
3 | * Based on: | ||
4 | * Author: Roy Huang <roy.huang@analog.com> | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: | ||
8 | * | ||
9 | * Copyright 2004-2007 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 | |||
30 | #ifndef __BF5XX_SPORT_H__ | ||
31 | #define __BF5XX_SPORT_H__ | ||
32 | |||
33 | #include <linux/types.h> | ||
34 | #include <linux/wait.h> | ||
35 | #include <linux/workqueue.h> | ||
36 | #include <asm/dma.h> | ||
37 | |||
38 | struct sport_register { | ||
39 | u16 tcr1; u16 reserved0; | ||
40 | u16 tcr2; u16 reserved1; | ||
41 | u16 tclkdiv; u16 reserved2; | ||
42 | u16 tfsdiv; u16 reserved3; | ||
43 | u32 tx; | ||
44 | u32 reserved_l0; | ||
45 | u32 rx; | ||
46 | u32 reserved_l1; | ||
47 | u16 rcr1; u16 reserved4; | ||
48 | u16 rcr2; u16 reserved5; | ||
49 | u16 rclkdiv; u16 reserved6; | ||
50 | u16 rfsdiv; u16 reserved7; | ||
51 | u16 stat; u16 reserved8; | ||
52 | u16 chnl; u16 reserved9; | ||
53 | u16 mcmc1; u16 reserved10; | ||
54 | u16 mcmc2; u16 reserved11; | ||
55 | u32 mtcs0; | ||
56 | u32 mtcs1; | ||
57 | u32 mtcs2; | ||
58 | u32 mtcs3; | ||
59 | u32 mrcs0; | ||
60 | u32 mrcs1; | ||
61 | u32 mrcs2; | ||
62 | u32 mrcs3; | ||
63 | }; | ||
64 | |||
65 | #define DESC_ELEMENT_COUNT 9 | ||
66 | |||
67 | struct sport_device { | ||
68 | int dma_rx_chan; | ||
69 | int dma_tx_chan; | ||
70 | int err_irq; | ||
71 | struct sport_register *regs; | ||
72 | |||
73 | unsigned char *rx_buf; | ||
74 | unsigned char *tx_buf; | ||
75 | unsigned int rx_fragsize; | ||
76 | unsigned int tx_fragsize; | ||
77 | unsigned int rx_frags; | ||
78 | unsigned int tx_frags; | ||
79 | unsigned int wdsize; | ||
80 | |||
81 | /* for dummy dma transfer */ | ||
82 | void *dummy_buf; | ||
83 | unsigned int dummy_count; | ||
84 | |||
85 | /* DMA descriptor ring head of current audio stream*/ | ||
86 | struct dmasg *dma_rx_desc; | ||
87 | struct dmasg *dma_tx_desc; | ||
88 | unsigned int rx_desc_bytes; | ||
89 | unsigned int tx_desc_bytes; | ||
90 | |||
91 | unsigned int rx_run:1; /* rx is running */ | ||
92 | unsigned int tx_run:1; /* tx is running */ | ||
93 | |||
94 | struct dmasg *dummy_rx_desc; | ||
95 | struct dmasg *dummy_tx_desc; | ||
96 | |||
97 | struct dmasg *curr_rx_desc; | ||
98 | struct dmasg *curr_tx_desc; | ||
99 | |||
100 | int rx_curr_frag; | ||
101 | int tx_curr_frag; | ||
102 | |||
103 | unsigned int rcr1; | ||
104 | unsigned int rcr2; | ||
105 | int rx_tdm_count; | ||
106 | |||
107 | unsigned int tcr1; | ||
108 | unsigned int tcr2; | ||
109 | int tx_tdm_count; | ||
110 | |||
111 | void (*rx_callback)(void *data); | ||
112 | void *rx_data; | ||
113 | void (*tx_callback)(void *data); | ||
114 | void *tx_data; | ||
115 | void (*err_callback)(void *data); | ||
116 | void *err_data; | ||
117 | unsigned char *tx_dma_buf; | ||
118 | unsigned char *rx_dma_buf; | ||
119 | #ifdef CONFIG_SND_MMAP_SUPPORT | ||
120 | dma_addr_t tx_dma_phy; | ||
121 | dma_addr_t rx_dma_phy; | ||
122 | int tx_pos;/*pcm sample count*/ | ||
123 | int rx_pos; | ||
124 | unsigned int tx_buffer_size; | ||
125 | unsigned int rx_buffer_size; | ||
126 | int tx_delay_pos; | ||
127 | int once; | ||
128 | #endif | ||
129 | void *private_data; | ||
130 | }; | ||
131 | |||
132 | extern struct sport_device *sport_handle; | ||
133 | |||
134 | struct sport_param { | ||
135 | int dma_rx_chan; | ||
136 | int dma_tx_chan; | ||
137 | int err_irq; | ||
138 | struct sport_register *regs; | ||
139 | }; | ||
140 | |||
141 | struct sport_device *sport_init(struct sport_param *param, unsigned wdsize, | ||
142 | unsigned dummy_count, void *private_data); | ||
143 | |||
144 | void sport_done(struct sport_device *sport); | ||
145 | |||
146 | /* first use these ...*/ | ||
147 | |||
148 | /* note: multichannel is in units of 8 channels, tdm_count is number of channels | ||
149 | * NOT / 8 ! all channels are enabled by default */ | ||
150 | int sport_set_multichannel(struct sport_device *sport, int tdm_count, | ||
151 | u32 mask, int packed); | ||
152 | |||
153 | int sport_config_rx(struct sport_device *sport, | ||
154 | unsigned int rcr1, unsigned int rcr2, | ||
155 | unsigned int clkdiv, unsigned int fsdiv); | ||
156 | |||
157 | int sport_config_tx(struct sport_device *sport, | ||
158 | unsigned int tcr1, unsigned int tcr2, | ||
159 | unsigned int clkdiv, unsigned int fsdiv); | ||
160 | |||
161 | /* ... then these: */ | ||
162 | |||
163 | /* buffer size (in bytes) == fragcount * fragsize_bytes */ | ||
164 | |||
165 | /* this is not a very general api, it sets the dma to 2d autobuffer mode */ | ||
166 | |||
167 | int sport_config_rx_dma(struct sport_device *sport, void *buf, | ||
168 | int fragcount, size_t fragsize_bytes); | ||
169 | |||
170 | int sport_config_tx_dma(struct sport_device *sport, void *buf, | ||
171 | int fragcount, size_t fragsize_bytes); | ||
172 | |||
173 | int sport_tx_start(struct sport_device *sport); | ||
174 | int sport_tx_stop(struct sport_device *sport); | ||
175 | int sport_rx_start(struct sport_device *sport); | ||
176 | int sport_rx_stop(struct sport_device *sport); | ||
177 | |||
178 | /* for use in interrupt handler */ | ||
179 | unsigned long sport_curr_offset_rx(struct sport_device *sport); | ||
180 | unsigned long sport_curr_offset_tx(struct sport_device *sport); | ||
181 | |||
182 | void sport_incfrag(struct sport_device *sport, int *frag, int tx); | ||
183 | void sport_decfrag(struct sport_device *sport, int *frag, int tx); | ||
184 | |||
185 | int sport_set_rx_callback(struct sport_device *sport, | ||
186 | void (*rx_callback)(void *), void *rx_data); | ||
187 | int sport_set_tx_callback(struct sport_device *sport, | ||
188 | void (*tx_callback)(void *), void *tx_data); | ||
189 | int sport_set_err_callback(struct sport_device *sport, | ||
190 | void (*err_callback)(void *), void *err_data); | ||
191 | |||
192 | int sport_send_and_recv(struct sport_device *sport, u8 *out_data, \ | ||
193 | u8 *in_data, int len); | ||
194 | #endif /* BF53X_SPORT_H */ | ||
diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c new file mode 100644 index 000000000000..e15f67fd7769 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ssm2602.c | |||
@@ -0,0 +1,186 @@ | |||
1 | /* | ||
2 | * File: sound/soc/blackfin/bf5xx-ssm2602.c | ||
3 | * Author: Cliff Cai <Cliff.Cai@analog.com> | ||
4 | * | ||
5 | * Created: Tue June 06 2008 | ||
6 | * Description: board driver for SSM2602 sound chip | ||
7 | * | ||
8 | * Modified: | ||
9 | * Copyright 2008 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/module.h> | ||
30 | #include <linux/moduleparam.h> | ||
31 | #include <linux/device.h> | ||
32 | |||
33 | #include <sound/core.h> | ||
34 | #include <sound/pcm.h> | ||
35 | #include <sound/soc.h> | ||
36 | #include <sound/soc-dapm.h> | ||
37 | #include <sound/pcm_params.h> | ||
38 | |||
39 | #include <asm/dma.h> | ||
40 | #include <asm/portmux.h> | ||
41 | #include <linux/gpio.h> | ||
42 | #include "../codecs/ssm2602.h" | ||
43 | #include "bf5xx-sport.h" | ||
44 | #include "bf5xx-i2s-pcm.h" | ||
45 | #include "bf5xx-i2s.h" | ||
46 | |||
47 | static struct snd_soc_machine bf5xx_ssm2602; | ||
48 | |||
49 | static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream) | ||
50 | { | ||
51 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
52 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
53 | |||
54 | pr_debug("%s enter\n", __func__); | ||
55 | cpu_dai->private_data = sport_handle; | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream, | ||
60 | struct snd_pcm_hw_params *params) | ||
61 | { | ||
62 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
63 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
64 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
65 | unsigned int clk = 0; | ||
66 | int ret = 0; | ||
67 | |||
68 | pr_debug("%s rate %d format %x\n", __func__, params_rate(params), | ||
69 | params_format(params)); | ||
70 | /* | ||
71 | * If you are using a crystal source which frequency is not 12MHz | ||
72 | * then modify the below case statement with frequency of the crystal. | ||
73 | * | ||
74 | * If you are using the SPORT to generate clocking then this is | ||
75 | * where to do it. | ||
76 | */ | ||
77 | |||
78 | switch (params_rate(params)) { | ||
79 | case 8000: | ||
80 | case 16000: | ||
81 | case 48000: | ||
82 | case 96000: | ||
83 | case 11025: | ||
84 | case 22050: | ||
85 | case 44100: | ||
86 | clk = 12000000; | ||
87 | break; | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * CODEC is master for BCLK and LRC in this configuration. | ||
92 | */ | ||
93 | |||
94 | /* set codec DAI configuration */ | ||
95 | ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
96 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||
97 | if (ret < 0) | ||
98 | return ret; | ||
99 | /* set cpu DAI configuration */ | ||
100 | ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
101 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||
102 | if (ret < 0) | ||
103 | return ret; | ||
104 | |||
105 | ret = codec_dai->dai_ops.set_sysclk(codec_dai, SSM2602_SYSCLK, clk, | ||
106 | SND_SOC_CLOCK_IN); | ||
107 | if (ret < 0) | ||
108 | return ret; | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static struct snd_soc_ops bf5xx_ssm2602_ops = { | ||
114 | .startup = bf5xx_ssm2602_startup, | ||
115 | .hw_params = bf5xx_ssm2602_hw_params, | ||
116 | }; | ||
117 | |||
118 | static struct snd_soc_dai_link bf5xx_ssm2602_dai = { | ||
119 | .name = "ssm2602", | ||
120 | .stream_name = "SSM2602", | ||
121 | .cpu_dai = &bf5xx_i2s_dai, | ||
122 | .codec_dai = &ssm2602_dai, | ||
123 | .ops = &bf5xx_ssm2602_ops, | ||
124 | }; | ||
125 | |||
126 | /* | ||
127 | * SSM2602 2 wire address is determined by CSB | ||
128 | * state during powerup. | ||
129 | * low = 0x1a | ||
130 | * high = 0x1b | ||
131 | */ | ||
132 | |||
133 | static struct ssm2602_setup_data bf5xx_ssm2602_setup = { | ||
134 | .i2c_bus = 0, | ||
135 | .i2c_address = 0x1b, | ||
136 | }; | ||
137 | |||
138 | static struct snd_soc_machine bf5xx_ssm2602 = { | ||
139 | .name = "bf5xx_ssm2602", | ||
140 | .dai_link = &bf5xx_ssm2602_dai, | ||
141 | .num_links = 1, | ||
142 | }; | ||
143 | |||
144 | static struct snd_soc_device bf5xx_ssm2602_snd_devdata = { | ||
145 | .machine = &bf5xx_ssm2602, | ||
146 | .platform = &bf5xx_i2s_soc_platform, | ||
147 | .codec_dev = &soc_codec_dev_ssm2602, | ||
148 | .codec_data = &bf5xx_ssm2602_setup, | ||
149 | }; | ||
150 | |||
151 | static struct platform_device *bf52x_ssm2602_snd_device; | ||
152 | |||
153 | static int __init bf5xx_ssm2602_init(void) | ||
154 | { | ||
155 | int ret; | ||
156 | |||
157 | pr_debug("%s enter\n", __func__); | ||
158 | bf52x_ssm2602_snd_device = platform_device_alloc("soc-audio", -1); | ||
159 | if (!bf52x_ssm2602_snd_device) | ||
160 | return -ENOMEM; | ||
161 | |||
162 | platform_set_drvdata(bf52x_ssm2602_snd_device, | ||
163 | &bf5xx_ssm2602_snd_devdata); | ||
164 | bf5xx_ssm2602_snd_devdata.dev = &bf52x_ssm2602_snd_device->dev; | ||
165 | ret = platform_device_add(bf52x_ssm2602_snd_device); | ||
166 | |||
167 | if (ret) | ||
168 | platform_device_put(bf52x_ssm2602_snd_device); | ||
169 | |||
170 | return ret; | ||
171 | } | ||
172 | |||
173 | static void __exit bf5xx_ssm2602_exit(void) | ||
174 | { | ||
175 | pr_debug("%s enter\n", __func__); | ||
176 | platform_device_unregister(bf52x_ssm2602_snd_device); | ||
177 | } | ||
178 | |||
179 | module_init(bf5xx_ssm2602_init); | ||
180 | module_exit(bf5xx_ssm2602_exit); | ||
181 | |||
182 | /* Module information */ | ||
183 | MODULE_AUTHOR("Cliff Cai"); | ||
184 | MODULE_DESCRIPTION("ALSA SoC SSM2602 BF527-EZKIT"); | ||
185 | MODULE_LICENSE("GPL"); | ||
186 | |||
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 1db04a28a53d..4975d8573e4f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -1,32 +1,45 @@ | |||
1 | config SND_SOC_AC97_CODEC | 1 | config SND_SOC_ALL_CODECS |
2 | tristate | 2 | tristate "Build all ASoC CODEC drivers" |
3 | select SND_AC97_CODEC | 3 | depends on I2C |
4 | 4 | select SPI | |
5 | config SND_SOC_AK4535 | 5 | select SPI_MASTER |
6 | tristate | 6 | select SND_SOC_AD73311 |
7 | 7 | select SND_SOC_AK4535 | |
8 | config SND_SOC_UDA1380 | 8 | select SND_SOC_CS4270 |
9 | tristate | 9 | select SND_SOC_SSM2602 |
10 | select SND_SOC_TLV320AIC23 | ||
11 | select SND_SOC_TLV320AIC26 | ||
12 | select SND_SOC_TLV320AIC3X | ||
13 | select SND_SOC_UDA1380 | ||
14 | select SND_SOC_WM8510 | ||
15 | select SND_SOC_WM8580 | ||
16 | select SND_SOC_WM8731 | ||
17 | select SND_SOC_WM8750 | ||
18 | select SND_SOC_WM8753 | ||
19 | select SND_SOC_WM8900 | ||
20 | select SND_SOC_WM8903 | ||
21 | select SND_SOC_WM8971 | ||
22 | select SND_SOC_WM8990 | ||
23 | help | ||
24 | Normally ASoC codec drivers are only built if a machine driver which | ||
25 | uses them is also built since they are only usable with a machine | ||
26 | driver. Selecting this option will allow these drivers to be built | ||
27 | without an explicit machine driver for test and development purposes. | ||
10 | 28 | ||
11 | config SND_SOC_WM8510 | 29 | If unsure select "N". |
12 | tristate | ||
13 | 30 | ||
14 | config SND_SOC_WM8731 | ||
15 | tristate | ||
16 | 31 | ||
17 | config SND_SOC_WM8750 | 32 | config SND_SOC_AC97_CODEC |
18 | tristate | ||
19 | |||
20 | config SND_SOC_WM8753 | ||
21 | tristate | 33 | tristate |
34 | select SND_AC97_CODEC | ||
22 | 35 | ||
23 | config SND_SOC_WM8990 | 36 | config SND_SOC_AD1980 |
24 | tristate | 37 | tristate |
25 | 38 | ||
26 | config SND_SOC_WM9712 | 39 | config SND_SOC_AD73311 |
27 | tristate | 40 | tristate |
28 | 41 | ||
29 | config SND_SOC_WM9713 | 42 | config SND_SOC_AK4535 |
30 | tristate | 43 | tristate |
31 | 44 | ||
32 | # Cirrus Logic CS4270 Codec | 45 | # Cirrus Logic CS4270 Codec |
@@ -47,6 +60,53 @@ config SND_SOC_CS4270_VD33_ERRATA | |||
47 | bool | 60 | bool |
48 | depends on SND_SOC_CS4270 | 61 | depends on SND_SOC_CS4270 |
49 | 62 | ||
63 | config SND_SOC_SSM2602 | ||
64 | tristate | ||
65 | |||
66 | config SND_SOC_TLV320AIC23 | ||
67 | tristate | ||
68 | depends on I2C | ||
69 | |||
70 | config SND_SOC_TLV320AIC26 | ||
71 | tristate "TI TLV320AIC26 Codec support" | ||
72 | depends on SPI | ||
73 | |||
50 | config SND_SOC_TLV320AIC3X | 74 | config SND_SOC_TLV320AIC3X |
51 | tristate | 75 | tristate |
52 | depends on I2C | 76 | depends on I2C |
77 | |||
78 | config SND_SOC_UDA1380 | ||
79 | tristate | ||
80 | |||
81 | config SND_SOC_WM8510 | ||
82 | tristate | ||
83 | |||
84 | config SND_SOC_WM8580 | ||
85 | tristate | ||
86 | |||
87 | config SND_SOC_WM8731 | ||
88 | tristate | ||
89 | |||
90 | config SND_SOC_WM8750 | ||
91 | tristate | ||
92 | |||
93 | config SND_SOC_WM8753 | ||
94 | tristate | ||
95 | |||
96 | config SND_SOC_WM8900 | ||
97 | tristate | ||
98 | |||
99 | config SND_SOC_WM8903 | ||
100 | tristate | ||
101 | |||
102 | config SND_SOC_WM8971 | ||
103 | tristate | ||
104 | |||
105 | config SND_SOC_WM8990 | ||
106 | tristate | ||
107 | |||
108 | config SND_SOC_WM9712 | ||
109 | tristate | ||
110 | |||
111 | config SND_SOC_WM9713 | ||
112 | tristate | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d7b97abcf729..90f0a585fc70 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -1,25 +1,43 @@ | |||
1 | snd-soc-ac97-objs := ac97.o | 1 | snd-soc-ac97-objs := ac97.o |
2 | snd-soc-ad1980-objs := ad1980.o | ||
3 | snd-soc-ad73311-objs := ad73311.o | ||
2 | snd-soc-ak4535-objs := ak4535.o | 4 | snd-soc-ak4535-objs := ak4535.o |
5 | snd-soc-cs4270-objs := cs4270.o | ||
6 | snd-soc-ssm2602-objs := ssm2602.o | ||
7 | snd-soc-tlv320aic23-objs := tlv320aic23.o | ||
8 | snd-soc-tlv320aic26-objs := tlv320aic26.o | ||
9 | snd-soc-tlv320aic3x-objs := tlv320aic3x.o | ||
3 | snd-soc-uda1380-objs := uda1380.o | 10 | snd-soc-uda1380-objs := uda1380.o |
4 | snd-soc-wm8510-objs := wm8510.o | 11 | snd-soc-wm8510-objs := wm8510.o |
12 | snd-soc-wm8580-objs := wm8580.o | ||
5 | snd-soc-wm8731-objs := wm8731.o | 13 | snd-soc-wm8731-objs := wm8731.o |
6 | snd-soc-wm8750-objs := wm8750.o | 14 | snd-soc-wm8750-objs := wm8750.o |
7 | snd-soc-wm8753-objs := wm8753.o | 15 | snd-soc-wm8753-objs := wm8753.o |
16 | snd-soc-wm8900-objs := wm8900.o | ||
17 | snd-soc-wm8903-objs := wm8903.o | ||
18 | snd-soc-wm8971-objs := wm8971.o | ||
8 | snd-soc-wm8990-objs := wm8990.o | 19 | snd-soc-wm8990-objs := wm8990.o |
9 | snd-soc-wm9712-objs := wm9712.o | 20 | snd-soc-wm9712-objs := wm9712.o |
10 | snd-soc-wm9713-objs := wm9713.o | 21 | snd-soc-wm9713-objs := wm9713.o |
11 | snd-soc-cs4270-objs := cs4270.o | ||
12 | snd-soc-tlv320aic3x-objs := tlv320aic3x.o | ||
13 | 22 | ||
14 | obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o | 23 | obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o |
24 | obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o | ||
25 | obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o | ||
15 | obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o | 26 | obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o |
27 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o | ||
28 | obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o | ||
29 | obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o | ||
30 | obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o | ||
31 | obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o | ||
16 | obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o | 32 | obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o |
17 | obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o | 33 | obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o |
34 | obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o | ||
18 | obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o | 35 | obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o |
19 | obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o | 36 | obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o |
20 | obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o | 37 | obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o |
38 | obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o | ||
39 | obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o | ||
40 | obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o | ||
21 | obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o | 41 | obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o |
22 | obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o | 42 | obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o |
23 | obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o | 43 | obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o |
24 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o | ||
25 | obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o | ||
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 61fd96ca7bc7..bd1ebdc6c86c 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c | |||
@@ -2,8 +2,7 @@ | |||
2 | * ac97.c -- ALSA Soc AC97 codec support | 2 | * ac97.c -- ALSA Soc AC97 codec support |
3 | * | 3 | * |
4 | * Copyright 2005 Wolfson Microelectronics PLC. | 4 | * Copyright 2005 Wolfson Microelectronics PLC. |
5 | * Author: Liam Girdwood | 5 | * Author: Liam Girdwood <lrg@slimlogic.co.uk> |
6 | * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com | ||
7 | * | 6 | * |
8 | * This program is free software; you can redistribute it and/or modify it | 7 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the | 8 | * under the terms of the GNU General Public License as published by the |
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c new file mode 100644 index 000000000000..1397b8e06c0b --- /dev/null +++ b/sound/soc/codecs/ad1980.c | |||
@@ -0,0 +1,308 @@ | |||
1 | /* | ||
2 | * ad1980.c -- ALSA Soc AD1980 codec support | ||
3 | * | ||
4 | * Copyright: Analog Device Inc. | ||
5 | * Author: Roy Huang <roy.huang@analog.com> | ||
6 | * Cliff Cai <cliff.cai@analog.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/init.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <sound/core.h> | ||
19 | #include <sound/pcm.h> | ||
20 | #include <sound/ac97_codec.h> | ||
21 | #include <sound/initval.h> | ||
22 | #include <sound/soc.h> | ||
23 | #include <sound/soc-dapm.h> | ||
24 | |||
25 | #include "ad1980.h" | ||
26 | |||
27 | static unsigned int ac97_read(struct snd_soc_codec *codec, | ||
28 | unsigned int reg); | ||
29 | static int ac97_write(struct snd_soc_codec *codec, | ||
30 | unsigned int reg, unsigned int val); | ||
31 | |||
32 | /* | ||
33 | * AD1980 register cache | ||
34 | */ | ||
35 | static const u16 ad1980_reg[] = { | ||
36 | 0x0090, 0x8000, 0x8000, 0x8000, /* 0 - 6 */ | ||
37 | 0x0000, 0x0000, 0x8008, 0x8008, /* 8 - e */ | ||
38 | 0x8808, 0x8808, 0x0000, 0x8808, /* 10 - 16 */ | ||
39 | 0x8808, 0x0000, 0x8000, 0x0000, /* 18 - 1e */ | ||
40 | 0x0000, 0x0000, 0x0000, 0x0000, /* 20 - 26 */ | ||
41 | 0x03c7, 0x0000, 0xbb80, 0xbb80, /* 28 - 2e */ | ||
42 | 0xbb80, 0xbb80, 0x0000, 0x8080, /* 30 - 36 */ | ||
43 | 0x8080, 0x2000, 0x0000, 0x0000, /* 38 - 3e */ | ||
44 | 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ | ||
45 | 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ | ||
46 | 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ | ||
47 | 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ | ||
48 | 0x8080, 0x0000, 0x0000, 0x0000, /* 60 - 66 */ | ||
49 | 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ | ||
50 | 0x0000, 0x0000, 0x1001, 0x0000, /* 70 - 76 */ | ||
51 | 0x0000, 0x0000, 0x4144, 0x5370 /* 78 - 7e */ | ||
52 | }; | ||
53 | |||
54 | static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line", | ||
55 | "Stereo Mix", "Mono Mix", "Phone"}; | ||
56 | |||
57 | static const struct soc_enum ad1980_cap_src = | ||
58 | SOC_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 7, ad1980_rec_sel); | ||
59 | |||
60 | static const struct snd_kcontrol_new ad1980_snd_ac97_controls[] = { | ||
61 | SOC_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1), | ||
62 | SOC_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1), | ||
63 | |||
64 | SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1), | ||
65 | SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1), | ||
66 | |||
67 | SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1), | ||
68 | SOC_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1), | ||
69 | |||
70 | SOC_DOUBLE("PCM Capture Volume", AC97_REC_GAIN, 8, 0, 31, 0), | ||
71 | SOC_SINGLE("PCM Capture Switch", AC97_REC_GAIN, 15, 1, 1), | ||
72 | |||
73 | SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1), | ||
74 | SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1), | ||
75 | |||
76 | SOC_SINGLE("Phone Capture Volume", AC97_PHONE, 0, 31, 1), | ||
77 | SOC_SINGLE("Phone Capture Switch", AC97_PHONE, 15, 1, 1), | ||
78 | |||
79 | SOC_SINGLE("Mic Volume", AC97_MIC, 0, 31, 1), | ||
80 | SOC_SINGLE("Mic Switch", AC97_MIC, 15, 1, 1), | ||
81 | |||
82 | SOC_SINGLE("Stereo Mic Switch", AC97_AD_MISC, 6, 1, 0), | ||
83 | SOC_DOUBLE("Line HP Swap Switch", AC97_AD_MISC, 10, 5, 1, 0), | ||
84 | |||
85 | SOC_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1), | ||
86 | SOC_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1), | ||
87 | |||
88 | SOC_ENUM("Capture Source", ad1980_cap_src), | ||
89 | |||
90 | SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0), | ||
91 | }; | ||
92 | |||
93 | /* add non dapm controls */ | ||
94 | static int ad1980_add_controls(struct snd_soc_codec *codec) | ||
95 | { | ||
96 | int err, i; | ||
97 | |||
98 | for (i = 0; i < ARRAY_SIZE(ad1980_snd_ac97_controls); i++) { | ||
99 | err = snd_ctl_add(codec->card, snd_soc_cnew( | ||
100 | &ad1980_snd_ac97_controls[i], codec, NULL)); | ||
101 | if (err < 0) | ||
102 | return err; | ||
103 | } | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static unsigned int ac97_read(struct snd_soc_codec *codec, | ||
108 | unsigned int reg) | ||
109 | { | ||
110 | u16 *cache = codec->reg_cache; | ||
111 | |||
112 | switch (reg) { | ||
113 | case AC97_RESET: | ||
114 | case AC97_INT_PAGING: | ||
115 | case AC97_POWERDOWN: | ||
116 | case AC97_EXTENDED_STATUS: | ||
117 | case AC97_VENDOR_ID1: | ||
118 | case AC97_VENDOR_ID2: | ||
119 | return soc_ac97_ops.read(codec->ac97, reg); | ||
120 | default: | ||
121 | reg = reg >> 1; | ||
122 | |||
123 | if (reg >= (ARRAY_SIZE(ad1980_reg))) | ||
124 | return -EINVAL; | ||
125 | |||
126 | return cache[reg]; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, | ||
131 | unsigned int val) | ||
132 | { | ||
133 | u16 *cache = codec->reg_cache; | ||
134 | |||
135 | soc_ac97_ops.write(codec->ac97, reg, val); | ||
136 | reg = reg >> 1; | ||
137 | if (reg < (ARRAY_SIZE(ad1980_reg))) | ||
138 | cache[reg] = val; | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | struct snd_soc_dai ad1980_dai = { | ||
144 | .name = "AC97", | ||
145 | .playback = { | ||
146 | .stream_name = "Playback", | ||
147 | .channels_min = 2, | ||
148 | .channels_max = 2, | ||
149 | .rates = SNDRV_PCM_RATE_48000, | ||
150 | .formats = SNDRV_PCM_FMTBIT_S16_LE, }, | ||
151 | .capture = { | ||
152 | .stream_name = "Capture", | ||
153 | .channels_min = 2, | ||
154 | .channels_max = 2, | ||
155 | .rates = SNDRV_PCM_RATE_48000, | ||
156 | .formats = SNDRV_PCM_FMTBIT_S16_LE, }, | ||
157 | }; | ||
158 | EXPORT_SYMBOL_GPL(ad1980_dai); | ||
159 | |||
160 | static int ad1980_reset(struct snd_soc_codec *codec, int try_warm) | ||
161 | { | ||
162 | u16 retry_cnt = 0; | ||
163 | |||
164 | retry: | ||
165 | if (try_warm && soc_ac97_ops.warm_reset) { | ||
166 | soc_ac97_ops.warm_reset(codec->ac97); | ||
167 | if (ac97_read(codec, AC97_RESET) == 0x0090) | ||
168 | return 1; | ||
169 | } | ||
170 | |||
171 | soc_ac97_ops.reset(codec->ac97); | ||
172 | /* Set bit 16slot in register 74h, then every slot will has only 16 | ||
173 | * bits. This command is sent out in 20bit mode, in which case the | ||
174 | * first nibble of data is eaten by the addr. (Tag is always 16 bit)*/ | ||
175 | ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900); | ||
176 | |||
177 | if (ac97_read(codec, AC97_RESET) != 0x0090) | ||
178 | goto err; | ||
179 | return 0; | ||
180 | |||
181 | err: | ||
182 | while (retry_cnt++ < 10) | ||
183 | goto retry; | ||
184 | |||
185 | printk(KERN_ERR "AD1980 AC97 reset failed\n"); | ||
186 | return -EIO; | ||
187 | } | ||
188 | |||
189 | static int ad1980_soc_probe(struct platform_device *pdev) | ||
190 | { | ||
191 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
192 | struct snd_soc_codec *codec; | ||
193 | int ret = 0; | ||
194 | u16 vendor_id2; | ||
195 | |||
196 | printk(KERN_INFO "AD1980 SoC Audio Codec\n"); | ||
197 | |||
198 | socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | ||
199 | if (socdev->codec == NULL) | ||
200 | return -ENOMEM; | ||
201 | codec = socdev->codec; | ||
202 | mutex_init(&codec->mutex); | ||
203 | |||
204 | codec->reg_cache = | ||
205 | kzalloc(sizeof(u16) * ARRAY_SIZE(ad1980_reg), GFP_KERNEL); | ||
206 | if (codec->reg_cache == NULL) { | ||
207 | ret = -ENOMEM; | ||
208 | goto cache_err; | ||
209 | } | ||
210 | memcpy(codec->reg_cache, ad1980_reg, sizeof(u16) * \ | ||
211 | ARRAY_SIZE(ad1980_reg)); | ||
212 | codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(ad1980_reg); | ||
213 | codec->reg_cache_step = 2; | ||
214 | codec->name = "AD1980"; | ||
215 | codec->owner = THIS_MODULE; | ||
216 | codec->dai = &ad1980_dai; | ||
217 | codec->num_dai = 1; | ||
218 | codec->write = ac97_write; | ||
219 | codec->read = ac97_read; | ||
220 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
221 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
222 | |||
223 | ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0); | ||
224 | if (ret < 0) { | ||
225 | printk(KERN_ERR "ad1980: failed to register AC97 codec\n"); | ||
226 | goto codec_err; | ||
227 | } | ||
228 | |||
229 | /* register pcms */ | ||
230 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
231 | if (ret < 0) | ||
232 | goto pcm_err; | ||
233 | |||
234 | |||
235 | ret = ad1980_reset(codec, 0); | ||
236 | if (ret < 0) { | ||
237 | printk(KERN_ERR "AC97 link error\n"); | ||
238 | goto reset_err; | ||
239 | } | ||
240 | |||
241 | /* Read out vendor ID to make sure it is ad1980 */ | ||
242 | if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144) | ||
243 | goto reset_err; | ||
244 | |||
245 | vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2); | ||
246 | |||
247 | if (vendor_id2 != 0x5370) { | ||
248 | if (vendor_id2 != 0x5374) | ||
249 | goto reset_err; | ||
250 | else | ||
251 | printk(KERN_WARNING "ad1980: " | ||
252 | "Found AD1981 - only 2/2 IN/OUT Channels " | ||
253 | "supported\n"); | ||
254 | } | ||
255 | |||
256 | ac97_write(codec, AC97_MASTER, 0x0000); /* unmute line out volume */ | ||
257 | ac97_write(codec, AC97_PCM, 0x0000); /* unmute PCM out volume */ | ||
258 | ac97_write(codec, AC97_REC_GAIN, 0x0000);/* unmute record volume */ | ||
259 | |||
260 | ad1980_add_controls(codec); | ||
261 | ret = snd_soc_register_card(socdev); | ||
262 | if (ret < 0) { | ||
263 | printk(KERN_ERR "ad1980: failed to register card\n"); | ||
264 | goto reset_err; | ||
265 | } | ||
266 | |||
267 | return 0; | ||
268 | |||
269 | reset_err: | ||
270 | snd_soc_free_pcms(socdev); | ||
271 | |||
272 | pcm_err: | ||
273 | snd_soc_free_ac97_codec(codec); | ||
274 | |||
275 | codec_err: | ||
276 | kfree(codec->reg_cache); | ||
277 | |||
278 | cache_err: | ||
279 | kfree(socdev->codec); | ||
280 | socdev->codec = NULL; | ||
281 | return ret; | ||
282 | } | ||
283 | |||
284 | static int ad1980_soc_remove(struct platform_device *pdev) | ||
285 | { | ||
286 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
287 | struct snd_soc_codec *codec = socdev->codec; | ||
288 | |||
289 | if (codec == NULL) | ||
290 | return 0; | ||
291 | |||
292 | snd_soc_dapm_free(socdev); | ||
293 | snd_soc_free_pcms(socdev); | ||
294 | snd_soc_free_ac97_codec(codec); | ||
295 | kfree(codec->reg_cache); | ||
296 | kfree(codec); | ||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | struct snd_soc_codec_device soc_codec_dev_ad1980 = { | ||
301 | .probe = ad1980_soc_probe, | ||
302 | .remove = ad1980_soc_remove, | ||
303 | }; | ||
304 | EXPORT_SYMBOL_GPL(soc_codec_dev_ad1980); | ||
305 | |||
306 | MODULE_DESCRIPTION("ASoC ad1980 driver"); | ||
307 | MODULE_AUTHOR("Roy Huang, Cliff Cai"); | ||
308 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/ad1980.h b/sound/soc/codecs/ad1980.h new file mode 100644 index 000000000000..db6c8500d66b --- /dev/null +++ b/sound/soc/codecs/ad1980.h | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * ad1980.h -- ad1980 Soc Audio driver | ||
3 | */ | ||
4 | |||
5 | #ifndef _AD1980_H | ||
6 | #define _AD1980_H | ||
7 | /* Bit definition of Power-Down Control/Status Register */ | ||
8 | #define ADC 0x0001 | ||
9 | #define DAC 0x0002 | ||
10 | #define ANL 0x0004 | ||
11 | #define REF 0x0008 | ||
12 | #define PR0 0x0100 | ||
13 | #define PR1 0x0200 | ||
14 | #define PR2 0x0400 | ||
15 | #define PR3 0x0800 | ||
16 | #define PR4 0x1000 | ||
17 | #define PR5 0x2000 | ||
18 | #define PR6 0x4000 | ||
19 | |||
20 | extern struct snd_soc_dai ad1980_dai; | ||
21 | extern struct snd_soc_codec_device soc_codec_dev_ad1980; | ||
22 | |||
23 | #endif | ||
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c new file mode 100644 index 000000000000..37af8607b00a --- /dev/null +++ b/sound/soc/codecs/ad73311.c | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * ad73311.c -- ALSA Soc AD73311 codec support | ||
3 | * | ||
4 | * Copyright: Analog Device Inc. | ||
5 | * Author: Cliff Cai <cliff.cai@analog.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | * | ||
12 | * Revision history | ||
13 | * 25th Sep 2008 Initial version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/init.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/version.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/device.h> | ||
21 | #include <sound/core.h> | ||
22 | #include <sound/pcm.h> | ||
23 | #include <sound/ac97_codec.h> | ||
24 | #include <sound/initval.h> | ||
25 | #include <sound/soc.h> | ||
26 | |||
27 | #include "ad73311.h" | ||
28 | |||
29 | struct snd_soc_dai ad73311_dai = { | ||
30 | .name = "AD73311", | ||
31 | .playback = { | ||
32 | .stream_name = "Playback", | ||
33 | .channels_min = 1, | ||
34 | .channels_max = 1, | ||
35 | .rates = SNDRV_PCM_RATE_8000, | ||
36 | .formats = SNDRV_PCM_FMTBIT_S16_LE, }, | ||
37 | .capture = { | ||
38 | .stream_name = "Capture", | ||
39 | .channels_min = 1, | ||
40 | .channels_max = 1, | ||
41 | .rates = SNDRV_PCM_RATE_8000, | ||
42 | .formats = SNDRV_PCM_FMTBIT_S16_LE, }, | ||
43 | }; | ||
44 | EXPORT_SYMBOL_GPL(ad73311_dai); | ||
45 | |||
46 | static int ad73311_soc_probe(struct platform_device *pdev) | ||
47 | { | ||
48 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
49 | struct snd_soc_codec *codec; | ||
50 | int ret = 0; | ||
51 | |||
52 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | ||
53 | if (codec == NULL) | ||
54 | return -ENOMEM; | ||
55 | mutex_init(&codec->mutex); | ||
56 | codec->name = "AD73311"; | ||
57 | codec->owner = THIS_MODULE; | ||
58 | codec->dai = &ad73311_dai; | ||
59 | codec->num_dai = 1; | ||
60 | socdev->codec = codec; | ||
61 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
62 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
63 | |||
64 | /* register pcms */ | ||
65 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
66 | if (ret < 0) { | ||
67 | printk(KERN_ERR "ad73311: failed to create pcms\n"); | ||
68 | goto pcm_err; | ||
69 | } | ||
70 | |||
71 | ret = snd_soc_register_card(socdev); | ||
72 | if (ret < 0) { | ||
73 | printk(KERN_ERR "ad73311: failed to register card\n"); | ||
74 | goto register_err; | ||
75 | } | ||
76 | |||
77 | return ret; | ||
78 | |||
79 | register_err: | ||
80 | snd_soc_free_pcms(socdev); | ||
81 | pcm_err: | ||
82 | kfree(socdev->codec); | ||
83 | socdev->codec = NULL; | ||
84 | return ret; | ||
85 | } | ||
86 | |||
87 | static int ad73311_soc_remove(struct platform_device *pdev) | ||
88 | { | ||
89 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
90 | struct snd_soc_codec *codec = socdev->codec; | ||
91 | |||
92 | if (codec == NULL) | ||
93 | return 0; | ||
94 | snd_soc_free_pcms(socdev); | ||
95 | kfree(codec); | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | struct snd_soc_codec_device soc_codec_dev_ad73311 = { | ||
100 | .probe = ad73311_soc_probe, | ||
101 | .remove = ad73311_soc_remove, | ||
102 | }; | ||
103 | EXPORT_SYMBOL_GPL(soc_codec_dev_ad73311); | ||
104 | |||
105 | MODULE_DESCRIPTION("ASoC ad73311 driver"); | ||
106 | MODULE_AUTHOR("Cliff Cai "); | ||
107 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/ad73311.h b/sound/soc/codecs/ad73311.h new file mode 100644 index 000000000000..507ce0c30edf --- /dev/null +++ b/sound/soc/codecs/ad73311.h | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * File: sound/soc/codec/ad73311.h | ||
3 | * Based on: | ||
4 | * Author: Cliff Cai <cliff.cai@analog.com> | ||
5 | * | ||
6 | * Created: Thur Sep 25, 2008 | ||
7 | * Description: definitions for AD73311 registers | ||
8 | * | ||
9 | * | ||
10 | * Modified: | ||
11 | * Copyright 2006 Analog Devices Inc. | ||
12 | * | ||
13 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or modify | ||
16 | * it under the terms of the GNU General Public License as published by | ||
17 | * the Free Software Foundation; either version 2 of the License, or | ||
18 | * (at your option) any later version. | ||
19 | * | ||
20 | * This program is distributed in the hope that it will be useful, | ||
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
23 | * GNU General Public License for more details. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License | ||
26 | * along with this program; if not, see the file COPYING, or write | ||
27 | * to the Free Software Foundation, Inc., | ||
28 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
29 | */ | ||
30 | |||
31 | #ifndef __AD73311_H__ | ||
32 | #define __AD73311_H__ | ||
33 | |||
34 | #define AD_CONTROL 0x8000 | ||
35 | #define AD_DATA 0x0000 | ||
36 | #define AD_READ 0x4000 | ||
37 | #define AD_WRITE 0x0000 | ||
38 | |||
39 | /* Control register A */ | ||
40 | #define CTRL_REG_A (0 << 8) | ||
41 | |||
42 | #define REGA_MODE_PRO 0x00 | ||
43 | #define REGA_MODE_DATA 0x01 | ||
44 | #define REGA_MODE_MIXED 0x03 | ||
45 | #define REGA_DLB 0x04 | ||
46 | #define REGA_SLB 0x08 | ||
47 | #define REGA_DEVC(x) ((x & 0x7) << 4) | ||
48 | #define REGA_RESET 0x80 | ||
49 | |||
50 | /* Control register B */ | ||
51 | #define CTRL_REG_B (1 << 8) | ||
52 | |||
53 | #define REGB_DIRATE(x) (x & 0x3) | ||
54 | #define REGB_SCDIV(x) ((x & 0x3) << 2) | ||
55 | #define REGB_MCDIV(x) ((x & 0x7) << 4) | ||
56 | #define REGB_CEE (1 << 7) | ||
57 | |||
58 | /* Control register C */ | ||
59 | #define CTRL_REG_C (2 << 8) | ||
60 | |||
61 | #define REGC_PUDEV (1 << 0) | ||
62 | #define REGC_PUADC (1 << 3) | ||
63 | #define REGC_PUDAC (1 << 4) | ||
64 | #define REGC_PUREF (1 << 5) | ||
65 | #define REGC_REFUSE (1 << 6) | ||
66 | |||
67 | /* Control register D */ | ||
68 | #define CTRL_REG_D (3 << 8) | ||
69 | |||
70 | #define REGD_IGS(x) (x & 0x7) | ||
71 | #define REGD_RMOD (1 << 3) | ||
72 | #define REGD_OGS(x) ((x & 0x7) << 4) | ||
73 | #define REGD_MUTE (x << 7) | ||
74 | |||
75 | /* Control register E */ | ||
76 | #define CTRL_REG_E (4 << 8) | ||
77 | |||
78 | #define REGE_DA(x) (x & 0x1f) | ||
79 | #define REGE_IBYP (1 << 5) | ||
80 | |||
81 | /* Control register F */ | ||
82 | #define CTRL_REG_F (5 << 8) | ||
83 | |||
84 | #define REGF_SEEN (1 << 5) | ||
85 | #define REGF_INV (1 << 6) | ||
86 | #define REGF_ALB (1 << 7) | ||
87 | |||
88 | extern struct snd_soc_dai ad73311_dai; | ||
89 | extern struct snd_soc_codec_device soc_codec_dev_ad73311; | ||
90 | #endif | ||
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 7da9f467b7b8..2a89b5888e11 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c | |||
@@ -28,7 +28,6 @@ | |||
28 | 28 | ||
29 | #include "ak4535.h" | 29 | #include "ak4535.h" |
30 | 30 | ||
31 | #define AUDIO_NAME "ak4535" | ||
32 | #define AK4535_VERSION "0.3" | 31 | #define AK4535_VERSION "0.3" |
33 | 32 | ||
34 | struct snd_soc_codec_device soc_codec_dev_ak4535; | 33 | struct snd_soc_codec_device soc_codec_dev_ak4535; |
@@ -535,87 +534,85 @@ static struct snd_soc_device *ak4535_socdev; | |||
535 | 534 | ||
536 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 535 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
537 | 536 | ||
538 | #define I2C_DRIVERID_AK4535 0xfefe /* liam - need a proper id */ | 537 | static int ak4535_i2c_probe(struct i2c_client *i2c, |
539 | 538 | const struct i2c_device_id *id) | |
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 | { | 539 | { |
552 | struct snd_soc_device *socdev = ak4535_socdev; | 540 | struct snd_soc_device *socdev = ak4535_socdev; |
553 | struct ak4535_setup_data *setup = socdev->codec_data; | ||
554 | struct snd_soc_codec *codec = socdev->codec; | 541 | struct snd_soc_codec *codec = socdev->codec; |
555 | struct i2c_client *i2c; | ||
556 | int ret; | 542 | int ret; |
557 | 543 | ||
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 | return -ENOMEM; | ||
567 | |||
568 | i2c_set_clientdata(i2c, codec); | 544 | i2c_set_clientdata(i2c, codec); |
569 | codec->control_data = i2c; | 545 | codec->control_data = i2c; |
570 | 546 | ||
571 | ret = i2c_attach_client(i2c); | ||
572 | if (ret < 0) { | ||
573 | printk(KERN_ERR "failed to attach codec at addr %x\n", addr); | ||
574 | goto err; | ||
575 | } | ||
576 | |||
577 | ret = ak4535_init(socdev); | 547 | ret = ak4535_init(socdev); |
578 | if (ret < 0) { | 548 | if (ret < 0) |
579 | printk(KERN_ERR "failed to initialise AK4535\n"); | 549 | printk(KERN_ERR "failed to initialise AK4535\n"); |
580 | goto err; | ||
581 | } | ||
582 | return ret; | ||
583 | 550 | ||
584 | err: | ||
585 | kfree(i2c); | ||
586 | return ret; | 551 | return ret; |
587 | } | 552 | } |
588 | 553 | ||
589 | static int ak4535_i2c_detach(struct i2c_client *client) | 554 | static int ak4535_i2c_remove(struct i2c_client *client) |
590 | { | 555 | { |
591 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | 556 | struct snd_soc_codec *codec = i2c_get_clientdata(client); |
592 | i2c_detach_client(client); | ||
593 | kfree(codec->reg_cache); | 557 | kfree(codec->reg_cache); |
594 | kfree(client); | ||
595 | return 0; | 558 | return 0; |
596 | } | 559 | } |
597 | 560 | ||
598 | static int ak4535_i2c_attach(struct i2c_adapter *adap) | 561 | static const struct i2c_device_id ak4535_i2c_id[] = { |
599 | { | 562 | { "ak4535", 0 }, |
600 | return i2c_probe(adap, &addr_data, ak4535_codec_probe); | 563 | { } |
601 | } | 564 | }; |
565 | MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id); | ||
602 | 566 | ||
603 | /* corgi i2c codec control layer */ | ||
604 | static struct i2c_driver ak4535_i2c_driver = { | 567 | static struct i2c_driver ak4535_i2c_driver = { |
605 | .driver = { | 568 | .driver = { |
606 | .name = "AK4535 I2C Codec", | 569 | .name = "AK4535 I2C Codec", |
607 | .owner = THIS_MODULE, | 570 | .owner = THIS_MODULE, |
608 | }, | 571 | }, |
609 | .id = I2C_DRIVERID_AK4535, | 572 | .probe = ak4535_i2c_probe, |
610 | .attach_adapter = ak4535_i2c_attach, | 573 | .remove = ak4535_i2c_remove, |
611 | .detach_client = ak4535_i2c_detach, | 574 | .id_table = ak4535_i2c_id, |
612 | .command = NULL, | ||
613 | }; | 575 | }; |
614 | 576 | ||
615 | static struct i2c_client client_template = { | 577 | static int ak4535_add_i2c_device(struct platform_device *pdev, |
616 | .name = "AK4535", | 578 | const struct ak4535_setup_data *setup) |
617 | .driver = &ak4535_i2c_driver, | 579 | { |
618 | }; | 580 | struct i2c_board_info info; |
581 | struct i2c_adapter *adapter; | ||
582 | struct i2c_client *client; | ||
583 | int ret; | ||
584 | |||
585 | ret = i2c_add_driver(&ak4535_i2c_driver); | ||
586 | if (ret != 0) { | ||
587 | dev_err(&pdev->dev, "can't add i2c driver\n"); | ||
588 | return ret; | ||
589 | } | ||
590 | |||
591 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
592 | info.addr = setup->i2c_address; | ||
593 | strlcpy(info.type, "ak4535", I2C_NAME_SIZE); | ||
594 | |||
595 | adapter = i2c_get_adapter(setup->i2c_bus); | ||
596 | if (!adapter) { | ||
597 | dev_err(&pdev->dev, "can't get i2c adapter %d\n", | ||
598 | setup->i2c_bus); | ||
599 | goto err_driver; | ||
600 | } | ||
601 | |||
602 | client = i2c_new_device(adapter, &info); | ||
603 | i2c_put_adapter(adapter); | ||
604 | if (!client) { | ||
605 | dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", | ||
606 | (unsigned int)info.addr); | ||
607 | goto err_driver; | ||
608 | } | ||
609 | |||
610 | return 0; | ||
611 | |||
612 | err_driver: | ||
613 | i2c_del_driver(&ak4535_i2c_driver); | ||
614 | return -ENODEV; | ||
615 | } | ||
619 | #endif | 616 | #endif |
620 | 617 | ||
621 | static int ak4535_probe(struct platform_device *pdev) | 618 | static int ak4535_probe(struct platform_device *pdev) |
@@ -624,7 +621,7 @@ static int ak4535_probe(struct platform_device *pdev) | |||
624 | struct ak4535_setup_data *setup; | 621 | struct ak4535_setup_data *setup; |
625 | struct snd_soc_codec *codec; | 622 | struct snd_soc_codec *codec; |
626 | struct ak4535_priv *ak4535; | 623 | struct ak4535_priv *ak4535; |
627 | int ret = 0; | 624 | int ret; |
628 | 625 | ||
629 | printk(KERN_INFO "AK4535 Audio Codec %s", AK4535_VERSION); | 626 | printk(KERN_INFO "AK4535 Audio Codec %s", AK4535_VERSION); |
630 | 627 | ||
@@ -646,17 +643,14 @@ static int ak4535_probe(struct platform_device *pdev) | |||
646 | INIT_LIST_HEAD(&codec->dapm_paths); | 643 | INIT_LIST_HEAD(&codec->dapm_paths); |
647 | 644 | ||
648 | ak4535_socdev = socdev; | 645 | ak4535_socdev = socdev; |
646 | ret = -ENODEV; | ||
647 | |||
649 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 648 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
650 | if (setup->i2c_address) { | 649 | if (setup->i2c_address) { |
651 | normal_i2c[0] = setup->i2c_address; | ||
652 | codec->hw_write = (hw_write_t)i2c_master_send; | 650 | codec->hw_write = (hw_write_t)i2c_master_send; |
653 | codec->hw_read = (hw_read_t)i2c_master_recv; | 651 | codec->hw_read = (hw_read_t)i2c_master_recv; |
654 | ret = i2c_add_driver(&ak4535_i2c_driver); | 652 | ret = ak4535_add_i2c_device(pdev, setup); |
655 | if (ret != 0) | ||
656 | printk(KERN_ERR "can't add i2c driver"); | ||
657 | } | 653 | } |
658 | #else | ||
659 | /* Add other interfaces here */ | ||
660 | #endif | 654 | #endif |
661 | 655 | ||
662 | if (ret != 0) { | 656 | if (ret != 0) { |
@@ -678,6 +672,7 @@ static int ak4535_remove(struct platform_device *pdev) | |||
678 | snd_soc_free_pcms(socdev); | 672 | snd_soc_free_pcms(socdev); |
679 | snd_soc_dapm_free(socdev); | 673 | snd_soc_dapm_free(socdev); |
680 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 674 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
675 | i2c_unregister_device(codec->control_data); | ||
681 | i2c_del_driver(&ak4535_i2c_driver); | 676 | i2c_del_driver(&ak4535_i2c_driver); |
682 | #endif | 677 | #endif |
683 | kfree(codec->private_data); | 678 | kfree(codec->private_data); |
diff --git a/sound/soc/codecs/ak4535.h b/sound/soc/codecs/ak4535.h index e9fe30e2c056..c7a58703ea39 100644 --- a/sound/soc/codecs/ak4535.h +++ b/sound/soc/codecs/ak4535.h | |||
@@ -37,6 +37,7 @@ | |||
37 | #define AK4535_CACHEREGNUM 0x10 | 37 | #define AK4535_CACHEREGNUM 0x10 |
38 | 38 | ||
39 | struct ak4535_setup_data { | 39 | struct ak4535_setup_data { |
40 | int i2c_bus; | ||
40 | unsigned short i2c_address; | 41 | unsigned short i2c_address; |
41 | }; | 42 | }; |
42 | 43 | ||
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 9deb8c74fdfd..0bbd94501d7e 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c | |||
@@ -490,34 +490,7 @@ static int cs4270_mute(struct snd_soc_dai *dai, int mute) | |||
490 | 490 | ||
491 | #endif | 491 | #endif |
492 | 492 | ||
493 | static int cs4270_i2c_probe(struct i2c_adapter *adap, int addr, int kind); | 493 | static int cs4270_i2c_probe(struct i2c_client *, const struct i2c_device_id *); |
494 | |||
495 | /* | ||
496 | * Notify the driver that a new I2C bus has been found. | ||
497 | * | ||
498 | * This function is called for each I2C bus in the system. The function | ||
499 | * then asks the I2C subsystem to probe that bus at the addresses on which | ||
500 | * our device (the CS4270) could exist. If a device is found at one of | ||
501 | * those addresses, then our probe function (cs4270_i2c_probe) is called. | ||
502 | */ | ||
503 | static int cs4270_i2c_attach(struct i2c_adapter *adapter) | ||
504 | { | ||
505 | return i2c_probe(adapter, &addr_data, cs4270_i2c_probe); | ||
506 | } | ||
507 | |||
508 | static int cs4270_i2c_detach(struct i2c_client *client) | ||
509 | { | ||
510 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | ||
511 | |||
512 | i2c_detach_client(client); | ||
513 | codec->control_data = NULL; | ||
514 | |||
515 | kfree(codec->reg_cache); | ||
516 | codec->reg_cache = NULL; | ||
517 | |||
518 | kfree(client); | ||
519 | return 0; | ||
520 | } | ||
521 | 494 | ||
522 | /* A list of non-DAPM controls that the CS4270 supports */ | 495 | /* A list of non-DAPM controls that the CS4270 supports */ |
523 | static const struct snd_kcontrol_new cs4270_snd_controls[] = { | 496 | static const struct snd_kcontrol_new cs4270_snd_controls[] = { |
@@ -525,14 +498,19 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = { | |||
525 | CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 1) | 498 | CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 1) |
526 | }; | 499 | }; |
527 | 500 | ||
501 | static const struct i2c_device_id cs4270_id[] = { | ||
502 | {"cs4270", 0}, | ||
503 | {} | ||
504 | }; | ||
505 | MODULE_DEVICE_TABLE(i2c, cs4270_id); | ||
506 | |||
528 | static struct i2c_driver cs4270_i2c_driver = { | 507 | static struct i2c_driver cs4270_i2c_driver = { |
529 | .driver = { | 508 | .driver = { |
530 | .name = "CS4270 I2C", | 509 | .name = "CS4270 I2C", |
531 | .owner = THIS_MODULE, | 510 | .owner = THIS_MODULE, |
532 | }, | 511 | }, |
533 | .id = I2C_DRIVERID_CS4270, | 512 | .id_table = cs4270_id, |
534 | .attach_adapter = cs4270_i2c_attach, | 513 | .probe = cs4270_i2c_probe, |
535 | .detach_client = cs4270_i2c_detach, | ||
536 | }; | 514 | }; |
537 | 515 | ||
538 | /* | 516 | /* |
@@ -561,11 +539,11 @@ static struct snd_soc_device *cs4270_socdev; | |||
561 | * Note: snd_soc_new_pcms() must be called before this function can be called, | 539 | * Note: snd_soc_new_pcms() must be called before this function can be called, |
562 | * because of snd_ctl_add(). | 540 | * because of snd_ctl_add(). |
563 | */ | 541 | */ |
564 | static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind) | 542 | static int cs4270_i2c_probe(struct i2c_client *i2c_client, |
543 | const struct i2c_device_id *id) | ||
565 | { | 544 | { |
566 | struct snd_soc_device *socdev = cs4270_socdev; | 545 | struct snd_soc_device *socdev = cs4270_socdev; |
567 | struct snd_soc_codec *codec = socdev->codec; | 546 | struct snd_soc_codec *codec = socdev->codec; |
568 | struct i2c_client *i2c_client = NULL; | ||
569 | int i; | 547 | int i; |
570 | int ret = 0; | 548 | int ret = 0; |
571 | 549 | ||
@@ -578,12 +556,6 @@ static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind) | |||
578 | 556 | ||
579 | /* Note: codec_dai->codec is NULL here */ | 557 | /* Note: codec_dai->codec is NULL here */ |
580 | 558 | ||
581 | i2c_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); | ||
582 | if (!i2c_client) { | ||
583 | printk(KERN_ERR "cs4270: could not allocate I2C client\n"); | ||
584 | return -ENOMEM; | ||
585 | } | ||
586 | |||
587 | codec->reg_cache = kzalloc(CS4270_NUMREGS, GFP_KERNEL); | 559 | codec->reg_cache = kzalloc(CS4270_NUMREGS, GFP_KERNEL); |
588 | if (!codec->reg_cache) { | 560 | if (!codec->reg_cache) { |
589 | printk(KERN_ERR "cs4270: could not allocate register cache\n"); | 561 | printk(KERN_ERR "cs4270: could not allocate register cache\n"); |
@@ -591,13 +563,6 @@ static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind) | |||
591 | goto error; | 563 | goto error; |
592 | } | 564 | } |
593 | 565 | ||
594 | i2c_set_clientdata(i2c_client, codec); | ||
595 | strcpy(i2c_client->name, "CS4270"); | ||
596 | |||
597 | i2c_client->driver = &cs4270_i2c_driver; | ||
598 | i2c_client->adapter = adapter; | ||
599 | i2c_client->addr = addr; | ||
600 | |||
601 | /* Verify that we have a CS4270 */ | 566 | /* Verify that we have a CS4270 */ |
602 | 567 | ||
603 | ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID); | 568 | ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID); |
@@ -612,18 +577,10 @@ static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind) | |||
612 | goto error; | 577 | goto error; |
613 | } | 578 | } |
614 | 579 | ||
615 | printk(KERN_INFO "cs4270: found device at I2C address %X\n", addr); | 580 | printk(KERN_INFO "cs4270: found device at I2C address %X\n", |
581 | i2c_client->addr); | ||
616 | printk(KERN_INFO "cs4270: hardware revision %X\n", ret & 0xF); | 582 | printk(KERN_INFO "cs4270: hardware revision %X\n", ret & 0xF); |
617 | 583 | ||
618 | /* Tell the I2C layer a new client has arrived */ | ||
619 | |||
620 | ret = i2c_attach_client(i2c_client); | ||
621 | if (ret) { | ||
622 | printk(KERN_ERR "cs4270: could not attach codec, " | ||
623 | "I2C address %x, error code %i\n", addr, ret); | ||
624 | goto error; | ||
625 | } | ||
626 | |||
627 | codec->control_data = i2c_client; | 584 | codec->control_data = i2c_client; |
628 | codec->read = cs4270_read_reg_cache; | 585 | codec->read = cs4270_read_reg_cache; |
629 | codec->write = cs4270_i2c_write; | 586 | codec->write = cs4270_i2c_write; |
@@ -648,20 +605,17 @@ static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind) | |||
648 | goto error; | 605 | goto error; |
649 | } | 606 | } |
650 | 607 | ||
608 | i2c_set_clientdata(i2c_client, codec); | ||
609 | |||
651 | return 0; | 610 | return 0; |
652 | 611 | ||
653 | error: | 612 | error: |
654 | if (codec->control_data) { | 613 | codec->control_data = NULL; |
655 | i2c_detach_client(i2c_client); | ||
656 | codec->control_data = NULL; | ||
657 | } | ||
658 | 614 | ||
659 | kfree(codec->reg_cache); | 615 | kfree(codec->reg_cache); |
660 | codec->reg_cache = NULL; | 616 | codec->reg_cache = NULL; |
661 | codec->reg_cache_size = 0; | 617 | codec->reg_cache_size = 0; |
662 | 618 | ||
663 | kfree(i2c_client); | ||
664 | |||
665 | return ret; | 619 | return ret; |
666 | } | 620 | } |
667 | 621 | ||
@@ -727,7 +681,7 @@ static int cs4270_probe(struct platform_device *pdev) | |||
727 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | 681 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); |
728 | if (ret < 0) { | 682 | if (ret < 0) { |
729 | printk(KERN_ERR "cs4270: failed to create PCMs\n"); | 683 | printk(KERN_ERR "cs4270: failed to create PCMs\n"); |
730 | return ret; | 684 | goto error_free_codec; |
731 | } | 685 | } |
732 | 686 | ||
733 | #ifdef USE_I2C | 687 | #ifdef USE_I2C |
@@ -736,8 +690,7 @@ static int cs4270_probe(struct platform_device *pdev) | |||
736 | ret = i2c_add_driver(&cs4270_i2c_driver); | 690 | ret = i2c_add_driver(&cs4270_i2c_driver); |
737 | if (ret) { | 691 | if (ret) { |
738 | printk(KERN_ERR "cs4270: failed to attach driver"); | 692 | printk(KERN_ERR "cs4270: failed to attach driver"); |
739 | snd_soc_free_pcms(socdev); | 693 | goto error_free_pcms; |
740 | return ret; | ||
741 | } | 694 | } |
742 | 695 | ||
743 | /* Did we find a CS4270 on the I2C bus? */ | 696 | /* Did we find a CS4270 on the I2C bus? */ |
@@ -759,10 +712,23 @@ static int cs4270_probe(struct platform_device *pdev) | |||
759 | ret = snd_soc_register_card(socdev); | 712 | ret = snd_soc_register_card(socdev); |
760 | if (ret < 0) { | 713 | if (ret < 0) { |
761 | printk(KERN_ERR "cs4270: failed to register card\n"); | 714 | printk(KERN_ERR "cs4270: failed to register card\n"); |
762 | snd_soc_free_pcms(socdev); | 715 | goto error_del_driver; |
763 | return ret; | ||
764 | } | 716 | } |
765 | 717 | ||
718 | return 0; | ||
719 | |||
720 | error_del_driver: | ||
721 | #ifdef USE_I2C | ||
722 | i2c_del_driver(&cs4270_i2c_driver); | ||
723 | |||
724 | error_free_pcms: | ||
725 | #endif | ||
726 | snd_soc_free_pcms(socdev); | ||
727 | |||
728 | error_free_codec: | ||
729 | kfree(socdev->codec); | ||
730 | socdev->codec = NULL; | ||
731 | |||
766 | return ret; | 732 | return ret; |
767 | } | 733 | } |
768 | 734 | ||
@@ -773,8 +739,7 @@ static int cs4270_remove(struct platform_device *pdev) | |||
773 | snd_soc_free_pcms(socdev); | 739 | snd_soc_free_pcms(socdev); |
774 | 740 | ||
775 | #ifdef USE_I2C | 741 | #ifdef USE_I2C |
776 | if (socdev->codec->control_data) | 742 | i2c_del_driver(&cs4270_i2c_driver); |
777 | i2c_del_driver(&cs4270_i2c_driver); | ||
778 | #endif | 743 | #endif |
779 | 744 | ||
780 | kfree(socdev->codec); | 745 | kfree(socdev->codec); |
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c new file mode 100644 index 000000000000..44ef0dacd564 --- /dev/null +++ b/sound/soc/codecs/ssm2602.c | |||
@@ -0,0 +1,775 @@ | |||
1 | /* | ||
2 | * File: sound/soc/codecs/ssm2602.c | ||
3 | * Author: Cliff Cai <Cliff.Cai@analog.com> | ||
4 | * | ||
5 | * Created: Tue June 06 2008 | ||
6 | * Description: Driver for ssm2602 sound chip | ||
7 | * | ||
8 | * Modified: | ||
9 | * Copyright 2008 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/module.h> | ||
30 | #include <linux/moduleparam.h> | ||
31 | #include <linux/init.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/pm.h> | ||
34 | #include <linux/i2c.h> | ||
35 | #include <linux/platform_device.h> | ||
36 | #include <sound/core.h> | ||
37 | #include <sound/pcm.h> | ||
38 | #include <sound/pcm_params.h> | ||
39 | #include <sound/soc.h> | ||
40 | #include <sound/soc-dapm.h> | ||
41 | #include <sound/initval.h> | ||
42 | |||
43 | #include "ssm2602.h" | ||
44 | |||
45 | #define SSM2602_VERSION "0.1" | ||
46 | |||
47 | struct snd_soc_codec_device soc_codec_dev_ssm2602; | ||
48 | |||
49 | /* codec private data */ | ||
50 | struct ssm2602_priv { | ||
51 | unsigned int sysclk; | ||
52 | struct snd_pcm_substream *master_substream; | ||
53 | struct snd_pcm_substream *slave_substream; | ||
54 | }; | ||
55 | |||
56 | /* | ||
57 | * ssm2602 register cache | ||
58 | * We can't read the ssm2602 register space when we are | ||
59 | * using 2 wire for device control, so we cache them instead. | ||
60 | * There is no point in caching the reset register | ||
61 | */ | ||
62 | static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = { | ||
63 | 0x0017, 0x0017, 0x0079, 0x0079, | ||
64 | 0x0000, 0x0000, 0x0000, 0x000a, | ||
65 | 0x0000, 0x0000 | ||
66 | }; | ||
67 | |||
68 | /* | ||
69 | * read ssm2602 register cache | ||
70 | */ | ||
71 | static inline unsigned int ssm2602_read_reg_cache(struct snd_soc_codec *codec, | ||
72 | unsigned int reg) | ||
73 | { | ||
74 | u16 *cache = codec->reg_cache; | ||
75 | if (reg == SSM2602_RESET) | ||
76 | return 0; | ||
77 | if (reg >= SSM2602_CACHEREGNUM) | ||
78 | return -1; | ||
79 | return cache[reg]; | ||
80 | } | ||
81 | |||
82 | /* | ||
83 | * write ssm2602 register cache | ||
84 | */ | ||
85 | static inline void ssm2602_write_reg_cache(struct snd_soc_codec *codec, | ||
86 | u16 reg, unsigned int value) | ||
87 | { | ||
88 | u16 *cache = codec->reg_cache; | ||
89 | if (reg >= SSM2602_CACHEREGNUM) | ||
90 | return; | ||
91 | cache[reg] = value; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * write to the ssm2602 register space | ||
96 | */ | ||
97 | static int ssm2602_write(struct snd_soc_codec *codec, unsigned int reg, | ||
98 | unsigned int value) | ||
99 | { | ||
100 | u8 data[2]; | ||
101 | |||
102 | /* data is | ||
103 | * D15..D9 ssm2602 register offset | ||
104 | * D8...D0 register data | ||
105 | */ | ||
106 | data[0] = (reg << 1) | ((value >> 8) & 0x0001); | ||
107 | data[1] = value & 0x00ff; | ||
108 | |||
109 | ssm2602_write_reg_cache(codec, reg, value); | ||
110 | if (codec->hw_write(codec->control_data, data, 2) == 2) | ||
111 | return 0; | ||
112 | else | ||
113 | return -EIO; | ||
114 | } | ||
115 | |||
116 | #define ssm2602_reset(c) ssm2602_write(c, SSM2602_RESET, 0) | ||
117 | |||
118 | /*Appending several "None"s just for OSS mixer use*/ | ||
119 | static const char *ssm2602_input_select[] = { | ||
120 | "Line", "Mic", "None", "None", "None", | ||
121 | "None", "None", "None", | ||
122 | }; | ||
123 | |||
124 | static const char *ssm2602_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; | ||
125 | |||
126 | static const struct soc_enum ssm2602_enum[] = { | ||
127 | SOC_ENUM_SINGLE(SSM2602_APANA, 2, 2, ssm2602_input_select), | ||
128 | SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph), | ||
129 | }; | ||
130 | |||
131 | static const struct snd_kcontrol_new ssm2602_snd_controls[] = { | ||
132 | |||
133 | SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V, | ||
134 | 0, 127, 0), | ||
135 | SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V, | ||
136 | 7, 1, 0), | ||
137 | |||
138 | SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0), | ||
139 | SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1), | ||
140 | |||
141 | SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0), | ||
142 | SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1), | ||
143 | |||
144 | SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1), | ||
145 | |||
146 | SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1), | ||
147 | SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0), | ||
148 | |||
149 | SOC_ENUM("Capture Source", ssm2602_enum[0]), | ||
150 | |||
151 | SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]), | ||
152 | }; | ||
153 | |||
154 | /* add non dapm controls */ | ||
155 | static int ssm2602_add_controls(struct snd_soc_codec *codec) | ||
156 | { | ||
157 | int err, i; | ||
158 | |||
159 | for (i = 0; i < ARRAY_SIZE(ssm2602_snd_controls); i++) { | ||
160 | err = snd_ctl_add(codec->card, | ||
161 | snd_soc_cnew(&ssm2602_snd_controls[i], codec, NULL)); | ||
162 | if (err < 0) | ||
163 | return err; | ||
164 | } | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | /* Output Mixer */ | ||
170 | static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = { | ||
171 | SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0), | ||
172 | SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0), | ||
173 | SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0), | ||
174 | }; | ||
175 | |||
176 | /* Input mux */ | ||
177 | static const struct snd_kcontrol_new ssm2602_input_mux_controls = | ||
178 | SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]); | ||
179 | |||
180 | static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = { | ||
181 | SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1, | ||
182 | &ssm2602_output_mixer_controls[0], | ||
183 | ARRAY_SIZE(ssm2602_output_mixer_controls)), | ||
184 | SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1), | ||
185 | SND_SOC_DAPM_OUTPUT("LOUT"), | ||
186 | SND_SOC_DAPM_OUTPUT("LHPOUT"), | ||
187 | SND_SOC_DAPM_OUTPUT("ROUT"), | ||
188 | SND_SOC_DAPM_OUTPUT("RHPOUT"), | ||
189 | SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1), | ||
190 | SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls), | ||
191 | SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0), | ||
192 | SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1), | ||
193 | SND_SOC_DAPM_INPUT("MICIN"), | ||
194 | SND_SOC_DAPM_INPUT("RLINEIN"), | ||
195 | SND_SOC_DAPM_INPUT("LLINEIN"), | ||
196 | }; | ||
197 | |||
198 | static const struct snd_soc_dapm_route audio_conn[] = { | ||
199 | /* output mixer */ | ||
200 | {"Output Mixer", "Line Bypass Switch", "Line Input"}, | ||
201 | {"Output Mixer", "HiFi Playback Switch", "DAC"}, | ||
202 | {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, | ||
203 | |||
204 | /* outputs */ | ||
205 | {"RHPOUT", NULL, "Output Mixer"}, | ||
206 | {"ROUT", NULL, "Output Mixer"}, | ||
207 | {"LHPOUT", NULL, "Output Mixer"}, | ||
208 | {"LOUT", NULL, "Output Mixer"}, | ||
209 | |||
210 | /* input mux */ | ||
211 | {"Input Mux", "Line", "Line Input"}, | ||
212 | {"Input Mux", "Mic", "Mic Bias"}, | ||
213 | {"ADC", NULL, "Input Mux"}, | ||
214 | |||
215 | /* inputs */ | ||
216 | {"Line Input", NULL, "LLINEIN"}, | ||
217 | {"Line Input", NULL, "RLINEIN"}, | ||
218 | {"Mic Bias", NULL, "MICIN"}, | ||
219 | }; | ||
220 | |||
221 | static int ssm2602_add_widgets(struct snd_soc_codec *codec) | ||
222 | { | ||
223 | snd_soc_dapm_new_controls(codec, ssm2602_dapm_widgets, | ||
224 | ARRAY_SIZE(ssm2602_dapm_widgets)); | ||
225 | |||
226 | snd_soc_dapm_add_routes(codec, audio_conn, ARRAY_SIZE(audio_conn)); | ||
227 | |||
228 | snd_soc_dapm_new_widgets(codec); | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | struct _coeff_div { | ||
233 | u32 mclk; | ||
234 | u32 rate; | ||
235 | u16 fs; | ||
236 | u8 sr:4; | ||
237 | u8 bosr:1; | ||
238 | u8 usb:1; | ||
239 | }; | ||
240 | |||
241 | /* codec mclk clock divider coefficients */ | ||
242 | static const struct _coeff_div coeff_div[] = { | ||
243 | /* 48k */ | ||
244 | {12288000, 48000, 256, 0x0, 0x0, 0x0}, | ||
245 | {18432000, 48000, 384, 0x0, 0x1, 0x0}, | ||
246 | {12000000, 48000, 250, 0x0, 0x0, 0x1}, | ||
247 | |||
248 | /* 32k */ | ||
249 | {12288000, 32000, 384, 0x6, 0x0, 0x0}, | ||
250 | {18432000, 32000, 576, 0x6, 0x1, 0x0}, | ||
251 | {12000000, 32000, 375, 0x6, 0x0, 0x1}, | ||
252 | |||
253 | /* 8k */ | ||
254 | {12288000, 8000, 1536, 0x3, 0x0, 0x0}, | ||
255 | {18432000, 8000, 2304, 0x3, 0x1, 0x0}, | ||
256 | {11289600, 8000, 1408, 0xb, 0x0, 0x0}, | ||
257 | {16934400, 8000, 2112, 0xb, 0x1, 0x0}, | ||
258 | {12000000, 8000, 1500, 0x3, 0x0, 0x1}, | ||
259 | |||
260 | /* 96k */ | ||
261 | {12288000, 96000, 128, 0x7, 0x0, 0x0}, | ||
262 | {18432000, 96000, 192, 0x7, 0x1, 0x0}, | ||
263 | {12000000, 96000, 125, 0x7, 0x0, 0x1}, | ||
264 | |||
265 | /* 44.1k */ | ||
266 | {11289600, 44100, 256, 0x8, 0x0, 0x0}, | ||
267 | {16934400, 44100, 384, 0x8, 0x1, 0x0}, | ||
268 | {12000000, 44100, 272, 0x8, 0x1, 0x1}, | ||
269 | |||
270 | /* 88.2k */ | ||
271 | {11289600, 88200, 128, 0xf, 0x0, 0x0}, | ||
272 | {16934400, 88200, 192, 0xf, 0x1, 0x0}, | ||
273 | {12000000, 88200, 136, 0xf, 0x1, 0x1}, | ||
274 | }; | ||
275 | |||
276 | static inline int get_coeff(int mclk, int rate) | ||
277 | { | ||
278 | int i; | ||
279 | |||
280 | for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { | ||
281 | if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) | ||
282 | return i; | ||
283 | } | ||
284 | return i; | ||
285 | } | ||
286 | |||
287 | static int ssm2602_hw_params(struct snd_pcm_substream *substream, | ||
288 | struct snd_pcm_hw_params *params) | ||
289 | { | ||
290 | u16 srate; | ||
291 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
292 | struct snd_soc_device *socdev = rtd->socdev; | ||
293 | struct snd_soc_codec *codec = socdev->codec; | ||
294 | struct ssm2602_priv *ssm2602 = codec->private_data; | ||
295 | u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3; | ||
296 | int i = get_coeff(ssm2602->sysclk, params_rate(params)); | ||
297 | |||
298 | /*no match is found*/ | ||
299 | if (i == ARRAY_SIZE(coeff_div)) | ||
300 | return -EINVAL; | ||
301 | |||
302 | srate = (coeff_div[i].sr << 2) | | ||
303 | (coeff_div[i].bosr << 1) | coeff_div[i].usb; | ||
304 | |||
305 | ssm2602_write(codec, SSM2602_ACTIVE, 0); | ||
306 | ssm2602_write(codec, SSM2602_SRATE, srate); | ||
307 | |||
308 | /* bit size */ | ||
309 | switch (params_format(params)) { | ||
310 | case SNDRV_PCM_FORMAT_S16_LE: | ||
311 | break; | ||
312 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
313 | iface |= 0x0004; | ||
314 | break; | ||
315 | case SNDRV_PCM_FORMAT_S24_LE: | ||
316 | iface |= 0x0008; | ||
317 | break; | ||
318 | case SNDRV_PCM_FORMAT_S32_LE: | ||
319 | iface |= 0x000c; | ||
320 | break; | ||
321 | } | ||
322 | ssm2602_write(codec, SSM2602_IFACE, iface); | ||
323 | ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC); | ||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | static int ssm2602_startup(struct snd_pcm_substream *substream) | ||
328 | { | ||
329 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
330 | struct snd_soc_device *socdev = rtd->socdev; | ||
331 | struct snd_soc_codec *codec = socdev->codec; | ||
332 | struct ssm2602_priv *ssm2602 = codec->private_data; | ||
333 | struct snd_pcm_runtime *master_runtime; | ||
334 | |||
335 | /* The DAI has shared clocks so if we already have a playback or | ||
336 | * capture going then constrain this substream to match it. | ||
337 | */ | ||
338 | if (ssm2602->master_substream) { | ||
339 | master_runtime = ssm2602->master_substream->runtime; | ||
340 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
341 | SNDRV_PCM_HW_PARAM_RATE, | ||
342 | master_runtime->rate, | ||
343 | master_runtime->rate); | ||
344 | |||
345 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
346 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, | ||
347 | master_runtime->sample_bits, | ||
348 | master_runtime->sample_bits); | ||
349 | |||
350 | ssm2602->slave_substream = substream; | ||
351 | } else | ||
352 | ssm2602->master_substream = substream; | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream) | ||
358 | { | ||
359 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
360 | struct snd_soc_device *socdev = rtd->socdev; | ||
361 | struct snd_soc_codec *codec = socdev->codec; | ||
362 | /* set active */ | ||
363 | ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC); | ||
364 | |||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | static void ssm2602_shutdown(struct snd_pcm_substream *substream) | ||
369 | { | ||
370 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
371 | struct snd_soc_device *socdev = rtd->socdev; | ||
372 | struct snd_soc_codec *codec = socdev->codec; | ||
373 | /* deactivate */ | ||
374 | if (!codec->active) | ||
375 | ssm2602_write(codec, SSM2602_ACTIVE, 0); | ||
376 | } | ||
377 | |||
378 | static int ssm2602_mute(struct snd_soc_dai *dai, int mute) | ||
379 | { | ||
380 | struct snd_soc_codec *codec = dai->codec; | ||
381 | u16 mute_reg = ssm2602_read_reg_cache(codec, SSM2602_APDIGI) & ~APDIGI_ENABLE_DAC_MUTE; | ||
382 | if (mute) | ||
383 | ssm2602_write(codec, SSM2602_APDIGI, | ||
384 | mute_reg | APDIGI_ENABLE_DAC_MUTE); | ||
385 | else | ||
386 | ssm2602_write(codec, SSM2602_APDIGI, mute_reg); | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai, | ||
391 | int clk_id, unsigned int freq, int dir) | ||
392 | { | ||
393 | struct snd_soc_codec *codec = codec_dai->codec; | ||
394 | struct ssm2602_priv *ssm2602 = codec->private_data; | ||
395 | switch (freq) { | ||
396 | case 11289600: | ||
397 | case 12000000: | ||
398 | case 12288000: | ||
399 | case 16934400: | ||
400 | case 18432000: | ||
401 | ssm2602->sysclk = freq; | ||
402 | return 0; | ||
403 | } | ||
404 | return -EINVAL; | ||
405 | } | ||
406 | |||
407 | static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
408 | unsigned int fmt) | ||
409 | { | ||
410 | struct snd_soc_codec *codec = codec_dai->codec; | ||
411 | u16 iface = 0; | ||
412 | |||
413 | /* set master/slave audio interface */ | ||
414 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
415 | case SND_SOC_DAIFMT_CBM_CFM: | ||
416 | iface |= 0x0040; | ||
417 | break; | ||
418 | case SND_SOC_DAIFMT_CBS_CFS: | ||
419 | break; | ||
420 | default: | ||
421 | return -EINVAL; | ||
422 | } | ||
423 | |||
424 | /* interface format */ | ||
425 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
426 | case SND_SOC_DAIFMT_I2S: | ||
427 | iface |= 0x0002; | ||
428 | break; | ||
429 | case SND_SOC_DAIFMT_RIGHT_J: | ||
430 | break; | ||
431 | case SND_SOC_DAIFMT_LEFT_J: | ||
432 | iface |= 0x0001; | ||
433 | break; | ||
434 | case SND_SOC_DAIFMT_DSP_A: | ||
435 | iface |= 0x0003; | ||
436 | break; | ||
437 | case SND_SOC_DAIFMT_DSP_B: | ||
438 | iface |= 0x0013; | ||
439 | break; | ||
440 | default: | ||
441 | return -EINVAL; | ||
442 | } | ||
443 | |||
444 | /* clock inversion */ | ||
445 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
446 | case SND_SOC_DAIFMT_NB_NF: | ||
447 | break; | ||
448 | case SND_SOC_DAIFMT_IB_IF: | ||
449 | iface |= 0x0090; | ||
450 | break; | ||
451 | case SND_SOC_DAIFMT_IB_NF: | ||
452 | iface |= 0x0080; | ||
453 | break; | ||
454 | case SND_SOC_DAIFMT_NB_IF: | ||
455 | iface |= 0x0010; | ||
456 | break; | ||
457 | default: | ||
458 | return -EINVAL; | ||
459 | } | ||
460 | |||
461 | /* set iface */ | ||
462 | ssm2602_write(codec, SSM2602_IFACE, iface); | ||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | static int ssm2602_set_bias_level(struct snd_soc_codec *codec, | ||
467 | enum snd_soc_bias_level level) | ||
468 | { | ||
469 | u16 reg = ssm2602_read_reg_cache(codec, SSM2602_PWR) & 0xff7f; | ||
470 | |||
471 | switch (level) { | ||
472 | case SND_SOC_BIAS_ON: | ||
473 | /* vref/mid, osc on, dac unmute */ | ||
474 | ssm2602_write(codec, SSM2602_PWR, reg); | ||
475 | break; | ||
476 | case SND_SOC_BIAS_PREPARE: | ||
477 | break; | ||
478 | case SND_SOC_BIAS_STANDBY: | ||
479 | /* everything off except vref/vmid, */ | ||
480 | ssm2602_write(codec, SSM2602_PWR, reg | PWR_CLK_OUT_PDN); | ||
481 | break; | ||
482 | case SND_SOC_BIAS_OFF: | ||
483 | /* everything off, dac mute, inactive */ | ||
484 | ssm2602_write(codec, SSM2602_ACTIVE, 0); | ||
485 | ssm2602_write(codec, SSM2602_PWR, 0xffff); | ||
486 | break; | ||
487 | |||
488 | } | ||
489 | codec->bias_level = level; | ||
490 | return 0; | ||
491 | } | ||
492 | |||
493 | #define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ | ||
494 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ | ||
495 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ | ||
496 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ | ||
497 | SNDRV_PCM_RATE_96000) | ||
498 | |||
499 | struct snd_soc_dai ssm2602_dai = { | ||
500 | .name = "SSM2602", | ||
501 | .playback = { | ||
502 | .stream_name = "Playback", | ||
503 | .channels_min = 2, | ||
504 | .channels_max = 2, | ||
505 | .rates = SSM2602_RATES, | ||
506 | .formats = SNDRV_PCM_FMTBIT_S32_LE,}, | ||
507 | .capture = { | ||
508 | .stream_name = "Capture", | ||
509 | .channels_min = 2, | ||
510 | .channels_max = 2, | ||
511 | .rates = SSM2602_RATES, | ||
512 | .formats = SNDRV_PCM_FMTBIT_S32_LE,}, | ||
513 | .ops = { | ||
514 | .startup = ssm2602_startup, | ||
515 | .prepare = ssm2602_pcm_prepare, | ||
516 | .hw_params = ssm2602_hw_params, | ||
517 | .shutdown = ssm2602_shutdown, | ||
518 | }, | ||
519 | .dai_ops = { | ||
520 | .digital_mute = ssm2602_mute, | ||
521 | .set_sysclk = ssm2602_set_dai_sysclk, | ||
522 | .set_fmt = ssm2602_set_dai_fmt, | ||
523 | } | ||
524 | }; | ||
525 | EXPORT_SYMBOL_GPL(ssm2602_dai); | ||
526 | |||
527 | static int ssm2602_suspend(struct platform_device *pdev, pm_message_t state) | ||
528 | { | ||
529 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
530 | struct snd_soc_codec *codec = socdev->codec; | ||
531 | |||
532 | ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | static int ssm2602_resume(struct platform_device *pdev) | ||
537 | { | ||
538 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
539 | struct snd_soc_codec *codec = socdev->codec; | ||
540 | int i; | ||
541 | u8 data[2]; | ||
542 | u16 *cache = codec->reg_cache; | ||
543 | |||
544 | /* Sync reg_cache with the hardware */ | ||
545 | for (i = 0; i < ARRAY_SIZE(ssm2602_reg); i++) { | ||
546 | data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); | ||
547 | data[1] = cache[i] & 0x00ff; | ||
548 | codec->hw_write(codec->control_data, data, 2); | ||
549 | } | ||
550 | ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
551 | ssm2602_set_bias_level(codec, codec->suspend_bias_level); | ||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | /* | ||
556 | * initialise the ssm2602 driver | ||
557 | * register the mixer and dsp interfaces with the kernel | ||
558 | */ | ||
559 | static int ssm2602_init(struct snd_soc_device *socdev) | ||
560 | { | ||
561 | struct snd_soc_codec *codec = socdev->codec; | ||
562 | int reg, ret = 0; | ||
563 | |||
564 | codec->name = "SSM2602"; | ||
565 | codec->owner = THIS_MODULE; | ||
566 | codec->read = ssm2602_read_reg_cache; | ||
567 | codec->write = ssm2602_write; | ||
568 | codec->set_bias_level = ssm2602_set_bias_level; | ||
569 | codec->dai = &ssm2602_dai; | ||
570 | codec->num_dai = 1; | ||
571 | codec->reg_cache_size = sizeof(ssm2602_reg); | ||
572 | codec->reg_cache = kmemdup(ssm2602_reg, sizeof(ssm2602_reg), | ||
573 | GFP_KERNEL); | ||
574 | if (codec->reg_cache == NULL) | ||
575 | return -ENOMEM; | ||
576 | |||
577 | ssm2602_reset(codec); | ||
578 | |||
579 | /* register pcms */ | ||
580 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
581 | if (ret < 0) { | ||
582 | pr_err("ssm2602: failed to create pcms\n"); | ||
583 | goto pcm_err; | ||
584 | } | ||
585 | /*power on device*/ | ||
586 | ssm2602_write(codec, SSM2602_ACTIVE, 0); | ||
587 | /* set the update bits */ | ||
588 | reg = ssm2602_read_reg_cache(codec, SSM2602_LINVOL); | ||
589 | ssm2602_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH); | ||
590 | reg = ssm2602_read_reg_cache(codec, SSM2602_RINVOL); | ||
591 | ssm2602_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH); | ||
592 | reg = ssm2602_read_reg_cache(codec, SSM2602_LOUT1V); | ||
593 | ssm2602_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH); | ||
594 | reg = ssm2602_read_reg_cache(codec, SSM2602_ROUT1V); | ||
595 | ssm2602_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH); | ||
596 | /*select Line in as default input*/ | ||
597 | ssm2602_write(codec, SSM2602_APANA, | ||
598 | APANA_ENABLE_MIC_BOOST2 | APANA_SELECT_DAC | | ||
599 | APANA_ENABLE_MIC_BOOST); | ||
600 | ssm2602_write(codec, SSM2602_PWR, 0); | ||
601 | |||
602 | ssm2602_add_controls(codec); | ||
603 | ssm2602_add_widgets(codec); | ||
604 | ret = snd_soc_register_card(socdev); | ||
605 | if (ret < 0) { | ||
606 | pr_err("ssm2602: failed to register card\n"); | ||
607 | goto card_err; | ||
608 | } | ||
609 | |||
610 | return ret; | ||
611 | |||
612 | card_err: | ||
613 | snd_soc_free_pcms(socdev); | ||
614 | snd_soc_dapm_free(socdev); | ||
615 | pcm_err: | ||
616 | kfree(codec->reg_cache); | ||
617 | return ret; | ||
618 | } | ||
619 | |||
620 | static struct snd_soc_device *ssm2602_socdev; | ||
621 | |||
622 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
623 | /* | ||
624 | * ssm2602 2 wire address is determined by GPIO5 | ||
625 | * state during powerup. | ||
626 | * low = 0x1a | ||
627 | * high = 0x1b | ||
628 | */ | ||
629 | static int ssm2602_i2c_probe(struct i2c_client *i2c, | ||
630 | const struct i2c_device_id *id) | ||
631 | { | ||
632 | struct snd_soc_device *socdev = ssm2602_socdev; | ||
633 | struct snd_soc_codec *codec = socdev->codec; | ||
634 | int ret; | ||
635 | |||
636 | i2c_set_clientdata(i2c, codec); | ||
637 | codec->control_data = i2c; | ||
638 | |||
639 | ret = ssm2602_init(socdev); | ||
640 | if (ret < 0) | ||
641 | pr_err("failed to initialise SSM2602\n"); | ||
642 | |||
643 | return ret; | ||
644 | } | ||
645 | |||
646 | static int ssm2602_i2c_remove(struct i2c_client *client) | ||
647 | { | ||
648 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | ||
649 | kfree(codec->reg_cache); | ||
650 | return 0; | ||
651 | } | ||
652 | |||
653 | static const struct i2c_device_id ssm2602_i2c_id[] = { | ||
654 | { "ssm2602", 0 }, | ||
655 | { } | ||
656 | }; | ||
657 | MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); | ||
658 | /* corgi i2c codec control layer */ | ||
659 | static struct i2c_driver ssm2602_i2c_driver = { | ||
660 | .driver = { | ||
661 | .name = "SSM2602 I2C Codec", | ||
662 | .owner = THIS_MODULE, | ||
663 | }, | ||
664 | .probe = ssm2602_i2c_probe, | ||
665 | .remove = ssm2602_i2c_remove, | ||
666 | .id_table = ssm2602_i2c_id, | ||
667 | }; | ||
668 | |||
669 | static int ssm2602_add_i2c_device(struct platform_device *pdev, | ||
670 | const struct ssm2602_setup_data *setup) | ||
671 | { | ||
672 | struct i2c_board_info info; | ||
673 | struct i2c_adapter *adapter; | ||
674 | struct i2c_client *client; | ||
675 | int ret; | ||
676 | |||
677 | ret = i2c_add_driver(&ssm2602_i2c_driver); | ||
678 | if (ret != 0) { | ||
679 | dev_err(&pdev->dev, "can't add i2c driver\n"); | ||
680 | return ret; | ||
681 | } | ||
682 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
683 | info.addr = setup->i2c_address; | ||
684 | strlcpy(info.type, "ssm2602", I2C_NAME_SIZE); | ||
685 | adapter = i2c_get_adapter(setup->i2c_bus); | ||
686 | if (!adapter) { | ||
687 | dev_err(&pdev->dev, "can't get i2c adapter %d\n", | ||
688 | setup->i2c_bus); | ||
689 | goto err_driver; | ||
690 | } | ||
691 | client = i2c_new_device(adapter, &info); | ||
692 | i2c_put_adapter(adapter); | ||
693 | if (!client) { | ||
694 | dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", | ||
695 | (unsigned int)info.addr); | ||
696 | goto err_driver; | ||
697 | } | ||
698 | return 0; | ||
699 | err_driver: | ||
700 | i2c_del_driver(&ssm2602_i2c_driver); | ||
701 | return -ENODEV; | ||
702 | } | ||
703 | #endif | ||
704 | |||
705 | static int ssm2602_probe(struct platform_device *pdev) | ||
706 | { | ||
707 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
708 | struct ssm2602_setup_data *setup; | ||
709 | struct snd_soc_codec *codec; | ||
710 | struct ssm2602_priv *ssm2602; | ||
711 | int ret = 0; | ||
712 | |||
713 | pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION); | ||
714 | |||
715 | setup = socdev->codec_data; | ||
716 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | ||
717 | if (codec == NULL) | ||
718 | return -ENOMEM; | ||
719 | |||
720 | ssm2602 = kzalloc(sizeof(struct ssm2602_priv), GFP_KERNEL); | ||
721 | if (ssm2602 == NULL) { | ||
722 | kfree(codec); | ||
723 | return -ENOMEM; | ||
724 | } | ||
725 | |||
726 | codec->private_data = ssm2602; | ||
727 | socdev->codec = codec; | ||
728 | mutex_init(&codec->mutex); | ||
729 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
730 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
731 | |||
732 | ssm2602_socdev = socdev; | ||
733 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
734 | if (setup->i2c_address) { | ||
735 | codec->hw_write = (hw_write_t)i2c_master_send; | ||
736 | ret = ssm2602_add_i2c_device(pdev, setup); | ||
737 | } | ||
738 | #else | ||
739 | /* other interfaces */ | ||
740 | #endif | ||
741 | return ret; | ||
742 | } | ||
743 | |||
744 | /* remove everything here */ | ||
745 | static int ssm2602_remove(struct platform_device *pdev) | ||
746 | { | ||
747 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
748 | struct snd_soc_codec *codec = socdev->codec; | ||
749 | |||
750 | if (codec->control_data) | ||
751 | ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
752 | |||
753 | snd_soc_free_pcms(socdev); | ||
754 | snd_soc_dapm_free(socdev); | ||
755 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
756 | i2c_unregister_device(codec->control_data); | ||
757 | i2c_del_driver(&ssm2602_i2c_driver); | ||
758 | #endif | ||
759 | kfree(codec->private_data); | ||
760 | kfree(codec); | ||
761 | |||
762 | return 0; | ||
763 | } | ||
764 | |||
765 | struct snd_soc_codec_device soc_codec_dev_ssm2602 = { | ||
766 | .probe = ssm2602_probe, | ||
767 | .remove = ssm2602_remove, | ||
768 | .suspend = ssm2602_suspend, | ||
769 | .resume = ssm2602_resume, | ||
770 | }; | ||
771 | EXPORT_SYMBOL_GPL(soc_codec_dev_ssm2602); | ||
772 | |||
773 | MODULE_DESCRIPTION("ASoC ssm2602 driver"); | ||
774 | MODULE_AUTHOR("Cliff Cai"); | ||
775 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/ssm2602.h b/sound/soc/codecs/ssm2602.h new file mode 100644 index 000000000000..f344e6d76e31 --- /dev/null +++ b/sound/soc/codecs/ssm2602.h | |||
@@ -0,0 +1,130 @@ | |||
1 | /* | ||
2 | * File: sound/soc/codecs/ssm2602.h | ||
3 | * Author: Cliff Cai <Cliff.Cai@analog.com> | ||
4 | * | ||
5 | * Created: Tue June 06 2008 | ||
6 | * | ||
7 | * Modified: | ||
8 | * Copyright 2008 Analog Devices Inc. | ||
9 | * | ||
10 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, see the file COPYING, or write | ||
24 | * to the Free Software Foundation, Inc., | ||
25 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
26 | */ | ||
27 | |||
28 | #ifndef _SSM2602_H | ||
29 | #define _SSM2602_H | ||
30 | |||
31 | /* SSM2602 Codec Register definitions */ | ||
32 | |||
33 | #define SSM2602_LINVOL 0x00 | ||
34 | #define SSM2602_RINVOL 0x01 | ||
35 | #define SSM2602_LOUT1V 0x02 | ||
36 | #define SSM2602_ROUT1V 0x03 | ||
37 | #define SSM2602_APANA 0x04 | ||
38 | #define SSM2602_APDIGI 0x05 | ||
39 | #define SSM2602_PWR 0x06 | ||
40 | #define SSM2602_IFACE 0x07 | ||
41 | #define SSM2602_SRATE 0x08 | ||
42 | #define SSM2602_ACTIVE 0x09 | ||
43 | #define SSM2602_RESET 0x0f | ||
44 | |||
45 | /*SSM2602 Codec Register Field definitions | ||
46 | *(Mask value to extract the corresponding Register field) | ||
47 | */ | ||
48 | |||
49 | /*Left ADC Volume Control (SSM2602_REG_LEFT_ADC_VOL)*/ | ||
50 | #define LINVOL_LIN_VOL 0x01F /* Left Channel PGA Volume control */ | ||
51 | #define LINVOL_LIN_ENABLE_MUTE 0x080 /* Left Channel Input Mute */ | ||
52 | #define LINVOL_LRIN_BOTH 0x100 /* Left Channel Line Input Volume update */ | ||
53 | |||
54 | /*Right ADC Volume Control (SSM2602_REG_RIGHT_ADC_VOL)*/ | ||
55 | #define RINVOL_RIN_VOL 0x01F /* Right Channel PGA Volume control */ | ||
56 | #define RINVOL_RIN_ENABLE_MUTE 0x080 /* Right Channel Input Mute */ | ||
57 | #define RINVOL_RLIN_BOTH 0x100 /* Right Channel Line Input Volume update */ | ||
58 | |||
59 | /*Left DAC Volume Control (SSM2602_REG_LEFT_DAC_VOL)*/ | ||
60 | #define LOUT1V_LHP_VOL 0x07F /* Left Channel Headphone volume control */ | ||
61 | #define LOUT1V_ENABLE_LZC 0x080 /* Left Channel Zero cross detect enable */ | ||
62 | #define LOUT1V_LRHP_BOTH 0x100 /* Left Channel Headphone volume update */ | ||
63 | |||
64 | /*Right DAC Volume Control (SSM2602_REG_RIGHT_DAC_VOL)*/ | ||
65 | #define ROUT1V_RHP_VOL 0x07F /* Right Channel Headphone volume control */ | ||
66 | #define ROUT1V_ENABLE_RZC 0x080 /* Right Channel Zero cross detect enable */ | ||
67 | #define ROUT1V_RLHP_BOTH 0x100 /* Right Channel Headphone volume update */ | ||
68 | |||
69 | /*Analogue Audio Path Control (SSM2602_REG_ANALOGUE_PATH)*/ | ||
70 | #define APANA_ENABLE_MIC_BOOST 0x001 /* Primary Microphone Amplifier gain booster control */ | ||
71 | #define APANA_ENABLE_MIC_MUTE 0x002 /* Microphone Mute Control */ | ||
72 | #define APANA_ADC_IN_SELECT 0x004 /* Microphone/Line IN select to ADC (1=MIC, 0=Line In) */ | ||
73 | #define APANA_ENABLE_BYPASS 0x008 /* Line input bypass to line output */ | ||
74 | #define APANA_SELECT_DAC 0x010 /* Select DAC (1=Select DAC, 0=Don't Select DAC) */ | ||
75 | #define APANA_ENABLE_SIDETONE 0x020 /* Enable/Disable Side Tone */ | ||
76 | #define APANA_SIDETONE_ATTN 0x0C0 /* Side Tone Attenuation */ | ||
77 | #define APANA_ENABLE_MIC_BOOST2 0x100 /* Secondary Microphone Amplifier gain booster control */ | ||
78 | |||
79 | /*Digital Audio Path Control (SSM2602_REG_DIGITAL_PATH)*/ | ||
80 | #define APDIGI_ENABLE_ADC_HPF 0x001 /* Enable/Disable ADC Highpass Filter */ | ||
81 | #define APDIGI_DE_EMPHASIS 0x006 /* De-Emphasis Control */ | ||
82 | #define APDIGI_ENABLE_DAC_MUTE 0x008 /* DAC Mute Control */ | ||
83 | #define APDIGI_STORE_OFFSET 0x010 /* Store/Clear DC offset when HPF is disabled */ | ||
84 | |||
85 | /*Power Down Control (SSM2602_REG_POWER) | ||
86 | *(1=Enable PowerDown, 0=Disable PowerDown) | ||
87 | */ | ||
88 | #define PWR_LINE_IN_PDN 0x001 /* Line Input Power Down */ | ||
89 | #define PWR_MIC_PDN 0x002 /* Microphone Input & Bias Power Down */ | ||
90 | #define PWR_ADC_PDN 0x004 /* ADC Power Down */ | ||
91 | #define PWR_DAC_PDN 0x008 /* DAC Power Down */ | ||
92 | #define PWR_OUT_PDN 0x010 /* Outputs Power Down */ | ||
93 | #define PWR_OSC_PDN 0x020 /* Oscillator Power Down */ | ||
94 | #define PWR_CLK_OUT_PDN 0x040 /* CLKOUT Power Down */ | ||
95 | #define PWR_POWER_OFF 0x080 /* POWEROFF Mode */ | ||
96 | |||
97 | /*Digital Audio Interface Format (SSM2602_REG_DIGITAL_IFACE)*/ | ||
98 | #define IFACE_IFACE_FORMAT 0x003 /* Digital Audio input format control */ | ||
99 | #define IFACE_AUDIO_DATA_LEN 0x00C /* Audio Data word length control */ | ||
100 | #define IFACE_DAC_LR_POLARITY 0x010 /* Polarity Control for clocks in RJ,LJ and I2S modes */ | ||
101 | #define IFACE_DAC_LR_SWAP 0x020 /* Swap DAC data control */ | ||
102 | #define IFACE_ENABLE_MASTER 0x040 /* Enable/Disable Master Mode */ | ||
103 | #define IFACE_BCLK_INVERT 0x080 /* Bit Clock Inversion control */ | ||
104 | |||
105 | /*Sampling Control (SSM2602_REG_SAMPLING_CTRL)*/ | ||
106 | #define SRATE_ENABLE_USB_MODE 0x001 /* Enable/Disable USB Mode */ | ||
107 | #define SRATE_BOS_RATE 0x002 /* Base Over-Sampling rate */ | ||
108 | #define SRATE_SAMPLE_RATE 0x03C /* Clock setting condition (Sampling rate control) */ | ||
109 | #define SRATE_CORECLK_DIV2 0x040 /* Core Clock divider select */ | ||
110 | #define SRATE_CLKOUT_DIV2 0x080 /* Clock Out divider select */ | ||
111 | |||
112 | /*Active Control (SSM2602_REG_ACTIVE_CTRL)*/ | ||
113 | #define ACTIVE_ACTIVATE_CODEC 0x001 /* Activate Codec Digital Audio Interface */ | ||
114 | |||
115 | /*********************************************************************/ | ||
116 | |||
117 | #define SSM2602_CACHEREGNUM 10 | ||
118 | |||
119 | #define SSM2602_SYSCLK 0 | ||
120 | #define SSM2602_DAI 0 | ||
121 | |||
122 | struct ssm2602_setup_data { | ||
123 | int i2c_bus; | ||
124 | unsigned short i2c_address; | ||
125 | }; | ||
126 | |||
127 | extern struct snd_soc_dai ssm2602_dai; | ||
128 | extern struct snd_soc_codec_device soc_codec_dev_ssm2602; | ||
129 | |||
130 | #endif | ||
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c new file mode 100644 index 000000000000..bac7815e00fb --- /dev/null +++ b/sound/soc/codecs/tlv320aic23.c | |||
@@ -0,0 +1,714 @@ | |||
1 | /* | ||
2 | * ALSA SoC TLV320AIC23 codec driver | ||
3 | * | ||
4 | * Author: Arun KS, <arunks@mistralsolutions.com> | ||
5 | * Copyright: (C) 2008 Mistral Solutions Pvt Ltd., | ||
6 | * | ||
7 | * Based on sound/soc/codecs/wm8731.c by Richard Purdie | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * Notes: | ||
14 | * The AIC23 is a driver for a low power stereo audio | ||
15 | * codec tlv320aic23 | ||
16 | * | ||
17 | * The machine layer should disable unsupported inputs/outputs by | ||
18 | * snd_soc_dapm_disable_pin(codec, "LHPOUT"), etc. | ||
19 | */ | ||
20 | |||
21 | #include <linux/module.h> | ||
22 | #include <linux/moduleparam.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/pm.h> | ||
26 | #include <linux/i2c.h> | ||
27 | #include <linux/platform_device.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/pcm.h> | ||
30 | #include <sound/pcm_params.h> | ||
31 | #include <sound/soc.h> | ||
32 | #include <sound/soc-dapm.h> | ||
33 | #include <sound/tlv.h> | ||
34 | #include <sound/initval.h> | ||
35 | |||
36 | #include "tlv320aic23.h" | ||
37 | |||
38 | #define AIC23_VERSION "0.1" | ||
39 | |||
40 | struct tlv320aic23_srate_reg_info { | ||
41 | u32 sample_rate; | ||
42 | u8 control; /* SR3, SR2, SR1, SR0 and BOSR */ | ||
43 | u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */ | ||
44 | }; | ||
45 | |||
46 | /* | ||
47 | * AIC23 register cache | ||
48 | */ | ||
49 | static const u16 tlv320aic23_reg[] = { | ||
50 | 0x0097, 0x0097, 0x00F9, 0x00F9, /* 0 */ | ||
51 | 0x001A, 0x0004, 0x0007, 0x0001, /* 4 */ | ||
52 | 0x0020, 0x0000, 0x0000, 0x0000, /* 8 */ | ||
53 | 0x0000, 0x0000, 0x0000, 0x0000, /* 12 */ | ||
54 | }; | ||
55 | |||
56 | /* | ||
57 | * read tlv320aic23 register cache | ||
58 | */ | ||
59 | static inline unsigned int tlv320aic23_read_reg_cache(struct snd_soc_codec | ||
60 | *codec, unsigned int reg) | ||
61 | { | ||
62 | u16 *cache = codec->reg_cache; | ||
63 | if (reg >= ARRAY_SIZE(tlv320aic23_reg)) | ||
64 | return -1; | ||
65 | return cache[reg]; | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * write tlv320aic23 register cache | ||
70 | */ | ||
71 | static inline void tlv320aic23_write_reg_cache(struct snd_soc_codec *codec, | ||
72 | u8 reg, u16 value) | ||
73 | { | ||
74 | u16 *cache = codec->reg_cache; | ||
75 | if (reg >= ARRAY_SIZE(tlv320aic23_reg)) | ||
76 | return; | ||
77 | cache[reg] = value; | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * write to the tlv320aic23 register space | ||
82 | */ | ||
83 | static int tlv320aic23_write(struct snd_soc_codec *codec, unsigned int reg, | ||
84 | unsigned int value) | ||
85 | { | ||
86 | |||
87 | u8 data; | ||
88 | |||
89 | /* TLV320AIC23 has 7 bit address and 9 bits of data | ||
90 | * so we need to switch one data bit into reg and rest | ||
91 | * of data into val | ||
92 | */ | ||
93 | |||
94 | if ((reg < 0 || reg > 9) && (reg != 15)) { | ||
95 | printk(KERN_WARNING "%s Invalid register R%d\n", __func__, reg); | ||
96 | return -1; | ||
97 | } | ||
98 | |||
99 | data = (reg << 1) | (value >> 8 & 0x01); | ||
100 | |||
101 | tlv320aic23_write_reg_cache(codec, reg, value); | ||
102 | |||
103 | if (codec->hw_write(codec->control_data, data, | ||
104 | (value & 0xff)) == 0) | ||
105 | return 0; | ||
106 | |||
107 | printk(KERN_ERR "%s cannot write %03x to register R%d\n", __func__, | ||
108 | value, reg); | ||
109 | |||
110 | return -EIO; | ||
111 | } | ||
112 | |||
113 | static const char *rec_src_text[] = { "Line", "Mic" }; | ||
114 | static const char *deemph_text[] = {"None", "32Khz", "44.1Khz", "48Khz"}; | ||
115 | |||
116 | static const struct soc_enum rec_src_enum = | ||
117 | SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text); | ||
118 | |||
119 | static const struct snd_kcontrol_new tlv320aic23_rec_src_mux_controls = | ||
120 | SOC_DAPM_ENUM("Input Select", rec_src_enum); | ||
121 | |||
122 | static const struct soc_enum tlv320aic23_rec_src = | ||
123 | SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text); | ||
124 | static const struct soc_enum tlv320aic23_deemph = | ||
125 | SOC_ENUM_SINGLE(TLV320AIC23_DIGT, 1, 4, deemph_text); | ||
126 | |||
127 | static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -12100, 100, 0); | ||
128 | static const DECLARE_TLV_DB_SCALE(input_gain_tlv, -1725, 75, 0); | ||
129 | static const DECLARE_TLV_DB_SCALE(sidetone_vol_tlv, -1800, 300, 0); | ||
130 | |||
131 | static int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol, | ||
132 | struct snd_ctl_elem_value *ucontrol) | ||
133 | { | ||
134 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
135 | u16 val, reg; | ||
136 | |||
137 | val = (ucontrol->value.integer.value[0] & 0x07); | ||
138 | |||
139 | /* linear conversion to userspace | ||
140 | * 000 = -6db | ||
141 | * 001 = -9db | ||
142 | * 010 = -12db | ||
143 | * 011 = -18db (Min) | ||
144 | * 100 = 0db (Max) | ||
145 | */ | ||
146 | val = (val >= 4) ? 4 : (3 - val); | ||
147 | |||
148 | reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG) & (~0x1C0); | ||
149 | tlv320aic23_write(codec, TLV320AIC23_ANLG, reg | (val << 6)); | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static int snd_soc_tlv320aic23_get_volsw(struct snd_kcontrol *kcontrol, | ||
155 | struct snd_ctl_elem_value *ucontrol) | ||
156 | { | ||
157 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
158 | u16 val; | ||
159 | |||
160 | val = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG) & (0x1C0); | ||
161 | val = val >> 6; | ||
162 | val = (val >= 4) ? 4 : (3 - val); | ||
163 | ucontrol->value.integer.value[0] = val; | ||
164 | return 0; | ||
165 | |||
166 | } | ||
167 | |||
168 | #define SOC_TLV320AIC23_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ | ||
169 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | ||
170 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ | ||
171 | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ | ||
172 | .tlv.p = (tlv_array), \ | ||
173 | .info = snd_soc_info_volsw, .get = snd_soc_tlv320aic23_get_volsw,\ | ||
174 | .put = snd_soc_tlv320aic23_put_volsw, \ | ||
175 | .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } | ||
176 | |||
177 | static const struct snd_kcontrol_new tlv320aic23_snd_controls[] = { | ||
178 | SOC_DOUBLE_R_TLV("Digital Playback Volume", TLV320AIC23_LCHNVOL, | ||
179 | TLV320AIC23_RCHNVOL, 0, 127, 0, out_gain_tlv), | ||
180 | SOC_SINGLE("Digital Playback Switch", TLV320AIC23_DIGT, 3, 1, 1), | ||
181 | SOC_DOUBLE_R("Line Input Switch", TLV320AIC23_LINVOL, | ||
182 | TLV320AIC23_RINVOL, 7, 1, 0), | ||
183 | SOC_DOUBLE_R_TLV("Line Input Volume", TLV320AIC23_LINVOL, | ||
184 | TLV320AIC23_RINVOL, 0, 31, 0, input_gain_tlv), | ||
185 | SOC_SINGLE("Mic Input Switch", TLV320AIC23_ANLG, 1, 1, 1), | ||
186 | SOC_SINGLE("Mic Booster Switch", TLV320AIC23_ANLG, 0, 1, 0), | ||
187 | SOC_TLV320AIC23_SINGLE_TLV("Sidetone Volume", TLV320AIC23_ANLG, | ||
188 | 6, 4, 0, sidetone_vol_tlv), | ||
189 | SOC_ENUM("Playback De-emphasis", tlv320aic23_deemph), | ||
190 | }; | ||
191 | |||
192 | /* add non dapm controls */ | ||
193 | static int tlv320aic23_add_controls(struct snd_soc_codec *codec) | ||
194 | { | ||
195 | |||
196 | int err, i; | ||
197 | |||
198 | for (i = 0; i < ARRAY_SIZE(tlv320aic23_snd_controls); i++) { | ||
199 | err = snd_ctl_add(codec->card, | ||
200 | snd_soc_cnew(&tlv320aic23_snd_controls[i], | ||
201 | codec, NULL)); | ||
202 | if (err < 0) | ||
203 | return err; | ||
204 | } | ||
205 | |||
206 | return 0; | ||
207 | |||
208 | } | ||
209 | |||
210 | /* PGA Mixer controls for Line and Mic switch */ | ||
211 | static const struct snd_kcontrol_new tlv320aic23_output_mixer_controls[] = { | ||
212 | SOC_DAPM_SINGLE("Line Bypass Switch", TLV320AIC23_ANLG, 3, 1, 0), | ||
213 | SOC_DAPM_SINGLE("Mic Sidetone Switch", TLV320AIC23_ANLG, 5, 1, 0), | ||
214 | SOC_DAPM_SINGLE("Playback Switch", TLV320AIC23_ANLG, 4, 1, 0), | ||
215 | }; | ||
216 | |||
217 | static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { | ||
218 | SND_SOC_DAPM_DAC("DAC", "Playback", TLV320AIC23_PWR, 3, 1), | ||
219 | SND_SOC_DAPM_ADC("ADC", "Capture", TLV320AIC23_PWR, 2, 1), | ||
220 | SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0, | ||
221 | &tlv320aic23_rec_src_mux_controls), | ||
222 | SND_SOC_DAPM_MIXER("Output Mixer", TLV320AIC23_PWR, 4, 1, | ||
223 | &tlv320aic23_output_mixer_controls[0], | ||
224 | ARRAY_SIZE(tlv320aic23_output_mixer_controls)), | ||
225 | SND_SOC_DAPM_PGA("Line Input", TLV320AIC23_PWR, 0, 1, NULL, 0), | ||
226 | SND_SOC_DAPM_PGA("Mic Input", TLV320AIC23_PWR, 1, 1, NULL, 0), | ||
227 | |||
228 | SND_SOC_DAPM_OUTPUT("LHPOUT"), | ||
229 | SND_SOC_DAPM_OUTPUT("RHPOUT"), | ||
230 | SND_SOC_DAPM_OUTPUT("LOUT"), | ||
231 | SND_SOC_DAPM_OUTPUT("ROUT"), | ||
232 | |||
233 | SND_SOC_DAPM_INPUT("LLINEIN"), | ||
234 | SND_SOC_DAPM_INPUT("RLINEIN"), | ||
235 | |||
236 | SND_SOC_DAPM_INPUT("MICIN"), | ||
237 | }; | ||
238 | |||
239 | static const struct snd_soc_dapm_route intercon[] = { | ||
240 | /* Output Mixer */ | ||
241 | {"Output Mixer", "Line Bypass Switch", "Line Input"}, | ||
242 | {"Output Mixer", "Playback Switch", "DAC"}, | ||
243 | {"Output Mixer", "Mic Sidetone Switch", "Mic Input"}, | ||
244 | |||
245 | /* Outputs */ | ||
246 | {"RHPOUT", NULL, "Output Mixer"}, | ||
247 | {"LHPOUT", NULL, "Output Mixer"}, | ||
248 | {"LOUT", NULL, "Output Mixer"}, | ||
249 | {"ROUT", NULL, "Output Mixer"}, | ||
250 | |||
251 | /* Inputs */ | ||
252 | {"Line Input", "NULL", "LLINEIN"}, | ||
253 | {"Line Input", "NULL", "RLINEIN"}, | ||
254 | |||
255 | {"Mic Input", "NULL", "MICIN"}, | ||
256 | |||
257 | /* input mux */ | ||
258 | {"Capture Source", "Line", "Line Input"}, | ||
259 | {"Capture Source", "Mic", "Mic Input"}, | ||
260 | {"ADC", NULL, "Capture Source"}, | ||
261 | |||
262 | }; | ||
263 | |||
264 | /* tlv320aic23 related */ | ||
265 | static const struct tlv320aic23_srate_reg_info srate_reg_info[] = { | ||
266 | {4000, 0x06, 1}, /* 4000 */ | ||
267 | {8000, 0x06, 0}, /* 8000 */ | ||
268 | {16000, 0x0C, 1}, /* 16000 */ | ||
269 | {22050, 0x11, 1}, /* 22050 */ | ||
270 | {24000, 0x00, 1}, /* 24000 */ | ||
271 | {32000, 0x0C, 0}, /* 32000 */ | ||
272 | {44100, 0x11, 0}, /* 44100 */ | ||
273 | {48000, 0x00, 0}, /* 48000 */ | ||
274 | {88200, 0x1F, 0}, /* 88200 */ | ||
275 | {96000, 0x0E, 0}, /* 96000 */ | ||
276 | }; | ||
277 | |||
278 | static int tlv320aic23_add_widgets(struct snd_soc_codec *codec) | ||
279 | { | ||
280 | snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, | ||
281 | ARRAY_SIZE(tlv320aic23_dapm_widgets)); | ||
282 | |||
283 | /* set up audio path interconnects */ | ||
284 | snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); | ||
285 | |||
286 | snd_soc_dapm_new_widgets(codec); | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | static int tlv320aic23_hw_params(struct snd_pcm_substream *substream, | ||
291 | struct snd_pcm_hw_params *params) | ||
292 | { | ||
293 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
294 | struct snd_soc_device *socdev = rtd->socdev; | ||
295 | struct snd_soc_codec *codec = socdev->codec; | ||
296 | u16 iface_reg, data; | ||
297 | u8 count = 0; | ||
298 | |||
299 | iface_reg = | ||
300 | tlv320aic23_read_reg_cache(codec, | ||
301 | TLV320AIC23_DIGT_FMT) & ~(0x03 << 2); | ||
302 | |||
303 | /* Search for the right sample rate */ | ||
304 | /* Verify what happens if the rate is not supported | ||
305 | * now it goes to 96Khz */ | ||
306 | while ((srate_reg_info[count].sample_rate != params_rate(params)) && | ||
307 | (count < ARRAY_SIZE(srate_reg_info))) { | ||
308 | count++; | ||
309 | } | ||
310 | |||
311 | data = (srate_reg_info[count].divider << TLV320AIC23_CLKIN_SHIFT) | | ||
312 | (srate_reg_info[count]. control << TLV320AIC23_BOSR_SHIFT) | | ||
313 | TLV320AIC23_USB_CLK_ON; | ||
314 | |||
315 | tlv320aic23_write(codec, TLV320AIC23_SRATE, data); | ||
316 | |||
317 | switch (params_format(params)) { | ||
318 | case SNDRV_PCM_FORMAT_S16_LE: | ||
319 | break; | ||
320 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
321 | iface_reg |= (0x01 << 2); | ||
322 | break; | ||
323 | case SNDRV_PCM_FORMAT_S24_LE: | ||
324 | iface_reg |= (0x02 << 2); | ||
325 | break; | ||
326 | case SNDRV_PCM_FORMAT_S32_LE: | ||
327 | iface_reg |= (0x03 << 2); | ||
328 | break; | ||
329 | } | ||
330 | tlv320aic23_write(codec, TLV320AIC23_DIGT_FMT, iface_reg); | ||
331 | |||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream) | ||
336 | { | ||
337 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
338 | struct snd_soc_device *socdev = rtd->socdev; | ||
339 | struct snd_soc_codec *codec = socdev->codec; | ||
340 | |||
341 | /* set active */ | ||
342 | tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0001); | ||
343 | |||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | static void tlv320aic23_shutdown(struct snd_pcm_substream *substream) | ||
348 | { | ||
349 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
350 | struct snd_soc_device *socdev = rtd->socdev; | ||
351 | struct snd_soc_codec *codec = socdev->codec; | ||
352 | |||
353 | /* deactivate */ | ||
354 | if (!codec->active) { | ||
355 | udelay(50); | ||
356 | tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0); | ||
357 | } | ||
358 | } | ||
359 | |||
360 | static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute) | ||
361 | { | ||
362 | struct snd_soc_codec *codec = dai->codec; | ||
363 | u16 reg; | ||
364 | |||
365 | reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_DIGT); | ||
366 | if (mute) | ||
367 | reg |= TLV320AIC23_DACM_MUTE; | ||
368 | |||
369 | else | ||
370 | reg &= ~TLV320AIC23_DACM_MUTE; | ||
371 | |||
372 | tlv320aic23_write(codec, TLV320AIC23_DIGT, reg); | ||
373 | |||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
378 | unsigned int fmt) | ||
379 | { | ||
380 | struct snd_soc_codec *codec = codec_dai->codec; | ||
381 | u16 iface_reg; | ||
382 | |||
383 | iface_reg = | ||
384 | tlv320aic23_read_reg_cache(codec, TLV320AIC23_DIGT_FMT) & (~0x03); | ||
385 | |||
386 | /* set master/slave audio interface */ | ||
387 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
388 | case SND_SOC_DAIFMT_CBM_CFM: | ||
389 | iface_reg |= TLV320AIC23_MS_MASTER; | ||
390 | break; | ||
391 | case SND_SOC_DAIFMT_CBS_CFS: | ||
392 | break; | ||
393 | default: | ||
394 | return -EINVAL; | ||
395 | |||
396 | } | ||
397 | |||
398 | /* interface format */ | ||
399 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
400 | case SND_SOC_DAIFMT_I2S: | ||
401 | iface_reg |= TLV320AIC23_FOR_I2S; | ||
402 | break; | ||
403 | case SND_SOC_DAIFMT_DSP_A: | ||
404 | iface_reg |= TLV320AIC23_FOR_DSP; | ||
405 | break; | ||
406 | case SND_SOC_DAIFMT_RIGHT_J: | ||
407 | break; | ||
408 | case SND_SOC_DAIFMT_LEFT_J: | ||
409 | iface_reg |= TLV320AIC23_FOR_LJUST; | ||
410 | break; | ||
411 | default: | ||
412 | return -EINVAL; | ||
413 | |||
414 | } | ||
415 | |||
416 | tlv320aic23_write(codec, TLV320AIC23_DIGT_FMT, iface_reg); | ||
417 | |||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | static int tlv320aic23_set_dai_sysclk(struct snd_soc_dai *codec_dai, | ||
422 | int clk_id, unsigned int freq, int dir) | ||
423 | { | ||
424 | struct snd_soc_codec *codec = codec_dai->codec; | ||
425 | |||
426 | switch (freq) { | ||
427 | case 12000000: | ||
428 | return 0; | ||
429 | } | ||
430 | return -EINVAL; | ||
431 | } | ||
432 | |||
433 | static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec, | ||
434 | enum snd_soc_bias_level level) | ||
435 | { | ||
436 | u16 reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_PWR) & 0xff7f; | ||
437 | |||
438 | switch (level) { | ||
439 | case SND_SOC_BIAS_ON: | ||
440 | /* vref/mid, osc on, dac unmute */ | ||
441 | tlv320aic23_write(codec, TLV320AIC23_PWR, reg); | ||
442 | break; | ||
443 | case SND_SOC_BIAS_PREPARE: | ||
444 | break; | ||
445 | case SND_SOC_BIAS_STANDBY: | ||
446 | /* everything off except vref/vmid, */ | ||
447 | tlv320aic23_write(codec, TLV320AIC23_PWR, reg | 0x0040); | ||
448 | break; | ||
449 | case SND_SOC_BIAS_OFF: | ||
450 | /* everything off, dac mute, inactive */ | ||
451 | tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0); | ||
452 | tlv320aic23_write(codec, TLV320AIC23_PWR, 0xffff); | ||
453 | break; | ||
454 | } | ||
455 | codec->bias_level = level; | ||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | #define AIC23_RATES SNDRV_PCM_RATE_8000_96000 | ||
460 | #define AIC23_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ | ||
461 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) | ||
462 | |||
463 | struct snd_soc_dai tlv320aic23_dai = { | ||
464 | .name = "tlv320aic23", | ||
465 | .playback = { | ||
466 | .stream_name = "Playback", | ||
467 | .channels_min = 2, | ||
468 | .channels_max = 2, | ||
469 | .rates = AIC23_RATES, | ||
470 | .formats = AIC23_FORMATS,}, | ||
471 | .capture = { | ||
472 | .stream_name = "Capture", | ||
473 | .channels_min = 2, | ||
474 | .channels_max = 2, | ||
475 | .rates = AIC23_RATES, | ||
476 | .formats = AIC23_FORMATS,}, | ||
477 | .ops = { | ||
478 | .prepare = tlv320aic23_pcm_prepare, | ||
479 | .hw_params = tlv320aic23_hw_params, | ||
480 | .shutdown = tlv320aic23_shutdown, | ||
481 | }, | ||
482 | .dai_ops = { | ||
483 | .digital_mute = tlv320aic23_mute, | ||
484 | .set_fmt = tlv320aic23_set_dai_fmt, | ||
485 | .set_sysclk = tlv320aic23_set_dai_sysclk, | ||
486 | } | ||
487 | }; | ||
488 | EXPORT_SYMBOL_GPL(tlv320aic23_dai); | ||
489 | |||
490 | static int tlv320aic23_suspend(struct platform_device *pdev, | ||
491 | pm_message_t state) | ||
492 | { | ||
493 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
494 | struct snd_soc_codec *codec = socdev->codec; | ||
495 | |||
496 | tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0); | ||
497 | tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
498 | |||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | static int tlv320aic23_resume(struct platform_device *pdev) | ||
503 | { | ||
504 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
505 | struct snd_soc_codec *codec = socdev->codec; | ||
506 | int i; | ||
507 | u16 reg; | ||
508 | |||
509 | /* Sync reg_cache with the hardware */ | ||
510 | for (reg = 0; reg < ARRAY_SIZE(tlv320aic23_reg); i++) { | ||
511 | u16 val = tlv320aic23_read_reg_cache(codec, reg); | ||
512 | tlv320aic23_write(codec, reg, val); | ||
513 | } | ||
514 | |||
515 | tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
516 | tlv320aic23_set_bias_level(codec, codec->suspend_bias_level); | ||
517 | |||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | /* | ||
522 | * initialise the AIC23 driver | ||
523 | * register the mixer and dsp interfaces with the kernel | ||
524 | */ | ||
525 | static int tlv320aic23_init(struct snd_soc_device *socdev) | ||
526 | { | ||
527 | struct snd_soc_codec *codec = socdev->codec; | ||
528 | int ret = 0; | ||
529 | u16 reg; | ||
530 | |||
531 | codec->name = "tlv320aic23"; | ||
532 | codec->owner = THIS_MODULE; | ||
533 | codec->read = tlv320aic23_read_reg_cache; | ||
534 | codec->write = tlv320aic23_write; | ||
535 | codec->set_bias_level = tlv320aic23_set_bias_level; | ||
536 | codec->dai = &tlv320aic23_dai; | ||
537 | codec->num_dai = 1; | ||
538 | codec->reg_cache_size = ARRAY_SIZE(tlv320aic23_reg); | ||
539 | codec->reg_cache = | ||
540 | kmemdup(tlv320aic23_reg, sizeof(tlv320aic23_reg), GFP_KERNEL); | ||
541 | if (codec->reg_cache == NULL) | ||
542 | return -ENOMEM; | ||
543 | |||
544 | /* Reset codec */ | ||
545 | tlv320aic23_write(codec, TLV320AIC23_RESET, 0); | ||
546 | |||
547 | /* register pcms */ | ||
548 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
549 | if (ret < 0) { | ||
550 | printk(KERN_ERR "tlv320aic23: failed to create pcms\n"); | ||
551 | goto pcm_err; | ||
552 | } | ||
553 | |||
554 | /* power on device */ | ||
555 | tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
556 | |||
557 | tlv320aic23_write(codec, TLV320AIC23_DIGT, TLV320AIC23_DEEMP_44K); | ||
558 | |||
559 | /* Unmute input */ | ||
560 | reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_LINVOL); | ||
561 | tlv320aic23_write(codec, TLV320AIC23_LINVOL, | ||
562 | (reg & (~TLV320AIC23_LIM_MUTED)) | | ||
563 | (TLV320AIC23_LRS_ENABLED)); | ||
564 | |||
565 | reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_RINVOL); | ||
566 | tlv320aic23_write(codec, TLV320AIC23_RINVOL, | ||
567 | (reg & (~TLV320AIC23_LIM_MUTED)) | | ||
568 | TLV320AIC23_LRS_ENABLED); | ||
569 | |||
570 | reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG); | ||
571 | tlv320aic23_write(codec, TLV320AIC23_ANLG, | ||
572 | (reg) & (~TLV320AIC23_BYPASS_ON) & | ||
573 | (~TLV320AIC23_MICM_MUTED)); | ||
574 | |||
575 | /* Default output volume */ | ||
576 | tlv320aic23_write(codec, TLV320AIC23_LCHNVOL, | ||
577 | TLV320AIC23_DEFAULT_OUT_VOL & | ||
578 | TLV320AIC23_OUT_VOL_MASK); | ||
579 | tlv320aic23_write(codec, TLV320AIC23_RCHNVOL, | ||
580 | TLV320AIC23_DEFAULT_OUT_VOL & | ||
581 | TLV320AIC23_OUT_VOL_MASK); | ||
582 | |||
583 | tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x1); | ||
584 | |||
585 | tlv320aic23_add_controls(codec); | ||
586 | tlv320aic23_add_widgets(codec); | ||
587 | ret = snd_soc_register_card(socdev); | ||
588 | if (ret < 0) { | ||
589 | printk(KERN_ERR "tlv320aic23: failed to register card\n"); | ||
590 | goto card_err; | ||
591 | } | ||
592 | |||
593 | return ret; | ||
594 | |||
595 | card_err: | ||
596 | snd_soc_free_pcms(socdev); | ||
597 | snd_soc_dapm_free(socdev); | ||
598 | pcm_err: | ||
599 | kfree(codec->reg_cache); | ||
600 | return ret; | ||
601 | } | ||
602 | static struct snd_soc_device *tlv320aic23_socdev; | ||
603 | |||
604 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
605 | /* | ||
606 | * If the i2c layer weren't so broken, we could pass this kind of data | ||
607 | * around | ||
608 | */ | ||
609 | static int tlv320aic23_codec_probe(struct i2c_client *i2c, | ||
610 | const struct i2c_device_id *i2c_id) | ||
611 | { | ||
612 | struct snd_soc_device *socdev = tlv320aic23_socdev; | ||
613 | struct snd_soc_codec *codec = socdev->codec; | ||
614 | int ret; | ||
615 | |||
616 | if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
617 | return -EINVAL; | ||
618 | |||
619 | i2c_set_clientdata(i2c, codec); | ||
620 | codec->control_data = i2c; | ||
621 | |||
622 | ret = tlv320aic23_init(socdev); | ||
623 | if (ret < 0) { | ||
624 | printk(KERN_ERR "tlv320aic23: failed to initialise AIC23\n"); | ||
625 | goto err; | ||
626 | } | ||
627 | return ret; | ||
628 | |||
629 | err: | ||
630 | kfree(codec); | ||
631 | kfree(i2c); | ||
632 | return ret; | ||
633 | } | ||
634 | static int __exit tlv320aic23_i2c_remove(struct i2c_client *i2c) | ||
635 | { | ||
636 | put_device(&i2c->dev); | ||
637 | return 0; | ||
638 | } | ||
639 | |||
640 | static const struct i2c_device_id tlv320aic23_id[] = { | ||
641 | {"tlv320aic23", 0}, | ||
642 | {} | ||
643 | }; | ||
644 | |||
645 | MODULE_DEVICE_TABLE(i2c, tlv320aic23_id); | ||
646 | |||
647 | static struct i2c_driver tlv320aic23_i2c_driver = { | ||
648 | .driver = { | ||
649 | .name = "tlv320aic23", | ||
650 | }, | ||
651 | .probe = tlv320aic23_codec_probe, | ||
652 | .remove = __exit_p(tlv320aic23_i2c_remove), | ||
653 | .id_table = tlv320aic23_id, | ||
654 | }; | ||
655 | |||
656 | #endif | ||
657 | |||
658 | static int tlv320aic23_probe(struct platform_device *pdev) | ||
659 | { | ||
660 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
661 | struct snd_soc_codec *codec; | ||
662 | int ret = 0; | ||
663 | |||
664 | printk(KERN_INFO "AIC23 Audio Codec %s\n", AIC23_VERSION); | ||
665 | |||
666 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | ||
667 | if (codec == NULL) | ||
668 | return -ENOMEM; | ||
669 | |||
670 | socdev->codec = codec; | ||
671 | mutex_init(&codec->mutex); | ||
672 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
673 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
674 | |||
675 | tlv320aic23_socdev = socdev; | ||
676 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
677 | codec->hw_write = (hw_write_t) i2c_smbus_write_byte_data; | ||
678 | codec->hw_read = NULL; | ||
679 | ret = i2c_add_driver(&tlv320aic23_i2c_driver); | ||
680 | if (ret != 0) | ||
681 | printk(KERN_ERR "can't add i2c driver"); | ||
682 | #endif | ||
683 | return ret; | ||
684 | } | ||
685 | |||
686 | static int tlv320aic23_remove(struct platform_device *pdev) | ||
687 | { | ||
688 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
689 | struct snd_soc_codec *codec = socdev->codec; | ||
690 | |||
691 | if (codec->control_data) | ||
692 | tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
693 | |||
694 | snd_soc_free_pcms(socdev); | ||
695 | snd_soc_dapm_free(socdev); | ||
696 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
697 | i2c_del_driver(&tlv320aic23_i2c_driver); | ||
698 | #endif | ||
699 | kfree(codec->reg_cache); | ||
700 | kfree(codec); | ||
701 | |||
702 | return 0; | ||
703 | } | ||
704 | struct snd_soc_codec_device soc_codec_dev_tlv320aic23 = { | ||
705 | .probe = tlv320aic23_probe, | ||
706 | .remove = tlv320aic23_remove, | ||
707 | .suspend = tlv320aic23_suspend, | ||
708 | .resume = tlv320aic23_resume, | ||
709 | }; | ||
710 | EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320aic23); | ||
711 | |||
712 | MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver"); | ||
713 | MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>"); | ||
714 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/tlv320aic23.h b/sound/soc/codecs/tlv320aic23.h new file mode 100644 index 000000000000..79d1faf8e570 --- /dev/null +++ b/sound/soc/codecs/tlv320aic23.h | |||
@@ -0,0 +1,122 @@ | |||
1 | /* | ||
2 | * ALSA SoC TLV320AIC23 codec driver | ||
3 | * | ||
4 | * Author: Arun KS, <arunks@mistralsolutions.com> | ||
5 | * Copyright: (C) 2008 Mistral Solutions Pvt Ltd | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef _TLV320AIC23_H | ||
13 | #define _TLV320AIC23_H | ||
14 | |||
15 | /* Codec TLV320AIC23 */ | ||
16 | #define TLV320AIC23_LINVOL 0x00 | ||
17 | #define TLV320AIC23_RINVOL 0x01 | ||
18 | #define TLV320AIC23_LCHNVOL 0x02 | ||
19 | #define TLV320AIC23_RCHNVOL 0x03 | ||
20 | #define TLV320AIC23_ANLG 0x04 | ||
21 | #define TLV320AIC23_DIGT 0x05 | ||
22 | #define TLV320AIC23_PWR 0x06 | ||
23 | #define TLV320AIC23_DIGT_FMT 0x07 | ||
24 | #define TLV320AIC23_SRATE 0x08 | ||
25 | #define TLV320AIC23_ACTIVE 0x09 | ||
26 | #define TLV320AIC23_RESET 0x0F | ||
27 | |||
28 | /* Left (right) line input volume control register */ | ||
29 | #define TLV320AIC23_LRS_ENABLED 0x0100 | ||
30 | #define TLV320AIC23_LIM_MUTED 0x0080 | ||
31 | #define TLV320AIC23_LIV_DEFAULT 0x0017 | ||
32 | #define TLV320AIC23_LIV_MAX 0x001f | ||
33 | #define TLV320AIC23_LIV_MIN 0x0000 | ||
34 | |||
35 | /* Left (right) channel headphone volume control register */ | ||
36 | #define TLV320AIC23_LZC_ON 0x0080 | ||
37 | #define TLV320AIC23_LHV_DEFAULT 0x0079 | ||
38 | #define TLV320AIC23_LHV_MAX 0x007f | ||
39 | #define TLV320AIC23_LHV_MIN 0x0000 | ||
40 | |||
41 | /* Analog audio path control register */ | ||
42 | #define TLV320AIC23_STA_REG(x) ((x)<<6) | ||
43 | #define TLV320AIC23_STE_ENABLED 0x0020 | ||
44 | #define TLV320AIC23_DAC_SELECTED 0x0010 | ||
45 | #define TLV320AIC23_BYPASS_ON 0x0008 | ||
46 | #define TLV320AIC23_INSEL_MIC 0x0004 | ||
47 | #define TLV320AIC23_MICM_MUTED 0x0002 | ||
48 | #define TLV320AIC23_MICB_20DB 0x0001 | ||
49 | |||
50 | /* Digital audio path control register */ | ||
51 | #define TLV320AIC23_DACM_MUTE 0x0008 | ||
52 | #define TLV320AIC23_DEEMP_32K 0x0002 | ||
53 | #define TLV320AIC23_DEEMP_44K 0x0004 | ||
54 | #define TLV320AIC23_DEEMP_48K 0x0006 | ||
55 | #define TLV320AIC23_ADCHP_ON 0x0001 | ||
56 | |||
57 | /* Power control down register */ | ||
58 | #define TLV320AIC23_DEVICE_PWR_OFF 0x0080 | ||
59 | #define TLV320AIC23_CLK_OFF 0x0040 | ||
60 | #define TLV320AIC23_OSC_OFF 0x0020 | ||
61 | #define TLV320AIC23_OUT_OFF 0x0010 | ||
62 | #define TLV320AIC23_DAC_OFF 0x0008 | ||
63 | #define TLV320AIC23_ADC_OFF 0x0004 | ||
64 | #define TLV320AIC23_MIC_OFF 0x0002 | ||
65 | #define TLV320AIC23_LINE_OFF 0x0001 | ||
66 | |||
67 | /* Digital audio interface register */ | ||
68 | #define TLV320AIC23_MS_MASTER 0x0040 | ||
69 | #define TLV320AIC23_LRSWAP_ON 0x0020 | ||
70 | #define TLV320AIC23_LRP_ON 0x0010 | ||
71 | #define TLV320AIC23_IWL_16 0x0000 | ||
72 | #define TLV320AIC23_IWL_20 0x0004 | ||
73 | #define TLV320AIC23_IWL_24 0x0008 | ||
74 | #define TLV320AIC23_IWL_32 0x000C | ||
75 | #define TLV320AIC23_FOR_I2S 0x0002 | ||
76 | #define TLV320AIC23_FOR_DSP 0x0003 | ||
77 | #define TLV320AIC23_FOR_LJUST 0x0001 | ||
78 | |||
79 | /* Sample rate control register */ | ||
80 | #define TLV320AIC23_CLKOUT_HALF 0x0080 | ||
81 | #define TLV320AIC23_CLKIN_HALF 0x0040 | ||
82 | #define TLV320AIC23_BOSR_384fs 0x0002 /* BOSR_272fs in USB mode */ | ||
83 | #define TLV320AIC23_USB_CLK_ON 0x0001 | ||
84 | #define TLV320AIC23_SR_MASK 0xf | ||
85 | #define TLV320AIC23_CLKOUT_SHIFT 7 | ||
86 | #define TLV320AIC23_CLKIN_SHIFT 6 | ||
87 | #define TLV320AIC23_SR_SHIFT 2 | ||
88 | #define TLV320AIC23_BOSR_SHIFT 1 | ||
89 | |||
90 | /* Digital interface register */ | ||
91 | #define TLV320AIC23_ACT_ON 0x0001 | ||
92 | |||
93 | /* | ||
94 | * AUDIO related MACROS | ||
95 | */ | ||
96 | |||
97 | #define TLV320AIC23_DEFAULT_OUT_VOL 0x70 | ||
98 | #define TLV320AIC23_DEFAULT_IN_VOLUME 0x10 | ||
99 | |||
100 | #define TLV320AIC23_OUT_VOL_MIN TLV320AIC23_LHV_MIN | ||
101 | #define TLV320AIC23_OUT_VOL_MAX TLV320AIC23_LHV_MAX | ||
102 | #define TLV320AIC23_OUT_VO_RANGE (TLV320AIC23_OUT_VOL_MAX - \ | ||
103 | TLV320AIC23_OUT_VOL_MIN) | ||
104 | #define TLV320AIC23_OUT_VOL_MASK TLV320AIC23_OUT_VOL_MAX | ||
105 | |||
106 | #define TLV320AIC23_IN_VOL_MIN TLV320AIC23_LIV_MIN | ||
107 | #define TLV320AIC23_IN_VOL_MAX TLV320AIC23_LIV_MAX | ||
108 | #define TLV320AIC23_IN_VOL_RANGE (TLV320AIC23_IN_VOL_MAX - \ | ||
109 | TLV320AIC23_IN_VOL_MIN) | ||
110 | #define TLV320AIC23_IN_VOL_MASK TLV320AIC23_IN_VOL_MAX | ||
111 | |||
112 | #define TLV320AIC23_SIDETONE_MASK 0x1c0 | ||
113 | #define TLV320AIC23_SIDETONE_0 0x100 | ||
114 | #define TLV320AIC23_SIDETONE_6 0x000 | ||
115 | #define TLV320AIC23_SIDETONE_9 0x040 | ||
116 | #define TLV320AIC23_SIDETONE_12 0x080 | ||
117 | #define TLV320AIC23_SIDETONE_18 0x0c0 | ||
118 | |||
119 | extern struct snd_soc_dai tlv320aic23_dai; | ||
120 | extern struct snd_soc_codec_device soc_codec_dev_tlv320aic23; | ||
121 | |||
122 | #endif /* _TLV320AIC23_H */ | ||
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c new file mode 100644 index 000000000000..bed8a9e63ddc --- /dev/null +++ b/sound/soc/codecs/tlv320aic26.c | |||
@@ -0,0 +1,520 @@ | |||
1 | /* | ||
2 | * Texas Instruments TLV320AIC26 low power audio CODEC | ||
3 | * ALSA SoC CODEC driver | ||
4 | * | ||
5 | * Copyright (C) 2008 Secret Lab Technologies Ltd. | ||
6 | */ | ||
7 | |||
8 | #include <linux/module.h> | ||
9 | #include <linux/moduleparam.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/delay.h> | ||
12 | #include <linux/pm.h> | ||
13 | #include <linux/device.h> | ||
14 | #include <linux/sysfs.h> | ||
15 | #include <linux/spi/spi.h> | ||
16 | #include <sound/core.h> | ||
17 | #include <sound/pcm.h> | ||
18 | #include <sound/pcm_params.h> | ||
19 | #include <sound/soc.h> | ||
20 | #include <sound/soc-dapm.h> | ||
21 | #include <sound/soc-of-simple.h> | ||
22 | #include <sound/initval.h> | ||
23 | |||
24 | #include "tlv320aic26.h" | ||
25 | |||
26 | MODULE_DESCRIPTION("ASoC TLV320AIC26 codec driver"); | ||
27 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); | ||
28 | MODULE_LICENSE("GPL"); | ||
29 | |||
30 | /* AIC26 driver private data */ | ||
31 | struct aic26 { | ||
32 | struct spi_device *spi; | ||
33 | struct snd_soc_codec codec; | ||
34 | u16 reg_cache[AIC26_NUM_REGS]; /* shadow registers */ | ||
35 | int master; | ||
36 | int datfm; | ||
37 | int mclk; | ||
38 | |||
39 | /* Keyclick parameters */ | ||
40 | int keyclick_amplitude; | ||
41 | int keyclick_freq; | ||
42 | int keyclick_len; | ||
43 | }; | ||
44 | |||
45 | /* --------------------------------------------------------------------- | ||
46 | * Register access routines | ||
47 | */ | ||
48 | static unsigned int aic26_reg_read(struct snd_soc_codec *codec, | ||
49 | unsigned int reg) | ||
50 | { | ||
51 | struct aic26 *aic26 = codec->private_data; | ||
52 | u16 *cache = codec->reg_cache; | ||
53 | u16 cmd, value; | ||
54 | u8 buffer[2]; | ||
55 | int rc; | ||
56 | |||
57 | if (reg >= AIC26_NUM_REGS) { | ||
58 | WARN_ON_ONCE(1); | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | /* Do SPI transfer; first 16bits are command; remaining is | ||
63 | * register contents */ | ||
64 | cmd = AIC26_READ_COMMAND_WORD(reg); | ||
65 | buffer[0] = (cmd >> 8) & 0xff; | ||
66 | buffer[1] = cmd & 0xff; | ||
67 | rc = spi_write_then_read(aic26->spi, buffer, 2, buffer, 2); | ||
68 | if (rc) { | ||
69 | dev_err(&aic26->spi->dev, "AIC26 reg read error\n"); | ||
70 | return -EIO; | ||
71 | } | ||
72 | value = (buffer[0] << 8) | buffer[1]; | ||
73 | |||
74 | /* Update the cache before returning with the value */ | ||
75 | cache[reg] = value; | ||
76 | return value; | ||
77 | } | ||
78 | |||
79 | static unsigned int aic26_reg_read_cache(struct snd_soc_codec *codec, | ||
80 | unsigned int reg) | ||
81 | { | ||
82 | u16 *cache = codec->reg_cache; | ||
83 | |||
84 | if (reg >= AIC26_NUM_REGS) { | ||
85 | WARN_ON_ONCE(1); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | return cache[reg]; | ||
90 | } | ||
91 | |||
92 | static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg, | ||
93 | unsigned int value) | ||
94 | { | ||
95 | struct aic26 *aic26 = codec->private_data; | ||
96 | u16 *cache = codec->reg_cache; | ||
97 | u16 cmd; | ||
98 | u8 buffer[4]; | ||
99 | int rc; | ||
100 | |||
101 | if (reg >= AIC26_NUM_REGS) { | ||
102 | WARN_ON_ONCE(1); | ||
103 | return -EINVAL; | ||
104 | } | ||
105 | |||
106 | /* Do SPI transfer; first 16bits are command; remaining is data | ||
107 | * to write into register */ | ||
108 | cmd = AIC26_WRITE_COMMAND_WORD(reg); | ||
109 | buffer[0] = (cmd >> 8) & 0xff; | ||
110 | buffer[1] = cmd & 0xff; | ||
111 | buffer[2] = value >> 8; | ||
112 | buffer[3] = value; | ||
113 | rc = spi_write(aic26->spi, buffer, 4); | ||
114 | if (rc) { | ||
115 | dev_err(&aic26->spi->dev, "AIC26 reg read error\n"); | ||
116 | return -EIO; | ||
117 | } | ||
118 | |||
119 | /* update cache before returning */ | ||
120 | cache[reg] = value; | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | /* --------------------------------------------------------------------- | ||
125 | * Digital Audio Interface Operations | ||
126 | */ | ||
127 | static int aic26_hw_params(struct snd_pcm_substream *substream, | ||
128 | struct snd_pcm_hw_params *params) | ||
129 | { | ||
130 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
131 | struct snd_soc_device *socdev = rtd->socdev; | ||
132 | struct snd_soc_codec *codec = socdev->codec; | ||
133 | struct aic26 *aic26 = codec->private_data; | ||
134 | int fsref, divisor, wlen, pval, jval, dval, qval; | ||
135 | u16 reg; | ||
136 | |||
137 | dev_dbg(&aic26->spi->dev, "aic26_hw_params(substream=%p, params=%p)\n", | ||
138 | substream, params); | ||
139 | dev_dbg(&aic26->spi->dev, "rate=%i format=%i\n", params_rate(params), | ||
140 | params_format(params)); | ||
141 | |||
142 | switch (params_rate(params)) { | ||
143 | case 8000: fsref = 48000; divisor = AIC26_DIV_6; break; | ||
144 | case 11025: fsref = 44100; divisor = AIC26_DIV_4; break; | ||
145 | case 12000: fsref = 48000; divisor = AIC26_DIV_4; break; | ||
146 | case 16000: fsref = 48000; divisor = AIC26_DIV_3; break; | ||
147 | case 22050: fsref = 44100; divisor = AIC26_DIV_2; break; | ||
148 | case 24000: fsref = 48000; divisor = AIC26_DIV_2; break; | ||
149 | case 32000: fsref = 48000; divisor = AIC26_DIV_1_5; break; | ||
150 | case 44100: fsref = 44100; divisor = AIC26_DIV_1; break; | ||
151 | case 48000: fsref = 48000; divisor = AIC26_DIV_1; break; | ||
152 | default: | ||
153 | dev_dbg(&aic26->spi->dev, "bad rate\n"); return -EINVAL; | ||
154 | } | ||
155 | |||
156 | /* select data word length */ | ||
157 | switch (params_format(params)) { | ||
158 | case SNDRV_PCM_FORMAT_S8: wlen = AIC26_WLEN_16; break; | ||
159 | case SNDRV_PCM_FORMAT_S16_BE: wlen = AIC26_WLEN_16; break; | ||
160 | case SNDRV_PCM_FORMAT_S24_BE: wlen = AIC26_WLEN_24; break; | ||
161 | case SNDRV_PCM_FORMAT_S32_BE: wlen = AIC26_WLEN_32; break; | ||
162 | default: | ||
163 | dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL; | ||
164 | } | ||
165 | |||
166 | /* Configure PLL */ | ||
167 | pval = 1; | ||
168 | jval = (fsref == 44100) ? 7 : 8; | ||
169 | dval = (fsref == 44100) ? 5264 : 1920; | ||
170 | qval = 0; | ||
171 | reg = 0x8000 | qval << 11 | pval << 8 | jval << 2; | ||
172 | aic26_reg_write(codec, AIC26_REG_PLL_PROG1, reg); | ||
173 | reg = dval << 2; | ||
174 | aic26_reg_write(codec, AIC26_REG_PLL_PROG2, reg); | ||
175 | |||
176 | /* Audio Control 3 (master mode, fsref rate) */ | ||
177 | reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL3); | ||
178 | reg &= ~0xf800; | ||
179 | if (aic26->master) | ||
180 | reg |= 0x0800; | ||
181 | if (fsref == 48000) | ||
182 | reg |= 0x2000; | ||
183 | aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg); | ||
184 | |||
185 | /* Audio Control 1 (FSref divisor) */ | ||
186 | reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL1); | ||
187 | reg &= ~0x0fff; | ||
188 | reg |= wlen | aic26->datfm | (divisor << 3) | divisor; | ||
189 | aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL1, reg); | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * aic26_mute - Mute control to reduce noise when changing audio format | ||
196 | */ | ||
197 | static int aic26_mute(struct snd_soc_dai *dai, int mute) | ||
198 | { | ||
199 | struct snd_soc_codec *codec = dai->codec; | ||
200 | struct aic26 *aic26 = codec->private_data; | ||
201 | u16 reg = aic26_reg_read_cache(codec, AIC26_REG_DAC_GAIN); | ||
202 | |||
203 | dev_dbg(&aic26->spi->dev, "aic26_mute(dai=%p, mute=%i)\n", | ||
204 | dai, mute); | ||
205 | |||
206 | if (mute) | ||
207 | reg |= 0x8080; | ||
208 | else | ||
209 | reg &= ~0x8080; | ||
210 | aic26_reg_write(codec, AIC26_REG_DAC_GAIN, reg); | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | static int aic26_set_sysclk(struct snd_soc_dai *codec_dai, | ||
216 | int clk_id, unsigned int freq, int dir) | ||
217 | { | ||
218 | struct snd_soc_codec *codec = codec_dai->codec; | ||
219 | struct aic26 *aic26 = codec->private_data; | ||
220 | |||
221 | dev_dbg(&aic26->spi->dev, "aic26_set_sysclk(dai=%p, clk_id==%i," | ||
222 | " freq=%i, dir=%i)\n", | ||
223 | codec_dai, clk_id, freq, dir); | ||
224 | |||
225 | /* MCLK needs to fall between 2MHz and 50 MHz */ | ||
226 | if ((freq < 2000000) || (freq > 50000000)) | ||
227 | return -EINVAL; | ||
228 | |||
229 | aic26->mclk = freq; | ||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | static int aic26_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) | ||
234 | { | ||
235 | struct snd_soc_codec *codec = codec_dai->codec; | ||
236 | struct aic26 *aic26 = codec->private_data; | ||
237 | |||
238 | dev_dbg(&aic26->spi->dev, "aic26_set_fmt(dai=%p, fmt==%i)\n", | ||
239 | codec_dai, fmt); | ||
240 | |||
241 | /* set master/slave audio interface */ | ||
242 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
243 | case SND_SOC_DAIFMT_CBM_CFM: aic26->master = 1; break; | ||
244 | case SND_SOC_DAIFMT_CBS_CFS: aic26->master = 0; break; | ||
245 | default: | ||
246 | dev_dbg(&aic26->spi->dev, "bad master\n"); return -EINVAL; | ||
247 | } | ||
248 | |||
249 | /* interface format */ | ||
250 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
251 | case SND_SOC_DAIFMT_I2S: aic26->datfm = AIC26_DATFM_I2S; break; | ||
252 | case SND_SOC_DAIFMT_DSP_A: aic26->datfm = AIC26_DATFM_DSP; break; | ||
253 | case SND_SOC_DAIFMT_RIGHT_J: aic26->datfm = AIC26_DATFM_RIGHTJ; break; | ||
254 | case SND_SOC_DAIFMT_LEFT_J: aic26->datfm = AIC26_DATFM_LEFTJ; break; | ||
255 | default: | ||
256 | dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL; | ||
257 | } | ||
258 | |||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | /* --------------------------------------------------------------------- | ||
263 | * Digital Audio Interface Definition | ||
264 | */ | ||
265 | #define AIC26_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ | ||
266 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ | ||
267 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ | ||
268 | SNDRV_PCM_RATE_48000) | ||
269 | #define AIC26_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |\ | ||
270 | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE) | ||
271 | |||
272 | struct snd_soc_dai aic26_dai = { | ||
273 | .name = "tlv320aic26", | ||
274 | .playback = { | ||
275 | .stream_name = "Playback", | ||
276 | .channels_min = 2, | ||
277 | .channels_max = 2, | ||
278 | .rates = AIC26_RATES, | ||
279 | .formats = AIC26_FORMATS, | ||
280 | }, | ||
281 | .capture = { | ||
282 | .stream_name = "Capture", | ||
283 | .channels_min = 2, | ||
284 | .channels_max = 2, | ||
285 | .rates = AIC26_RATES, | ||
286 | .formats = AIC26_FORMATS, | ||
287 | }, | ||
288 | .ops = { | ||
289 | .hw_params = aic26_hw_params, | ||
290 | }, | ||
291 | .dai_ops = { | ||
292 | .digital_mute = aic26_mute, | ||
293 | .set_sysclk = aic26_set_sysclk, | ||
294 | .set_fmt = aic26_set_fmt, | ||
295 | }, | ||
296 | }; | ||
297 | EXPORT_SYMBOL_GPL(aic26_dai); | ||
298 | |||
299 | /* --------------------------------------------------------------------- | ||
300 | * ALSA controls | ||
301 | */ | ||
302 | static const char *aic26_capture_src_text[] = {"Mic", "Aux"}; | ||
303 | static const struct soc_enum aic26_capture_src_enum = | ||
304 | SOC_ENUM_SINGLE(AIC26_REG_AUDIO_CTRL1, 12, 2, aic26_capture_src_text); | ||
305 | |||
306 | static const struct snd_kcontrol_new aic26_snd_controls[] = { | ||
307 | /* Output */ | ||
308 | SOC_DOUBLE("PCM Playback Volume", AIC26_REG_DAC_GAIN, 8, 0, 0x7f, 1), | ||
309 | SOC_DOUBLE("PCM Playback Switch", AIC26_REG_DAC_GAIN, 15, 7, 1, 1), | ||
310 | SOC_SINGLE("PCM Capture Volume", AIC26_REG_ADC_GAIN, 8, 0x7f, 0), | ||
311 | SOC_SINGLE("PCM Capture Mute", AIC26_REG_ADC_GAIN, 15, 1, 1), | ||
312 | SOC_SINGLE("Keyclick activate", AIC26_REG_AUDIO_CTRL2, 15, 0x1, 0), | ||
313 | SOC_SINGLE("Keyclick amplitude", AIC26_REG_AUDIO_CTRL2, 12, 0x7, 0), | ||
314 | SOC_SINGLE("Keyclick frequency", AIC26_REG_AUDIO_CTRL2, 8, 0x7, 0), | ||
315 | SOC_SINGLE("Keyclick period", AIC26_REG_AUDIO_CTRL2, 4, 0xf, 0), | ||
316 | SOC_ENUM("Capture Source", aic26_capture_src_enum), | ||
317 | }; | ||
318 | |||
319 | /* --------------------------------------------------------------------- | ||
320 | * SoC CODEC portion of driver: probe and release routines | ||
321 | */ | ||
322 | static int aic26_probe(struct platform_device *pdev) | ||
323 | { | ||
324 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
325 | struct snd_soc_codec *codec; | ||
326 | struct snd_kcontrol *kcontrol; | ||
327 | struct aic26 *aic26; | ||
328 | int i, ret, err; | ||
329 | |||
330 | dev_info(&pdev->dev, "Probing AIC26 SoC CODEC driver\n"); | ||
331 | dev_dbg(&pdev->dev, "socdev=%p\n", socdev); | ||
332 | dev_dbg(&pdev->dev, "codec_data=%p\n", socdev->codec_data); | ||
333 | |||
334 | /* Fetch the relevant aic26 private data here (it's already been | ||
335 | * stored in the .codec pointer) */ | ||
336 | aic26 = socdev->codec_data; | ||
337 | if (aic26 == NULL) { | ||
338 | dev_err(&pdev->dev, "aic26: missing codec pointer\n"); | ||
339 | return -ENODEV; | ||
340 | } | ||
341 | codec = &aic26->codec; | ||
342 | socdev->codec = codec; | ||
343 | |||
344 | dev_dbg(&pdev->dev, "Registering PCMs, dev=%p, socdev->dev=%p\n", | ||
345 | &pdev->dev, socdev->dev); | ||
346 | /* register pcms */ | ||
347 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
348 | if (ret < 0) { | ||
349 | dev_err(&pdev->dev, "aic26: failed to create pcms\n"); | ||
350 | return -ENODEV; | ||
351 | } | ||
352 | |||
353 | /* register controls */ | ||
354 | dev_dbg(&pdev->dev, "Registering controls\n"); | ||
355 | for (i = 0; i < ARRAY_SIZE(aic26_snd_controls); i++) { | ||
356 | kcontrol = snd_soc_cnew(&aic26_snd_controls[i], codec, NULL); | ||
357 | err = snd_ctl_add(codec->card, kcontrol); | ||
358 | WARN_ON(err < 0); | ||
359 | } | ||
360 | |||
361 | /* CODEC is setup, we can register the card now */ | ||
362 | dev_dbg(&pdev->dev, "Registering card\n"); | ||
363 | ret = snd_soc_register_card(socdev); | ||
364 | if (ret < 0) { | ||
365 | dev_err(&pdev->dev, "aic26: failed to register card\n"); | ||
366 | goto card_err; | ||
367 | } | ||
368 | return 0; | ||
369 | |||
370 | card_err: | ||
371 | snd_soc_free_pcms(socdev); | ||
372 | return ret; | ||
373 | } | ||
374 | |||
375 | static int aic26_remove(struct platform_device *pdev) | ||
376 | { | ||
377 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
378 | snd_soc_free_pcms(socdev); | ||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | struct snd_soc_codec_device aic26_soc_codec_dev = { | ||
383 | .probe = aic26_probe, | ||
384 | .remove = aic26_remove, | ||
385 | }; | ||
386 | EXPORT_SYMBOL_GPL(aic26_soc_codec_dev); | ||
387 | |||
388 | /* --------------------------------------------------------------------- | ||
389 | * SPI device portion of driver: sysfs files for debugging | ||
390 | */ | ||
391 | |||
392 | static ssize_t aic26_keyclick_show(struct device *dev, | ||
393 | struct device_attribute *attr, char *buf) | ||
394 | { | ||
395 | struct aic26 *aic26 = dev_get_drvdata(dev); | ||
396 | int val, amp, freq, len; | ||
397 | |||
398 | val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2); | ||
399 | amp = (val >> 12) & 0x7; | ||
400 | freq = (125 << ((val >> 8) & 0x7)) >> 1; | ||
401 | len = 2 * (1 + ((val >> 4) & 0xf)); | ||
402 | |||
403 | return sprintf(buf, "amp=%x freq=%iHz len=%iclks\n", amp, freq, len); | ||
404 | } | ||
405 | |||
406 | /* Any write to the keyclick attribute will trigger the keyclick event */ | ||
407 | static ssize_t aic26_keyclick_set(struct device *dev, | ||
408 | struct device_attribute *attr, | ||
409 | const char *buf, size_t count) | ||
410 | { | ||
411 | struct aic26 *aic26 = dev_get_drvdata(dev); | ||
412 | int val; | ||
413 | |||
414 | val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2); | ||
415 | val |= 0x8000; | ||
416 | aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL2, val); | ||
417 | |||
418 | return count; | ||
419 | } | ||
420 | |||
421 | static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set); | ||
422 | |||
423 | /* --------------------------------------------------------------------- | ||
424 | * SPI device portion of driver: probe and release routines and SPI | ||
425 | * driver registration. | ||
426 | */ | ||
427 | static int aic26_spi_probe(struct spi_device *spi) | ||
428 | { | ||
429 | struct aic26 *aic26; | ||
430 | int rc, i, reg; | ||
431 | |||
432 | dev_dbg(&spi->dev, "probing tlv320aic26 spi device\n"); | ||
433 | |||
434 | /* Allocate driver data */ | ||
435 | aic26 = kzalloc(sizeof *aic26, GFP_KERNEL); | ||
436 | if (!aic26) | ||
437 | return -ENOMEM; | ||
438 | |||
439 | /* Initialize the driver data */ | ||
440 | aic26->spi = spi; | ||
441 | dev_set_drvdata(&spi->dev, aic26); | ||
442 | |||
443 | /* Setup what we can in the codec structure so that the register | ||
444 | * access functions will work as expected. More will be filled | ||
445 | * out when it is probed by the SoC CODEC part of this driver */ | ||
446 | aic26->codec.private_data = aic26; | ||
447 | aic26->codec.name = "aic26"; | ||
448 | aic26->codec.owner = THIS_MODULE; | ||
449 | aic26->codec.dai = &aic26_dai; | ||
450 | aic26->codec.num_dai = 1; | ||
451 | aic26->codec.read = aic26_reg_read; | ||
452 | aic26->codec.write = aic26_reg_write; | ||
453 | aic26->master = 1; | ||
454 | mutex_init(&aic26->codec.mutex); | ||
455 | INIT_LIST_HEAD(&aic26->codec.dapm_widgets); | ||
456 | INIT_LIST_HEAD(&aic26->codec.dapm_paths); | ||
457 | aic26->codec.reg_cache_size = AIC26_NUM_REGS; | ||
458 | aic26->codec.reg_cache = aic26->reg_cache; | ||
459 | |||
460 | /* Reset the codec to power on defaults */ | ||
461 | aic26_reg_write(&aic26->codec, AIC26_REG_RESET, 0xBB00); | ||
462 | |||
463 | /* Power up CODEC */ | ||
464 | aic26_reg_write(&aic26->codec, AIC26_REG_POWER_CTRL, 0); | ||
465 | |||
466 | /* Audio Control 3 (master mode, fsref rate) */ | ||
467 | reg = aic26_reg_read(&aic26->codec, AIC26_REG_AUDIO_CTRL3); | ||
468 | reg &= ~0xf800; | ||
469 | reg |= 0x0800; /* set master mode */ | ||
470 | aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL3, reg); | ||
471 | |||
472 | /* Fill register cache */ | ||
473 | for (i = 0; i < ARRAY_SIZE(aic26->reg_cache); i++) | ||
474 | aic26_reg_read(&aic26->codec, i); | ||
475 | |||
476 | /* Register the sysfs files for debugging */ | ||
477 | /* Create SysFS files */ | ||
478 | rc = device_create_file(&spi->dev, &dev_attr_keyclick); | ||
479 | if (rc) | ||
480 | dev_info(&spi->dev, "error creating sysfs files\n"); | ||
481 | |||
482 | #if defined(CONFIG_SND_SOC_OF_SIMPLE) | ||
483 | /* Tell the of_soc helper about this codec */ | ||
484 | of_snd_soc_register_codec(&aic26_soc_codec_dev, aic26, &aic26_dai, | ||
485 | spi->dev.archdata.of_node); | ||
486 | #endif | ||
487 | |||
488 | dev_dbg(&spi->dev, "SPI device initialized\n"); | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | static int aic26_spi_remove(struct spi_device *spi) | ||
493 | { | ||
494 | struct aic26 *aic26 = dev_get_drvdata(&spi->dev); | ||
495 | |||
496 | kfree(aic26); | ||
497 | |||
498 | return 0; | ||
499 | } | ||
500 | |||
501 | static struct spi_driver aic26_spi = { | ||
502 | .driver = { | ||
503 | .name = "tlv320aic26", | ||
504 | .owner = THIS_MODULE, | ||
505 | }, | ||
506 | .probe = aic26_spi_probe, | ||
507 | .remove = aic26_spi_remove, | ||
508 | }; | ||
509 | |||
510 | static int __init aic26_init(void) | ||
511 | { | ||
512 | return spi_register_driver(&aic26_spi); | ||
513 | } | ||
514 | module_init(aic26_init); | ||
515 | |||
516 | static void __exit aic26_exit(void) | ||
517 | { | ||
518 | spi_unregister_driver(&aic26_spi); | ||
519 | } | ||
520 | module_exit(aic26_exit); | ||
diff --git a/sound/soc/codecs/tlv320aic26.h b/sound/soc/codecs/tlv320aic26.h new file mode 100644 index 000000000000..786ba16c945f --- /dev/null +++ b/sound/soc/codecs/tlv320aic26.h | |||
@@ -0,0 +1,96 @@ | |||
1 | /* | ||
2 | * Texas Instruments TLV320AIC26 low power audio CODEC | ||
3 | * register definitions | ||
4 | * | ||
5 | * Copyright (C) 2008 Secret Lab Technologies Ltd. | ||
6 | */ | ||
7 | |||
8 | #ifndef _TLV320AIC16_H_ | ||
9 | #define _TLV320AIC16_H_ | ||
10 | |||
11 | /* AIC26 Registers */ | ||
12 | #define AIC26_READ_COMMAND_WORD(addr) ((1 << 15) | (addr << 5)) | ||
13 | #define AIC26_WRITE_COMMAND_WORD(addr) ((0 << 15) | (addr << 5)) | ||
14 | #define AIC26_PAGE_ADDR(page, offset) ((page << 6) | offset) | ||
15 | #define AIC26_NUM_REGS AIC26_PAGE_ADDR(3, 0) | ||
16 | |||
17 | /* Page 0: Auxillary data registers */ | ||
18 | #define AIC26_REG_BAT1 AIC26_PAGE_ADDR(0, 0x05) | ||
19 | #define AIC26_REG_BAT2 AIC26_PAGE_ADDR(0, 0x06) | ||
20 | #define AIC26_REG_AUX AIC26_PAGE_ADDR(0, 0x07) | ||
21 | #define AIC26_REG_TEMP1 AIC26_PAGE_ADDR(0, 0x09) | ||
22 | #define AIC26_REG_TEMP2 AIC26_PAGE_ADDR(0, 0x0A) | ||
23 | |||
24 | /* Page 1: Auxillary control registers */ | ||
25 | #define AIC26_REG_AUX_ADC AIC26_PAGE_ADDR(1, 0x00) | ||
26 | #define AIC26_REG_STATUS AIC26_PAGE_ADDR(1, 0x01) | ||
27 | #define AIC26_REG_REFERENCE AIC26_PAGE_ADDR(1, 0x03) | ||
28 | #define AIC26_REG_RESET AIC26_PAGE_ADDR(1, 0x04) | ||
29 | |||
30 | /* Page 2: Audio control registers */ | ||
31 | #define AIC26_REG_AUDIO_CTRL1 AIC26_PAGE_ADDR(2, 0x00) | ||
32 | #define AIC26_REG_ADC_GAIN AIC26_PAGE_ADDR(2, 0x01) | ||
33 | #define AIC26_REG_DAC_GAIN AIC26_PAGE_ADDR(2, 0x02) | ||
34 | #define AIC26_REG_SIDETONE AIC26_PAGE_ADDR(2, 0x03) | ||
35 | #define AIC26_REG_AUDIO_CTRL2 AIC26_PAGE_ADDR(2, 0x04) | ||
36 | #define AIC26_REG_POWER_CTRL AIC26_PAGE_ADDR(2, 0x05) | ||
37 | #define AIC26_REG_AUDIO_CTRL3 AIC26_PAGE_ADDR(2, 0x06) | ||
38 | |||
39 | #define AIC26_REG_FILTER_COEFF_L_N0 AIC26_PAGE_ADDR(2, 0x07) | ||
40 | #define AIC26_REG_FILTER_COEFF_L_N1 AIC26_PAGE_ADDR(2, 0x08) | ||
41 | #define AIC26_REG_FILTER_COEFF_L_N2 AIC26_PAGE_ADDR(2, 0x09) | ||
42 | #define AIC26_REG_FILTER_COEFF_L_N3 AIC26_PAGE_ADDR(2, 0x0A) | ||
43 | #define AIC26_REG_FILTER_COEFF_L_N4 AIC26_PAGE_ADDR(2, 0x0B) | ||
44 | #define AIC26_REG_FILTER_COEFF_L_N5 AIC26_PAGE_ADDR(2, 0x0C) | ||
45 | #define AIC26_REG_FILTER_COEFF_L_D1 AIC26_PAGE_ADDR(2, 0x0D) | ||
46 | #define AIC26_REG_FILTER_COEFF_L_D2 AIC26_PAGE_ADDR(2, 0x0E) | ||
47 | #define AIC26_REG_FILTER_COEFF_L_D4 AIC26_PAGE_ADDR(2, 0x0F) | ||
48 | #define AIC26_REG_FILTER_COEFF_L_D5 AIC26_PAGE_ADDR(2, 0x10) | ||
49 | #define AIC26_REG_FILTER_COEFF_R_N0 AIC26_PAGE_ADDR(2, 0x11) | ||
50 | #define AIC26_REG_FILTER_COEFF_R_N1 AIC26_PAGE_ADDR(2, 0x12) | ||
51 | #define AIC26_REG_FILTER_COEFF_R_N2 AIC26_PAGE_ADDR(2, 0x13) | ||
52 | #define AIC26_REG_FILTER_COEFF_R_N3 AIC26_PAGE_ADDR(2, 0x14) | ||
53 | #define AIC26_REG_FILTER_COEFF_R_N4 AIC26_PAGE_ADDR(2, 0x15) | ||
54 | #define AIC26_REG_FILTER_COEFF_R_N5 AIC26_PAGE_ADDR(2, 0x16) | ||
55 | #define AIC26_REG_FILTER_COEFF_R_D1 AIC26_PAGE_ADDR(2, 0x17) | ||
56 | #define AIC26_REG_FILTER_COEFF_R_D2 AIC26_PAGE_ADDR(2, 0x18) | ||
57 | #define AIC26_REG_FILTER_COEFF_R_D4 AIC26_PAGE_ADDR(2, 0x19) | ||
58 | #define AIC26_REG_FILTER_COEFF_R_D5 AIC26_PAGE_ADDR(2, 0x1A) | ||
59 | |||
60 | #define AIC26_REG_PLL_PROG1 AIC26_PAGE_ADDR(2, 0x1B) | ||
61 | #define AIC26_REG_PLL_PROG2 AIC26_PAGE_ADDR(2, 0x1C) | ||
62 | #define AIC26_REG_AUDIO_CTRL4 AIC26_PAGE_ADDR(2, 0x1D) | ||
63 | #define AIC26_REG_AUDIO_CTRL5 AIC26_PAGE_ADDR(2, 0x1E) | ||
64 | |||
65 | /* fsref dividers; used in register 'Audio Control 1' */ | ||
66 | enum aic26_divisors { | ||
67 | AIC26_DIV_1 = 0, | ||
68 | AIC26_DIV_1_5 = 1, | ||
69 | AIC26_DIV_2 = 2, | ||
70 | AIC26_DIV_3 = 3, | ||
71 | AIC26_DIV_4 = 4, | ||
72 | AIC26_DIV_5 = 5, | ||
73 | AIC26_DIV_5_5 = 6, | ||
74 | AIC26_DIV_6 = 7, | ||
75 | }; | ||
76 | |||
77 | /* Digital data format */ | ||
78 | enum aic26_datfm { | ||
79 | AIC26_DATFM_I2S = 0 << 8, | ||
80 | AIC26_DATFM_DSP = 1 << 8, | ||
81 | AIC26_DATFM_RIGHTJ = 2 << 8, /* right justified */ | ||
82 | AIC26_DATFM_LEFTJ = 3 << 8, /* left justified */ | ||
83 | }; | ||
84 | |||
85 | /* Sample word length in bits; used in register 'Audio Control 1' */ | ||
86 | enum aic26_wlen { | ||
87 | AIC26_WLEN_16 = 0 << 10, | ||
88 | AIC26_WLEN_20 = 1 << 10, | ||
89 | AIC26_WLEN_24 = 2 << 10, | ||
90 | AIC26_WLEN_32 = 3 << 10, | ||
91 | }; | ||
92 | |||
93 | extern struct snd_soc_dai aic26_dai; | ||
94 | extern struct snd_soc_codec_device aic26_soc_codec_dev; | ||
95 | |||
96 | #endif /* _TLV320AIC16_H_ */ | ||
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 5f9abb199435..05336ed7e493 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * ALSA SoC TLV320AIC3X codec driver | 2 | * ALSA SoC TLV320AIC3X codec driver |
3 | * | 3 | * |
4 | * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> | 4 | * Author: Vladimir Barinov, <vbarinov@embeddedalley.com> |
5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> | 5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> |
6 | * | 6 | * |
7 | * Based on sound/soc/codecs/wm8753.c by Liam Girdwood | 7 | * Based on sound/soc/codecs/wm8753.c by Liam Girdwood |
@@ -48,7 +48,6 @@ | |||
48 | 48 | ||
49 | #include "tlv320aic3x.h" | 49 | #include "tlv320aic3x.h" |
50 | 50 | ||
51 | #define AUDIO_NAME "aic3x" | ||
52 | #define AIC3X_VERSION "0.2" | 51 | #define AIC3X_VERSION "0.2" |
53 | 52 | ||
54 | /* codec private data */ | 53 | /* codec private data */ |
@@ -991,7 +990,7 @@ EXPORT_SYMBOL_GPL(aic3x_headset_detected); | |||
991 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) | 990 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) |
992 | 991 | ||
993 | struct snd_soc_dai aic3x_dai = { | 992 | struct snd_soc_dai aic3x_dai = { |
994 | .name = "aic3x", | 993 | .name = "tlv320aic3x", |
995 | .playback = { | 994 | .playback = { |
996 | .stream_name = "Playback", | 995 | .stream_name = "Playback", |
997 | .channels_min = 1, | 996 | .channels_min = 1, |
@@ -1055,7 +1054,7 @@ static int aic3x_init(struct snd_soc_device *socdev) | |||
1055 | struct aic3x_setup_data *setup = socdev->codec_data; | 1054 | struct aic3x_setup_data *setup = socdev->codec_data; |
1056 | int reg, ret = 0; | 1055 | int reg, ret = 0; |
1057 | 1056 | ||
1058 | codec->name = "aic3x"; | 1057 | codec->name = "tlv320aic3x"; |
1059 | codec->owner = THIS_MODULE; | 1058 | codec->owner = THIS_MODULE; |
1060 | codec->read = aic3x_read_reg_cache; | 1059 | codec->read = aic3x_read_reg_cache; |
1061 | codec->write = aic3x_write; | 1060 | codec->write = aic3x_write; |
@@ -1172,71 +1171,39 @@ static struct snd_soc_device *aic3x_socdev; | |||
1172 | * AIC3X 2 wire address can be up to 4 devices with device addresses | 1171 | * AIC3X 2 wire address can be up to 4 devices with device addresses |
1173 | * 0x18, 0x19, 0x1A, 0x1B | 1172 | * 0x18, 0x19, 0x1A, 0x1B |
1174 | */ | 1173 | */ |
1175 | static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; | ||
1176 | |||
1177 | /* Magic definition of all other variables and things */ | ||
1178 | I2C_CLIENT_INSMOD; | ||
1179 | |||
1180 | static struct i2c_driver aic3x_i2c_driver; | ||
1181 | static struct i2c_client client_template; | ||
1182 | 1174 | ||
1183 | /* | 1175 | /* |
1184 | * If the i2c layer weren't so broken, we could pass this kind of data | 1176 | * If the i2c layer weren't so broken, we could pass this kind of data |
1185 | * around | 1177 | * around |
1186 | */ | 1178 | */ |
1187 | static int aic3x_codec_probe(struct i2c_adapter *adap, int addr, int kind) | 1179 | static int aic3x_i2c_probe(struct i2c_client *i2c, |
1180 | const struct i2c_device_id *id) | ||
1188 | { | 1181 | { |
1189 | struct snd_soc_device *socdev = aic3x_socdev; | 1182 | struct snd_soc_device *socdev = aic3x_socdev; |
1190 | struct aic3x_setup_data *setup = socdev->codec_data; | ||
1191 | struct snd_soc_codec *codec = socdev->codec; | 1183 | struct snd_soc_codec *codec = socdev->codec; |
1192 | struct i2c_client *i2c; | ||
1193 | int ret; | 1184 | int ret; |
1194 | 1185 | ||
1195 | if (addr != setup->i2c_address) | ||
1196 | return -ENODEV; | ||
1197 | |||
1198 | client_template.adapter = adap; | ||
1199 | client_template.addr = addr; | ||
1200 | |||
1201 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | ||
1202 | if (i2c == NULL) | ||
1203 | return -ENOMEM; | ||
1204 | |||
1205 | i2c_set_clientdata(i2c, codec); | 1186 | i2c_set_clientdata(i2c, codec); |
1206 | codec->control_data = i2c; | 1187 | codec->control_data = i2c; |
1207 | 1188 | ||
1208 | ret = i2c_attach_client(i2c); | ||
1209 | if (ret < 0) { | ||
1210 | printk(KERN_ERR "aic3x: failed to attach codec at addr %x\n", | ||
1211 | addr); | ||
1212 | goto err; | ||
1213 | } | ||
1214 | |||
1215 | ret = aic3x_init(socdev); | 1189 | ret = aic3x_init(socdev); |
1216 | if (ret < 0) { | 1190 | if (ret < 0) |
1217 | printk(KERN_ERR "aic3x: failed to initialise AIC3X\n"); | 1191 | printk(KERN_ERR "aic3x: failed to initialise AIC3X\n"); |
1218 | goto err; | ||
1219 | } | ||
1220 | return ret; | ||
1221 | |||
1222 | err: | ||
1223 | kfree(i2c); | ||
1224 | return ret; | 1192 | return ret; |
1225 | } | 1193 | } |
1226 | 1194 | ||
1227 | static int aic3x_i2c_detach(struct i2c_client *client) | 1195 | static int aic3x_i2c_remove(struct i2c_client *client) |
1228 | { | 1196 | { |
1229 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | 1197 | struct snd_soc_codec *codec = i2c_get_clientdata(client); |
1230 | i2c_detach_client(client); | ||
1231 | kfree(codec->reg_cache); | 1198 | kfree(codec->reg_cache); |
1232 | kfree(client); | ||
1233 | return 0; | 1199 | return 0; |
1234 | } | 1200 | } |
1235 | 1201 | ||
1236 | static int aic3x_i2c_attach(struct i2c_adapter *adap) | 1202 | static const struct i2c_device_id aic3x_i2c_id[] = { |
1237 | { | 1203 | { "tlv320aic3x", 0 }, |
1238 | return i2c_probe(adap, &addr_data, aic3x_codec_probe); | 1204 | { } |
1239 | } | 1205 | }; |
1206 | MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); | ||
1240 | 1207 | ||
1241 | /* machine i2c codec control layer */ | 1208 | /* machine i2c codec control layer */ |
1242 | static struct i2c_driver aic3x_i2c_driver = { | 1209 | static struct i2c_driver aic3x_i2c_driver = { |
@@ -1244,13 +1211,9 @@ static struct i2c_driver aic3x_i2c_driver = { | |||
1244 | .name = "aic3x I2C Codec", | 1211 | .name = "aic3x I2C Codec", |
1245 | .owner = THIS_MODULE, | 1212 | .owner = THIS_MODULE, |
1246 | }, | 1213 | }, |
1247 | .attach_adapter = aic3x_i2c_attach, | 1214 | .probe = aic3x_i2c_probe, |
1248 | .detach_client = aic3x_i2c_detach, | 1215 | .remove = aic3x_i2c_remove, |
1249 | }; | 1216 | .id_table = aic3x_i2c_id, |
1250 | |||
1251 | static struct i2c_client client_template = { | ||
1252 | .name = "AIC3X", | ||
1253 | .driver = &aic3x_i2c_driver, | ||
1254 | }; | 1217 | }; |
1255 | 1218 | ||
1256 | static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len) | 1219 | static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len) |
@@ -1258,6 +1221,46 @@ static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len) | |||
1258 | value[0] = i2c_smbus_read_byte_data(client, value[0]); | 1221 | value[0] = i2c_smbus_read_byte_data(client, value[0]); |
1259 | return (len == 1); | 1222 | return (len == 1); |
1260 | } | 1223 | } |
1224 | |||
1225 | static int aic3x_add_i2c_device(struct platform_device *pdev, | ||
1226 | const struct aic3x_setup_data *setup) | ||
1227 | { | ||
1228 | struct i2c_board_info info; | ||
1229 | struct i2c_adapter *adapter; | ||
1230 | struct i2c_client *client; | ||
1231 | int ret; | ||
1232 | |||
1233 | ret = i2c_add_driver(&aic3x_i2c_driver); | ||
1234 | if (ret != 0) { | ||
1235 | dev_err(&pdev->dev, "can't add i2c driver\n"); | ||
1236 | return ret; | ||
1237 | } | ||
1238 | |||
1239 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
1240 | info.addr = setup->i2c_address; | ||
1241 | strlcpy(info.type, "tlv320aic3x", I2C_NAME_SIZE); | ||
1242 | |||
1243 | adapter = i2c_get_adapter(setup->i2c_bus); | ||
1244 | if (!adapter) { | ||
1245 | dev_err(&pdev->dev, "can't get i2c adapter %d\n", | ||
1246 | setup->i2c_bus); | ||
1247 | goto err_driver; | ||
1248 | } | ||
1249 | |||
1250 | client = i2c_new_device(adapter, &info); | ||
1251 | i2c_put_adapter(adapter); | ||
1252 | if (!client) { | ||
1253 | dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", | ||
1254 | (unsigned int)info.addr); | ||
1255 | goto err_driver; | ||
1256 | } | ||
1257 | |||
1258 | return 0; | ||
1259 | |||
1260 | err_driver: | ||
1261 | i2c_del_driver(&aic3x_i2c_driver); | ||
1262 | return -ENODEV; | ||
1263 | } | ||
1261 | #endif | 1264 | #endif |
1262 | 1265 | ||
1263 | static int aic3x_probe(struct platform_device *pdev) | 1266 | static int aic3x_probe(struct platform_device *pdev) |
@@ -1290,12 +1293,9 @@ static int aic3x_probe(struct platform_device *pdev) | |||
1290 | aic3x_socdev = socdev; | 1293 | aic3x_socdev = socdev; |
1291 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 1294 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
1292 | if (setup->i2c_address) { | 1295 | if (setup->i2c_address) { |
1293 | normal_i2c[0] = setup->i2c_address; | ||
1294 | codec->hw_write = (hw_write_t) i2c_master_send; | 1296 | codec->hw_write = (hw_write_t) i2c_master_send; |
1295 | codec->hw_read = (hw_read_t) aic3x_i2c_read; | 1297 | codec->hw_read = (hw_read_t) aic3x_i2c_read; |
1296 | ret = i2c_add_driver(&aic3x_i2c_driver); | 1298 | ret = aic3x_add_i2c_device(pdev, setup); |
1297 | if (ret != 0) | ||
1298 | printk(KERN_ERR "can't add i2c driver"); | ||
1299 | } | 1299 | } |
1300 | #else | 1300 | #else |
1301 | /* Add other interfaces here */ | 1301 | /* Add other interfaces here */ |
@@ -1320,6 +1320,7 @@ static int aic3x_remove(struct platform_device *pdev) | |||
1320 | snd_soc_free_pcms(socdev); | 1320 | snd_soc_free_pcms(socdev); |
1321 | snd_soc_dapm_free(socdev); | 1321 | snd_soc_dapm_free(socdev); |
1322 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 1322 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
1323 | i2c_unregister_device(codec->control_data); | ||
1323 | i2c_del_driver(&aic3x_i2c_driver); | 1324 | i2c_del_driver(&aic3x_i2c_driver); |
1324 | #endif | 1325 | #endif |
1325 | kfree(codec->private_data); | 1326 | kfree(codec->private_data); |
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h index d76c079b86e7..00a195aa02e4 100644 --- a/sound/soc/codecs/tlv320aic3x.h +++ b/sound/soc/codecs/tlv320aic3x.h | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * ALSA SoC TLV320AIC3X codec driver | 2 | * ALSA SoC TLV320AIC3X codec driver |
3 | * | 3 | * |
4 | * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> | 4 | * Author: Vladimir Barinov, <vbarinov@embeddedalley.com> |
5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> | 5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
@@ -224,6 +224,7 @@ int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio); | |||
224 | int aic3x_headset_detected(struct snd_soc_codec *codec); | 224 | int aic3x_headset_detected(struct snd_soc_codec *codec); |
225 | 225 | ||
226 | struct aic3x_setup_data { | 226 | struct aic3x_setup_data { |
227 | int i2c_bus; | ||
227 | unsigned short i2c_address; | 228 | unsigned short i2c_address; |
228 | unsigned int gpio_func[2]; | 229 | unsigned int gpio_func[2]; |
229 | }; | 230 | }; |
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 807318fbdc8f..a69ee72a7af5 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c | |||
@@ -36,7 +36,6 @@ | |||
36 | #include "uda1380.h" | 36 | #include "uda1380.h" |
37 | 37 | ||
38 | #define UDA1380_VERSION "0.6" | 38 | #define UDA1380_VERSION "0.6" |
39 | #define AUDIO_NAME "uda1380" | ||
40 | 39 | ||
41 | /* | 40 | /* |
42 | * uda1380 register cache | 41 | * uda1380 register cache |
@@ -701,87 +700,86 @@ static struct snd_soc_device *uda1380_socdev; | |||
701 | 700 | ||
702 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 701 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
703 | 702 | ||
704 | #define I2C_DRIVERID_UDA1380 0xfefe /* liam - need a proper id */ | 703 | static int uda1380_i2c_probe(struct i2c_client *i2c, |
705 | 704 | const struct i2c_device_id *id) | |
706 | static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; | ||
707 | |||
708 | /* Magic definition of all other variables and things */ | ||
709 | I2C_CLIENT_INSMOD; | ||
710 | |||
711 | static struct i2c_driver uda1380_i2c_driver; | ||
712 | static struct i2c_client client_template; | ||
713 | |||
714 | /* If the i2c layer weren't so broken, we could pass this kind of data | ||
715 | around */ | ||
716 | |||
717 | static int uda1380_codec_probe(struct i2c_adapter *adap, int addr, int kind) | ||
718 | { | 705 | { |
719 | struct snd_soc_device *socdev = uda1380_socdev; | 706 | struct snd_soc_device *socdev = uda1380_socdev; |
720 | struct uda1380_setup_data *setup = socdev->codec_data; | 707 | struct uda1380_setup_data *setup = socdev->codec_data; |
721 | struct snd_soc_codec *codec = socdev->codec; | 708 | struct snd_soc_codec *codec = socdev->codec; |
722 | struct i2c_client *i2c; | ||
723 | int ret; | 709 | int ret; |
724 | 710 | ||
725 | if (addr != setup->i2c_address) | ||
726 | return -ENODEV; | ||
727 | |||
728 | client_template.adapter = adap; | ||
729 | client_template.addr = addr; | ||
730 | |||
731 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | ||
732 | if (i2c == NULL) | ||
733 | return -ENOMEM; | ||
734 | |||
735 | i2c_set_clientdata(i2c, codec); | 711 | i2c_set_clientdata(i2c, codec); |
736 | codec->control_data = i2c; | 712 | codec->control_data = i2c; |
737 | 713 | ||
738 | ret = i2c_attach_client(i2c); | ||
739 | if (ret < 0) { | ||
740 | pr_err("uda1380: failed to attach codec at addr %x\n", addr); | ||
741 | goto err; | ||
742 | } | ||
743 | |||
744 | ret = uda1380_init(socdev, setup->dac_clk); | 714 | ret = uda1380_init(socdev, setup->dac_clk); |
745 | if (ret < 0) { | 715 | if (ret < 0) |
746 | pr_err("uda1380: failed to initialise UDA1380\n"); | 716 | pr_err("uda1380: failed to initialise UDA1380\n"); |
747 | goto err; | ||
748 | } | ||
749 | return ret; | ||
750 | 717 | ||
751 | err: | ||
752 | kfree(i2c); | ||
753 | return ret; | 718 | return ret; |
754 | } | 719 | } |
755 | 720 | ||
756 | static int uda1380_i2c_detach(struct i2c_client *client) | 721 | static int uda1380_i2c_remove(struct i2c_client *client) |
757 | { | 722 | { |
758 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | 723 | struct snd_soc_codec *codec = i2c_get_clientdata(client); |
759 | i2c_detach_client(client); | ||
760 | kfree(codec->reg_cache); | 724 | kfree(codec->reg_cache); |
761 | kfree(client); | ||
762 | return 0; | 725 | return 0; |
763 | } | 726 | } |
764 | 727 | ||
765 | static int uda1380_i2c_attach(struct i2c_adapter *adap) | 728 | static const struct i2c_device_id uda1380_i2c_id[] = { |
766 | { | 729 | { "uda1380", 0 }, |
767 | return i2c_probe(adap, &addr_data, uda1380_codec_probe); | 730 | { } |
768 | } | 731 | }; |
732 | MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id); | ||
769 | 733 | ||
770 | static struct i2c_driver uda1380_i2c_driver = { | 734 | static struct i2c_driver uda1380_i2c_driver = { |
771 | .driver = { | 735 | .driver = { |
772 | .name = "UDA1380 I2C Codec", | 736 | .name = "UDA1380 I2C Codec", |
773 | .owner = THIS_MODULE, | 737 | .owner = THIS_MODULE, |
774 | }, | 738 | }, |
775 | .id = I2C_DRIVERID_UDA1380, | 739 | .probe = uda1380_i2c_probe, |
776 | .attach_adapter = uda1380_i2c_attach, | 740 | .remove = uda1380_i2c_remove, |
777 | .detach_client = uda1380_i2c_detach, | 741 | .id_table = uda1380_i2c_id, |
778 | .command = NULL, | ||
779 | }; | 742 | }; |
780 | 743 | ||
781 | static struct i2c_client client_template = { | 744 | static int uda1380_add_i2c_device(struct platform_device *pdev, |
782 | .name = "UDA1380", | 745 | const struct uda1380_setup_data *setup) |
783 | .driver = &uda1380_i2c_driver, | 746 | { |
784 | }; | 747 | struct i2c_board_info info; |
748 | struct i2c_adapter *adapter; | ||
749 | struct i2c_client *client; | ||
750 | int ret; | ||
751 | |||
752 | ret = i2c_add_driver(&uda1380_i2c_driver); | ||
753 | if (ret != 0) { | ||
754 | dev_err(&pdev->dev, "can't add i2c driver\n"); | ||
755 | return ret; | ||
756 | } | ||
757 | |||
758 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
759 | info.addr = setup->i2c_address; | ||
760 | strlcpy(info.type, "uda1380", I2C_NAME_SIZE); | ||
761 | |||
762 | adapter = i2c_get_adapter(setup->i2c_bus); | ||
763 | if (!adapter) { | ||
764 | dev_err(&pdev->dev, "can't get i2c adapter %d\n", | ||
765 | setup->i2c_bus); | ||
766 | goto err_driver; | ||
767 | } | ||
768 | |||
769 | client = i2c_new_device(adapter, &info); | ||
770 | i2c_put_adapter(adapter); | ||
771 | if (!client) { | ||
772 | dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", | ||
773 | (unsigned int)info.addr); | ||
774 | goto err_driver; | ||
775 | } | ||
776 | |||
777 | return 0; | ||
778 | |||
779 | err_driver: | ||
780 | i2c_del_driver(&uda1380_i2c_driver); | ||
781 | return -ENODEV; | ||
782 | } | ||
785 | #endif | 783 | #endif |
786 | 784 | ||
787 | static int uda1380_probe(struct platform_device *pdev) | 785 | static int uda1380_probe(struct platform_device *pdev) |
@@ -789,7 +787,7 @@ static int uda1380_probe(struct platform_device *pdev) | |||
789 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 787 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
790 | struct uda1380_setup_data *setup; | 788 | struct uda1380_setup_data *setup; |
791 | struct snd_soc_codec *codec; | 789 | struct snd_soc_codec *codec; |
792 | int ret = 0; | 790 | int ret; |
793 | 791 | ||
794 | pr_info("UDA1380 Audio Codec %s", UDA1380_VERSION); | 792 | pr_info("UDA1380 Audio Codec %s", UDA1380_VERSION); |
795 | 793 | ||
@@ -804,16 +802,13 @@ static int uda1380_probe(struct platform_device *pdev) | |||
804 | INIT_LIST_HEAD(&codec->dapm_paths); | 802 | INIT_LIST_HEAD(&codec->dapm_paths); |
805 | 803 | ||
806 | uda1380_socdev = socdev; | 804 | uda1380_socdev = socdev; |
805 | ret = -ENODEV; | ||
806 | |||
807 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 807 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
808 | if (setup->i2c_address) { | 808 | if (setup->i2c_address) { |
809 | normal_i2c[0] = setup->i2c_address; | ||
810 | codec->hw_write = (hw_write_t)i2c_master_send; | 809 | codec->hw_write = (hw_write_t)i2c_master_send; |
811 | ret = i2c_add_driver(&uda1380_i2c_driver); | 810 | ret = uda1380_add_i2c_device(pdev, setup); |
812 | if (ret != 0) | ||
813 | printk(KERN_ERR "can't add i2c driver"); | ||
814 | } | 811 | } |
815 | #else | ||
816 | /* Add other interfaces here */ | ||
817 | #endif | 812 | #endif |
818 | 813 | ||
819 | if (ret != 0) | 814 | if (ret != 0) |
@@ -833,6 +828,7 @@ static int uda1380_remove(struct platform_device *pdev) | |||
833 | snd_soc_free_pcms(socdev); | 828 | snd_soc_free_pcms(socdev); |
834 | snd_soc_dapm_free(socdev); | 829 | snd_soc_dapm_free(socdev); |
835 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 830 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
831 | i2c_unregister_device(codec->control_data); | ||
836 | i2c_del_driver(&uda1380_i2c_driver); | 832 | i2c_del_driver(&uda1380_i2c_driver); |
837 | #endif | 833 | #endif |
838 | kfree(codec); | 834 | kfree(codec); |
diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h index 50c603e2c9f2..c55c17a52a12 100644 --- a/sound/soc/codecs/uda1380.h +++ b/sound/soc/codecs/uda1380.h | |||
@@ -73,6 +73,7 @@ | |||
73 | #define R23_AGC_EN 0x0001 | 73 | #define R23_AGC_EN 0x0001 |
74 | 74 | ||
75 | struct uda1380_setup_data { | 75 | struct uda1380_setup_data { |
76 | int i2c_bus; | ||
76 | unsigned short i2c_address; | 77 | unsigned short i2c_address; |
77 | int dac_clk; | 78 | int dac_clk; |
78 | #define UDA1380_DAC_CLK_SYSCLK 0 | 79 | #define UDA1380_DAC_CLK_SYSCLK 0 |
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index 3d998e6a997e..d8ca2da8d634 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Copyright 2006 Wolfson Microelectronics PLC. | 4 | * Copyright 2006 Wolfson Microelectronics PLC. |
5 | * | 5 | * |
6 | * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com> | 6 | * Author: Liam Girdwood <lrg@slimlogic.co.uk> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/pm.h> | 18 | #include <linux/pm.h> |
19 | #include <linux/i2c.h> | 19 | #include <linux/i2c.h> |
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/spi/spi.h> | ||
21 | #include <sound/core.h> | 22 | #include <sound/core.h> |
22 | #include <sound/pcm.h> | 23 | #include <sound/pcm.h> |
23 | #include <sound/pcm_params.h> | 24 | #include <sound/pcm_params.h> |
@@ -27,7 +28,6 @@ | |||
27 | 28 | ||
28 | #include "wm8510.h" | 29 | #include "wm8510.h" |
29 | 30 | ||
30 | #define AUDIO_NAME "wm8510" | ||
31 | #define WM8510_VERSION "0.6" | 31 | #define WM8510_VERSION "0.6" |
32 | 32 | ||
33 | struct snd_soc_codec_device soc_codec_dev_wm8510; | 33 | struct snd_soc_codec_device soc_codec_dev_wm8510; |
@@ -55,6 +55,9 @@ static const u16 wm8510_reg[WM8510_CACHEREGNUM] = { | |||
55 | 0x0001, | 55 | 0x0001, |
56 | }; | 56 | }; |
57 | 57 | ||
58 | #define WM8510_POWER1_BIASEN 0x08 | ||
59 | #define WM8510_POWER1_BUFIOEN 0x10 | ||
60 | |||
58 | /* | 61 | /* |
59 | * read wm8510 register cache | 62 | * read wm8510 register cache |
60 | */ | 63 | */ |
@@ -199,7 +202,7 @@ SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 0), | |||
199 | }; | 202 | }; |
200 | 203 | ||
201 | static const struct snd_kcontrol_new wm8510_boost_controls[] = { | 204 | static const struct snd_kcontrol_new wm8510_boost_controls[] = { |
202 | SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA, 6, 1, 0), | 205 | SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA, 6, 1, 1), |
203 | SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0), | 206 | SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0), |
204 | SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0), | 207 | SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0), |
205 | }; | 208 | }; |
@@ -224,9 +227,9 @@ SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0), | |||
224 | SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0), | 227 | SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0), |
225 | SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0), | 228 | SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0), |
226 | 229 | ||
227 | SND_SOC_DAPM_PGA("Mic PGA", WM8510_POWER2, 2, 0, | 230 | SND_SOC_DAPM_MIXER("Mic PGA", WM8510_POWER2, 2, 0, |
228 | &wm8510_micpga_controls[0], | 231 | &wm8510_micpga_controls[0], |
229 | ARRAY_SIZE(wm8510_micpga_controls)), | 232 | ARRAY_SIZE(wm8510_micpga_controls)), |
230 | SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0, | 233 | SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0, |
231 | &wm8510_boost_controls[0], | 234 | &wm8510_boost_controls[0], |
232 | ARRAY_SIZE(wm8510_boost_controls)), | 235 | ARRAY_SIZE(wm8510_boost_controls)), |
@@ -526,23 +529,35 @@ static int wm8510_mute(struct snd_soc_dai *dai, int mute) | |||
526 | static int wm8510_set_bias_level(struct snd_soc_codec *codec, | 529 | static int wm8510_set_bias_level(struct snd_soc_codec *codec, |
527 | enum snd_soc_bias_level level) | 530 | enum snd_soc_bias_level level) |
528 | { | 531 | { |
532 | u16 power1 = wm8510_read_reg_cache(codec, WM8510_POWER1) & ~0x3; | ||
529 | 533 | ||
530 | switch (level) { | 534 | switch (level) { |
531 | case SND_SOC_BIAS_ON: | 535 | case SND_SOC_BIAS_ON: |
532 | wm8510_write(codec, WM8510_POWER1, 0x1ff); | ||
533 | wm8510_write(codec, WM8510_POWER2, 0x1ff); | ||
534 | wm8510_write(codec, WM8510_POWER3, 0x1ff); | ||
535 | break; | ||
536 | case SND_SOC_BIAS_PREPARE: | 536 | case SND_SOC_BIAS_PREPARE: |
537 | power1 |= 0x1; /* VMID 50k */ | ||
538 | wm8510_write(codec, WM8510_POWER1, power1); | ||
539 | break; | ||
540 | |||
537 | case SND_SOC_BIAS_STANDBY: | 541 | case SND_SOC_BIAS_STANDBY: |
542 | power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN; | ||
543 | |||
544 | if (codec->bias_level == SND_SOC_BIAS_OFF) { | ||
545 | /* Initial cap charge at VMID 5k */ | ||
546 | wm8510_write(codec, WM8510_POWER1, power1 | 0x3); | ||
547 | mdelay(100); | ||
548 | } | ||
549 | |||
550 | power1 |= 0x2; /* VMID 500k */ | ||
551 | wm8510_write(codec, WM8510_POWER1, power1); | ||
538 | break; | 552 | break; |
553 | |||
539 | case SND_SOC_BIAS_OFF: | 554 | case SND_SOC_BIAS_OFF: |
540 | /* everything off, dac mute, inactive */ | 555 | wm8510_write(codec, WM8510_POWER1, 0); |
541 | wm8510_write(codec, WM8510_POWER1, 0x0); | 556 | wm8510_write(codec, WM8510_POWER2, 0); |
542 | wm8510_write(codec, WM8510_POWER2, 0x0); | 557 | wm8510_write(codec, WM8510_POWER3, 0); |
543 | wm8510_write(codec, WM8510_POWER3, 0x0); | ||
544 | break; | 558 | break; |
545 | } | 559 | } |
560 | |||
546 | codec->bias_level = level; | 561 | codec->bias_level = level; |
547 | return 0; | 562 | return 0; |
548 | } | 563 | } |
@@ -640,6 +655,7 @@ static int wm8510_init(struct snd_soc_device *socdev) | |||
640 | } | 655 | } |
641 | 656 | ||
642 | /* power on device */ | 657 | /* power on device */ |
658 | codec->bias_level = SND_SOC_BIAS_OFF; | ||
643 | wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 659 | wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
644 | wm8510_add_controls(codec); | 660 | wm8510_add_controls(codec); |
645 | wm8510_add_widgets(codec); | 661 | wm8510_add_widgets(codec); |
@@ -665,90 +681,144 @@ static struct snd_soc_device *wm8510_socdev; | |||
665 | /* | 681 | /* |
666 | * WM8510 2 wire address is 0x1a | 682 | * WM8510 2 wire address is 0x1a |
667 | */ | 683 | */ |
668 | #define I2C_DRIVERID_WM8510 0xfefe /* liam - need a proper id */ | ||
669 | |||
670 | static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; | ||
671 | 684 | ||
672 | /* Magic definition of all other variables and things */ | 685 | static int wm8510_i2c_probe(struct i2c_client *i2c, |
673 | I2C_CLIENT_INSMOD; | 686 | const struct i2c_device_id *id) |
674 | |||
675 | static struct i2c_driver wm8510_i2c_driver; | ||
676 | static struct i2c_client client_template; | ||
677 | |||
678 | /* If the i2c layer weren't so broken, we could pass this kind of data | ||
679 | around */ | ||
680 | |||
681 | static int wm8510_codec_probe(struct i2c_adapter *adap, int addr, int kind) | ||
682 | { | 687 | { |
683 | struct snd_soc_device *socdev = wm8510_socdev; | 688 | struct snd_soc_device *socdev = wm8510_socdev; |
684 | struct wm8510_setup_data *setup = socdev->codec_data; | ||
685 | struct snd_soc_codec *codec = socdev->codec; | 689 | struct snd_soc_codec *codec = socdev->codec; |
686 | struct i2c_client *i2c; | ||
687 | int ret; | 690 | int ret; |
688 | 691 | ||
689 | if (addr != setup->i2c_address) | ||
690 | return -ENODEV; | ||
691 | |||
692 | client_template.adapter = adap; | ||
693 | client_template.addr = addr; | ||
694 | |||
695 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | ||
696 | if (i2c == NULL) | ||
697 | return -ENOMEM; | ||
698 | |||
699 | i2c_set_clientdata(i2c, codec); | 692 | i2c_set_clientdata(i2c, codec); |
700 | codec->control_data = i2c; | 693 | codec->control_data = i2c; |
701 | 694 | ||
702 | ret = i2c_attach_client(i2c); | ||
703 | if (ret < 0) { | ||
704 | pr_err("failed to attach codec at addr %x\n", addr); | ||
705 | goto err; | ||
706 | } | ||
707 | |||
708 | ret = wm8510_init(socdev); | 695 | ret = wm8510_init(socdev); |
709 | if (ret < 0) { | 696 | if (ret < 0) |
710 | pr_err("failed to initialise WM8510\n"); | 697 | pr_err("failed to initialise WM8510\n"); |
711 | goto err; | ||
712 | } | ||
713 | return ret; | ||
714 | 698 | ||
715 | err: | ||
716 | kfree(i2c); | ||
717 | return ret; | 699 | return ret; |
718 | } | 700 | } |
719 | 701 | ||
720 | static int wm8510_i2c_detach(struct i2c_client *client) | 702 | static int wm8510_i2c_remove(struct i2c_client *client) |
721 | { | 703 | { |
722 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | 704 | struct snd_soc_codec *codec = i2c_get_clientdata(client); |
723 | i2c_detach_client(client); | ||
724 | kfree(codec->reg_cache); | 705 | kfree(codec->reg_cache); |
725 | kfree(client); | ||
726 | return 0; | 706 | return 0; |
727 | } | 707 | } |
728 | 708 | ||
729 | static int wm8510_i2c_attach(struct i2c_adapter *adap) | 709 | static const struct i2c_device_id wm8510_i2c_id[] = { |
730 | { | 710 | { "wm8510", 0 }, |
731 | return i2c_probe(adap, &addr_data, wm8510_codec_probe); | 711 | { } |
732 | } | 712 | }; |
713 | MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id); | ||
733 | 714 | ||
734 | /* corgi i2c codec control layer */ | ||
735 | static struct i2c_driver wm8510_i2c_driver = { | 715 | static struct i2c_driver wm8510_i2c_driver = { |
736 | .driver = { | 716 | .driver = { |
737 | .name = "WM8510 I2C Codec", | 717 | .name = "WM8510 I2C Codec", |
738 | .owner = THIS_MODULE, | 718 | .owner = THIS_MODULE, |
739 | }, | 719 | }, |
740 | .id = I2C_DRIVERID_WM8510, | 720 | .probe = wm8510_i2c_probe, |
741 | .attach_adapter = wm8510_i2c_attach, | 721 | .remove = wm8510_i2c_remove, |
742 | .detach_client = wm8510_i2c_detach, | 722 | .id_table = wm8510_i2c_id, |
743 | .command = NULL, | ||
744 | }; | 723 | }; |
745 | 724 | ||
746 | static struct i2c_client client_template = { | 725 | static int wm8510_add_i2c_device(struct platform_device *pdev, |
747 | .name = "WM8510", | 726 | const struct wm8510_setup_data *setup) |
748 | .driver = &wm8510_i2c_driver, | 727 | { |
749 | }; | 728 | struct i2c_board_info info; |
729 | struct i2c_adapter *adapter; | ||
730 | struct i2c_client *client; | ||
731 | int ret; | ||
732 | |||
733 | ret = i2c_add_driver(&wm8510_i2c_driver); | ||
734 | if (ret != 0) { | ||
735 | dev_err(&pdev->dev, "can't add i2c driver\n"); | ||
736 | return ret; | ||
737 | } | ||
738 | |||
739 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
740 | info.addr = setup->i2c_address; | ||
741 | strlcpy(info.type, "wm8510", I2C_NAME_SIZE); | ||
742 | |||
743 | adapter = i2c_get_adapter(setup->i2c_bus); | ||
744 | if (!adapter) { | ||
745 | dev_err(&pdev->dev, "can't get i2c adapter %d\n", | ||
746 | setup->i2c_bus); | ||
747 | goto err_driver; | ||
748 | } | ||
749 | |||
750 | client = i2c_new_device(adapter, &info); | ||
751 | i2c_put_adapter(adapter); | ||
752 | if (!client) { | ||
753 | dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", | ||
754 | (unsigned int)info.addr); | ||
755 | goto err_driver; | ||
756 | } | ||
757 | |||
758 | return 0; | ||
759 | |||
760 | err_driver: | ||
761 | i2c_del_driver(&wm8510_i2c_driver); | ||
762 | return -ENODEV; | ||
763 | } | ||
750 | #endif | 764 | #endif |
751 | 765 | ||
766 | #if defined(CONFIG_SPI_MASTER) | ||
767 | static int __devinit wm8510_spi_probe(struct spi_device *spi) | ||
768 | { | ||
769 | struct snd_soc_device *socdev = wm8510_socdev; | ||
770 | struct snd_soc_codec *codec = socdev->codec; | ||
771 | int ret; | ||
772 | |||
773 | codec->control_data = spi; | ||
774 | |||
775 | ret = wm8510_init(socdev); | ||
776 | if (ret < 0) | ||
777 | dev_err(&spi->dev, "failed to initialise WM8510\n"); | ||
778 | |||
779 | return ret; | ||
780 | } | ||
781 | |||
782 | static int __devexit wm8510_spi_remove(struct spi_device *spi) | ||
783 | { | ||
784 | return 0; | ||
785 | } | ||
786 | |||
787 | static struct spi_driver wm8510_spi_driver = { | ||
788 | .driver = { | ||
789 | .name = "wm8510", | ||
790 | .bus = &spi_bus_type, | ||
791 | .owner = THIS_MODULE, | ||
792 | }, | ||
793 | .probe = wm8510_spi_probe, | ||
794 | .remove = __devexit_p(wm8510_spi_remove), | ||
795 | }; | ||
796 | |||
797 | static int wm8510_spi_write(struct spi_device *spi, const char *data, int len) | ||
798 | { | ||
799 | struct spi_transfer t; | ||
800 | struct spi_message m; | ||
801 | u8 msg[2]; | ||
802 | |||
803 | if (len <= 0) | ||
804 | return 0; | ||
805 | |||
806 | msg[0] = data[0]; | ||
807 | msg[1] = data[1]; | ||
808 | |||
809 | spi_message_init(&m); | ||
810 | memset(&t, 0, (sizeof t)); | ||
811 | |||
812 | t.tx_buf = &msg[0]; | ||
813 | t.len = len; | ||
814 | |||
815 | spi_message_add_tail(&t, &m); | ||
816 | spi_sync(spi, &m); | ||
817 | |||
818 | return len; | ||
819 | } | ||
820 | #endif /* CONFIG_SPI_MASTER */ | ||
821 | |||
752 | static int wm8510_probe(struct platform_device *pdev) | 822 | static int wm8510_probe(struct platform_device *pdev) |
753 | { | 823 | { |
754 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 824 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
@@ -771,14 +841,17 @@ static int wm8510_probe(struct platform_device *pdev) | |||
771 | wm8510_socdev = socdev; | 841 | wm8510_socdev = socdev; |
772 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 842 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
773 | if (setup->i2c_address) { | 843 | if (setup->i2c_address) { |
774 | normal_i2c[0] = setup->i2c_address; | ||
775 | codec->hw_write = (hw_write_t)i2c_master_send; | 844 | codec->hw_write = (hw_write_t)i2c_master_send; |
776 | ret = i2c_add_driver(&wm8510_i2c_driver); | 845 | ret = wm8510_add_i2c_device(pdev, setup); |
846 | } | ||
847 | #endif | ||
848 | #if defined(CONFIG_SPI_MASTER) | ||
849 | if (setup->spi) { | ||
850 | codec->hw_write = (hw_write_t)wm8510_spi_write; | ||
851 | ret = spi_register_driver(&wm8510_spi_driver); | ||
777 | if (ret != 0) | 852 | if (ret != 0) |
778 | printk(KERN_ERR "can't add i2c driver"); | 853 | printk(KERN_ERR "can't add spi driver"); |
779 | } | 854 | } |
780 | #else | ||
781 | /* Add other interfaces here */ | ||
782 | #endif | 855 | #endif |
783 | 856 | ||
784 | if (ret != 0) | 857 | if (ret != 0) |
@@ -798,8 +871,12 @@ static int wm8510_remove(struct platform_device *pdev) | |||
798 | snd_soc_free_pcms(socdev); | 871 | snd_soc_free_pcms(socdev); |
799 | snd_soc_dapm_free(socdev); | 872 | snd_soc_dapm_free(socdev); |
800 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 873 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
874 | i2c_unregister_device(codec->control_data); | ||
801 | i2c_del_driver(&wm8510_i2c_driver); | 875 | i2c_del_driver(&wm8510_i2c_driver); |
802 | #endif | 876 | #endif |
877 | #if defined(CONFIG_SPI_MASTER) | ||
878 | spi_unregister_driver(&wm8510_spi_driver); | ||
879 | #endif | ||
803 | kfree(codec); | 880 | kfree(codec); |
804 | 881 | ||
805 | return 0; | 882 | return 0; |
diff --git a/sound/soc/codecs/wm8510.h b/sound/soc/codecs/wm8510.h index f5d2e42eb3f4..bdefcf5c69ff 100644 --- a/sound/soc/codecs/wm8510.h +++ b/sound/soc/codecs/wm8510.h | |||
@@ -94,6 +94,8 @@ | |||
94 | #define WM8510_MCLKDIV_12 (7 << 5) | 94 | #define WM8510_MCLKDIV_12 (7 << 5) |
95 | 95 | ||
96 | struct wm8510_setup_data { | 96 | struct wm8510_setup_data { |
97 | int spi; | ||
98 | int i2c_bus; | ||
97 | unsigned short i2c_address; | 99 | unsigned short i2c_address; |
98 | }; | 100 | }; |
99 | 101 | ||
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c new file mode 100644 index 000000000000..627ebfb4209b --- /dev/null +++ b/sound/soc/codecs/wm8580.c | |||
@@ -0,0 +1,1053 @@ | |||
1 | /* | ||
2 | * wm8580.c -- WM8580 ALSA Soc Audio driver | ||
3 | * | ||
4 | * Copyright 2008 Wolfson Microelectronics PLC. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
9 | * option) any later version. | ||
10 | * | ||
11 | * Notes: | ||
12 | * The WM8580 is a multichannel codec with S/PDIF support, featuring six | ||
13 | * DAC channels and two ADC channels. | ||
14 | * | ||
15 | * Currently only the primary audio interface is supported - S/PDIF and | ||
16 | * the secondary audio interfaces are not. | ||
17 | */ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/moduleparam.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/pm.h> | ||
25 | #include <linux/i2c.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/pcm.h> | ||
29 | #include <sound/pcm_params.h> | ||
30 | #include <sound/soc.h> | ||
31 | #include <sound/soc-dapm.h> | ||
32 | #include <sound/tlv.h> | ||
33 | #include <sound/initval.h> | ||
34 | #include <asm/div64.h> | ||
35 | |||
36 | #include "wm8580.h" | ||
37 | |||
38 | #define WM8580_VERSION "0.1" | ||
39 | |||
40 | struct pll_state { | ||
41 | unsigned int in; | ||
42 | unsigned int out; | ||
43 | }; | ||
44 | |||
45 | /* codec private data */ | ||
46 | struct wm8580_priv { | ||
47 | struct pll_state a; | ||
48 | struct pll_state b; | ||
49 | }; | ||
50 | |||
51 | /* WM8580 register space */ | ||
52 | #define WM8580_PLLA1 0x00 | ||
53 | #define WM8580_PLLA2 0x01 | ||
54 | #define WM8580_PLLA3 0x02 | ||
55 | #define WM8580_PLLA4 0x03 | ||
56 | #define WM8580_PLLB1 0x04 | ||
57 | #define WM8580_PLLB2 0x05 | ||
58 | #define WM8580_PLLB3 0x06 | ||
59 | #define WM8580_PLLB4 0x07 | ||
60 | #define WM8580_CLKSEL 0x08 | ||
61 | #define WM8580_PAIF1 0x09 | ||
62 | #define WM8580_PAIF2 0x0A | ||
63 | #define WM8580_SAIF1 0x0B | ||
64 | #define WM8580_PAIF3 0x0C | ||
65 | #define WM8580_PAIF4 0x0D | ||
66 | #define WM8580_SAIF2 0x0E | ||
67 | #define WM8580_DAC_CONTROL1 0x0F | ||
68 | #define WM8580_DAC_CONTROL2 0x10 | ||
69 | #define WM8580_DAC_CONTROL3 0x11 | ||
70 | #define WM8580_DAC_CONTROL4 0x12 | ||
71 | #define WM8580_DAC_CONTROL5 0x13 | ||
72 | #define WM8580_DIGITAL_ATTENUATION_DACL1 0x14 | ||
73 | #define WM8580_DIGITAL_ATTENUATION_DACR1 0x15 | ||
74 | #define WM8580_DIGITAL_ATTENUATION_DACL2 0x16 | ||
75 | #define WM8580_DIGITAL_ATTENUATION_DACR2 0x17 | ||
76 | #define WM8580_DIGITAL_ATTENUATION_DACL3 0x18 | ||
77 | #define WM8580_DIGITAL_ATTENUATION_DACR3 0x19 | ||
78 | #define WM8580_MASTER_DIGITAL_ATTENUATION 0x1C | ||
79 | #define WM8580_ADC_CONTROL1 0x1D | ||
80 | #define WM8580_SPDTXCHAN0 0x1E | ||
81 | #define WM8580_SPDTXCHAN1 0x1F | ||
82 | #define WM8580_SPDTXCHAN2 0x20 | ||
83 | #define WM8580_SPDTXCHAN3 0x21 | ||
84 | #define WM8580_SPDTXCHAN4 0x22 | ||
85 | #define WM8580_SPDTXCHAN5 0x23 | ||
86 | #define WM8580_SPDMODE 0x24 | ||
87 | #define WM8580_INTMASK 0x25 | ||
88 | #define WM8580_GPO1 0x26 | ||
89 | #define WM8580_GPO2 0x27 | ||
90 | #define WM8580_GPO3 0x28 | ||
91 | #define WM8580_GPO4 0x29 | ||
92 | #define WM8580_GPO5 0x2A | ||
93 | #define WM8580_INTSTAT 0x2B | ||
94 | #define WM8580_SPDRXCHAN1 0x2C | ||
95 | #define WM8580_SPDRXCHAN2 0x2D | ||
96 | #define WM8580_SPDRXCHAN3 0x2E | ||
97 | #define WM8580_SPDRXCHAN4 0x2F | ||
98 | #define WM8580_SPDRXCHAN5 0x30 | ||
99 | #define WM8580_SPDSTAT 0x31 | ||
100 | #define WM8580_PWRDN1 0x32 | ||
101 | #define WM8580_PWRDN2 0x33 | ||
102 | #define WM8580_READBACK 0x34 | ||
103 | #define WM8580_RESET 0x35 | ||
104 | |||
105 | /* PLLB4 (register 7h) */ | ||
106 | #define WM8580_PLLB4_MCLKOUTSRC_MASK 0x60 | ||
107 | #define WM8580_PLLB4_MCLKOUTSRC_PLLA 0x20 | ||
108 | #define WM8580_PLLB4_MCLKOUTSRC_PLLB 0x40 | ||
109 | #define WM8580_PLLB4_MCLKOUTSRC_OSC 0x60 | ||
110 | |||
111 | #define WM8580_PLLB4_CLKOUTSRC_MASK 0x180 | ||
112 | #define WM8580_PLLB4_CLKOUTSRC_PLLACLK 0x080 | ||
113 | #define WM8580_PLLB4_CLKOUTSRC_PLLBCLK 0x100 | ||
114 | #define WM8580_PLLB4_CLKOUTSRC_OSCCLK 0x180 | ||
115 | |||
116 | /* CLKSEL (register 8h) */ | ||
117 | #define WM8580_CLKSEL_DAC_CLKSEL_MASK 0x03 | ||
118 | #define WM8580_CLKSEL_DAC_CLKSEL_PLLA 0x01 | ||
119 | #define WM8580_CLKSEL_DAC_CLKSEL_PLLB 0x02 | ||
120 | |||
121 | /* AIF control 1 (registers 9h-bh) */ | ||
122 | #define WM8580_AIF_RATE_MASK 0x7 | ||
123 | #define WM8580_AIF_RATE_128 0x0 | ||
124 | #define WM8580_AIF_RATE_192 0x1 | ||
125 | #define WM8580_AIF_RATE_256 0x2 | ||
126 | #define WM8580_AIF_RATE_384 0x3 | ||
127 | #define WM8580_AIF_RATE_512 0x4 | ||
128 | #define WM8580_AIF_RATE_768 0x5 | ||
129 | #define WM8580_AIF_RATE_1152 0x6 | ||
130 | |||
131 | #define WM8580_AIF_BCLKSEL_MASK 0x18 | ||
132 | #define WM8580_AIF_BCLKSEL_64 0x00 | ||
133 | #define WM8580_AIF_BCLKSEL_128 0x08 | ||
134 | #define WM8580_AIF_BCLKSEL_256 0x10 | ||
135 | #define WM8580_AIF_BCLKSEL_SYSCLK 0x18 | ||
136 | |||
137 | #define WM8580_AIF_MS 0x20 | ||
138 | |||
139 | #define WM8580_AIF_CLKSRC_MASK 0xc0 | ||
140 | #define WM8580_AIF_CLKSRC_PLLA 0x40 | ||
141 | #define WM8580_AIF_CLKSRC_PLLB 0x40 | ||
142 | #define WM8580_AIF_CLKSRC_MCLK 0xc0 | ||
143 | |||
144 | /* AIF control 2 (registers ch-eh) */ | ||
145 | #define WM8580_AIF_FMT_MASK 0x03 | ||
146 | #define WM8580_AIF_FMT_RIGHTJ 0x00 | ||
147 | #define WM8580_AIF_FMT_LEFTJ 0x01 | ||
148 | #define WM8580_AIF_FMT_I2S 0x02 | ||
149 | #define WM8580_AIF_FMT_DSP 0x03 | ||
150 | |||
151 | #define WM8580_AIF_LENGTH_MASK 0x0c | ||
152 | #define WM8580_AIF_LENGTH_16 0x00 | ||
153 | #define WM8580_AIF_LENGTH_20 0x04 | ||
154 | #define WM8580_AIF_LENGTH_24 0x08 | ||
155 | #define WM8580_AIF_LENGTH_32 0x0c | ||
156 | |||
157 | #define WM8580_AIF_LRP 0x10 | ||
158 | #define WM8580_AIF_BCP 0x20 | ||
159 | |||
160 | /* Powerdown Register 1 (register 32h) */ | ||
161 | #define WM8580_PWRDN1_PWDN 0x001 | ||
162 | #define WM8580_PWRDN1_ALLDACPD 0x040 | ||
163 | |||
164 | /* Powerdown Register 2 (register 33h) */ | ||
165 | #define WM8580_PWRDN2_OSSCPD 0x001 | ||
166 | #define WM8580_PWRDN2_PLLAPD 0x002 | ||
167 | #define WM8580_PWRDN2_PLLBPD 0x004 | ||
168 | #define WM8580_PWRDN2_SPDIFPD 0x008 | ||
169 | #define WM8580_PWRDN2_SPDIFTXD 0x010 | ||
170 | #define WM8580_PWRDN2_SPDIFRXD 0x020 | ||
171 | |||
172 | #define WM8580_DAC_CONTROL5_MUTEALL 0x10 | ||
173 | |||
174 | /* | ||
175 | * wm8580 register cache | ||
176 | * We can't read the WM8580 register space when we | ||
177 | * are using 2 wire for device control, so we cache them instead. | ||
178 | */ | ||
179 | static const u16 wm8580_reg[] = { | ||
180 | 0x0121, 0x017e, 0x007d, 0x0014, /*R3*/ | ||
181 | 0x0121, 0x017e, 0x007d, 0x0194, /*R7*/ | ||
182 | 0x001c, 0x0002, 0x0002, 0x00c2, /*R11*/ | ||
183 | 0x0182, 0x0082, 0x000a, 0x0024, /*R15*/ | ||
184 | 0x0009, 0x0000, 0x00ff, 0x0000, /*R19*/ | ||
185 | 0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R23*/ | ||
186 | 0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R27*/ | ||
187 | 0x01f0, 0x0040, 0x0000, 0x0000, /*R31(0x1F)*/ | ||
188 | 0x0000, 0x0000, 0x0031, 0x000b, /*R35*/ | ||
189 | 0x0039, 0x0000, 0x0010, 0x0032, /*R39*/ | ||
190 | 0x0054, 0x0076, 0x0098, 0x0000, /*R43(0x2B)*/ | ||
191 | 0x0000, 0x0000, 0x0000, 0x0000, /*R47*/ | ||
192 | 0x0000, 0x0000, 0x005e, 0x003e, /*R51(0x33)*/ | ||
193 | 0x0000, 0x0000 /*R53*/ | ||
194 | }; | ||
195 | |||
196 | /* | ||
197 | * read wm8580 register cache | ||
198 | */ | ||
199 | static inline unsigned int wm8580_read_reg_cache(struct snd_soc_codec *codec, | ||
200 | unsigned int reg) | ||
201 | { | ||
202 | u16 *cache = codec->reg_cache; | ||
203 | BUG_ON(reg > ARRAY_SIZE(wm8580_reg)); | ||
204 | return cache[reg]; | ||
205 | } | ||
206 | |||
207 | /* | ||
208 | * write wm8580 register cache | ||
209 | */ | ||
210 | static inline void wm8580_write_reg_cache(struct snd_soc_codec *codec, | ||
211 | unsigned int reg, unsigned int value) | ||
212 | { | ||
213 | u16 *cache = codec->reg_cache; | ||
214 | |||
215 | cache[reg] = value; | ||
216 | } | ||
217 | |||
218 | /* | ||
219 | * write to the WM8580 register space | ||
220 | */ | ||
221 | static int wm8580_write(struct snd_soc_codec *codec, unsigned int reg, | ||
222 | unsigned int value) | ||
223 | { | ||
224 | u8 data[2]; | ||
225 | |||
226 | BUG_ON(reg > ARRAY_SIZE(wm8580_reg)); | ||
227 | |||
228 | /* Registers are 9 bits wide */ | ||
229 | value &= 0x1ff; | ||
230 | |||
231 | switch (reg) { | ||
232 | case WM8580_RESET: | ||
233 | /* Uncached */ | ||
234 | break; | ||
235 | default: | ||
236 | if (value == wm8580_read_reg_cache(codec, reg)) | ||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | /* data is | ||
241 | * D15..D9 WM8580 register offset | ||
242 | * D8...D0 register data | ||
243 | */ | ||
244 | data[0] = (reg << 1) | ((value >> 8) & 0x0001); | ||
245 | data[1] = value & 0x00ff; | ||
246 | |||
247 | wm8580_write_reg_cache(codec, reg, value); | ||
248 | if (codec->hw_write(codec->control_data, data, 2) == 2) | ||
249 | return 0; | ||
250 | else | ||
251 | return -EIO; | ||
252 | } | ||
253 | |||
254 | static inline unsigned int wm8580_read(struct snd_soc_codec *codec, | ||
255 | unsigned int reg) | ||
256 | { | ||
257 | switch (reg) { | ||
258 | default: | ||
259 | return wm8580_read_reg_cache(codec, reg); | ||
260 | } | ||
261 | } | ||
262 | |||
263 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); | ||
264 | |||
265 | static int wm8580_out_vu(struct snd_kcontrol *kcontrol, | ||
266 | struct snd_ctl_elem_value *ucontrol) | ||
267 | { | ||
268 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
269 | int reg = kcontrol->private_value & 0xff; | ||
270 | int reg2 = (kcontrol->private_value >> 24) & 0xff; | ||
271 | int ret; | ||
272 | u16 val; | ||
273 | |||
274 | /* Clear the register cache so we write without VU set */ | ||
275 | wm8580_write_reg_cache(codec, reg, 0); | ||
276 | wm8580_write_reg_cache(codec, reg2, 0); | ||
277 | |||
278 | ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); | ||
279 | if (ret < 0) | ||
280 | return ret; | ||
281 | |||
282 | /* Now write again with the volume update bit set */ | ||
283 | val = wm8580_read_reg_cache(codec, reg); | ||
284 | wm8580_write(codec, reg, val | 0x0100); | ||
285 | |||
286 | val = wm8580_read_reg_cache(codec, reg2); | ||
287 | wm8580_write(codec, reg2, val | 0x0100); | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | #define SOC_WM8580_OUT_DOUBLE_R_TLV(xname, reg_left, reg_right, shift, max, invert, tlv_array) \ | ||
293 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ | ||
294 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ | ||
295 | SNDRV_CTL_ELEM_ACCESS_READWRITE, \ | ||
296 | .tlv.p = (tlv_array), \ | ||
297 | .info = snd_soc_info_volsw_2r, \ | ||
298 | .get = snd_soc_get_volsw_2r, .put = wm8580_out_vu, \ | ||
299 | .private_value = (reg_left) | ((shift) << 8) | \ | ||
300 | ((max) << 12) | ((invert) << 20) | ((reg_right) << 24) } | ||
301 | |||
302 | static const struct snd_kcontrol_new wm8580_snd_controls[] = { | ||
303 | SOC_WM8580_OUT_DOUBLE_R_TLV("DAC1 Playback Volume", | ||
304 | WM8580_DIGITAL_ATTENUATION_DACL1, | ||
305 | WM8580_DIGITAL_ATTENUATION_DACR1, | ||
306 | 0, 0xff, 0, dac_tlv), | ||
307 | SOC_WM8580_OUT_DOUBLE_R_TLV("DAC2 Playback Volume", | ||
308 | WM8580_DIGITAL_ATTENUATION_DACL2, | ||
309 | WM8580_DIGITAL_ATTENUATION_DACR2, | ||
310 | 0, 0xff, 0, dac_tlv), | ||
311 | SOC_WM8580_OUT_DOUBLE_R_TLV("DAC3 Playback Volume", | ||
312 | WM8580_DIGITAL_ATTENUATION_DACL3, | ||
313 | WM8580_DIGITAL_ATTENUATION_DACR3, | ||
314 | 0, 0xff, 0, dac_tlv), | ||
315 | |||
316 | SOC_SINGLE("DAC1 Deemphasis Switch", WM8580_DAC_CONTROL3, 0, 1, 0), | ||
317 | SOC_SINGLE("DAC2 Deemphasis Switch", WM8580_DAC_CONTROL3, 1, 1, 0), | ||
318 | SOC_SINGLE("DAC3 Deemphasis Switch", WM8580_DAC_CONTROL3, 2, 1, 0), | ||
319 | |||
320 | SOC_DOUBLE("DAC1 Invert Switch", WM8580_DAC_CONTROL4, 0, 1, 1, 0), | ||
321 | SOC_DOUBLE("DAC2 Invert Switch", WM8580_DAC_CONTROL4, 2, 3, 1, 0), | ||
322 | SOC_DOUBLE("DAC3 Invert Switch", WM8580_DAC_CONTROL4, 4, 5, 1, 0), | ||
323 | |||
324 | SOC_SINGLE("DAC ZC Switch", WM8580_DAC_CONTROL5, 5, 1, 0), | ||
325 | SOC_SINGLE("DAC1 Switch", WM8580_DAC_CONTROL5, 0, 1, 0), | ||
326 | SOC_SINGLE("DAC2 Switch", WM8580_DAC_CONTROL5, 1, 1, 0), | ||
327 | SOC_SINGLE("DAC3 Switch", WM8580_DAC_CONTROL5, 2, 1, 0), | ||
328 | |||
329 | SOC_DOUBLE("ADC Mute Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 0), | ||
330 | SOC_SINGLE("ADC High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0), | ||
331 | }; | ||
332 | |||
333 | /* Add non-DAPM controls */ | ||
334 | static int wm8580_add_controls(struct snd_soc_codec *codec) | ||
335 | { | ||
336 | int err, i; | ||
337 | |||
338 | for (i = 0; i < ARRAY_SIZE(wm8580_snd_controls); i++) { | ||
339 | err = snd_ctl_add(codec->card, | ||
340 | snd_soc_cnew(&wm8580_snd_controls[i], | ||
341 | codec, NULL)); | ||
342 | if (err < 0) | ||
343 | return err; | ||
344 | } | ||
345 | return 0; | ||
346 | } | ||
347 | static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = { | ||
348 | SND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1), | ||
349 | SND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1), | ||
350 | SND_SOC_DAPM_DAC("DAC3", "Playback", WM8580_PWRDN1, 4, 1), | ||
351 | |||
352 | SND_SOC_DAPM_OUTPUT("VOUT1L"), | ||
353 | SND_SOC_DAPM_OUTPUT("VOUT1R"), | ||
354 | SND_SOC_DAPM_OUTPUT("VOUT2L"), | ||
355 | SND_SOC_DAPM_OUTPUT("VOUT2R"), | ||
356 | SND_SOC_DAPM_OUTPUT("VOUT3L"), | ||
357 | SND_SOC_DAPM_OUTPUT("VOUT3R"), | ||
358 | |||
359 | SND_SOC_DAPM_ADC("ADC", "Capture", WM8580_PWRDN1, 1, 1), | ||
360 | |||
361 | SND_SOC_DAPM_INPUT("AINL"), | ||
362 | SND_SOC_DAPM_INPUT("AINR"), | ||
363 | }; | ||
364 | |||
365 | static const struct snd_soc_dapm_route audio_map[] = { | ||
366 | { "VOUT1L", NULL, "DAC1" }, | ||
367 | { "VOUT1R", NULL, "DAC1" }, | ||
368 | |||
369 | { "VOUT2L", NULL, "DAC2" }, | ||
370 | { "VOUT2R", NULL, "DAC2" }, | ||
371 | |||
372 | { "VOUT3L", NULL, "DAC3" }, | ||
373 | { "VOUT3R", NULL, "DAC3" }, | ||
374 | |||
375 | { "ADC", NULL, "AINL" }, | ||
376 | { "ADC", NULL, "AINR" }, | ||
377 | }; | ||
378 | |||
379 | static int wm8580_add_widgets(struct snd_soc_codec *codec) | ||
380 | { | ||
381 | snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets, | ||
382 | ARRAY_SIZE(wm8580_dapm_widgets)); | ||
383 | |||
384 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
385 | |||
386 | snd_soc_dapm_new_widgets(codec); | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | /* PLL divisors */ | ||
391 | struct _pll_div { | ||
392 | u32 prescale:1; | ||
393 | u32 postscale:1; | ||
394 | u32 freqmode:2; | ||
395 | u32 n:4; | ||
396 | u32 k:24; | ||
397 | }; | ||
398 | |||
399 | /* The size in bits of the pll divide */ | ||
400 | #define FIXED_PLL_SIZE (1 << 22) | ||
401 | |||
402 | /* PLL rate to output rate divisions */ | ||
403 | static struct { | ||
404 | unsigned int div; | ||
405 | unsigned int freqmode; | ||
406 | unsigned int postscale; | ||
407 | } post_table[] = { | ||
408 | { 2, 0, 0 }, | ||
409 | { 4, 0, 1 }, | ||
410 | { 4, 1, 0 }, | ||
411 | { 8, 1, 1 }, | ||
412 | { 8, 2, 0 }, | ||
413 | { 16, 2, 1 }, | ||
414 | { 12, 3, 0 }, | ||
415 | { 24, 3, 1 } | ||
416 | }; | ||
417 | |||
418 | static int pll_factors(struct _pll_div *pll_div, unsigned int target, | ||
419 | unsigned int source) | ||
420 | { | ||
421 | u64 Kpart; | ||
422 | unsigned int K, Ndiv, Nmod; | ||
423 | int i; | ||
424 | |||
425 | pr_debug("wm8580: PLL %dHz->%dHz\n", source, target); | ||
426 | |||
427 | /* Scale the output frequency up; the PLL should run in the | ||
428 | * region of 90-100MHz. | ||
429 | */ | ||
430 | for (i = 0; i < ARRAY_SIZE(post_table); i++) { | ||
431 | if (target * post_table[i].div >= 90000000 && | ||
432 | target * post_table[i].div <= 100000000) { | ||
433 | pll_div->freqmode = post_table[i].freqmode; | ||
434 | pll_div->postscale = post_table[i].postscale; | ||
435 | target *= post_table[i].div; | ||
436 | break; | ||
437 | } | ||
438 | } | ||
439 | |||
440 | if (i == ARRAY_SIZE(post_table)) { | ||
441 | printk(KERN_ERR "wm8580: Unable to scale output frequency " | ||
442 | "%u\n", target); | ||
443 | return -EINVAL; | ||
444 | } | ||
445 | |||
446 | Ndiv = target / source; | ||
447 | |||
448 | if (Ndiv < 5) { | ||
449 | source /= 2; | ||
450 | pll_div->prescale = 1; | ||
451 | Ndiv = target / source; | ||
452 | } else | ||
453 | pll_div->prescale = 0; | ||
454 | |||
455 | if ((Ndiv < 5) || (Ndiv > 13)) { | ||
456 | printk(KERN_ERR | ||
457 | "WM8580 N=%d outside supported range\n", Ndiv); | ||
458 | return -EINVAL; | ||
459 | } | ||
460 | |||
461 | pll_div->n = Ndiv; | ||
462 | Nmod = target % source; | ||
463 | Kpart = FIXED_PLL_SIZE * (long long)Nmod; | ||
464 | |||
465 | do_div(Kpart, source); | ||
466 | |||
467 | K = Kpart & 0xFFFFFFFF; | ||
468 | |||
469 | pll_div->k = K; | ||
470 | |||
471 | pr_debug("PLL %x.%x prescale %d freqmode %d postscale %d\n", | ||
472 | pll_div->n, pll_div->k, pll_div->prescale, pll_div->freqmode, | ||
473 | pll_div->postscale); | ||
474 | |||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, | ||
479 | int pll_id, unsigned int freq_in, unsigned int freq_out) | ||
480 | { | ||
481 | int offset; | ||
482 | struct snd_soc_codec *codec = codec_dai->codec; | ||
483 | struct wm8580_priv *wm8580 = codec->private_data; | ||
484 | struct pll_state *state; | ||
485 | struct _pll_div pll_div; | ||
486 | unsigned int reg; | ||
487 | unsigned int pwr_mask; | ||
488 | int ret; | ||
489 | |||
490 | /* GCC isn't able to work out the ifs below for initialising/using | ||
491 | * pll_div so suppress warnings. | ||
492 | */ | ||
493 | memset(&pll_div, 0, sizeof(pll_div)); | ||
494 | |||
495 | switch (pll_id) { | ||
496 | case WM8580_PLLA: | ||
497 | state = &wm8580->a; | ||
498 | offset = 0; | ||
499 | pwr_mask = WM8580_PWRDN2_PLLAPD; | ||
500 | break; | ||
501 | case WM8580_PLLB: | ||
502 | state = &wm8580->b; | ||
503 | offset = 4; | ||
504 | pwr_mask = WM8580_PWRDN2_PLLBPD; | ||
505 | break; | ||
506 | default: | ||
507 | return -ENODEV; | ||
508 | } | ||
509 | |||
510 | if (freq_in && freq_out) { | ||
511 | ret = pll_factors(&pll_div, freq_out, freq_in); | ||
512 | if (ret != 0) | ||
513 | return ret; | ||
514 | } | ||
515 | |||
516 | state->in = freq_in; | ||
517 | state->out = freq_out; | ||
518 | |||
519 | /* Always disable the PLL - it is not safe to leave it running | ||
520 | * while reprogramming it. | ||
521 | */ | ||
522 | reg = wm8580_read(codec, WM8580_PWRDN2); | ||
523 | wm8580_write(codec, WM8580_PWRDN2, reg | pwr_mask); | ||
524 | |||
525 | if (!freq_in || !freq_out) | ||
526 | return 0; | ||
527 | |||
528 | wm8580_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff); | ||
529 | wm8580_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0xff); | ||
530 | wm8580_write(codec, WM8580_PLLA3 + offset, | ||
531 | (pll_div.k >> 18 & 0xf) | (pll_div.n << 4)); | ||
532 | |||
533 | reg = wm8580_read(codec, WM8580_PLLA4 + offset); | ||
534 | reg &= ~0x3f; | ||
535 | reg |= pll_div.prescale | pll_div.postscale << 1 | | ||
536 | pll_div.freqmode << 4; | ||
537 | |||
538 | wm8580_write(codec, WM8580_PLLA4 + offset, reg); | ||
539 | |||
540 | /* All done, turn it on */ | ||
541 | reg = wm8580_read(codec, WM8580_PWRDN2); | ||
542 | wm8580_write(codec, WM8580_PWRDN2, reg & ~pwr_mask); | ||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | /* | ||
548 | * Set PCM DAI bit size and sample rate. | ||
549 | */ | ||
550 | static int wm8580_paif_hw_params(struct snd_pcm_substream *substream, | ||
551 | struct snd_pcm_hw_params *params) | ||
552 | { | ||
553 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
554 | struct snd_soc_dai_link *dai = rtd->dai; | ||
555 | struct snd_soc_device *socdev = rtd->socdev; | ||
556 | struct snd_soc_codec *codec = socdev->codec; | ||
557 | u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->codec_dai->id); | ||
558 | |||
559 | paifb &= ~WM8580_AIF_LENGTH_MASK; | ||
560 | /* bit size */ | ||
561 | switch (params_format(params)) { | ||
562 | case SNDRV_PCM_FORMAT_S16_LE: | ||
563 | break; | ||
564 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
565 | paifb |= WM8580_AIF_LENGTH_20; | ||
566 | break; | ||
567 | case SNDRV_PCM_FORMAT_S24_LE: | ||
568 | paifb |= WM8580_AIF_LENGTH_24; | ||
569 | break; | ||
570 | case SNDRV_PCM_FORMAT_S32_LE: | ||
571 | paifb |= WM8580_AIF_LENGTH_24; | ||
572 | break; | ||
573 | default: | ||
574 | return -EINVAL; | ||
575 | } | ||
576 | |||
577 | wm8580_write(codec, WM8580_PAIF3 + dai->codec_dai->id, paifb); | ||
578 | return 0; | ||
579 | } | ||
580 | |||
581 | static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, | ||
582 | unsigned int fmt) | ||
583 | { | ||
584 | struct snd_soc_codec *codec = codec_dai->codec; | ||
585 | unsigned int aifa; | ||
586 | unsigned int aifb; | ||
587 | int can_invert_lrclk; | ||
588 | |||
589 | aifa = wm8580_read(codec, WM8580_PAIF1 + codec_dai->id); | ||
590 | aifb = wm8580_read(codec, WM8580_PAIF3 + codec_dai->id); | ||
591 | |||
592 | aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP); | ||
593 | |||
594 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
595 | case SND_SOC_DAIFMT_CBS_CFS: | ||
596 | aifa &= ~WM8580_AIF_MS; | ||
597 | break; | ||
598 | case SND_SOC_DAIFMT_CBM_CFM: | ||
599 | aifa |= WM8580_AIF_MS; | ||
600 | break; | ||
601 | default: | ||
602 | return -EINVAL; | ||
603 | } | ||
604 | |||
605 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
606 | case SND_SOC_DAIFMT_I2S: | ||
607 | can_invert_lrclk = 1; | ||
608 | aifb |= WM8580_AIF_FMT_I2S; | ||
609 | break; | ||
610 | case SND_SOC_DAIFMT_RIGHT_J: | ||
611 | can_invert_lrclk = 1; | ||
612 | aifb |= WM8580_AIF_FMT_RIGHTJ; | ||
613 | break; | ||
614 | case SND_SOC_DAIFMT_LEFT_J: | ||
615 | can_invert_lrclk = 1; | ||
616 | aifb |= WM8580_AIF_FMT_LEFTJ; | ||
617 | break; | ||
618 | case SND_SOC_DAIFMT_DSP_A: | ||
619 | can_invert_lrclk = 0; | ||
620 | aifb |= WM8580_AIF_FMT_DSP; | ||
621 | break; | ||
622 | case SND_SOC_DAIFMT_DSP_B: | ||
623 | can_invert_lrclk = 0; | ||
624 | aifb |= WM8580_AIF_FMT_DSP; | ||
625 | aifb |= WM8580_AIF_LRP; | ||
626 | break; | ||
627 | default: | ||
628 | return -EINVAL; | ||
629 | } | ||
630 | |||
631 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
632 | case SND_SOC_DAIFMT_NB_NF: | ||
633 | break; | ||
634 | |||
635 | case SND_SOC_DAIFMT_IB_IF: | ||
636 | if (!can_invert_lrclk) | ||
637 | return -EINVAL; | ||
638 | aifb |= WM8580_AIF_BCP; | ||
639 | aifb |= WM8580_AIF_LRP; | ||
640 | break; | ||
641 | |||
642 | case SND_SOC_DAIFMT_IB_NF: | ||
643 | aifb |= WM8580_AIF_BCP; | ||
644 | break; | ||
645 | |||
646 | case SND_SOC_DAIFMT_NB_IF: | ||
647 | if (!can_invert_lrclk) | ||
648 | return -EINVAL; | ||
649 | aifb |= WM8580_AIF_LRP; | ||
650 | break; | ||
651 | |||
652 | default: | ||
653 | return -EINVAL; | ||
654 | } | ||
655 | |||
656 | wm8580_write(codec, WM8580_PAIF1 + codec_dai->id, aifa); | ||
657 | wm8580_write(codec, WM8580_PAIF3 + codec_dai->id, aifb); | ||
658 | |||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, | ||
663 | int div_id, int div) | ||
664 | { | ||
665 | struct snd_soc_codec *codec = codec_dai->codec; | ||
666 | unsigned int reg; | ||
667 | |||
668 | switch (div_id) { | ||
669 | case WM8580_MCLK: | ||
670 | reg = wm8580_read(codec, WM8580_PLLB4); | ||
671 | reg &= ~WM8580_PLLB4_MCLKOUTSRC_MASK; | ||
672 | |||
673 | switch (div) { | ||
674 | case WM8580_CLKSRC_MCLK: | ||
675 | /* Input */ | ||
676 | break; | ||
677 | |||
678 | case WM8580_CLKSRC_PLLA: | ||
679 | reg |= WM8580_PLLB4_MCLKOUTSRC_PLLA; | ||
680 | break; | ||
681 | case WM8580_CLKSRC_PLLB: | ||
682 | reg |= WM8580_PLLB4_MCLKOUTSRC_PLLB; | ||
683 | break; | ||
684 | |||
685 | case WM8580_CLKSRC_OSC: | ||
686 | reg |= WM8580_PLLB4_MCLKOUTSRC_OSC; | ||
687 | break; | ||
688 | |||
689 | default: | ||
690 | return -EINVAL; | ||
691 | } | ||
692 | wm8580_write(codec, WM8580_PLLB4, reg); | ||
693 | break; | ||
694 | |||
695 | case WM8580_DAC_CLKSEL: | ||
696 | reg = wm8580_read(codec, WM8580_CLKSEL); | ||
697 | reg &= ~WM8580_CLKSEL_DAC_CLKSEL_MASK; | ||
698 | |||
699 | switch (div) { | ||
700 | case WM8580_CLKSRC_MCLK: | ||
701 | break; | ||
702 | |||
703 | case WM8580_CLKSRC_PLLA: | ||
704 | reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLA; | ||
705 | break; | ||
706 | |||
707 | case WM8580_CLKSRC_PLLB: | ||
708 | reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLB; | ||
709 | break; | ||
710 | |||
711 | default: | ||
712 | return -EINVAL; | ||
713 | } | ||
714 | wm8580_write(codec, WM8580_CLKSEL, reg); | ||
715 | break; | ||
716 | |||
717 | case WM8580_CLKOUTSRC: | ||
718 | reg = wm8580_read(codec, WM8580_PLLB4); | ||
719 | reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK; | ||
720 | |||
721 | switch (div) { | ||
722 | case WM8580_CLKSRC_NONE: | ||
723 | break; | ||
724 | |||
725 | case WM8580_CLKSRC_PLLA: | ||
726 | reg |= WM8580_PLLB4_CLKOUTSRC_PLLACLK; | ||
727 | break; | ||
728 | |||
729 | case WM8580_CLKSRC_PLLB: | ||
730 | reg |= WM8580_PLLB4_CLKOUTSRC_PLLBCLK; | ||
731 | break; | ||
732 | |||
733 | case WM8580_CLKSRC_OSC: | ||
734 | reg |= WM8580_PLLB4_CLKOUTSRC_OSCCLK; | ||
735 | break; | ||
736 | |||
737 | default: | ||
738 | return -EINVAL; | ||
739 | } | ||
740 | wm8580_write(codec, WM8580_PLLB4, reg); | ||
741 | break; | ||
742 | |||
743 | default: | ||
744 | return -EINVAL; | ||
745 | } | ||
746 | |||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute) | ||
751 | { | ||
752 | struct snd_soc_codec *codec = codec_dai->codec; | ||
753 | unsigned int reg; | ||
754 | |||
755 | reg = wm8580_read(codec, WM8580_DAC_CONTROL5); | ||
756 | |||
757 | if (mute) | ||
758 | reg |= WM8580_DAC_CONTROL5_MUTEALL; | ||
759 | else | ||
760 | reg &= ~WM8580_DAC_CONTROL5_MUTEALL; | ||
761 | |||
762 | wm8580_write(codec, WM8580_DAC_CONTROL5, reg); | ||
763 | |||
764 | return 0; | ||
765 | } | ||
766 | |||
767 | static int wm8580_set_bias_level(struct snd_soc_codec *codec, | ||
768 | enum snd_soc_bias_level level) | ||
769 | { | ||
770 | u16 reg; | ||
771 | switch (level) { | ||
772 | case SND_SOC_BIAS_ON: | ||
773 | case SND_SOC_BIAS_PREPARE: | ||
774 | case SND_SOC_BIAS_STANDBY: | ||
775 | break; | ||
776 | case SND_SOC_BIAS_OFF: | ||
777 | reg = wm8580_read(codec, WM8580_PWRDN1); | ||
778 | wm8580_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN); | ||
779 | break; | ||
780 | } | ||
781 | codec->bias_level = level; | ||
782 | return 0; | ||
783 | } | ||
784 | |||
785 | #define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ | ||
786 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) | ||
787 | |||
788 | struct snd_soc_dai wm8580_dai[] = { | ||
789 | { | ||
790 | .name = "WM8580 PAIFRX", | ||
791 | .id = 0, | ||
792 | .playback = { | ||
793 | .stream_name = "Playback", | ||
794 | .channels_min = 1, | ||
795 | .channels_max = 6, | ||
796 | .rates = SNDRV_PCM_RATE_8000_192000, | ||
797 | .formats = WM8580_FORMATS, | ||
798 | }, | ||
799 | .ops = { | ||
800 | .hw_params = wm8580_paif_hw_params, | ||
801 | }, | ||
802 | .dai_ops = { | ||
803 | .set_fmt = wm8580_set_paif_dai_fmt, | ||
804 | .set_clkdiv = wm8580_set_dai_clkdiv, | ||
805 | .set_pll = wm8580_set_dai_pll, | ||
806 | .digital_mute = wm8580_digital_mute, | ||
807 | }, | ||
808 | }, | ||
809 | { | ||
810 | .name = "WM8580 PAIFTX", | ||
811 | .id = 1, | ||
812 | .capture = { | ||
813 | .stream_name = "Capture", | ||
814 | .channels_min = 2, | ||
815 | .channels_max = 2, | ||
816 | .rates = SNDRV_PCM_RATE_8000_192000, | ||
817 | .formats = WM8580_FORMATS, | ||
818 | }, | ||
819 | .ops = { | ||
820 | .hw_params = wm8580_paif_hw_params, | ||
821 | }, | ||
822 | .dai_ops = { | ||
823 | .set_fmt = wm8580_set_paif_dai_fmt, | ||
824 | .set_clkdiv = wm8580_set_dai_clkdiv, | ||
825 | .set_pll = wm8580_set_dai_pll, | ||
826 | }, | ||
827 | }, | ||
828 | }; | ||
829 | EXPORT_SYMBOL_GPL(wm8580_dai); | ||
830 | |||
831 | /* | ||
832 | * initialise the WM8580 driver | ||
833 | * register the mixer and dsp interfaces with the kernel | ||
834 | */ | ||
835 | static int wm8580_init(struct snd_soc_device *socdev) | ||
836 | { | ||
837 | struct snd_soc_codec *codec = socdev->codec; | ||
838 | int ret = 0; | ||
839 | |||
840 | codec->name = "WM8580"; | ||
841 | codec->owner = THIS_MODULE; | ||
842 | codec->read = wm8580_read_reg_cache; | ||
843 | codec->write = wm8580_write; | ||
844 | codec->set_bias_level = wm8580_set_bias_level; | ||
845 | codec->dai = wm8580_dai; | ||
846 | codec->num_dai = ARRAY_SIZE(wm8580_dai); | ||
847 | codec->reg_cache_size = ARRAY_SIZE(wm8580_reg); | ||
848 | codec->reg_cache = kmemdup(wm8580_reg, sizeof(wm8580_reg), | ||
849 | GFP_KERNEL); | ||
850 | |||
851 | if (codec->reg_cache == NULL) | ||
852 | return -ENOMEM; | ||
853 | |||
854 | /* Get the codec into a known state */ | ||
855 | wm8580_write(codec, WM8580_RESET, 0); | ||
856 | |||
857 | /* Power up and get individual control of the DACs */ | ||
858 | wm8580_write(codec, WM8580_PWRDN1, wm8580_read(codec, WM8580_PWRDN1) & | ||
859 | ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD)); | ||
860 | |||
861 | /* Make VMID high impedence */ | ||
862 | wm8580_write(codec, WM8580_ADC_CONTROL1, | ||
863 | wm8580_read(codec, WM8580_ADC_CONTROL1) & ~0x100); | ||
864 | |||
865 | /* register pcms */ | ||
866 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, | ||
867 | SNDRV_DEFAULT_STR1); | ||
868 | if (ret < 0) { | ||
869 | printk(KERN_ERR "wm8580: failed to create pcms\n"); | ||
870 | goto pcm_err; | ||
871 | } | ||
872 | |||
873 | wm8580_add_controls(codec); | ||
874 | wm8580_add_widgets(codec); | ||
875 | |||
876 | ret = snd_soc_register_card(socdev); | ||
877 | if (ret < 0) { | ||
878 | printk(KERN_ERR "wm8580: failed to register card\n"); | ||
879 | goto card_err; | ||
880 | } | ||
881 | return ret; | ||
882 | |||
883 | card_err: | ||
884 | snd_soc_free_pcms(socdev); | ||
885 | snd_soc_dapm_free(socdev); | ||
886 | pcm_err: | ||
887 | kfree(codec->reg_cache); | ||
888 | return ret; | ||
889 | } | ||
890 | |||
891 | /* If the i2c layer weren't so broken, we could pass this kind of data | ||
892 | around */ | ||
893 | static struct snd_soc_device *wm8580_socdev; | ||
894 | |||
895 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
896 | |||
897 | /* | ||
898 | * WM8580 2 wire address is determined by GPIO5 | ||
899 | * state during powerup. | ||
900 | * low = 0x1a | ||
901 | * high = 0x1b | ||
902 | */ | ||
903 | static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; | ||
904 | |||
905 | /* Magic definition of all other variables and things */ | ||
906 | I2C_CLIENT_INSMOD; | ||
907 | |||
908 | static struct i2c_driver wm8580_i2c_driver; | ||
909 | static struct i2c_client client_template; | ||
910 | |||
911 | static int wm8580_codec_probe(struct i2c_adapter *adap, int addr, int kind) | ||
912 | { | ||
913 | struct snd_soc_device *socdev = wm8580_socdev; | ||
914 | struct wm8580_setup_data *setup = socdev->codec_data; | ||
915 | struct snd_soc_codec *codec = socdev->codec; | ||
916 | struct i2c_client *i2c; | ||
917 | int ret; | ||
918 | |||
919 | if (addr != setup->i2c_address) | ||
920 | return -ENODEV; | ||
921 | |||
922 | client_template.adapter = adap; | ||
923 | client_template.addr = addr; | ||
924 | |||
925 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | ||
926 | if (i2c == NULL) { | ||
927 | kfree(codec); | ||
928 | return -ENOMEM; | ||
929 | } | ||
930 | i2c_set_clientdata(i2c, codec); | ||
931 | codec->control_data = i2c; | ||
932 | |||
933 | ret = i2c_attach_client(i2c); | ||
934 | if (ret < 0) { | ||
935 | dev_err(&i2c->dev, "failed to attach codec at addr %x\n", addr); | ||
936 | goto err; | ||
937 | } | ||
938 | |||
939 | ret = wm8580_init(socdev); | ||
940 | if (ret < 0) { | ||
941 | dev_err(&i2c->dev, "failed to initialise WM8580\n"); | ||
942 | goto err; | ||
943 | } | ||
944 | |||
945 | return ret; | ||
946 | |||
947 | err: | ||
948 | kfree(codec); | ||
949 | kfree(i2c); | ||
950 | return ret; | ||
951 | } | ||
952 | |||
953 | static int wm8580_i2c_detach(struct i2c_client *client) | ||
954 | { | ||
955 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | ||
956 | i2c_detach_client(client); | ||
957 | kfree(codec->reg_cache); | ||
958 | kfree(client); | ||
959 | return 0; | ||
960 | } | ||
961 | |||
962 | static int wm8580_i2c_attach(struct i2c_adapter *adap) | ||
963 | { | ||
964 | return i2c_probe(adap, &addr_data, wm8580_codec_probe); | ||
965 | } | ||
966 | |||
967 | /* corgi i2c codec control layer */ | ||
968 | static struct i2c_driver wm8580_i2c_driver = { | ||
969 | .driver = { | ||
970 | .name = "WM8580 I2C Codec", | ||
971 | .owner = THIS_MODULE, | ||
972 | }, | ||
973 | .attach_adapter = wm8580_i2c_attach, | ||
974 | .detach_client = wm8580_i2c_detach, | ||
975 | .command = NULL, | ||
976 | }; | ||
977 | |||
978 | static struct i2c_client client_template = { | ||
979 | .name = "WM8580", | ||
980 | .driver = &wm8580_i2c_driver, | ||
981 | }; | ||
982 | #endif | ||
983 | |||
984 | static int wm8580_probe(struct platform_device *pdev) | ||
985 | { | ||
986 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
987 | struct wm8580_setup_data *setup; | ||
988 | struct snd_soc_codec *codec; | ||
989 | struct wm8580_priv *wm8580; | ||
990 | int ret = 0; | ||
991 | |||
992 | pr_info("WM8580 Audio Codec %s\n", WM8580_VERSION); | ||
993 | |||
994 | setup = socdev->codec_data; | ||
995 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | ||
996 | if (codec == NULL) | ||
997 | return -ENOMEM; | ||
998 | |||
999 | wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL); | ||
1000 | if (wm8580 == NULL) { | ||
1001 | kfree(codec); | ||
1002 | return -ENOMEM; | ||
1003 | } | ||
1004 | |||
1005 | codec->private_data = wm8580; | ||
1006 | socdev->codec = codec; | ||
1007 | mutex_init(&codec->mutex); | ||
1008 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
1009 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
1010 | wm8580_socdev = socdev; | ||
1011 | |||
1012 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
1013 | if (setup->i2c_address) { | ||
1014 | normal_i2c[0] = setup->i2c_address; | ||
1015 | codec->hw_write = (hw_write_t)i2c_master_send; | ||
1016 | ret = i2c_add_driver(&wm8580_i2c_driver); | ||
1017 | if (ret != 0) | ||
1018 | printk(KERN_ERR "can't add i2c driver"); | ||
1019 | } | ||
1020 | #else | ||
1021 | /* Add other interfaces here */ | ||
1022 | #endif | ||
1023 | return ret; | ||
1024 | } | ||
1025 | |||
1026 | /* power down chip */ | ||
1027 | static int wm8580_remove(struct platform_device *pdev) | ||
1028 | { | ||
1029 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1030 | struct snd_soc_codec *codec = socdev->codec; | ||
1031 | |||
1032 | if (codec->control_data) | ||
1033 | wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
1034 | snd_soc_free_pcms(socdev); | ||
1035 | snd_soc_dapm_free(socdev); | ||
1036 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
1037 | i2c_del_driver(&wm8580_i2c_driver); | ||
1038 | #endif | ||
1039 | kfree(codec->private_data); | ||
1040 | kfree(codec); | ||
1041 | |||
1042 | return 0; | ||
1043 | } | ||
1044 | |||
1045 | struct snd_soc_codec_device soc_codec_dev_wm8580 = { | ||
1046 | .probe = wm8580_probe, | ||
1047 | .remove = wm8580_remove, | ||
1048 | }; | ||
1049 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580); | ||
1050 | |||
1051 | MODULE_DESCRIPTION("ASoC WM8580 driver"); | ||
1052 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | ||
1053 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/wm8580.h b/sound/soc/codecs/wm8580.h new file mode 100644 index 000000000000..589ddaba21d7 --- /dev/null +++ b/sound/soc/codecs/wm8580.h | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * wm8580.h -- audio driver for WM8580 | ||
3 | * | ||
4 | * Copyright 2008 Samsung Electronics. | ||
5 | * Author: Ryu Euiyoul | ||
6 | * ryu.real@gmail.com | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #ifndef _WM8580_H | ||
16 | #define _WM8580_H | ||
17 | |||
18 | #define WM8580_PLLA 1 | ||
19 | #define WM8580_PLLB 2 | ||
20 | |||
21 | #define WM8580_MCLK 1 | ||
22 | #define WM8580_DAC_CLKSEL 2 | ||
23 | #define WM8580_CLKOUTSRC 3 | ||
24 | |||
25 | #define WM8580_CLKSRC_MCLK 1 | ||
26 | #define WM8580_CLKSRC_PLLA 2 | ||
27 | #define WM8580_CLKSRC_PLLB 3 | ||
28 | #define WM8580_CLKSRC_OSC 4 | ||
29 | #define WM8580_CLKSRC_NONE 5 | ||
30 | |||
31 | struct wm8580_setup_data { | ||
32 | unsigned short i2c_address; | ||
33 | }; | ||
34 | |||
35 | #define WM8580_DAI_PAIFRX 0 | ||
36 | #define WM8580_DAI_PAIFTX 1 | ||
37 | |||
38 | extern struct snd_soc_dai wm8580_dai[]; | ||
39 | extern struct snd_soc_codec_device soc_codec_dev_wm8580; | ||
40 | |||
41 | #endif | ||
42 | |||
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 9402fcaf04fa..7f8a7e36b33e 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/pm.h> | 19 | #include <linux/pm.h> |
20 | #include <linux/i2c.h> | 20 | #include <linux/i2c.h> |
21 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
22 | #include <linux/spi/spi.h> | ||
22 | #include <sound/core.h> | 23 | #include <sound/core.h> |
23 | #include <sound/pcm.h> | 24 | #include <sound/pcm.h> |
24 | #include <sound/pcm_params.h> | 25 | #include <sound/pcm_params.h> |
@@ -28,7 +29,6 @@ | |||
28 | 29 | ||
29 | #include "wm8731.h" | 30 | #include "wm8731.h" |
30 | 31 | ||
31 | #define AUDIO_NAME "wm8731" | ||
32 | #define WM8731_VERSION "0.13" | 32 | #define WM8731_VERSION "0.13" |
33 | 33 | ||
34 | struct snd_soc_codec_device soc_codec_dev_wm8731; | 34 | struct snd_soc_codec_device soc_codec_dev_wm8731; |
@@ -570,88 +570,144 @@ static struct snd_soc_device *wm8731_socdev; | |||
570 | * low = 0x1a | 570 | * low = 0x1a |
571 | * high = 0x1b | 571 | * high = 0x1b |
572 | */ | 572 | */ |
573 | static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; | ||
574 | 573 | ||
575 | /* Magic definition of all other variables and things */ | 574 | static int wm8731_i2c_probe(struct i2c_client *i2c, |
576 | I2C_CLIENT_INSMOD; | 575 | const struct i2c_device_id *id) |
577 | |||
578 | static struct i2c_driver wm8731_i2c_driver; | ||
579 | static struct i2c_client client_template; | ||
580 | |||
581 | /* If the i2c layer weren't so broken, we could pass this kind of data | ||
582 | around */ | ||
583 | |||
584 | static int wm8731_codec_probe(struct i2c_adapter *adap, int addr, int kind) | ||
585 | { | 576 | { |
586 | struct snd_soc_device *socdev = wm8731_socdev; | 577 | struct snd_soc_device *socdev = wm8731_socdev; |
587 | struct wm8731_setup_data *setup = socdev->codec_data; | ||
588 | struct snd_soc_codec *codec = socdev->codec; | 578 | struct snd_soc_codec *codec = socdev->codec; |
589 | struct i2c_client *i2c; | ||
590 | int ret; | 579 | int ret; |
591 | 580 | ||
592 | if (addr != setup->i2c_address) | ||
593 | return -ENODEV; | ||
594 | |||
595 | client_template.adapter = adap; | ||
596 | client_template.addr = addr; | ||
597 | |||
598 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | ||
599 | if (i2c == NULL) | ||
600 | return -ENOMEM; | ||
601 | |||
602 | i2c_set_clientdata(i2c, codec); | 581 | i2c_set_clientdata(i2c, codec); |
603 | codec->control_data = i2c; | 582 | codec->control_data = i2c; |
604 | 583 | ||
605 | ret = i2c_attach_client(i2c); | ||
606 | if (ret < 0) { | ||
607 | pr_err("failed to attach codec at addr %x\n", addr); | ||
608 | goto err; | ||
609 | } | ||
610 | |||
611 | ret = wm8731_init(socdev); | 584 | ret = wm8731_init(socdev); |
612 | if (ret < 0) { | 585 | if (ret < 0) |
613 | pr_err("failed to initialise WM8731\n"); | 586 | pr_err("failed to initialise WM8731\n"); |
614 | goto err; | ||
615 | } | ||
616 | return ret; | ||
617 | 587 | ||
618 | err: | ||
619 | kfree(i2c); | ||
620 | return ret; | 588 | return ret; |
621 | } | 589 | } |
622 | 590 | ||
623 | static int wm8731_i2c_detach(struct i2c_client *client) | 591 | static int wm8731_i2c_remove(struct i2c_client *client) |
624 | { | 592 | { |
625 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | 593 | struct snd_soc_codec *codec = i2c_get_clientdata(client); |
626 | i2c_detach_client(client); | ||
627 | kfree(codec->reg_cache); | 594 | kfree(codec->reg_cache); |
628 | kfree(client); | ||
629 | return 0; | 595 | return 0; |
630 | } | 596 | } |
631 | 597 | ||
632 | static int wm8731_i2c_attach(struct i2c_adapter *adap) | 598 | static const struct i2c_device_id wm8731_i2c_id[] = { |
633 | { | 599 | { "wm8731", 0 }, |
634 | return i2c_probe(adap, &addr_data, wm8731_codec_probe); | 600 | { } |
635 | } | 601 | }; |
602 | MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id); | ||
636 | 603 | ||
637 | /* corgi i2c codec control layer */ | ||
638 | static struct i2c_driver wm8731_i2c_driver = { | 604 | static struct i2c_driver wm8731_i2c_driver = { |
639 | .driver = { | 605 | .driver = { |
640 | .name = "WM8731 I2C Codec", | 606 | .name = "WM8731 I2C Codec", |
641 | .owner = THIS_MODULE, | 607 | .owner = THIS_MODULE, |
642 | }, | 608 | }, |
643 | .id = I2C_DRIVERID_WM8731, | 609 | .probe = wm8731_i2c_probe, |
644 | .attach_adapter = wm8731_i2c_attach, | 610 | .remove = wm8731_i2c_remove, |
645 | .detach_client = wm8731_i2c_detach, | 611 | .id_table = wm8731_i2c_id, |
646 | .command = NULL, | ||
647 | }; | 612 | }; |
648 | 613 | ||
649 | static struct i2c_client client_template = { | 614 | static int wm8731_add_i2c_device(struct platform_device *pdev, |
650 | .name = "WM8731", | 615 | const struct wm8731_setup_data *setup) |
651 | .driver = &wm8731_i2c_driver, | 616 | { |
652 | }; | 617 | struct i2c_board_info info; |
618 | struct i2c_adapter *adapter; | ||
619 | struct i2c_client *client; | ||
620 | int ret; | ||
621 | |||
622 | ret = i2c_add_driver(&wm8731_i2c_driver); | ||
623 | if (ret != 0) { | ||
624 | dev_err(&pdev->dev, "can't add i2c driver\n"); | ||
625 | return ret; | ||
626 | } | ||
627 | |||
628 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
629 | info.addr = setup->i2c_address; | ||
630 | strlcpy(info.type, "wm8731", I2C_NAME_SIZE); | ||
631 | |||
632 | adapter = i2c_get_adapter(setup->i2c_bus); | ||
633 | if (!adapter) { | ||
634 | dev_err(&pdev->dev, "can't get i2c adapter %d\n", | ||
635 | setup->i2c_bus); | ||
636 | goto err_driver; | ||
637 | } | ||
638 | |||
639 | client = i2c_new_device(adapter, &info); | ||
640 | i2c_put_adapter(adapter); | ||
641 | if (!client) { | ||
642 | dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", | ||
643 | (unsigned int)info.addr); | ||
644 | goto err_driver; | ||
645 | } | ||
646 | |||
647 | return 0; | ||
648 | |||
649 | err_driver: | ||
650 | i2c_del_driver(&wm8731_i2c_driver); | ||
651 | return -ENODEV; | ||
652 | } | ||
653 | #endif | 653 | #endif |
654 | 654 | ||
655 | #if defined(CONFIG_SPI_MASTER) | ||
656 | static int __devinit wm8731_spi_probe(struct spi_device *spi) | ||
657 | { | ||
658 | struct snd_soc_device *socdev = wm8731_socdev; | ||
659 | struct snd_soc_codec *codec = socdev->codec; | ||
660 | int ret; | ||
661 | |||
662 | codec->control_data = spi; | ||
663 | |||
664 | ret = wm8731_init(socdev); | ||
665 | if (ret < 0) | ||
666 | dev_err(&spi->dev, "failed to initialise WM8731\n"); | ||
667 | |||
668 | return ret; | ||
669 | } | ||
670 | |||
671 | static int __devexit wm8731_spi_remove(struct spi_device *spi) | ||
672 | { | ||
673 | return 0; | ||
674 | } | ||
675 | |||
676 | static struct spi_driver wm8731_spi_driver = { | ||
677 | .driver = { | ||
678 | .name = "wm8731", | ||
679 | .bus = &spi_bus_type, | ||
680 | .owner = THIS_MODULE, | ||
681 | }, | ||
682 | .probe = wm8731_spi_probe, | ||
683 | .remove = __devexit_p(wm8731_spi_remove), | ||
684 | }; | ||
685 | |||
686 | static int wm8731_spi_write(struct spi_device *spi, const char *data, int len) | ||
687 | { | ||
688 | struct spi_transfer t; | ||
689 | struct spi_message m; | ||
690 | u8 msg[2]; | ||
691 | |||
692 | if (len <= 0) | ||
693 | return 0; | ||
694 | |||
695 | msg[0] = data[0]; | ||
696 | msg[1] = data[1]; | ||
697 | |||
698 | spi_message_init(&m); | ||
699 | memset(&t, 0, (sizeof t)); | ||
700 | |||
701 | t.tx_buf = &msg[0]; | ||
702 | t.len = len; | ||
703 | |||
704 | spi_message_add_tail(&t, &m); | ||
705 | spi_sync(spi, &m); | ||
706 | |||
707 | return len; | ||
708 | } | ||
709 | #endif /* CONFIG_SPI_MASTER */ | ||
710 | |||
655 | static int wm8731_probe(struct platform_device *pdev) | 711 | static int wm8731_probe(struct platform_device *pdev) |
656 | { | 712 | { |
657 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 713 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
@@ -680,16 +736,21 @@ static int wm8731_probe(struct platform_device *pdev) | |||
680 | INIT_LIST_HEAD(&codec->dapm_paths); | 736 | INIT_LIST_HEAD(&codec->dapm_paths); |
681 | 737 | ||
682 | wm8731_socdev = socdev; | 738 | wm8731_socdev = socdev; |
739 | ret = -ENODEV; | ||
740 | |||
683 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 741 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
684 | if (setup->i2c_address) { | 742 | if (setup->i2c_address) { |
685 | normal_i2c[0] = setup->i2c_address; | ||
686 | codec->hw_write = (hw_write_t)i2c_master_send; | 743 | codec->hw_write = (hw_write_t)i2c_master_send; |
687 | ret = i2c_add_driver(&wm8731_i2c_driver); | 744 | ret = wm8731_add_i2c_device(pdev, setup); |
745 | } | ||
746 | #endif | ||
747 | #if defined(CONFIG_SPI_MASTER) | ||
748 | if (setup->spi) { | ||
749 | codec->hw_write = (hw_write_t)wm8731_spi_write; | ||
750 | ret = spi_register_driver(&wm8731_spi_driver); | ||
688 | if (ret != 0) | 751 | if (ret != 0) |
689 | printk(KERN_ERR "can't add i2c driver"); | 752 | printk(KERN_ERR "can't add spi driver"); |
690 | } | 753 | } |
691 | #else | ||
692 | /* Add other interfaces here */ | ||
693 | #endif | 754 | #endif |
694 | 755 | ||
695 | if (ret != 0) { | 756 | if (ret != 0) { |
@@ -711,8 +772,12 @@ static int wm8731_remove(struct platform_device *pdev) | |||
711 | snd_soc_free_pcms(socdev); | 772 | snd_soc_free_pcms(socdev); |
712 | snd_soc_dapm_free(socdev); | 773 | snd_soc_dapm_free(socdev); |
713 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 774 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
775 | i2c_unregister_device(codec->control_data); | ||
714 | i2c_del_driver(&wm8731_i2c_driver); | 776 | i2c_del_driver(&wm8731_i2c_driver); |
715 | #endif | 777 | #endif |
778 | #if defined(CONFIG_SPI_MASTER) | ||
779 | spi_unregister_driver(&wm8731_spi_driver); | ||
780 | #endif | ||
716 | kfree(codec->private_data); | 781 | kfree(codec->private_data); |
717 | kfree(codec); | 782 | kfree(codec); |
718 | 783 | ||
diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h index 99f2e3c60e33..95190e9c0c14 100644 --- a/sound/soc/codecs/wm8731.h +++ b/sound/soc/codecs/wm8731.h | |||
@@ -35,6 +35,8 @@ | |||
35 | #define WM8731_DAI 0 | 35 | #define WM8731_DAI 0 |
36 | 36 | ||
37 | struct wm8731_setup_data { | 37 | struct wm8731_setup_data { |
38 | int spi; | ||
39 | int i2c_bus; | ||
38 | unsigned short i2c_address; | 40 | unsigned short i2c_address; |
39 | }; | 41 | }; |
40 | 42 | ||
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index dd1f55404b29..9b7296ee5b08 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/pm.h> | 19 | #include <linux/pm.h> |
20 | #include <linux/i2c.h> | 20 | #include <linux/i2c.h> |
21 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
22 | #include <linux/spi/spi.h> | ||
22 | #include <sound/core.h> | 23 | #include <sound/core.h> |
23 | #include <sound/pcm.h> | 24 | #include <sound/pcm.h> |
24 | #include <sound/pcm_params.h> | 25 | #include <sound/pcm_params.h> |
@@ -28,7 +29,6 @@ | |||
28 | 29 | ||
29 | #include "wm8750.h" | 30 | #include "wm8750.h" |
30 | 31 | ||
31 | #define AUDIO_NAME "WM8750" | ||
32 | #define WM8750_VERSION "0.12" | 32 | #define WM8750_VERSION "0.12" |
33 | 33 | ||
34 | /* codec private data */ | 34 | /* codec private data */ |
@@ -841,88 +841,147 @@ static struct snd_soc_device *wm8750_socdev; | |||
841 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 841 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
842 | 842 | ||
843 | /* | 843 | /* |
844 | * WM8731 2 wire address is determined by GPIO5 | 844 | * WM8750 2 wire address is determined by GPIO5 |
845 | * state during powerup. | 845 | * state during powerup. |
846 | * low = 0x1a | 846 | * low = 0x1a |
847 | * high = 0x1b | 847 | * high = 0x1b |
848 | */ | 848 | */ |
849 | static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; | ||
850 | 849 | ||
851 | /* Magic definition of all other variables and things */ | 850 | static int wm8750_i2c_probe(struct i2c_client *i2c, |
852 | I2C_CLIENT_INSMOD; | 851 | const struct i2c_device_id *id) |
853 | |||
854 | static struct i2c_driver wm8750_i2c_driver; | ||
855 | static struct i2c_client client_template; | ||
856 | |||
857 | static int wm8750_codec_probe(struct i2c_adapter *adap, int addr, int kind) | ||
858 | { | 852 | { |
859 | struct snd_soc_device *socdev = wm8750_socdev; | 853 | struct snd_soc_device *socdev = wm8750_socdev; |
860 | struct wm8750_setup_data *setup = socdev->codec_data; | ||
861 | struct snd_soc_codec *codec = socdev->codec; | 854 | struct snd_soc_codec *codec = socdev->codec; |
862 | struct i2c_client *i2c; | ||
863 | int ret; | 855 | int ret; |
864 | 856 | ||
865 | if (addr != setup->i2c_address) | ||
866 | return -ENODEV; | ||
867 | |||
868 | client_template.adapter = adap; | ||
869 | client_template.addr = addr; | ||
870 | |||
871 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | ||
872 | if (i2c == NULL) | ||
873 | return -ENOMEM; | ||
874 | |||
875 | i2c_set_clientdata(i2c, codec); | 857 | i2c_set_clientdata(i2c, codec); |
876 | codec->control_data = i2c; | 858 | codec->control_data = i2c; |
877 | 859 | ||
878 | ret = i2c_attach_client(i2c); | ||
879 | if (ret < 0) { | ||
880 | pr_err("failed to attach codec at addr %x\n", addr); | ||
881 | goto err; | ||
882 | } | ||
883 | |||
884 | ret = wm8750_init(socdev); | 860 | ret = wm8750_init(socdev); |
885 | if (ret < 0) { | 861 | if (ret < 0) |
886 | pr_err("failed to initialise WM8750\n"); | 862 | pr_err("failed to initialise WM8750\n"); |
887 | goto err; | ||
888 | } | ||
889 | return ret; | ||
890 | 863 | ||
891 | err: | ||
892 | kfree(i2c); | ||
893 | return ret; | 864 | return ret; |
894 | } | 865 | } |
895 | 866 | ||
896 | static int wm8750_i2c_detach(struct i2c_client *client) | 867 | static int wm8750_i2c_remove(struct i2c_client *client) |
897 | { | 868 | { |
898 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | 869 | struct snd_soc_codec *codec = i2c_get_clientdata(client); |
899 | i2c_detach_client(client); | ||
900 | kfree(codec->reg_cache); | 870 | kfree(codec->reg_cache); |
901 | kfree(client); | ||
902 | return 0; | 871 | return 0; |
903 | } | 872 | } |
904 | 873 | ||
905 | static int wm8750_i2c_attach(struct i2c_adapter *adap) | 874 | static const struct i2c_device_id wm8750_i2c_id[] = { |
906 | { | 875 | { "wm8750", 0 }, |
907 | return i2c_probe(adap, &addr_data, wm8750_codec_probe); | 876 | { } |
908 | } | 877 | }; |
878 | MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id); | ||
909 | 879 | ||
910 | /* corgi i2c codec control layer */ | ||
911 | static struct i2c_driver wm8750_i2c_driver = { | 880 | static struct i2c_driver wm8750_i2c_driver = { |
912 | .driver = { | 881 | .driver = { |
913 | .name = "WM8750 I2C Codec", | 882 | .name = "WM8750 I2C Codec", |
914 | .owner = THIS_MODULE, | 883 | .owner = THIS_MODULE, |
915 | }, | 884 | }, |
916 | .id = I2C_DRIVERID_WM8750, | 885 | .probe = wm8750_i2c_probe, |
917 | .attach_adapter = wm8750_i2c_attach, | 886 | .remove = wm8750_i2c_remove, |
918 | .detach_client = wm8750_i2c_detach, | 887 | .id_table = wm8750_i2c_id, |
919 | .command = NULL, | ||
920 | }; | 888 | }; |
921 | 889 | ||
922 | static struct i2c_client client_template = { | 890 | static int wm8750_add_i2c_device(struct platform_device *pdev, |
923 | .name = "WM8750", | 891 | const struct wm8750_setup_data *setup) |
924 | .driver = &wm8750_i2c_driver, | 892 | { |
893 | struct i2c_board_info info; | ||
894 | struct i2c_adapter *adapter; | ||
895 | struct i2c_client *client; | ||
896 | int ret; | ||
897 | |||
898 | ret = i2c_add_driver(&wm8750_i2c_driver); | ||
899 | if (ret != 0) { | ||
900 | dev_err(&pdev->dev, "can't add i2c driver\n"); | ||
901 | return ret; | ||
902 | } | ||
903 | |||
904 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
905 | info.addr = setup->i2c_address; | ||
906 | strlcpy(info.type, "wm8750", I2C_NAME_SIZE); | ||
907 | |||
908 | adapter = i2c_get_adapter(setup->i2c_bus); | ||
909 | if (!adapter) { | ||
910 | dev_err(&pdev->dev, "can't get i2c adapter %d\n", | ||
911 | setup->i2c_bus); | ||
912 | goto err_driver; | ||
913 | } | ||
914 | |||
915 | client = i2c_new_device(adapter, &info); | ||
916 | i2c_put_adapter(adapter); | ||
917 | if (!client) { | ||
918 | dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", | ||
919 | (unsigned int)info.addr); | ||
920 | goto err_driver; | ||
921 | } | ||
922 | |||
923 | return 0; | ||
924 | |||
925 | err_driver: | ||
926 | i2c_del_driver(&wm8750_i2c_driver); | ||
927 | return -ENODEV; | ||
928 | } | ||
929 | #endif | ||
930 | |||
931 | #if defined(CONFIG_SPI_MASTER) | ||
932 | static int __devinit wm8750_spi_probe(struct spi_device *spi) | ||
933 | { | ||
934 | struct snd_soc_device *socdev = wm8750_socdev; | ||
935 | struct snd_soc_codec *codec = socdev->codec; | ||
936 | int ret; | ||
937 | |||
938 | codec->control_data = spi; | ||
939 | |||
940 | ret = wm8750_init(socdev); | ||
941 | if (ret < 0) | ||
942 | dev_err(&spi->dev, "failed to initialise WM8750\n"); | ||
943 | |||
944 | return ret; | ||
945 | } | ||
946 | |||
947 | static int __devexit wm8750_spi_remove(struct spi_device *spi) | ||
948 | { | ||
949 | return 0; | ||
950 | } | ||
951 | |||
952 | static struct spi_driver wm8750_spi_driver = { | ||
953 | .driver = { | ||
954 | .name = "wm8750", | ||
955 | .bus = &spi_bus_type, | ||
956 | .owner = THIS_MODULE, | ||
957 | }, | ||
958 | .probe = wm8750_spi_probe, | ||
959 | .remove = __devexit_p(wm8750_spi_remove), | ||
925 | }; | 960 | }; |
961 | |||
962 | static int wm8750_spi_write(struct spi_device *spi, const char *data, int len) | ||
963 | { | ||
964 | struct spi_transfer t; | ||
965 | struct spi_message m; | ||
966 | u8 msg[2]; | ||
967 | |||
968 | if (len <= 0) | ||
969 | return 0; | ||
970 | |||
971 | msg[0] = data[0]; | ||
972 | msg[1] = data[1]; | ||
973 | |||
974 | spi_message_init(&m); | ||
975 | memset(&t, 0, (sizeof t)); | ||
976 | |||
977 | t.tx_buf = &msg[0]; | ||
978 | t.len = len; | ||
979 | |||
980 | spi_message_add_tail(&t, &m); | ||
981 | spi_sync(spi, &m); | ||
982 | |||
983 | return len; | ||
984 | } | ||
926 | #endif | 985 | #endif |
927 | 986 | ||
928 | static int wm8750_probe(struct platform_device *pdev) | 987 | static int wm8750_probe(struct platform_device *pdev) |
@@ -931,7 +990,7 @@ static int wm8750_probe(struct platform_device *pdev) | |||
931 | struct wm8750_setup_data *setup = socdev->codec_data; | 990 | struct wm8750_setup_data *setup = socdev->codec_data; |
932 | struct snd_soc_codec *codec; | 991 | struct snd_soc_codec *codec; |
933 | struct wm8750_priv *wm8750; | 992 | struct wm8750_priv *wm8750; |
934 | int ret = 0; | 993 | int ret; |
935 | 994 | ||
936 | pr_info("WM8750 Audio Codec %s", WM8750_VERSION); | 995 | pr_info("WM8750 Audio Codec %s", WM8750_VERSION); |
937 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | 996 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); |
@@ -952,16 +1011,21 @@ static int wm8750_probe(struct platform_device *pdev) | |||
952 | wm8750_socdev = socdev; | 1011 | wm8750_socdev = socdev; |
953 | INIT_DELAYED_WORK(&codec->delayed_work, wm8750_work); | 1012 | INIT_DELAYED_WORK(&codec->delayed_work, wm8750_work); |
954 | 1013 | ||
1014 | ret = -ENODEV; | ||
1015 | |||
955 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 1016 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
956 | if (setup->i2c_address) { | 1017 | if (setup->i2c_address) { |
957 | normal_i2c[0] = setup->i2c_address; | ||
958 | codec->hw_write = (hw_write_t)i2c_master_send; | 1018 | codec->hw_write = (hw_write_t)i2c_master_send; |
959 | ret = i2c_add_driver(&wm8750_i2c_driver); | 1019 | ret = wm8750_add_i2c_device(pdev, setup); |
1020 | } | ||
1021 | #endif | ||
1022 | #if defined(CONFIG_SPI_MASTER) | ||
1023 | if (setup->spi) { | ||
1024 | codec->hw_write = (hw_write_t)wm8750_spi_write; | ||
1025 | ret = spi_register_driver(&wm8750_spi_driver); | ||
960 | if (ret != 0) | 1026 | if (ret != 0) |
961 | printk(KERN_ERR "can't add i2c driver"); | 1027 | printk(KERN_ERR "can't add spi driver"); |
962 | } | 1028 | } |
963 | #else | ||
964 | /* Add other interfaces here */ | ||
965 | #endif | 1029 | #endif |
966 | 1030 | ||
967 | if (ret != 0) { | 1031 | if (ret != 0) { |
@@ -1002,8 +1066,12 @@ static int wm8750_remove(struct platform_device *pdev) | |||
1002 | snd_soc_free_pcms(socdev); | 1066 | snd_soc_free_pcms(socdev); |
1003 | snd_soc_dapm_free(socdev); | 1067 | snd_soc_dapm_free(socdev); |
1004 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 1068 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
1069 | i2c_unregister_device(codec->control_data); | ||
1005 | i2c_del_driver(&wm8750_i2c_driver); | 1070 | i2c_del_driver(&wm8750_i2c_driver); |
1006 | #endif | 1071 | #endif |
1072 | #if defined(CONFIG_SPI_MASTER) | ||
1073 | spi_unregister_driver(&wm8750_spi_driver); | ||
1074 | #endif | ||
1007 | kfree(codec->private_data); | 1075 | kfree(codec->private_data); |
1008 | kfree(codec); | 1076 | kfree(codec); |
1009 | 1077 | ||
diff --git a/sound/soc/codecs/wm8750.h b/sound/soc/codecs/wm8750.h index 8ef30e628b21..1dc100e19cfe 100644 --- a/sound/soc/codecs/wm8750.h +++ b/sound/soc/codecs/wm8750.h | |||
@@ -58,6 +58,8 @@ | |||
58 | #define WM8750_SYSCLK 0 | 58 | #define WM8750_SYSCLK 0 |
59 | 59 | ||
60 | struct wm8750_setup_data { | 60 | struct wm8750_setup_data { |
61 | int spi; | ||
62 | int i2c_bus; | ||
61 | unsigned short i2c_address; | 63 | unsigned short i2c_address; |
62 | }; | 64 | }; |
63 | 65 | ||
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 5761164fe16d..d426eaa22185 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c | |||
@@ -2,8 +2,7 @@ | |||
2 | * wm8753.c -- WM8753 ALSA Soc Audio driver | 2 | * wm8753.c -- WM8753 ALSA Soc Audio driver |
3 | * | 3 | * |
4 | * Copyright 2003 Wolfson Microelectronics PLC. | 4 | * Copyright 2003 Wolfson Microelectronics PLC. |
5 | * Author: Liam Girdwood | 5 | * Author: Liam Girdwood <lrg@slimlogic.co.uk> |
6 | * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com | ||
7 | * | 6 | * |
8 | * This program is free software; you can redistribute it and/or modify it | 7 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the | 8 | * under the terms of the GNU General Public License as published by the |
@@ -40,6 +39,7 @@ | |||
40 | #include <linux/pm.h> | 39 | #include <linux/pm.h> |
41 | #include <linux/i2c.h> | 40 | #include <linux/i2c.h> |
42 | #include <linux/platform_device.h> | 41 | #include <linux/platform_device.h> |
42 | #include <linux/spi/spi.h> | ||
43 | #include <sound/core.h> | 43 | #include <sound/core.h> |
44 | #include <sound/pcm.h> | 44 | #include <sound/pcm.h> |
45 | #include <sound/pcm_params.h> | 45 | #include <sound/pcm_params.h> |
@@ -51,7 +51,6 @@ | |||
51 | 51 | ||
52 | #include "wm8753.h" | 52 | #include "wm8753.h" |
53 | 53 | ||
54 | #define AUDIO_NAME "wm8753" | ||
55 | #define WM8753_VERSION "0.16" | 54 | #define WM8753_VERSION "0.16" |
56 | 55 | ||
57 | static int caps_charge = 2000; | 56 | static int caps_charge = 2000; |
@@ -583,7 +582,7 @@ static const struct snd_soc_dapm_route audio_map[] = { | |||
583 | 582 | ||
584 | /* out 4 */ | 583 | /* out 4 */ |
585 | {"Out4 Mux", "VREF", "VREF"}, | 584 | {"Out4 Mux", "VREF", "VREF"}, |
586 | {"Out4 Mux", "Capture ST", "Capture ST Mixer"}, | 585 | {"Out4 Mux", "Capture ST", "Playback Mixer"}, |
587 | {"Out4 Mux", "LOUT2", "LOUT2"}, | 586 | {"Out4 Mux", "LOUT2", "LOUT2"}, |
588 | {"Out 4", NULL, "Out4 Mux"}, | 587 | {"Out 4", NULL, "Out4 Mux"}, |
589 | {"OUT4", NULL, "Out 4"}, | 588 | {"OUT4", NULL, "Out 4"}, |
@@ -607,7 +606,7 @@ static const struct snd_soc_dapm_route audio_map[] = { | |||
607 | /* Capture Right Mux */ | 606 | /* Capture Right Mux */ |
608 | {"Capture Right Mux", "PGA", "Right Capture Volume"}, | 607 | {"Capture Right Mux", "PGA", "Right Capture Volume"}, |
609 | {"Capture Right Mux", "Line or RXP-RXN", "Line Right Mux"}, | 608 | {"Capture Right Mux", "Line or RXP-RXN", "Line Right Mux"}, |
610 | {"Capture Right Mux", "Sidetone", "Capture ST Mixer"}, | 609 | {"Capture Right Mux", "Sidetone", "Playback Mixer"}, |
611 | 610 | ||
612 | /* Mono Capture mixer-mux */ | 611 | /* Mono Capture mixer-mux */ |
613 | {"Capture Right Mixer", "Stereo", "Capture Right Mux"}, | 612 | {"Capture Right Mixer", "Stereo", "Capture Right Mux"}, |
@@ -1637,86 +1636,145 @@ static struct snd_soc_device *wm8753_socdev; | |||
1637 | * low = 0x1a | 1636 | * low = 0x1a |
1638 | * high = 0x1b | 1637 | * high = 0x1b |
1639 | */ | 1638 | */ |
1640 | static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; | ||
1641 | 1639 | ||
1642 | /* Magic definition of all other variables and things */ | 1640 | static int wm8753_i2c_probe(struct i2c_client *i2c, |
1643 | I2C_CLIENT_INSMOD; | 1641 | const struct i2c_device_id *id) |
1644 | |||
1645 | static struct i2c_driver wm8753_i2c_driver; | ||
1646 | static struct i2c_client client_template; | ||
1647 | |||
1648 | static int wm8753_codec_probe(struct i2c_adapter *adap, int addr, int kind) | ||
1649 | { | 1642 | { |
1650 | struct snd_soc_device *socdev = wm8753_socdev; | 1643 | struct snd_soc_device *socdev = wm8753_socdev; |
1651 | struct wm8753_setup_data *setup = socdev->codec_data; | ||
1652 | struct snd_soc_codec *codec = socdev->codec; | 1644 | struct snd_soc_codec *codec = socdev->codec; |
1653 | struct i2c_client *i2c; | ||
1654 | int ret; | 1645 | int ret; |
1655 | 1646 | ||
1656 | if (addr != setup->i2c_address) | ||
1657 | return -ENODEV; | ||
1658 | |||
1659 | client_template.adapter = adap; | ||
1660 | client_template.addr = addr; | ||
1661 | |||
1662 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | ||
1663 | if (!i2c) | ||
1664 | return -ENOMEM; | ||
1665 | |||
1666 | i2c_set_clientdata(i2c, codec); | 1647 | i2c_set_clientdata(i2c, codec); |
1667 | codec->control_data = i2c; | 1648 | codec->control_data = i2c; |
1668 | 1649 | ||
1669 | ret = i2c_attach_client(i2c); | ||
1670 | if (ret < 0) { | ||
1671 | pr_err("failed to attach codec at addr %x\n", addr); | ||
1672 | goto err; | ||
1673 | } | ||
1674 | |||
1675 | ret = wm8753_init(socdev); | 1650 | ret = wm8753_init(socdev); |
1676 | if (ret < 0) { | 1651 | if (ret < 0) |
1677 | pr_err("failed to initialise WM8753\n"); | 1652 | pr_err("failed to initialise WM8753\n"); |
1678 | goto err; | ||
1679 | } | ||
1680 | |||
1681 | return ret; | ||
1682 | 1653 | ||
1683 | err: | ||
1684 | kfree(i2c); | ||
1685 | return ret; | 1654 | return ret; |
1686 | } | 1655 | } |
1687 | 1656 | ||
1688 | static int wm8753_i2c_detach(struct i2c_client *client) | 1657 | static int wm8753_i2c_remove(struct i2c_client *client) |
1689 | { | 1658 | { |
1690 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | 1659 | struct snd_soc_codec *codec = i2c_get_clientdata(client); |
1691 | i2c_detach_client(client); | ||
1692 | kfree(codec->reg_cache); | 1660 | kfree(codec->reg_cache); |
1693 | kfree(client); | ||
1694 | return 0; | 1661 | return 0; |
1695 | } | 1662 | } |
1696 | 1663 | ||
1697 | static int wm8753_i2c_attach(struct i2c_adapter *adap) | 1664 | static const struct i2c_device_id wm8753_i2c_id[] = { |
1698 | { | 1665 | { "wm8753", 0 }, |
1699 | return i2c_probe(adap, &addr_data, wm8753_codec_probe); | 1666 | { } |
1700 | } | 1667 | }; |
1668 | MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id); | ||
1701 | 1669 | ||
1702 | /* corgi i2c codec control layer */ | ||
1703 | static struct i2c_driver wm8753_i2c_driver = { | 1670 | static struct i2c_driver wm8753_i2c_driver = { |
1704 | .driver = { | 1671 | .driver = { |
1705 | .name = "WM8753 I2C Codec", | 1672 | .name = "WM8753 I2C Codec", |
1706 | .owner = THIS_MODULE, | 1673 | .owner = THIS_MODULE, |
1707 | }, | 1674 | }, |
1708 | .id = I2C_DRIVERID_WM8753, | 1675 | .probe = wm8753_i2c_probe, |
1709 | .attach_adapter = wm8753_i2c_attach, | 1676 | .remove = wm8753_i2c_remove, |
1710 | .detach_client = wm8753_i2c_detach, | 1677 | .id_table = wm8753_i2c_id, |
1711 | .command = NULL, | ||
1712 | }; | 1678 | }; |
1713 | 1679 | ||
1714 | static struct i2c_client client_template = { | 1680 | static int wm8753_add_i2c_device(struct platform_device *pdev, |
1715 | .name = "WM8753", | 1681 | const struct wm8753_setup_data *setup) |
1716 | .driver = &wm8753_i2c_driver, | 1682 | { |
1683 | struct i2c_board_info info; | ||
1684 | struct i2c_adapter *adapter; | ||
1685 | struct i2c_client *client; | ||
1686 | int ret; | ||
1687 | |||
1688 | ret = i2c_add_driver(&wm8753_i2c_driver); | ||
1689 | if (ret != 0) { | ||
1690 | dev_err(&pdev->dev, "can't add i2c driver\n"); | ||
1691 | return ret; | ||
1692 | } | ||
1693 | |||
1694 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
1695 | info.addr = setup->i2c_address; | ||
1696 | strlcpy(info.type, "wm8753", I2C_NAME_SIZE); | ||
1697 | |||
1698 | adapter = i2c_get_adapter(setup->i2c_bus); | ||
1699 | if (!adapter) { | ||
1700 | dev_err(&pdev->dev, "can't get i2c adapter %d\n", | ||
1701 | setup->i2c_bus); | ||
1702 | goto err_driver; | ||
1703 | } | ||
1704 | |||
1705 | client = i2c_new_device(adapter, &info); | ||
1706 | i2c_put_adapter(adapter); | ||
1707 | if (!client) { | ||
1708 | dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", | ||
1709 | (unsigned int)info.addr); | ||
1710 | goto err_driver; | ||
1711 | } | ||
1712 | |||
1713 | return 0; | ||
1714 | |||
1715 | err_driver: | ||
1716 | i2c_del_driver(&wm8753_i2c_driver); | ||
1717 | return -ENODEV; | ||
1718 | } | ||
1719 | #endif | ||
1720 | |||
1721 | #if defined(CONFIG_SPI_MASTER) | ||
1722 | static int __devinit wm8753_spi_probe(struct spi_device *spi) | ||
1723 | { | ||
1724 | struct snd_soc_device *socdev = wm8753_socdev; | ||
1725 | struct snd_soc_codec *codec = socdev->codec; | ||
1726 | int ret; | ||
1727 | |||
1728 | codec->control_data = spi; | ||
1729 | |||
1730 | ret = wm8753_init(socdev); | ||
1731 | if (ret < 0) | ||
1732 | dev_err(&spi->dev, "failed to initialise WM8753\n"); | ||
1733 | |||
1734 | return ret; | ||
1735 | } | ||
1736 | |||
1737 | static int __devexit wm8753_spi_remove(struct spi_device *spi) | ||
1738 | { | ||
1739 | return 0; | ||
1740 | } | ||
1741 | |||
1742 | static struct spi_driver wm8753_spi_driver = { | ||
1743 | .driver = { | ||
1744 | .name = "wm8753", | ||
1745 | .bus = &spi_bus_type, | ||
1746 | .owner = THIS_MODULE, | ||
1747 | }, | ||
1748 | .probe = wm8753_spi_probe, | ||
1749 | .remove = __devexit_p(wm8753_spi_remove), | ||
1717 | }; | 1750 | }; |
1751 | |||
1752 | static int wm8753_spi_write(struct spi_device *spi, const char *data, int len) | ||
1753 | { | ||
1754 | struct spi_transfer t; | ||
1755 | struct spi_message m; | ||
1756 | u8 msg[2]; | ||
1757 | |||
1758 | if (len <= 0) | ||
1759 | return 0; | ||
1760 | |||
1761 | msg[0] = data[0]; | ||
1762 | msg[1] = data[1]; | ||
1763 | |||
1764 | spi_message_init(&m); | ||
1765 | memset(&t, 0, (sizeof t)); | ||
1766 | |||
1767 | t.tx_buf = &msg[0]; | ||
1768 | t.len = len; | ||
1769 | |||
1770 | spi_message_add_tail(&t, &m); | ||
1771 | spi_sync(spi, &m); | ||
1772 | |||
1773 | return len; | ||
1774 | } | ||
1718 | #endif | 1775 | #endif |
1719 | 1776 | ||
1777 | |||
1720 | static int wm8753_probe(struct platform_device *pdev) | 1778 | static int wm8753_probe(struct platform_device *pdev) |
1721 | { | 1779 | { |
1722 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 1780 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
@@ -1748,14 +1806,17 @@ static int wm8753_probe(struct platform_device *pdev) | |||
1748 | 1806 | ||
1749 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 1807 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
1750 | if (setup->i2c_address) { | 1808 | if (setup->i2c_address) { |
1751 | normal_i2c[0] = setup->i2c_address; | ||
1752 | codec->hw_write = (hw_write_t)i2c_master_send; | 1809 | codec->hw_write = (hw_write_t)i2c_master_send; |
1753 | ret = i2c_add_driver(&wm8753_i2c_driver); | 1810 | ret = wm8753_add_i2c_device(pdev, setup); |
1811 | } | ||
1812 | #endif | ||
1813 | #if defined(CONFIG_SPI_MASTER) | ||
1814 | if (setup->spi) { | ||
1815 | codec->hw_write = (hw_write_t)wm8753_spi_write; | ||
1816 | ret = spi_register_driver(&wm8753_spi_driver); | ||
1754 | if (ret != 0) | 1817 | if (ret != 0) |
1755 | printk(KERN_ERR "can't add i2c driver"); | 1818 | printk(KERN_ERR "can't add spi driver"); |
1756 | } | 1819 | } |
1757 | #else | ||
1758 | /* Add other interfaces here */ | ||
1759 | #endif | 1820 | #endif |
1760 | 1821 | ||
1761 | if (ret != 0) { | 1822 | if (ret != 0) { |
@@ -1796,8 +1857,12 @@ static int wm8753_remove(struct platform_device *pdev) | |||
1796 | snd_soc_free_pcms(socdev); | 1857 | snd_soc_free_pcms(socdev); |
1797 | snd_soc_dapm_free(socdev); | 1858 | snd_soc_dapm_free(socdev); |
1798 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 1859 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
1860 | i2c_unregister_device(codec->control_data); | ||
1799 | i2c_del_driver(&wm8753_i2c_driver); | 1861 | i2c_del_driver(&wm8753_i2c_driver); |
1800 | #endif | 1862 | #endif |
1863 | #if defined(CONFIG_SPI_MASTER) | ||
1864 | spi_unregister_driver(&wm8753_spi_driver); | ||
1865 | #endif | ||
1801 | kfree(codec->private_data); | 1866 | kfree(codec->private_data); |
1802 | kfree(codec); | 1867 | kfree(codec); |
1803 | 1868 | ||
diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h index 44f5f1ff0cc7..f55704ce931b 100644 --- a/sound/soc/codecs/wm8753.h +++ b/sound/soc/codecs/wm8753.h | |||
@@ -2,8 +2,7 @@ | |||
2 | * wm8753.h -- audio driver for WM8753 | 2 | * wm8753.h -- audio driver for WM8753 |
3 | * | 3 | * |
4 | * Copyright 2003 Wolfson Microelectronics PLC. | 4 | * Copyright 2003 Wolfson Microelectronics PLC. |
5 | * Author: Liam Girdwood | 5 | * Author: Liam Girdwood <lrg@slimlogic.co.uk> |
6 | * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com | ||
7 | * | 6 | * |
8 | * This program is free software; you can redistribute it and/or modify it | 7 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the | 8 | * under the terms of the GNU General Public License as published by the |
@@ -79,6 +78,8 @@ | |||
79 | #define WM8753_ADCTL2 0x3f | 78 | #define WM8753_ADCTL2 0x3f |
80 | 79 | ||
81 | struct wm8753_setup_data { | 80 | struct wm8753_setup_data { |
81 | int spi; | ||
82 | int i2c_bus; | ||
82 | unsigned short i2c_address; | 83 | unsigned short i2c_address; |
83 | }; | 84 | }; |
84 | 85 | ||
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c new file mode 100644 index 000000000000..3b326c9b5586 --- /dev/null +++ b/sound/soc/codecs/wm8900.c | |||
@@ -0,0 +1,1541 @@ | |||
1 | /* | ||
2 | * wm8900.c -- WM8900 ALSA Soc Audio driver | ||
3 | * | ||
4 | * Copyright 2007, 2008 Wolfson Microelectronics PLC. | ||
5 | * | ||
6 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * TODO: | ||
13 | * - Tristating. | ||
14 | * - TDM. | ||
15 | * - Jack detect. | ||
16 | * - FLL source configuration, currently only MCLK is supported. | ||
17 | */ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/moduleparam.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/pm.h> | ||
25 | #include <linux/i2c.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/pcm.h> | ||
29 | #include <sound/pcm_params.h> | ||
30 | #include <sound/soc.h> | ||
31 | #include <sound/soc-dapm.h> | ||
32 | #include <sound/initval.h> | ||
33 | #include <sound/tlv.h> | ||
34 | |||
35 | #include "wm8900.h" | ||
36 | |||
37 | /* WM8900 register space */ | ||
38 | #define WM8900_REG_RESET 0x0 | ||
39 | #define WM8900_REG_ID 0x0 | ||
40 | #define WM8900_REG_POWER1 0x1 | ||
41 | #define WM8900_REG_POWER2 0x2 | ||
42 | #define WM8900_REG_POWER3 0x3 | ||
43 | #define WM8900_REG_AUDIO1 0x4 | ||
44 | #define WM8900_REG_AUDIO2 0x5 | ||
45 | #define WM8900_REG_CLOCKING1 0x6 | ||
46 | #define WM8900_REG_CLOCKING2 0x7 | ||
47 | #define WM8900_REG_AUDIO3 0x8 | ||
48 | #define WM8900_REG_AUDIO4 0x9 | ||
49 | #define WM8900_REG_DACCTRL 0xa | ||
50 | #define WM8900_REG_LDAC_DV 0xb | ||
51 | #define WM8900_REG_RDAC_DV 0xc | ||
52 | #define WM8900_REG_SIDETONE 0xd | ||
53 | #define WM8900_REG_ADCCTRL 0xe | ||
54 | #define WM8900_REG_LADC_DV 0xf | ||
55 | #define WM8900_REG_RADC_DV 0x10 | ||
56 | #define WM8900_REG_GPIO 0x12 | ||
57 | #define WM8900_REG_INCTL 0x15 | ||
58 | #define WM8900_REG_LINVOL 0x16 | ||
59 | #define WM8900_REG_RINVOL 0x17 | ||
60 | #define WM8900_REG_INBOOSTMIX1 0x18 | ||
61 | #define WM8900_REG_INBOOSTMIX2 0x19 | ||
62 | #define WM8900_REG_ADCPATH 0x1a | ||
63 | #define WM8900_REG_AUXBOOST 0x1b | ||
64 | #define WM8900_REG_ADDCTL 0x1e | ||
65 | #define WM8900_REG_FLLCTL1 0x24 | ||
66 | #define WM8900_REG_FLLCTL2 0x25 | ||
67 | #define WM8900_REG_FLLCTL3 0x26 | ||
68 | #define WM8900_REG_FLLCTL4 0x27 | ||
69 | #define WM8900_REG_FLLCTL5 0x28 | ||
70 | #define WM8900_REG_FLLCTL6 0x29 | ||
71 | #define WM8900_REG_LOUTMIXCTL1 0x2c | ||
72 | #define WM8900_REG_ROUTMIXCTL1 0x2d | ||
73 | #define WM8900_REG_BYPASS1 0x2e | ||
74 | #define WM8900_REG_BYPASS2 0x2f | ||
75 | #define WM8900_REG_AUXOUT_CTL 0x30 | ||
76 | #define WM8900_REG_LOUT1CTL 0x33 | ||
77 | #define WM8900_REG_ROUT1CTL 0x34 | ||
78 | #define WM8900_REG_LOUT2CTL 0x35 | ||
79 | #define WM8900_REG_ROUT2CTL 0x36 | ||
80 | #define WM8900_REG_HPCTL1 0x3a | ||
81 | #define WM8900_REG_OUTBIASCTL 0x73 | ||
82 | |||
83 | #define WM8900_MAXREG 0x80 | ||
84 | |||
85 | #define WM8900_REG_ADDCTL_OUT1_DIS 0x80 | ||
86 | #define WM8900_REG_ADDCTL_OUT2_DIS 0x40 | ||
87 | #define WM8900_REG_ADDCTL_VMID_DIS 0x20 | ||
88 | #define WM8900_REG_ADDCTL_BIAS_SRC 0x10 | ||
89 | #define WM8900_REG_ADDCTL_VMID_SOFTST 0x04 | ||
90 | #define WM8900_REG_ADDCTL_TEMP_SD 0x02 | ||
91 | |||
92 | #define WM8900_REG_GPIO_TEMP_ENA 0x2 | ||
93 | |||
94 | #define WM8900_REG_POWER1_STARTUP_BIAS_ENA 0x0100 | ||
95 | #define WM8900_REG_POWER1_BIAS_ENA 0x0008 | ||
96 | #define WM8900_REG_POWER1_VMID_BUF_ENA 0x0004 | ||
97 | #define WM8900_REG_POWER1_FLL_ENA 0x0040 | ||
98 | |||
99 | #define WM8900_REG_POWER2_SYSCLK_ENA 0x8000 | ||
100 | #define WM8900_REG_POWER2_ADCL_ENA 0x0002 | ||
101 | #define WM8900_REG_POWER2_ADCR_ENA 0x0001 | ||
102 | |||
103 | #define WM8900_REG_POWER3_DACL_ENA 0x0002 | ||
104 | #define WM8900_REG_POWER3_DACR_ENA 0x0001 | ||
105 | |||
106 | #define WM8900_REG_AUDIO1_AIF_FMT_MASK 0x0018 | ||
107 | #define WM8900_REG_AUDIO1_LRCLK_INV 0x0080 | ||
108 | #define WM8900_REG_AUDIO1_BCLK_INV 0x0100 | ||
109 | |||
110 | #define WM8900_REG_CLOCKING1_BCLK_DIR 0x1 | ||
111 | #define WM8900_REG_CLOCKING1_MCLK_SRC 0x100 | ||
112 | #define WM8900_REG_CLOCKING1_BCLK_MASK (~0x01e) | ||
113 | #define WM8900_REG_CLOCKING1_OPCLK_MASK (~0x7000) | ||
114 | |||
115 | #define WM8900_REG_CLOCKING2_ADC_CLKDIV 0xe0 | ||
116 | #define WM8900_REG_CLOCKING2_DAC_CLKDIV 0x1c | ||
117 | |||
118 | #define WM8900_REG_DACCTRL_MUTE 0x004 | ||
119 | #define WM8900_REG_DACCTRL_AIF_LRCLKRATE 0x400 | ||
120 | |||
121 | #define WM8900_REG_AUDIO3_ADCLRC_DIR 0x0800 | ||
122 | |||
123 | #define WM8900_REG_AUDIO4_DACLRC_DIR 0x0800 | ||
124 | |||
125 | #define WM8900_REG_FLLCTL1_OSC_ENA 0x100 | ||
126 | |||
127 | #define WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF 0x100 | ||
128 | |||
129 | #define WM8900_REG_HPCTL1_HP_IPSTAGE_ENA 0x80 | ||
130 | #define WM8900_REG_HPCTL1_HP_OPSTAGE_ENA 0x40 | ||
131 | #define WM8900_REG_HPCTL1_HP_CLAMP_IP 0x20 | ||
132 | #define WM8900_REG_HPCTL1_HP_CLAMP_OP 0x10 | ||
133 | #define WM8900_REG_HPCTL1_HP_SHORT 0x08 | ||
134 | #define WM8900_REG_HPCTL1_HP_SHORT2 0x04 | ||
135 | |||
136 | #define WM8900_LRC_MASK 0xfc00 | ||
137 | |||
138 | struct snd_soc_codec_device soc_codec_dev_wm8900; | ||
139 | |||
140 | struct wm8900_priv { | ||
141 | u32 fll_in; /* FLL input frequency */ | ||
142 | u32 fll_out; /* FLL output frequency */ | ||
143 | }; | ||
144 | |||
145 | /* | ||
146 | * wm8900 register cache. We can't read the entire register space and we | ||
147 | * have slow control buses so we cache the registers. | ||
148 | */ | ||
149 | static const u16 wm8900_reg_defaults[WM8900_MAXREG] = { | ||
150 | 0x8900, 0x0000, | ||
151 | 0xc000, 0x0000, | ||
152 | 0x4050, 0x4000, | ||
153 | 0x0008, 0x0000, | ||
154 | 0x0040, 0x0040, | ||
155 | 0x1004, 0x00c0, | ||
156 | 0x00c0, 0x0000, | ||
157 | 0x0100, 0x00c0, | ||
158 | 0x00c0, 0x0000, | ||
159 | 0xb001, 0x0000, | ||
160 | 0x0000, 0x0044, | ||
161 | 0x004c, 0x004c, | ||
162 | 0x0044, 0x0044, | ||
163 | 0x0000, 0x0044, | ||
164 | 0x0000, 0x0000, | ||
165 | 0x0002, 0x0000, | ||
166 | 0x0000, 0x0000, | ||
167 | 0x0000, 0x0000, | ||
168 | 0x0008, 0x0000, | ||
169 | 0x0000, 0x0008, | ||
170 | 0x0097, 0x0100, | ||
171 | 0x0000, 0x0000, | ||
172 | 0x0050, 0x0050, | ||
173 | 0x0055, 0x0055, | ||
174 | 0x0055, 0x0000, | ||
175 | 0x0000, 0x0079, | ||
176 | 0x0079, 0x0079, | ||
177 | 0x0079, 0x0000, | ||
178 | /* Remaining registers all zero */ | ||
179 | }; | ||
180 | |||
181 | /* | ||
182 | * read wm8900 register cache | ||
183 | */ | ||
184 | static inline unsigned int wm8900_read_reg_cache(struct snd_soc_codec *codec, | ||
185 | unsigned int reg) | ||
186 | { | ||
187 | u16 *cache = codec->reg_cache; | ||
188 | |||
189 | BUG_ON(reg >= WM8900_MAXREG); | ||
190 | |||
191 | if (reg == WM8900_REG_ID) | ||
192 | return 0; | ||
193 | |||
194 | return cache[reg]; | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * write wm8900 register cache | ||
199 | */ | ||
200 | static inline void wm8900_write_reg_cache(struct snd_soc_codec *codec, | ||
201 | u16 reg, unsigned int value) | ||
202 | { | ||
203 | u16 *cache = codec->reg_cache; | ||
204 | |||
205 | BUG_ON(reg >= WM8900_MAXREG); | ||
206 | |||
207 | cache[reg] = value; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * write to the WM8900 register space | ||
212 | */ | ||
213 | static int wm8900_write(struct snd_soc_codec *codec, unsigned int reg, | ||
214 | unsigned int value) | ||
215 | { | ||
216 | u8 data[3]; | ||
217 | |||
218 | if (value == wm8900_read_reg_cache(codec, reg)) | ||
219 | return 0; | ||
220 | |||
221 | /* data is | ||
222 | * D15..D9 WM8900 register offset | ||
223 | * D8...D0 register data | ||
224 | */ | ||
225 | data[0] = reg; | ||
226 | data[1] = value >> 8; | ||
227 | data[2] = value & 0x00ff; | ||
228 | |||
229 | wm8900_write_reg_cache(codec, reg, value); | ||
230 | if (codec->hw_write(codec->control_data, data, 3) == 3) | ||
231 | return 0; | ||
232 | else | ||
233 | return -EIO; | ||
234 | } | ||
235 | |||
236 | /* | ||
237 | * Read from the wm8900. | ||
238 | */ | ||
239 | static unsigned int wm8900_chip_read(struct snd_soc_codec *codec, u8 reg) | ||
240 | { | ||
241 | struct i2c_msg xfer[2]; | ||
242 | u16 data; | ||
243 | int ret; | ||
244 | struct i2c_client *client = codec->control_data; | ||
245 | |||
246 | BUG_ON(reg != WM8900_REG_ID && reg != WM8900_REG_POWER1); | ||
247 | |||
248 | /* Write register */ | ||
249 | xfer[0].addr = client->addr; | ||
250 | xfer[0].flags = 0; | ||
251 | xfer[0].len = 1; | ||
252 | xfer[0].buf = ® | ||
253 | |||
254 | /* Read data */ | ||
255 | xfer[1].addr = client->addr; | ||
256 | xfer[1].flags = I2C_M_RD; | ||
257 | xfer[1].len = 2; | ||
258 | xfer[1].buf = (u8 *)&data; | ||
259 | |||
260 | ret = i2c_transfer(client->adapter, xfer, 2); | ||
261 | if (ret != 2) { | ||
262 | printk(KERN_CRIT "i2c_transfer returned %d\n", ret); | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | return (data >> 8) | ((data & 0xff) << 8); | ||
267 | } | ||
268 | |||
269 | /* | ||
270 | * Read from the WM8900 register space. Most registers can't be read | ||
271 | * and are therefore supplied from cache. | ||
272 | */ | ||
273 | static unsigned int wm8900_read(struct snd_soc_codec *codec, unsigned int reg) | ||
274 | { | ||
275 | switch (reg) { | ||
276 | case WM8900_REG_ID: | ||
277 | return wm8900_chip_read(codec, reg); | ||
278 | default: | ||
279 | return wm8900_read_reg_cache(codec, reg); | ||
280 | } | ||
281 | } | ||
282 | |||
283 | static void wm8900_reset(struct snd_soc_codec *codec) | ||
284 | { | ||
285 | wm8900_write(codec, WM8900_REG_RESET, 0); | ||
286 | |||
287 | memcpy(codec->reg_cache, wm8900_reg_defaults, | ||
288 | sizeof(codec->reg_cache)); | ||
289 | } | ||
290 | |||
291 | static int wm8900_hp_event(struct snd_soc_dapm_widget *w, | ||
292 | struct snd_kcontrol *kcontrol, int event) | ||
293 | { | ||
294 | struct snd_soc_codec *codec = w->codec; | ||
295 | u16 hpctl1 = wm8900_read(codec, WM8900_REG_HPCTL1); | ||
296 | |||
297 | switch (event) { | ||
298 | case SND_SOC_DAPM_PRE_PMU: | ||
299 | /* Clamp headphone outputs */ | ||
300 | hpctl1 = WM8900_REG_HPCTL1_HP_CLAMP_IP | | ||
301 | WM8900_REG_HPCTL1_HP_CLAMP_OP; | ||
302 | wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); | ||
303 | break; | ||
304 | |||
305 | case SND_SOC_DAPM_POST_PMU: | ||
306 | /* Enable the input stage */ | ||
307 | hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_IP; | ||
308 | hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT | | ||
309 | WM8900_REG_HPCTL1_HP_SHORT2 | | ||
310 | WM8900_REG_HPCTL1_HP_IPSTAGE_ENA; | ||
311 | wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); | ||
312 | |||
313 | msleep(400); | ||
314 | |||
315 | /* Enable the output stage */ | ||
316 | hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_OP; | ||
317 | hpctl1 |= WM8900_REG_HPCTL1_HP_OPSTAGE_ENA; | ||
318 | wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); | ||
319 | |||
320 | /* Remove the shorts */ | ||
321 | hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT2; | ||
322 | wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); | ||
323 | hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT; | ||
324 | wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); | ||
325 | break; | ||
326 | |||
327 | case SND_SOC_DAPM_PRE_PMD: | ||
328 | /* Short the output */ | ||
329 | hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT; | ||
330 | wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); | ||
331 | |||
332 | /* Disable the output stage */ | ||
333 | hpctl1 &= ~WM8900_REG_HPCTL1_HP_OPSTAGE_ENA; | ||
334 | wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); | ||
335 | |||
336 | /* Clamp the outputs and power down input */ | ||
337 | hpctl1 |= WM8900_REG_HPCTL1_HP_CLAMP_IP | | ||
338 | WM8900_REG_HPCTL1_HP_CLAMP_OP; | ||
339 | hpctl1 &= ~WM8900_REG_HPCTL1_HP_IPSTAGE_ENA; | ||
340 | wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); | ||
341 | break; | ||
342 | |||
343 | case SND_SOC_DAPM_POST_PMD: | ||
344 | /* Disable everything */ | ||
345 | wm8900_write(codec, WM8900_REG_HPCTL1, 0); | ||
346 | break; | ||
347 | |||
348 | default: | ||
349 | BUG(); | ||
350 | } | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -5700, 100, 0); | ||
356 | |||
357 | static const DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 0); | ||
358 | |||
359 | static const DECLARE_TLV_DB_SCALE(in_boost_tlv, -1200, 600, 0); | ||
360 | |||
361 | static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1200, 100, 0); | ||
362 | |||
363 | static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0); | ||
364 | |||
365 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -7200, 75, 1); | ||
366 | |||
367 | static const DECLARE_TLV_DB_SCALE(adc_svol_tlv, -3600, 300, 0); | ||
368 | |||
369 | static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1); | ||
370 | |||
371 | static const char *mic_bias_level_txt[] = { "0.9*AVDD", "0.65*AVDD" }; | ||
372 | |||
373 | static const struct soc_enum mic_bias_level = | ||
374 | SOC_ENUM_SINGLE(WM8900_REG_INCTL, 8, 2, mic_bias_level_txt); | ||
375 | |||
376 | static const char *dac_mute_rate_txt[] = { "Fast", "Slow" }; | ||
377 | |||
378 | static const struct soc_enum dac_mute_rate = | ||
379 | SOC_ENUM_SINGLE(WM8900_REG_DACCTRL, 7, 2, dac_mute_rate_txt); | ||
380 | |||
381 | static const char *dac_deemphasis_txt[] = { | ||
382 | "Disabled", "32kHz", "44.1kHz", "48kHz" | ||
383 | }; | ||
384 | |||
385 | static const struct soc_enum dac_deemphasis = | ||
386 | SOC_ENUM_SINGLE(WM8900_REG_DACCTRL, 4, 4, dac_deemphasis_txt); | ||
387 | |||
388 | static const char *adc_hpf_cut_txt[] = { | ||
389 | "Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3" | ||
390 | }; | ||
391 | |||
392 | static const struct soc_enum adc_hpf_cut = | ||
393 | SOC_ENUM_SINGLE(WM8900_REG_ADCCTRL, 5, 4, adc_hpf_cut_txt); | ||
394 | |||
395 | static const char *lr_txt[] = { | ||
396 | "Left", "Right" | ||
397 | }; | ||
398 | |||
399 | static const struct soc_enum aifl_src = | ||
400 | SOC_ENUM_SINGLE(WM8900_REG_AUDIO1, 15, 2, lr_txt); | ||
401 | |||
402 | static const struct soc_enum aifr_src = | ||
403 | SOC_ENUM_SINGLE(WM8900_REG_AUDIO1, 14, 2, lr_txt); | ||
404 | |||
405 | static const struct soc_enum dacl_src = | ||
406 | SOC_ENUM_SINGLE(WM8900_REG_AUDIO2, 15, 2, lr_txt); | ||
407 | |||
408 | static const struct soc_enum dacr_src = | ||
409 | SOC_ENUM_SINGLE(WM8900_REG_AUDIO2, 14, 2, lr_txt); | ||
410 | |||
411 | static const char *sidetone_txt[] = { | ||
412 | "Disabled", "Left ADC", "Right ADC" | ||
413 | }; | ||
414 | |||
415 | static const struct soc_enum dacl_sidetone = | ||
416 | SOC_ENUM_SINGLE(WM8900_REG_SIDETONE, 2, 3, sidetone_txt); | ||
417 | |||
418 | static const struct soc_enum dacr_sidetone = | ||
419 | SOC_ENUM_SINGLE(WM8900_REG_SIDETONE, 0, 3, sidetone_txt); | ||
420 | |||
421 | static const struct snd_kcontrol_new wm8900_snd_controls[] = { | ||
422 | SOC_ENUM("Mic Bias Level", mic_bias_level), | ||
423 | |||
424 | SOC_SINGLE_TLV("Left Input PGA Volume", WM8900_REG_LINVOL, 0, 31, 0, | ||
425 | in_pga_tlv), | ||
426 | SOC_SINGLE("Left Input PGA Switch", WM8900_REG_LINVOL, 6, 1, 1), | ||
427 | SOC_SINGLE("Left Input PGA ZC Switch", WM8900_REG_LINVOL, 7, 1, 0), | ||
428 | |||
429 | SOC_SINGLE_TLV("Right Input PGA Volume", WM8900_REG_RINVOL, 0, 31, 0, | ||
430 | in_pga_tlv), | ||
431 | SOC_SINGLE("Right Input PGA Switch", WM8900_REG_RINVOL, 6, 1, 1), | ||
432 | SOC_SINGLE("Right Input PGA ZC Switch", WM8900_REG_RINVOL, 7, 1, 0), | ||
433 | |||
434 | SOC_SINGLE("DAC Soft Mute Switch", WM8900_REG_DACCTRL, 6, 1, 1), | ||
435 | SOC_ENUM("DAC Mute Rate", dac_mute_rate), | ||
436 | SOC_SINGLE("DAC Mono Switch", WM8900_REG_DACCTRL, 9, 1, 0), | ||
437 | SOC_ENUM("DAC Deemphasis", dac_deemphasis), | ||
438 | SOC_SINGLE("DAC Sloping Stopband Filter Switch", WM8900_REG_DACCTRL, 8, 1, 0), | ||
439 | SOC_SINGLE("DAC Sigma-Delta Modulator Clock Switch", WM8900_REG_DACCTRL, | ||
440 | 12, 1, 0), | ||
441 | |||
442 | SOC_SINGLE("ADC HPF Switch", WM8900_REG_ADCCTRL, 8, 1, 0), | ||
443 | SOC_ENUM("ADC HPF Cut-Off", adc_hpf_cut), | ||
444 | SOC_DOUBLE("ADC Invert Switch", WM8900_REG_ADCCTRL, 1, 0, 1, 0), | ||
445 | SOC_SINGLE_TLV("Left ADC Sidetone Volume", WM8900_REG_SIDETONE, 9, 12, 0, | ||
446 | adc_svol_tlv), | ||
447 | SOC_SINGLE_TLV("Right ADC Sidetone Volume", WM8900_REG_SIDETONE, 5, 12, 0, | ||
448 | adc_svol_tlv), | ||
449 | SOC_ENUM("Left Digital Audio Source", aifl_src), | ||
450 | SOC_ENUM("Right Digital Audio Source", aifr_src), | ||
451 | |||
452 | SOC_SINGLE_TLV("DAC Input Boost Volume", WM8900_REG_AUDIO2, 10, 4, 0, | ||
453 | dac_boost_tlv), | ||
454 | SOC_ENUM("Left DAC Source", dacl_src), | ||
455 | SOC_ENUM("Right DAC Source", dacr_src), | ||
456 | SOC_ENUM("Left DAC Sidetone", dacl_sidetone), | ||
457 | SOC_ENUM("Right DAC Sidetone", dacr_sidetone), | ||
458 | SOC_DOUBLE("DAC Invert Switch", WM8900_REG_DACCTRL, 1, 0, 1, 0), | ||
459 | |||
460 | SOC_DOUBLE_R_TLV("Digital Playback Volume", | ||
461 | WM8900_REG_LDAC_DV, WM8900_REG_RDAC_DV, | ||
462 | 1, 96, 0, dac_tlv), | ||
463 | SOC_DOUBLE_R_TLV("Digital Capture Volume", | ||
464 | WM8900_REG_LADC_DV, WM8900_REG_RADC_DV, 1, 119, 0, adc_tlv), | ||
465 | |||
466 | SOC_SINGLE_TLV("LINPUT3 Bypass Volume", WM8900_REG_LOUTMIXCTL1, 4, 7, 0, | ||
467 | out_mix_tlv), | ||
468 | SOC_SINGLE_TLV("RINPUT3 Bypass Volume", WM8900_REG_ROUTMIXCTL1, 4, 7, 0, | ||
469 | out_mix_tlv), | ||
470 | SOC_SINGLE_TLV("Left AUX Bypass Volume", WM8900_REG_AUXOUT_CTL, 4, 7, 0, | ||
471 | out_mix_tlv), | ||
472 | SOC_SINGLE_TLV("Right AUX Bypass Volume", WM8900_REG_AUXOUT_CTL, 0, 7, 0, | ||
473 | out_mix_tlv), | ||
474 | |||
475 | SOC_SINGLE_TLV("LeftIn to RightOut Mixer Volume", WM8900_REG_BYPASS1, 0, 7, 0, | ||
476 | out_mix_tlv), | ||
477 | SOC_SINGLE_TLV("LeftIn to LeftOut Mixer Volume", WM8900_REG_BYPASS1, 4, 7, 0, | ||
478 | out_mix_tlv), | ||
479 | SOC_SINGLE_TLV("RightIn to LeftOut Mixer Volume", WM8900_REG_BYPASS2, 0, 7, 0, | ||
480 | out_mix_tlv), | ||
481 | SOC_SINGLE_TLV("RightIn to RightOut Mixer Volume", WM8900_REG_BYPASS2, 4, 7, 0, | ||
482 | out_mix_tlv), | ||
483 | |||
484 | SOC_SINGLE_TLV("IN2L Boost Volume", WM8900_REG_INBOOSTMIX1, 0, 3, 0, | ||
485 | in_boost_tlv), | ||
486 | SOC_SINGLE_TLV("IN3L Boost Volume", WM8900_REG_INBOOSTMIX1, 4, 3, 0, | ||
487 | in_boost_tlv), | ||
488 | SOC_SINGLE_TLV("IN2R Boost Volume", WM8900_REG_INBOOSTMIX2, 0, 3, 0, | ||
489 | in_boost_tlv), | ||
490 | SOC_SINGLE_TLV("IN3R Boost Volume", WM8900_REG_INBOOSTMIX2, 4, 3, 0, | ||
491 | in_boost_tlv), | ||
492 | SOC_SINGLE_TLV("Left AUX Boost Volume", WM8900_REG_AUXBOOST, 4, 3, 0, | ||
493 | in_boost_tlv), | ||
494 | SOC_SINGLE_TLV("Right AUX Boost Volume", WM8900_REG_AUXBOOST, 0, 3, 0, | ||
495 | in_boost_tlv), | ||
496 | |||
497 | SOC_DOUBLE_R_TLV("LINEOUT1 Volume", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL, | ||
498 | 0, 63, 0, out_pga_tlv), | ||
499 | SOC_DOUBLE_R("LINEOUT1 Switch", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL, | ||
500 | 6, 1, 1), | ||
501 | SOC_DOUBLE_R("LINEOUT1 ZC Switch", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL, | ||
502 | 7, 1, 0), | ||
503 | |||
504 | SOC_DOUBLE_R_TLV("LINEOUT2 Volume", | ||
505 | WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, | ||
506 | 0, 63, 0, out_pga_tlv), | ||
507 | SOC_DOUBLE_R("LINEOUT2 Switch", | ||
508 | WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, 6, 1, 1), | ||
509 | SOC_DOUBLE_R("LINEOUT2 ZC Switch", | ||
510 | WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, 7, 1, 0), | ||
511 | SOC_SINGLE("LINEOUT2 LP -12dB", WM8900_REG_LOUTMIXCTL1, | ||
512 | 0, 1, 1), | ||
513 | |||
514 | }; | ||
515 | |||
516 | /* add non dapm controls */ | ||
517 | static int wm8900_add_controls(struct snd_soc_codec *codec) | ||
518 | { | ||
519 | int err, i; | ||
520 | |||
521 | for (i = 0; i < ARRAY_SIZE(wm8900_snd_controls); i++) { | ||
522 | err = snd_ctl_add(codec->card, | ||
523 | snd_soc_cnew(&wm8900_snd_controls[i], | ||
524 | codec, NULL)); | ||
525 | if (err < 0) | ||
526 | return err; | ||
527 | } | ||
528 | |||
529 | return 0; | ||
530 | } | ||
531 | |||
532 | static const struct snd_kcontrol_new wm8900_dapm_loutput2_control = | ||
533 | SOC_DAPM_SINGLE("LINEOUT2L Switch", WM8900_REG_POWER3, 6, 1, 0); | ||
534 | |||
535 | static const struct snd_kcontrol_new wm8900_dapm_routput2_control = | ||
536 | SOC_DAPM_SINGLE("LINEOUT2R Switch", WM8900_REG_POWER3, 5, 1, 0); | ||
537 | |||
538 | static const struct snd_kcontrol_new wm8900_loutmix_controls[] = { | ||
539 | SOC_DAPM_SINGLE("LINPUT3 Bypass Switch", WM8900_REG_LOUTMIXCTL1, 7, 1, 0), | ||
540 | SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 7, 1, 0), | ||
541 | SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 7, 1, 0), | ||
542 | SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 3, 1, 0), | ||
543 | SOC_DAPM_SINGLE("DACL Switch", WM8900_REG_LOUTMIXCTL1, 8, 1, 0), | ||
544 | }; | ||
545 | |||
546 | static const struct snd_kcontrol_new wm8900_routmix_controls[] = { | ||
547 | SOC_DAPM_SINGLE("RINPUT3 Bypass Switch", WM8900_REG_ROUTMIXCTL1, 7, 1, 0), | ||
548 | SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 3, 1, 0), | ||
549 | SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 3, 1, 0), | ||
550 | SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 7, 1, 0), | ||
551 | SOC_DAPM_SINGLE("DACR Switch", WM8900_REG_ROUTMIXCTL1, 8, 1, 0), | ||
552 | }; | ||
553 | |||
554 | static const struct snd_kcontrol_new wm8900_linmix_controls[] = { | ||
555 | SOC_DAPM_SINGLE("LINPUT2 Switch", WM8900_REG_INBOOSTMIX1, 2, 1, 1), | ||
556 | SOC_DAPM_SINGLE("LINPUT3 Switch", WM8900_REG_INBOOSTMIX1, 6, 1, 1), | ||
557 | SOC_DAPM_SINGLE("AUX Switch", WM8900_REG_AUXBOOST, 6, 1, 1), | ||
558 | SOC_DAPM_SINGLE("Input PGA Switch", WM8900_REG_ADCPATH, 6, 1, 0), | ||
559 | }; | ||
560 | |||
561 | static const struct snd_kcontrol_new wm8900_rinmix_controls[] = { | ||
562 | SOC_DAPM_SINGLE("RINPUT2 Switch", WM8900_REG_INBOOSTMIX2, 2, 1, 1), | ||
563 | SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INBOOSTMIX2, 6, 1, 1), | ||
564 | SOC_DAPM_SINGLE("AUX Switch", WM8900_REG_AUXBOOST, 2, 1, 1), | ||
565 | SOC_DAPM_SINGLE("Input PGA Switch", WM8900_REG_ADCPATH, 2, 1, 0), | ||
566 | }; | ||
567 | |||
568 | static const struct snd_kcontrol_new wm8900_linpga_controls[] = { | ||
569 | SOC_DAPM_SINGLE("LINPUT1 Switch", WM8900_REG_INCTL, 6, 1, 0), | ||
570 | SOC_DAPM_SINGLE("LINPUT2 Switch", WM8900_REG_INCTL, 5, 1, 0), | ||
571 | SOC_DAPM_SINGLE("LINPUT3 Switch", WM8900_REG_INCTL, 4, 1, 0), | ||
572 | }; | ||
573 | |||
574 | static const struct snd_kcontrol_new wm8900_rinpga_controls[] = { | ||
575 | SOC_DAPM_SINGLE("RINPUT1 Switch", WM8900_REG_INCTL, 2, 1, 0), | ||
576 | SOC_DAPM_SINGLE("RINPUT2 Switch", WM8900_REG_INCTL, 1, 1, 0), | ||
577 | SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INCTL, 0, 1, 0), | ||
578 | }; | ||
579 | |||
580 | static const char *wm9700_lp_mux[] = { "Disabled", "Enabled" }; | ||
581 | |||
582 | static const struct soc_enum wm8900_lineout2_lp_mux = | ||
583 | SOC_ENUM_SINGLE(WM8900_REG_LOUTMIXCTL1, 1, 2, wm9700_lp_mux); | ||
584 | |||
585 | static const struct snd_kcontrol_new wm8900_lineout2_lp = | ||
586 | SOC_DAPM_ENUM("Route", wm8900_lineout2_lp_mux); | ||
587 | |||
588 | static const struct snd_soc_dapm_widget wm8900_dapm_widgets[] = { | ||
589 | |||
590 | /* Externally visible pins */ | ||
591 | SND_SOC_DAPM_OUTPUT("LINEOUT1L"), | ||
592 | SND_SOC_DAPM_OUTPUT("LINEOUT1R"), | ||
593 | SND_SOC_DAPM_OUTPUT("LINEOUT2L"), | ||
594 | SND_SOC_DAPM_OUTPUT("LINEOUT2R"), | ||
595 | SND_SOC_DAPM_OUTPUT("HP_L"), | ||
596 | SND_SOC_DAPM_OUTPUT("HP_R"), | ||
597 | |||
598 | SND_SOC_DAPM_INPUT("RINPUT1"), | ||
599 | SND_SOC_DAPM_INPUT("LINPUT1"), | ||
600 | SND_SOC_DAPM_INPUT("RINPUT2"), | ||
601 | SND_SOC_DAPM_INPUT("LINPUT2"), | ||
602 | SND_SOC_DAPM_INPUT("RINPUT3"), | ||
603 | SND_SOC_DAPM_INPUT("LINPUT3"), | ||
604 | SND_SOC_DAPM_INPUT("AUX"), | ||
605 | |||
606 | SND_SOC_DAPM_VMID("VMID"), | ||
607 | |||
608 | /* Input */ | ||
609 | SND_SOC_DAPM_MIXER("Left Input PGA", WM8900_REG_POWER2, 3, 0, | ||
610 | wm8900_linpga_controls, | ||
611 | ARRAY_SIZE(wm8900_linpga_controls)), | ||
612 | SND_SOC_DAPM_MIXER("Right Input PGA", WM8900_REG_POWER2, 2, 0, | ||
613 | wm8900_rinpga_controls, | ||
614 | ARRAY_SIZE(wm8900_rinpga_controls)), | ||
615 | |||
616 | SND_SOC_DAPM_MIXER("Left Input Mixer", WM8900_REG_POWER2, 5, 0, | ||
617 | wm8900_linmix_controls, | ||
618 | ARRAY_SIZE(wm8900_linmix_controls)), | ||
619 | SND_SOC_DAPM_MIXER("Right Input Mixer", WM8900_REG_POWER2, 4, 0, | ||
620 | wm8900_rinmix_controls, | ||
621 | ARRAY_SIZE(wm8900_rinmix_controls)), | ||
622 | |||
623 | SND_SOC_DAPM_MICBIAS("Mic Bias", WM8900_REG_POWER1, 4, 0), | ||
624 | |||
625 | SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8900_REG_POWER2, 1, 0), | ||
626 | SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8900_REG_POWER2, 0, 0), | ||
627 | |||
628 | /* Output */ | ||
629 | SND_SOC_DAPM_DAC("DACL", "Left HiFi Playback", WM8900_REG_POWER3, 1, 0), | ||
630 | SND_SOC_DAPM_DAC("DACR", "Right HiFi Playback", WM8900_REG_POWER3, 0, 0), | ||
631 | |||
632 | SND_SOC_DAPM_PGA_E("Headphone Amplifier", WM8900_REG_POWER3, 7, 0, NULL, 0, | ||
633 | wm8900_hp_event, | ||
634 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | | ||
635 | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), | ||
636 | |||
637 | SND_SOC_DAPM_PGA("LINEOUT1L PGA", WM8900_REG_POWER2, 8, 0, NULL, 0), | ||
638 | SND_SOC_DAPM_PGA("LINEOUT1R PGA", WM8900_REG_POWER2, 7, 0, NULL, 0), | ||
639 | |||
640 | SND_SOC_DAPM_MUX("LINEOUT2 LP", SND_SOC_NOPM, 0, 0, &wm8900_lineout2_lp), | ||
641 | SND_SOC_DAPM_PGA("LINEOUT2L PGA", WM8900_REG_POWER3, 6, 0, NULL, 0), | ||
642 | SND_SOC_DAPM_PGA("LINEOUT2R PGA", WM8900_REG_POWER3, 5, 0, NULL, 0), | ||
643 | |||
644 | SND_SOC_DAPM_MIXER("Left Output Mixer", WM8900_REG_POWER3, 3, 0, | ||
645 | wm8900_loutmix_controls, | ||
646 | ARRAY_SIZE(wm8900_loutmix_controls)), | ||
647 | SND_SOC_DAPM_MIXER("Right Output Mixer", WM8900_REG_POWER3, 2, 0, | ||
648 | wm8900_routmix_controls, | ||
649 | ARRAY_SIZE(wm8900_routmix_controls)), | ||
650 | }; | ||
651 | |||
652 | /* Target, Path, Source */ | ||
653 | static const struct snd_soc_dapm_route audio_map[] = { | ||
654 | /* Inputs */ | ||
655 | {"Left Input PGA", "LINPUT1 Switch", "LINPUT1"}, | ||
656 | {"Left Input PGA", "LINPUT2 Switch", "LINPUT2"}, | ||
657 | {"Left Input PGA", "LINPUT3 Switch", "LINPUT3"}, | ||
658 | |||
659 | {"Right Input PGA", "RINPUT1 Switch", "RINPUT1"}, | ||
660 | {"Right Input PGA", "RINPUT2 Switch", "RINPUT2"}, | ||
661 | {"Right Input PGA", "RINPUT3 Switch", "RINPUT3"}, | ||
662 | |||
663 | {"Left Input Mixer", "LINPUT2 Switch", "LINPUT2"}, | ||
664 | {"Left Input Mixer", "LINPUT3 Switch", "LINPUT3"}, | ||
665 | {"Left Input Mixer", "AUX Switch", "AUX"}, | ||
666 | {"Left Input Mixer", "Input PGA Switch", "Left Input PGA"}, | ||
667 | |||
668 | {"Right Input Mixer", "RINPUT2 Switch", "RINPUT2"}, | ||
669 | {"Right Input Mixer", "RINPUT3 Switch", "RINPUT3"}, | ||
670 | {"Right Input Mixer", "AUX Switch", "AUX"}, | ||
671 | {"Right Input Mixer", "Input PGA Switch", "Right Input PGA"}, | ||
672 | |||
673 | {"ADCL", NULL, "Left Input Mixer"}, | ||
674 | {"ADCR", NULL, "Right Input Mixer"}, | ||
675 | |||
676 | /* Outputs */ | ||
677 | {"LINEOUT1L", NULL, "LINEOUT1L PGA"}, | ||
678 | {"LINEOUT1L PGA", NULL, "Left Output Mixer"}, | ||
679 | {"LINEOUT1R", NULL, "LINEOUT1R PGA"}, | ||
680 | {"LINEOUT1R PGA", NULL, "Right Output Mixer"}, | ||
681 | |||
682 | {"LINEOUT2L PGA", NULL, "Left Output Mixer"}, | ||
683 | {"LINEOUT2 LP", "Disabled", "LINEOUT2L PGA"}, | ||
684 | {"LINEOUT2 LP", "Enabled", "Left Output Mixer"}, | ||
685 | {"LINEOUT2L", NULL, "LINEOUT2 LP"}, | ||
686 | |||
687 | {"LINEOUT2R PGA", NULL, "Right Output Mixer"}, | ||
688 | {"LINEOUT2 LP", "Disabled", "LINEOUT2R PGA"}, | ||
689 | {"LINEOUT2 LP", "Enabled", "Right Output Mixer"}, | ||
690 | {"LINEOUT2R", NULL, "LINEOUT2 LP"}, | ||
691 | |||
692 | {"Left Output Mixer", "LINPUT3 Bypass Switch", "LINPUT3"}, | ||
693 | {"Left Output Mixer", "AUX Bypass Switch", "AUX"}, | ||
694 | {"Left Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"}, | ||
695 | {"Left Output Mixer", "Right Input Mixer Switch", "Right Input Mixer"}, | ||
696 | {"Left Output Mixer", "DACL Switch", "DACL"}, | ||
697 | |||
698 | {"Right Output Mixer", "RINPUT3 Bypass Switch", "RINPUT3"}, | ||
699 | {"Right Output Mixer", "AUX Bypass Switch", "AUX"}, | ||
700 | {"Right Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"}, | ||
701 | {"Right Output Mixer", "Right Input Mixer Switch", "Right Input Mixer"}, | ||
702 | {"Right Output Mixer", "DACR Switch", "DACR"}, | ||
703 | |||
704 | /* Note that the headphone output stage needs to be connected | ||
705 | * externally to LINEOUT2 via DC blocking capacitors. Other | ||
706 | * configurations are not supported. | ||
707 | * | ||
708 | * Note also that left and right headphone paths are treated as a | ||
709 | * mono path. | ||
710 | */ | ||
711 | {"Headphone Amplifier", NULL, "LINEOUT2 LP"}, | ||
712 | {"Headphone Amplifier", NULL, "LINEOUT2 LP"}, | ||
713 | {"HP_L", NULL, "Headphone Amplifier"}, | ||
714 | {"HP_R", NULL, "Headphone Amplifier"}, | ||
715 | }; | ||
716 | |||
717 | static int wm8900_add_widgets(struct snd_soc_codec *codec) | ||
718 | { | ||
719 | snd_soc_dapm_new_controls(codec, wm8900_dapm_widgets, | ||
720 | ARRAY_SIZE(wm8900_dapm_widgets)); | ||
721 | |||
722 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
723 | |||
724 | snd_soc_dapm_new_widgets(codec); | ||
725 | |||
726 | return 0; | ||
727 | } | ||
728 | |||
729 | static int wm8900_hw_params(struct snd_pcm_substream *substream, | ||
730 | struct snd_pcm_hw_params *params) | ||
731 | { | ||
732 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
733 | struct snd_soc_device *socdev = rtd->socdev; | ||
734 | struct snd_soc_codec *codec = socdev->codec; | ||
735 | u16 reg; | ||
736 | |||
737 | reg = wm8900_read(codec, WM8900_REG_AUDIO1) & ~0x60; | ||
738 | |||
739 | switch (params_format(params)) { | ||
740 | case SNDRV_PCM_FORMAT_S16_LE: | ||
741 | break; | ||
742 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
743 | reg |= 0x20; | ||
744 | break; | ||
745 | case SNDRV_PCM_FORMAT_S24_LE: | ||
746 | reg |= 0x40; | ||
747 | break; | ||
748 | case SNDRV_PCM_FORMAT_S32_LE: | ||
749 | reg |= 0x60; | ||
750 | break; | ||
751 | default: | ||
752 | return -EINVAL; | ||
753 | } | ||
754 | |||
755 | wm8900_write(codec, WM8900_REG_AUDIO1, reg); | ||
756 | |||
757 | return 0; | ||
758 | } | ||
759 | |||
760 | /* FLL divisors */ | ||
761 | struct _fll_div { | ||
762 | u16 fll_ratio; | ||
763 | u16 fllclk_div; | ||
764 | u16 fll_slow_lock_ref; | ||
765 | u16 n; | ||
766 | u16 k; | ||
767 | }; | ||
768 | |||
769 | /* The size in bits of the FLL divide multiplied by 10 | ||
770 | * to allow rounding later */ | ||
771 | #define FIXED_FLL_SIZE ((1 << 16) * 10) | ||
772 | |||
773 | static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, | ||
774 | unsigned int Fout) | ||
775 | { | ||
776 | u64 Kpart; | ||
777 | unsigned int K, Ndiv, Nmod, target; | ||
778 | unsigned int div; | ||
779 | |||
780 | BUG_ON(!Fout); | ||
781 | |||
782 | /* The FLL must run at 90-100MHz which is then scaled down to | ||
783 | * the output value by FLLCLK_DIV. */ | ||
784 | target = Fout; | ||
785 | div = 1; | ||
786 | while (target < 90000000) { | ||
787 | div *= 2; | ||
788 | target *= 2; | ||
789 | } | ||
790 | |||
791 | if (target > 100000000) | ||
792 | printk(KERN_WARNING "wm8900: FLL rate %d out of range, Fref=%d" | ||
793 | " Fout=%d\n", target, Fref, Fout); | ||
794 | if (div > 32) { | ||
795 | printk(KERN_ERR "wm8900: Invalid FLL division rate %u, " | ||
796 | "Fref=%d, Fout=%d, target=%d\n", | ||
797 | div, Fref, Fout, target); | ||
798 | return -EINVAL; | ||
799 | } | ||
800 | |||
801 | fll_div->fllclk_div = div >> 2; | ||
802 | |||
803 | if (Fref < 48000) | ||
804 | fll_div->fll_slow_lock_ref = 1; | ||
805 | else | ||
806 | fll_div->fll_slow_lock_ref = 0; | ||
807 | |||
808 | Ndiv = target / Fref; | ||
809 | |||
810 | if (Fref < 1000000) | ||
811 | fll_div->fll_ratio = 8; | ||
812 | else | ||
813 | fll_div->fll_ratio = 1; | ||
814 | |||
815 | fll_div->n = Ndiv / fll_div->fll_ratio; | ||
816 | Nmod = (target / fll_div->fll_ratio) % Fref; | ||
817 | |||
818 | /* Calculate fractional part - scale up so we can round. */ | ||
819 | Kpart = FIXED_FLL_SIZE * (long long)Nmod; | ||
820 | |||
821 | do_div(Kpart, Fref); | ||
822 | |||
823 | K = Kpart & 0xFFFFFFFF; | ||
824 | |||
825 | if ((K % 10) >= 5) | ||
826 | K += 5; | ||
827 | |||
828 | /* Move down to proper range now rounding is done */ | ||
829 | fll_div->k = K / 10; | ||
830 | |||
831 | BUG_ON(target != Fout * (fll_div->fllclk_div << 2)); | ||
832 | BUG_ON(!K && target != Fref * fll_div->fll_ratio * fll_div->n); | ||
833 | |||
834 | return 0; | ||
835 | } | ||
836 | |||
837 | static int wm8900_set_fll(struct snd_soc_codec *codec, | ||
838 | int fll_id, unsigned int freq_in, unsigned int freq_out) | ||
839 | { | ||
840 | struct wm8900_priv *wm8900 = codec->private_data; | ||
841 | struct _fll_div fll_div; | ||
842 | unsigned int reg; | ||
843 | |||
844 | if (wm8900->fll_in == freq_in && wm8900->fll_out == freq_out) | ||
845 | return 0; | ||
846 | |||
847 | /* The digital side should be disabled during any change. */ | ||
848 | reg = wm8900_read(codec, WM8900_REG_POWER1); | ||
849 | wm8900_write(codec, WM8900_REG_POWER1, | ||
850 | reg & (~WM8900_REG_POWER1_FLL_ENA)); | ||
851 | |||
852 | /* Disable the FLL? */ | ||
853 | if (!freq_in || !freq_out) { | ||
854 | reg = wm8900_read(codec, WM8900_REG_CLOCKING1); | ||
855 | wm8900_write(codec, WM8900_REG_CLOCKING1, | ||
856 | reg & (~WM8900_REG_CLOCKING1_MCLK_SRC)); | ||
857 | |||
858 | reg = wm8900_read(codec, WM8900_REG_FLLCTL1); | ||
859 | wm8900_write(codec, WM8900_REG_FLLCTL1, | ||
860 | reg & (~WM8900_REG_FLLCTL1_OSC_ENA)); | ||
861 | |||
862 | wm8900->fll_in = freq_in; | ||
863 | wm8900->fll_out = freq_out; | ||
864 | |||
865 | return 0; | ||
866 | } | ||
867 | |||
868 | if (fll_factors(&fll_div, freq_in, freq_out) != 0) | ||
869 | goto reenable; | ||
870 | |||
871 | wm8900->fll_in = freq_in; | ||
872 | wm8900->fll_out = freq_out; | ||
873 | |||
874 | /* The osclilator *MUST* be enabled before we enable the | ||
875 | * digital circuit. */ | ||
876 | wm8900_write(codec, WM8900_REG_FLLCTL1, | ||
877 | fll_div.fll_ratio | WM8900_REG_FLLCTL1_OSC_ENA); | ||
878 | |||
879 | wm8900_write(codec, WM8900_REG_FLLCTL4, fll_div.n >> 5); | ||
880 | wm8900_write(codec, WM8900_REG_FLLCTL5, | ||
881 | (fll_div.fllclk_div << 6) | (fll_div.n & 0x1f)); | ||
882 | |||
883 | if (fll_div.k) { | ||
884 | wm8900_write(codec, WM8900_REG_FLLCTL2, | ||
885 | (fll_div.k >> 8) | 0x100); | ||
886 | wm8900_write(codec, WM8900_REG_FLLCTL3, fll_div.k & 0xff); | ||
887 | } else | ||
888 | wm8900_write(codec, WM8900_REG_FLLCTL2, 0); | ||
889 | |||
890 | if (fll_div.fll_slow_lock_ref) | ||
891 | wm8900_write(codec, WM8900_REG_FLLCTL6, | ||
892 | WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF); | ||
893 | else | ||
894 | wm8900_write(codec, WM8900_REG_FLLCTL6, 0); | ||
895 | |||
896 | reg = wm8900_read(codec, WM8900_REG_POWER1); | ||
897 | wm8900_write(codec, WM8900_REG_POWER1, | ||
898 | reg | WM8900_REG_POWER1_FLL_ENA); | ||
899 | |||
900 | reenable: | ||
901 | reg = wm8900_read(codec, WM8900_REG_CLOCKING1); | ||
902 | wm8900_write(codec, WM8900_REG_CLOCKING1, | ||
903 | reg | WM8900_REG_CLOCKING1_MCLK_SRC); | ||
904 | |||
905 | return 0; | ||
906 | } | ||
907 | |||
908 | static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai, | ||
909 | int pll_id, unsigned int freq_in, unsigned int freq_out) | ||
910 | { | ||
911 | return wm8900_set_fll(codec_dai->codec, pll_id, freq_in, freq_out); | ||
912 | } | ||
913 | |||
914 | static int wm8900_set_dai_clkdiv(struct snd_soc_dai *codec_dai, | ||
915 | int div_id, int div) | ||
916 | { | ||
917 | struct snd_soc_codec *codec = codec_dai->codec; | ||
918 | unsigned int reg; | ||
919 | |||
920 | switch (div_id) { | ||
921 | case WM8900_BCLK_DIV: | ||
922 | reg = wm8900_read(codec, WM8900_REG_CLOCKING1); | ||
923 | wm8900_write(codec, WM8900_REG_CLOCKING1, | ||
924 | div | (reg & WM8900_REG_CLOCKING1_BCLK_MASK)); | ||
925 | break; | ||
926 | case WM8900_OPCLK_DIV: | ||
927 | reg = wm8900_read(codec, WM8900_REG_CLOCKING1); | ||
928 | wm8900_write(codec, WM8900_REG_CLOCKING1, | ||
929 | div | (reg & WM8900_REG_CLOCKING1_OPCLK_MASK)); | ||
930 | break; | ||
931 | case WM8900_DAC_LRCLK: | ||
932 | reg = wm8900_read(codec, WM8900_REG_AUDIO4); | ||
933 | wm8900_write(codec, WM8900_REG_AUDIO4, | ||
934 | div | (reg & WM8900_LRC_MASK)); | ||
935 | break; | ||
936 | case WM8900_ADC_LRCLK: | ||
937 | reg = wm8900_read(codec, WM8900_REG_AUDIO3); | ||
938 | wm8900_write(codec, WM8900_REG_AUDIO3, | ||
939 | div | (reg & WM8900_LRC_MASK)); | ||
940 | break; | ||
941 | case WM8900_DAC_CLKDIV: | ||
942 | reg = wm8900_read(codec, WM8900_REG_CLOCKING2); | ||
943 | wm8900_write(codec, WM8900_REG_CLOCKING2, | ||
944 | div | (reg & WM8900_REG_CLOCKING2_DAC_CLKDIV)); | ||
945 | break; | ||
946 | case WM8900_ADC_CLKDIV: | ||
947 | reg = wm8900_read(codec, WM8900_REG_CLOCKING2); | ||
948 | wm8900_write(codec, WM8900_REG_CLOCKING2, | ||
949 | div | (reg & WM8900_REG_CLOCKING2_ADC_CLKDIV)); | ||
950 | break; | ||
951 | case WM8900_LRCLK_MODE: | ||
952 | reg = wm8900_read(codec, WM8900_REG_DACCTRL); | ||
953 | wm8900_write(codec, WM8900_REG_DACCTRL, | ||
954 | div | (reg & WM8900_REG_DACCTRL_AIF_LRCLKRATE)); | ||
955 | break; | ||
956 | default: | ||
957 | return -EINVAL; | ||
958 | } | ||
959 | |||
960 | return 0; | ||
961 | } | ||
962 | |||
963 | |||
964 | static int wm8900_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
965 | unsigned int fmt) | ||
966 | { | ||
967 | struct snd_soc_codec *codec = codec_dai->codec; | ||
968 | unsigned int clocking1, aif1, aif3, aif4; | ||
969 | |||
970 | clocking1 = wm8900_read(codec, WM8900_REG_CLOCKING1); | ||
971 | aif1 = wm8900_read(codec, WM8900_REG_AUDIO1); | ||
972 | aif3 = wm8900_read(codec, WM8900_REG_AUDIO3); | ||
973 | aif4 = wm8900_read(codec, WM8900_REG_AUDIO4); | ||
974 | |||
975 | /* set master/slave audio interface */ | ||
976 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
977 | case SND_SOC_DAIFMT_CBS_CFS: | ||
978 | clocking1 &= ~WM8900_REG_CLOCKING1_BCLK_DIR; | ||
979 | aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR; | ||
980 | aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR; | ||
981 | break; | ||
982 | case SND_SOC_DAIFMT_CBS_CFM: | ||
983 | clocking1 &= ~WM8900_REG_CLOCKING1_BCLK_DIR; | ||
984 | aif3 |= WM8900_REG_AUDIO3_ADCLRC_DIR; | ||
985 | aif4 |= WM8900_REG_AUDIO4_DACLRC_DIR; | ||
986 | break; | ||
987 | case SND_SOC_DAIFMT_CBM_CFM: | ||
988 | clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR; | ||
989 | aif3 |= WM8900_REG_AUDIO3_ADCLRC_DIR; | ||
990 | aif4 |= WM8900_REG_AUDIO4_DACLRC_DIR; | ||
991 | break; | ||
992 | case SND_SOC_DAIFMT_CBM_CFS: | ||
993 | clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR; | ||
994 | aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR; | ||
995 | aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR; | ||
996 | break; | ||
997 | default: | ||
998 | return -EINVAL; | ||
999 | } | ||
1000 | |||
1001 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
1002 | case SND_SOC_DAIFMT_DSP_A: | ||
1003 | aif1 |= WM8900_REG_AUDIO1_AIF_FMT_MASK; | ||
1004 | aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV; | ||
1005 | break; | ||
1006 | case SND_SOC_DAIFMT_DSP_B: | ||
1007 | aif1 |= WM8900_REG_AUDIO1_AIF_FMT_MASK; | ||
1008 | aif1 |= WM8900_REG_AUDIO1_LRCLK_INV; | ||
1009 | break; | ||
1010 | case SND_SOC_DAIFMT_I2S: | ||
1011 | aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK; | ||
1012 | aif1 |= 0x10; | ||
1013 | break; | ||
1014 | case SND_SOC_DAIFMT_RIGHT_J: | ||
1015 | aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK; | ||
1016 | break; | ||
1017 | case SND_SOC_DAIFMT_LEFT_J: | ||
1018 | aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK; | ||
1019 | aif1 |= 0x8; | ||
1020 | break; | ||
1021 | default: | ||
1022 | return -EINVAL; | ||
1023 | } | ||
1024 | |||
1025 | /* Clock inversion */ | ||
1026 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
1027 | case SND_SOC_DAIFMT_DSP_A: | ||
1028 | case SND_SOC_DAIFMT_DSP_B: | ||
1029 | /* frame inversion not valid for DSP modes */ | ||
1030 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
1031 | case SND_SOC_DAIFMT_NB_NF: | ||
1032 | aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV; | ||
1033 | break; | ||
1034 | case SND_SOC_DAIFMT_IB_NF: | ||
1035 | aif1 |= WM8900_REG_AUDIO1_BCLK_INV; | ||
1036 | break; | ||
1037 | default: | ||
1038 | return -EINVAL; | ||
1039 | } | ||
1040 | break; | ||
1041 | case SND_SOC_DAIFMT_I2S: | ||
1042 | case SND_SOC_DAIFMT_RIGHT_J: | ||
1043 | case SND_SOC_DAIFMT_LEFT_J: | ||
1044 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
1045 | case SND_SOC_DAIFMT_NB_NF: | ||
1046 | aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV; | ||
1047 | aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV; | ||
1048 | break; | ||
1049 | case SND_SOC_DAIFMT_IB_IF: | ||
1050 | aif1 |= WM8900_REG_AUDIO1_BCLK_INV; | ||
1051 | aif1 |= WM8900_REG_AUDIO1_LRCLK_INV; | ||
1052 | break; | ||
1053 | case SND_SOC_DAIFMT_IB_NF: | ||
1054 | aif1 |= WM8900_REG_AUDIO1_BCLK_INV; | ||
1055 | aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV; | ||
1056 | break; | ||
1057 | case SND_SOC_DAIFMT_NB_IF: | ||
1058 | aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV; | ||
1059 | aif1 |= WM8900_REG_AUDIO1_LRCLK_INV; | ||
1060 | break; | ||
1061 | default: | ||
1062 | return -EINVAL; | ||
1063 | } | ||
1064 | break; | ||
1065 | default: | ||
1066 | return -EINVAL; | ||
1067 | } | ||
1068 | |||
1069 | wm8900_write(codec, WM8900_REG_CLOCKING1, clocking1); | ||
1070 | wm8900_write(codec, WM8900_REG_AUDIO1, aif1); | ||
1071 | wm8900_write(codec, WM8900_REG_AUDIO3, aif3); | ||
1072 | wm8900_write(codec, WM8900_REG_AUDIO4, aif4); | ||
1073 | |||
1074 | return 0; | ||
1075 | } | ||
1076 | |||
1077 | static int wm8900_digital_mute(struct snd_soc_dai *codec_dai, int mute) | ||
1078 | { | ||
1079 | struct snd_soc_codec *codec = codec_dai->codec; | ||
1080 | u16 reg; | ||
1081 | |||
1082 | reg = wm8900_read(codec, WM8900_REG_DACCTRL); | ||
1083 | |||
1084 | if (mute) | ||
1085 | reg |= WM8900_REG_DACCTRL_MUTE; | ||
1086 | else | ||
1087 | reg &= ~WM8900_REG_DACCTRL_MUTE; | ||
1088 | |||
1089 | wm8900_write(codec, WM8900_REG_DACCTRL, reg); | ||
1090 | |||
1091 | return 0; | ||
1092 | } | ||
1093 | |||
1094 | #define WM8900_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ | ||
1095 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ | ||
1096 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) | ||
1097 | |||
1098 | #define WM8900_PCM_FORMATS \ | ||
1099 | (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ | ||
1100 | SNDRV_PCM_FORMAT_S24_LE) | ||
1101 | |||
1102 | struct snd_soc_dai wm8900_dai = { | ||
1103 | .name = "WM8900 HiFi", | ||
1104 | .playback = { | ||
1105 | .stream_name = "HiFi Playback", | ||
1106 | .channels_min = 1, | ||
1107 | .channels_max = 2, | ||
1108 | .rates = WM8900_RATES, | ||
1109 | .formats = WM8900_PCM_FORMATS, | ||
1110 | }, | ||
1111 | .capture = { | ||
1112 | .stream_name = "HiFi Capture", | ||
1113 | .channels_min = 1, | ||
1114 | .channels_max = 2, | ||
1115 | .rates = WM8900_RATES, | ||
1116 | .formats = WM8900_PCM_FORMATS, | ||
1117 | }, | ||
1118 | .ops = { | ||
1119 | .hw_params = wm8900_hw_params, | ||
1120 | }, | ||
1121 | .dai_ops = { | ||
1122 | .set_clkdiv = wm8900_set_dai_clkdiv, | ||
1123 | .set_pll = wm8900_set_dai_pll, | ||
1124 | .set_fmt = wm8900_set_dai_fmt, | ||
1125 | .digital_mute = wm8900_digital_mute, | ||
1126 | }, | ||
1127 | }; | ||
1128 | EXPORT_SYMBOL_GPL(wm8900_dai); | ||
1129 | |||
1130 | static int wm8900_set_bias_level(struct snd_soc_codec *codec, | ||
1131 | enum snd_soc_bias_level level) | ||
1132 | { | ||
1133 | u16 reg; | ||
1134 | |||
1135 | switch (level) { | ||
1136 | case SND_SOC_BIAS_ON: | ||
1137 | /* Enable thermal shutdown */ | ||
1138 | reg = wm8900_read(codec, WM8900_REG_GPIO); | ||
1139 | wm8900_write(codec, WM8900_REG_GPIO, | ||
1140 | reg | WM8900_REG_GPIO_TEMP_ENA); | ||
1141 | reg = wm8900_read(codec, WM8900_REG_ADDCTL); | ||
1142 | wm8900_write(codec, WM8900_REG_ADDCTL, | ||
1143 | reg | WM8900_REG_ADDCTL_TEMP_SD); | ||
1144 | break; | ||
1145 | |||
1146 | case SND_SOC_BIAS_PREPARE: | ||
1147 | break; | ||
1148 | |||
1149 | case SND_SOC_BIAS_STANDBY: | ||
1150 | /* Charge capacitors if initial power up */ | ||
1151 | if (codec->bias_level == SND_SOC_BIAS_OFF) { | ||
1152 | /* STARTUP_BIAS_ENA on */ | ||
1153 | wm8900_write(codec, WM8900_REG_POWER1, | ||
1154 | WM8900_REG_POWER1_STARTUP_BIAS_ENA); | ||
1155 | |||
1156 | /* Startup bias mode */ | ||
1157 | wm8900_write(codec, WM8900_REG_ADDCTL, | ||
1158 | WM8900_REG_ADDCTL_BIAS_SRC | | ||
1159 | WM8900_REG_ADDCTL_VMID_SOFTST); | ||
1160 | |||
1161 | /* VMID 2x50k */ | ||
1162 | wm8900_write(codec, WM8900_REG_POWER1, | ||
1163 | WM8900_REG_POWER1_STARTUP_BIAS_ENA | 0x1); | ||
1164 | |||
1165 | /* Allow capacitors to charge */ | ||
1166 | schedule_timeout_interruptible(msecs_to_jiffies(400)); | ||
1167 | |||
1168 | /* Enable bias */ | ||
1169 | wm8900_write(codec, WM8900_REG_POWER1, | ||
1170 | WM8900_REG_POWER1_STARTUP_BIAS_ENA | | ||
1171 | WM8900_REG_POWER1_BIAS_ENA | 0x1); | ||
1172 | |||
1173 | wm8900_write(codec, WM8900_REG_ADDCTL, 0); | ||
1174 | |||
1175 | wm8900_write(codec, WM8900_REG_POWER1, | ||
1176 | WM8900_REG_POWER1_BIAS_ENA | 0x1); | ||
1177 | } | ||
1178 | |||
1179 | reg = wm8900_read(codec, WM8900_REG_POWER1); | ||
1180 | wm8900_write(codec, WM8900_REG_POWER1, | ||
1181 | (reg & WM8900_REG_POWER1_FLL_ENA) | | ||
1182 | WM8900_REG_POWER1_BIAS_ENA | 0x1); | ||
1183 | wm8900_write(codec, WM8900_REG_POWER2, | ||
1184 | WM8900_REG_POWER2_SYSCLK_ENA); | ||
1185 | wm8900_write(codec, WM8900_REG_POWER3, 0); | ||
1186 | break; | ||
1187 | |||
1188 | case SND_SOC_BIAS_OFF: | ||
1189 | /* Startup bias enable */ | ||
1190 | reg = wm8900_read(codec, WM8900_REG_POWER1); | ||
1191 | wm8900_write(codec, WM8900_REG_POWER1, | ||
1192 | reg & WM8900_REG_POWER1_STARTUP_BIAS_ENA); | ||
1193 | wm8900_write(codec, WM8900_REG_ADDCTL, | ||
1194 | WM8900_REG_ADDCTL_BIAS_SRC | | ||
1195 | WM8900_REG_ADDCTL_VMID_SOFTST); | ||
1196 | |||
1197 | /* Discharge caps */ | ||
1198 | wm8900_write(codec, WM8900_REG_POWER1, | ||
1199 | WM8900_REG_POWER1_STARTUP_BIAS_ENA); | ||
1200 | schedule_timeout_interruptible(msecs_to_jiffies(500)); | ||
1201 | |||
1202 | /* Remove clamp */ | ||
1203 | wm8900_write(codec, WM8900_REG_HPCTL1, 0); | ||
1204 | |||
1205 | /* Power down */ | ||
1206 | wm8900_write(codec, WM8900_REG_ADDCTL, 0); | ||
1207 | wm8900_write(codec, WM8900_REG_POWER1, 0); | ||
1208 | wm8900_write(codec, WM8900_REG_POWER2, 0); | ||
1209 | wm8900_write(codec, WM8900_REG_POWER3, 0); | ||
1210 | |||
1211 | /* Need to let things settle before stopping the clock | ||
1212 | * to ensure that restart works, see "Stopping the | ||
1213 | * master clock" in the datasheet. */ | ||
1214 | schedule_timeout_interruptible(msecs_to_jiffies(1)); | ||
1215 | wm8900_write(codec, WM8900_REG_POWER2, | ||
1216 | WM8900_REG_POWER2_SYSCLK_ENA); | ||
1217 | break; | ||
1218 | } | ||
1219 | codec->bias_level = level; | ||
1220 | return 0; | ||
1221 | } | ||
1222 | |||
1223 | static int wm8900_suspend(struct platform_device *pdev, pm_message_t state) | ||
1224 | { | ||
1225 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1226 | struct snd_soc_codec *codec = socdev->codec; | ||
1227 | struct wm8900_priv *wm8900 = codec->private_data; | ||
1228 | int fll_out = wm8900->fll_out; | ||
1229 | int fll_in = wm8900->fll_in; | ||
1230 | int ret; | ||
1231 | |||
1232 | /* Stop the FLL in an orderly fashion */ | ||
1233 | ret = wm8900_set_fll(codec, 0, 0, 0); | ||
1234 | if (ret != 0) { | ||
1235 | dev_err(&pdev->dev, "Failed to stop FLL\n"); | ||
1236 | return ret; | ||
1237 | } | ||
1238 | |||
1239 | wm8900->fll_out = fll_out; | ||
1240 | wm8900->fll_in = fll_in; | ||
1241 | |||
1242 | wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
1243 | |||
1244 | return 0; | ||
1245 | } | ||
1246 | |||
1247 | static int wm8900_resume(struct platform_device *pdev) | ||
1248 | { | ||
1249 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1250 | struct snd_soc_codec *codec = socdev->codec; | ||
1251 | struct wm8900_priv *wm8900 = codec->private_data; | ||
1252 | u16 *cache; | ||
1253 | int i, ret; | ||
1254 | |||
1255 | cache = kmemdup(codec->reg_cache, sizeof(wm8900_reg_defaults), | ||
1256 | GFP_KERNEL); | ||
1257 | |||
1258 | wm8900_reset(codec); | ||
1259 | wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
1260 | |||
1261 | /* Restart the FLL? */ | ||
1262 | if (wm8900->fll_out) { | ||
1263 | int fll_out = wm8900->fll_out; | ||
1264 | int fll_in = wm8900->fll_in; | ||
1265 | |||
1266 | wm8900->fll_in = 0; | ||
1267 | wm8900->fll_out = 0; | ||
1268 | |||
1269 | ret = wm8900_set_fll(codec, 0, fll_in, fll_out); | ||
1270 | if (ret != 0) { | ||
1271 | dev_err(&pdev->dev, "Failed to restart FLL\n"); | ||
1272 | return ret; | ||
1273 | } | ||
1274 | } | ||
1275 | |||
1276 | if (cache) { | ||
1277 | for (i = 0; i < WM8900_MAXREG; i++) | ||
1278 | wm8900_write(codec, i, cache[i]); | ||
1279 | kfree(cache); | ||
1280 | } else | ||
1281 | dev_err(&pdev->dev, "Unable to allocate register cache\n"); | ||
1282 | |||
1283 | return 0; | ||
1284 | } | ||
1285 | |||
1286 | /* | ||
1287 | * initialise the WM8900 driver | ||
1288 | * register the mixer and dsp interfaces with the kernel | ||
1289 | */ | ||
1290 | static int wm8900_init(struct snd_soc_device *socdev) | ||
1291 | { | ||
1292 | struct snd_soc_codec *codec = socdev->codec; | ||
1293 | int ret = 0; | ||
1294 | unsigned int reg; | ||
1295 | struct i2c_client *i2c_client = socdev->codec->control_data; | ||
1296 | |||
1297 | codec->name = "WM8900"; | ||
1298 | codec->owner = THIS_MODULE; | ||
1299 | codec->read = wm8900_read; | ||
1300 | codec->write = wm8900_write; | ||
1301 | codec->dai = &wm8900_dai; | ||
1302 | codec->num_dai = 1; | ||
1303 | codec->reg_cache_size = WM8900_MAXREG; | ||
1304 | codec->reg_cache = kmemdup(wm8900_reg_defaults, | ||
1305 | sizeof(wm8900_reg_defaults), GFP_KERNEL); | ||
1306 | |||
1307 | if (codec->reg_cache == NULL) | ||
1308 | return -ENOMEM; | ||
1309 | |||
1310 | reg = wm8900_read(codec, WM8900_REG_ID); | ||
1311 | if (reg != 0x8900) { | ||
1312 | dev_err(&i2c_client->dev, "Device is not a WM8900 - ID %x\n", | ||
1313 | reg); | ||
1314 | return -ENODEV; | ||
1315 | } | ||
1316 | |||
1317 | codec->private_data = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL); | ||
1318 | if (codec->private_data == NULL) { | ||
1319 | ret = -ENOMEM; | ||
1320 | goto priv_err; | ||
1321 | } | ||
1322 | |||
1323 | /* Read back from the chip */ | ||
1324 | reg = wm8900_chip_read(codec, WM8900_REG_POWER1); | ||
1325 | reg = (reg >> 12) & 0xf; | ||
1326 | dev_info(&i2c_client->dev, "WM8900 revision %d\n", reg); | ||
1327 | |||
1328 | wm8900_reset(codec); | ||
1329 | |||
1330 | /* Latch the volume update bits */ | ||
1331 | wm8900_write(codec, WM8900_REG_LINVOL, | ||
1332 | wm8900_read(codec, WM8900_REG_LINVOL) | 0x100); | ||
1333 | wm8900_write(codec, WM8900_REG_RINVOL, | ||
1334 | wm8900_read(codec, WM8900_REG_RINVOL) | 0x100); | ||
1335 | wm8900_write(codec, WM8900_REG_LOUT1CTL, | ||
1336 | wm8900_read(codec, WM8900_REG_LOUT1CTL) | 0x100); | ||
1337 | wm8900_write(codec, WM8900_REG_ROUT1CTL, | ||
1338 | wm8900_read(codec, WM8900_REG_ROUT1CTL) | 0x100); | ||
1339 | wm8900_write(codec, WM8900_REG_LOUT2CTL, | ||
1340 | wm8900_read(codec, WM8900_REG_LOUT2CTL) | 0x100); | ||
1341 | wm8900_write(codec, WM8900_REG_ROUT2CTL, | ||
1342 | wm8900_read(codec, WM8900_REG_ROUT2CTL) | 0x100); | ||
1343 | wm8900_write(codec, WM8900_REG_LDAC_DV, | ||
1344 | wm8900_read(codec, WM8900_REG_LDAC_DV) | 0x100); | ||
1345 | wm8900_write(codec, WM8900_REG_RDAC_DV, | ||
1346 | wm8900_read(codec, WM8900_REG_RDAC_DV) | 0x100); | ||
1347 | wm8900_write(codec, WM8900_REG_LADC_DV, | ||
1348 | wm8900_read(codec, WM8900_REG_LADC_DV) | 0x100); | ||
1349 | wm8900_write(codec, WM8900_REG_RADC_DV, | ||
1350 | wm8900_read(codec, WM8900_REG_RADC_DV) | 0x100); | ||
1351 | |||
1352 | /* Set the DAC and mixer output bias */ | ||
1353 | wm8900_write(codec, WM8900_REG_OUTBIASCTL, 0x81); | ||
1354 | |||
1355 | /* Register pcms */ | ||
1356 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
1357 | if (ret < 0) { | ||
1358 | dev_err(&i2c_client->dev, "Failed to register new PCMs\n"); | ||
1359 | goto pcm_err; | ||
1360 | } | ||
1361 | |||
1362 | /* Turn the chip on */ | ||
1363 | codec->bias_level = SND_SOC_BIAS_OFF; | ||
1364 | wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
1365 | |||
1366 | wm8900_add_controls(codec); | ||
1367 | wm8900_add_widgets(codec); | ||
1368 | |||
1369 | ret = snd_soc_register_card(socdev); | ||
1370 | if (ret < 0) { | ||
1371 | dev_err(&i2c_client->dev, "Failed to register card\n"); | ||
1372 | goto card_err; | ||
1373 | } | ||
1374 | return ret; | ||
1375 | |||
1376 | card_err: | ||
1377 | snd_soc_free_pcms(socdev); | ||
1378 | snd_soc_dapm_free(socdev); | ||
1379 | pcm_err: | ||
1380 | kfree(codec->reg_cache); | ||
1381 | priv_err: | ||
1382 | kfree(codec->private_data); | ||
1383 | return ret; | ||
1384 | } | ||
1385 | |||
1386 | static struct snd_soc_device *wm8900_socdev; | ||
1387 | |||
1388 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
1389 | |||
1390 | static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; | ||
1391 | |||
1392 | /* Magic definition of all other variables and things */ | ||
1393 | I2C_CLIENT_INSMOD; | ||
1394 | |||
1395 | static struct i2c_driver wm8900_i2c_driver; | ||
1396 | static struct i2c_client client_template; | ||
1397 | |||
1398 | /* If the i2c layer weren't so broken, we could pass this kind of data | ||
1399 | around */ | ||
1400 | static int wm8900_codec_probe(struct i2c_adapter *adap, int addr, int kind) | ||
1401 | { | ||
1402 | struct snd_soc_device *socdev = wm8900_socdev; | ||
1403 | struct wm8900_setup_data *setup = socdev->codec_data; | ||
1404 | struct snd_soc_codec *codec = socdev->codec; | ||
1405 | struct i2c_client *i2c; | ||
1406 | int ret; | ||
1407 | |||
1408 | if (addr != setup->i2c_address) | ||
1409 | return -ENODEV; | ||
1410 | |||
1411 | dev_err(&adap->dev, "Probe on %x\n", addr); | ||
1412 | |||
1413 | client_template.adapter = adap; | ||
1414 | client_template.addr = addr; | ||
1415 | |||
1416 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | ||
1417 | if (i2c == NULL) { | ||
1418 | kfree(codec); | ||
1419 | return -ENOMEM; | ||
1420 | } | ||
1421 | i2c_set_clientdata(i2c, codec); | ||
1422 | codec->control_data = i2c; | ||
1423 | |||
1424 | ret = i2c_attach_client(i2c); | ||
1425 | if (ret < 0) { | ||
1426 | dev_err(&adap->dev, | ||
1427 | "failed to attach codec at addr %x\n", addr); | ||
1428 | goto err; | ||
1429 | } | ||
1430 | |||
1431 | ret = wm8900_init(socdev); | ||
1432 | if (ret < 0) { | ||
1433 | dev_err(&adap->dev, "failed to initialise WM8900\n"); | ||
1434 | goto err; | ||
1435 | } | ||
1436 | return ret; | ||
1437 | |||
1438 | err: | ||
1439 | kfree(codec); | ||
1440 | kfree(i2c); | ||
1441 | return ret; | ||
1442 | } | ||
1443 | |||
1444 | static int wm8900_i2c_detach(struct i2c_client *client) | ||
1445 | { | ||
1446 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | ||
1447 | i2c_detach_client(client); | ||
1448 | kfree(codec->reg_cache); | ||
1449 | kfree(client); | ||
1450 | return 0; | ||
1451 | } | ||
1452 | |||
1453 | static int wm8900_i2c_attach(struct i2c_adapter *adap) | ||
1454 | { | ||
1455 | return i2c_probe(adap, &addr_data, wm8900_codec_probe); | ||
1456 | } | ||
1457 | |||
1458 | /* corgi i2c codec control layer */ | ||
1459 | static struct i2c_driver wm8900_i2c_driver = { | ||
1460 | .driver = { | ||
1461 | .name = "WM8900 I2C codec", | ||
1462 | .owner = THIS_MODULE, | ||
1463 | }, | ||
1464 | .attach_adapter = wm8900_i2c_attach, | ||
1465 | .detach_client = wm8900_i2c_detach, | ||
1466 | .command = NULL, | ||
1467 | }; | ||
1468 | |||
1469 | static struct i2c_client client_template = { | ||
1470 | .name = "WM8900", | ||
1471 | .driver = &wm8900_i2c_driver, | ||
1472 | }; | ||
1473 | #endif | ||
1474 | |||
1475 | static int wm8900_probe(struct platform_device *pdev) | ||
1476 | { | ||
1477 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1478 | struct wm8900_setup_data *setup; | ||
1479 | struct snd_soc_codec *codec; | ||
1480 | int ret = 0; | ||
1481 | |||
1482 | dev_info(&pdev->dev, "WM8900 Audio Codec\n"); | ||
1483 | |||
1484 | setup = socdev->codec_data; | ||
1485 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | ||
1486 | if (codec == NULL) | ||
1487 | return -ENOMEM; | ||
1488 | |||
1489 | mutex_init(&codec->mutex); | ||
1490 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
1491 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
1492 | |||
1493 | socdev->codec = codec; | ||
1494 | |||
1495 | codec->set_bias_level = wm8900_set_bias_level; | ||
1496 | |||
1497 | wm8900_socdev = socdev; | ||
1498 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
1499 | if (setup->i2c_address) { | ||
1500 | normal_i2c[0] = setup->i2c_address; | ||
1501 | codec->hw_write = (hw_write_t)i2c_master_send; | ||
1502 | ret = i2c_add_driver(&wm8900_i2c_driver); | ||
1503 | if (ret != 0) | ||
1504 | printk(KERN_ERR "can't add i2c driver"); | ||
1505 | } | ||
1506 | #else | ||
1507 | #error Non-I2C interfaces not yet supported | ||
1508 | #endif | ||
1509 | return ret; | ||
1510 | } | ||
1511 | |||
1512 | /* power down chip */ | ||
1513 | static int wm8900_remove(struct platform_device *pdev) | ||
1514 | { | ||
1515 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1516 | struct snd_soc_codec *codec = socdev->codec; | ||
1517 | |||
1518 | if (codec->control_data) | ||
1519 | wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
1520 | |||
1521 | snd_soc_free_pcms(socdev); | ||
1522 | snd_soc_dapm_free(socdev); | ||
1523 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
1524 | i2c_del_driver(&wm8900_i2c_driver); | ||
1525 | #endif | ||
1526 | kfree(codec); | ||
1527 | |||
1528 | return 0; | ||
1529 | } | ||
1530 | |||
1531 | struct snd_soc_codec_device soc_codec_dev_wm8900 = { | ||
1532 | .probe = wm8900_probe, | ||
1533 | .remove = wm8900_remove, | ||
1534 | .suspend = wm8900_suspend, | ||
1535 | .resume = wm8900_resume, | ||
1536 | }; | ||
1537 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8900); | ||
1538 | |||
1539 | MODULE_DESCRIPTION("ASoC WM8900 driver"); | ||
1540 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>"); | ||
1541 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/wm8900.h b/sound/soc/codecs/wm8900.h new file mode 100644 index 000000000000..ba450d99e902 --- /dev/null +++ b/sound/soc/codecs/wm8900.h | |||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | * wm8900.h -- WM890 Soc Audio driver | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef _WM8900_H | ||
10 | #define _WM8900_H | ||
11 | |||
12 | #define WM8900_FLL 1 | ||
13 | |||
14 | #define WM8900_BCLK_DIV 1 | ||
15 | #define WM8900_ADC_CLKDIV 2 | ||
16 | #define WM8900_DAC_CLKDIV 3 | ||
17 | #define WM8900_ADC_LRCLK 4 | ||
18 | #define WM8900_DAC_LRCLK 5 | ||
19 | #define WM8900_OPCLK_DIV 6 | ||
20 | #define WM8900_LRCLK_MODE 7 | ||
21 | |||
22 | #define WM8900_BCLK_DIV_1 0x00 | ||
23 | #define WM8900_BCLK_DIV_1_5 0x02 | ||
24 | #define WM8900_BCLK_DIV_2 0x04 | ||
25 | #define WM8900_BCLK_DIV_3 0x06 | ||
26 | #define WM8900_BCLK_DIV_4 0x08 | ||
27 | #define WM8900_BCLK_DIV_5_5 0x0a | ||
28 | #define WM8900_BCLK_DIV_6 0x0c | ||
29 | #define WM8900_BCLK_DIV_8 0x0e | ||
30 | #define WM8900_BCLK_DIV_11 0x10 | ||
31 | #define WM8900_BCLK_DIV_12 0x12 | ||
32 | #define WM8900_BCLK_DIV_16 0x14 | ||
33 | #define WM8900_BCLK_DIV_22 0x16 | ||
34 | #define WM8900_BCLK_DIV_24 0x18 | ||
35 | #define WM8900_BCLK_DIV_32 0x1a | ||
36 | #define WM8900_BCLK_DIV_44 0x1c | ||
37 | #define WM8900_BCLK_DIV_48 0x1e | ||
38 | |||
39 | #define WM8900_ADC_CLKDIV_1 0x00 | ||
40 | #define WM8900_ADC_CLKDIV_1_5 0x20 | ||
41 | #define WM8900_ADC_CLKDIV_2 0x40 | ||
42 | #define WM8900_ADC_CLKDIV_3 0x60 | ||
43 | #define WM8900_ADC_CLKDIV_4 0x80 | ||
44 | #define WM8900_ADC_CLKDIV_5_5 0xa0 | ||
45 | #define WM8900_ADC_CLKDIV_6 0xc0 | ||
46 | |||
47 | #define WM8900_DAC_CLKDIV_1 0x00 | ||
48 | #define WM8900_DAC_CLKDIV_1_5 0x04 | ||
49 | #define WM8900_DAC_CLKDIV_2 0x08 | ||
50 | #define WM8900_DAC_CLKDIV_3 0x0c | ||
51 | #define WM8900_DAC_CLKDIV_4 0x10 | ||
52 | #define WM8900_DAC_CLKDIV_5_5 0x14 | ||
53 | #define WM8900_DAC_CLKDIV_6 0x18 | ||
54 | |||
55 | #define WM8900_ | ||
56 | |||
57 | struct wm8900_setup_data { | ||
58 | unsigned short i2c_address; | ||
59 | }; | ||
60 | |||
61 | extern struct snd_soc_dai wm8900_dai; | ||
62 | extern struct snd_soc_codec_device soc_codec_dev_wm8900; | ||
63 | |||
64 | #endif | ||
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c new file mode 100644 index 000000000000..ce40d7877605 --- /dev/null +++ b/sound/soc/codecs/wm8903.c | |||
@@ -0,0 +1,1813 @@ | |||
1 | /* | ||
2 | * wm8903.c -- WM8903 ALSA SoC Audio driver | ||
3 | * | ||
4 | * Copyright 2008 Wolfson Microelectronics | ||
5 | * | ||
6 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * TODO: | ||
13 | * - TDM mode configuration. | ||
14 | * - Mic detect. | ||
15 | * - Digital microphone support. | ||
16 | * - Interrupt support (mic detect and sequencer). | ||
17 | */ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/moduleparam.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/pm.h> | ||
24 | #include <linux/i2c.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/pcm.h> | ||
28 | #include <sound/pcm_params.h> | ||
29 | #include <sound/tlv.h> | ||
30 | #include <sound/soc.h> | ||
31 | #include <sound/soc-dapm.h> | ||
32 | #include <sound/initval.h> | ||
33 | |||
34 | #include "wm8903.h" | ||
35 | |||
36 | struct wm8903_priv { | ||
37 | int sysclk; | ||
38 | |||
39 | /* Reference counts */ | ||
40 | int charge_pump_users; | ||
41 | int class_w_users; | ||
42 | int playback_active; | ||
43 | int capture_active; | ||
44 | |||
45 | struct snd_pcm_substream *master_substream; | ||
46 | struct snd_pcm_substream *slave_substream; | ||
47 | }; | ||
48 | |||
49 | /* Register defaults at reset */ | ||
50 | static u16 wm8903_reg_defaults[] = { | ||
51 | 0x8903, /* R0 - SW Reset and ID */ | ||
52 | 0x0000, /* R1 - Revision Number */ | ||
53 | 0x0000, /* R2 */ | ||
54 | 0x0000, /* R3 */ | ||
55 | 0x0018, /* R4 - Bias Control 0 */ | ||
56 | 0x0000, /* R5 - VMID Control 0 */ | ||
57 | 0x0000, /* R6 - Mic Bias Control 0 */ | ||
58 | 0x0000, /* R7 */ | ||
59 | 0x0001, /* R8 - Analogue DAC 0 */ | ||
60 | 0x0000, /* R9 */ | ||
61 | 0x0001, /* R10 - Analogue ADC 0 */ | ||
62 | 0x0000, /* R11 */ | ||
63 | 0x0000, /* R12 - Power Management 0 */ | ||
64 | 0x0000, /* R13 - Power Management 1 */ | ||
65 | 0x0000, /* R14 - Power Management 2 */ | ||
66 | 0x0000, /* R15 - Power Management 3 */ | ||
67 | 0x0000, /* R16 - Power Management 4 */ | ||
68 | 0x0000, /* R17 - Power Management 5 */ | ||
69 | 0x0000, /* R18 - Power Management 6 */ | ||
70 | 0x0000, /* R19 */ | ||
71 | 0x0400, /* R20 - Clock Rates 0 */ | ||
72 | 0x0D07, /* R21 - Clock Rates 1 */ | ||
73 | 0x0000, /* R22 - Clock Rates 2 */ | ||
74 | 0x0000, /* R23 */ | ||
75 | 0x0050, /* R24 - Audio Interface 0 */ | ||
76 | 0x0242, /* R25 - Audio Interface 1 */ | ||
77 | 0x0008, /* R26 - Audio Interface 2 */ | ||
78 | 0x0022, /* R27 - Audio Interface 3 */ | ||
79 | 0x0000, /* R28 */ | ||
80 | 0x0000, /* R29 */ | ||
81 | 0x00C0, /* R30 - DAC Digital Volume Left */ | ||
82 | 0x00C0, /* R31 - DAC Digital Volume Right */ | ||
83 | 0x0000, /* R32 - DAC Digital 0 */ | ||
84 | 0x0000, /* R33 - DAC Digital 1 */ | ||
85 | 0x0000, /* R34 */ | ||
86 | 0x0000, /* R35 */ | ||
87 | 0x00C0, /* R36 - ADC Digital Volume Left */ | ||
88 | 0x00C0, /* R37 - ADC Digital Volume Right */ | ||
89 | 0x0000, /* R38 - ADC Digital 0 */ | ||
90 | 0x0073, /* R39 - Digital Microphone 0 */ | ||
91 | 0x09BF, /* R40 - DRC 0 */ | ||
92 | 0x3241, /* R41 - DRC 1 */ | ||
93 | 0x0020, /* R42 - DRC 2 */ | ||
94 | 0x0000, /* R43 - DRC 3 */ | ||
95 | 0x0085, /* R44 - Analogue Left Input 0 */ | ||
96 | 0x0085, /* R45 - Analogue Right Input 0 */ | ||
97 | 0x0044, /* R46 - Analogue Left Input 1 */ | ||
98 | 0x0044, /* R47 - Analogue Right Input 1 */ | ||
99 | 0x0000, /* R48 */ | ||
100 | 0x0000, /* R49 */ | ||
101 | 0x0008, /* R50 - Analogue Left Mix 0 */ | ||
102 | 0x0004, /* R51 - Analogue Right Mix 0 */ | ||
103 | 0x0000, /* R52 - Analogue Spk Mix Left 0 */ | ||
104 | 0x0000, /* R53 - Analogue Spk Mix Left 1 */ | ||
105 | 0x0000, /* R54 - Analogue Spk Mix Right 0 */ | ||
106 | 0x0000, /* R55 - Analogue Spk Mix Right 1 */ | ||
107 | 0x0000, /* R56 */ | ||
108 | 0x002D, /* R57 - Analogue OUT1 Left */ | ||
109 | 0x002D, /* R58 - Analogue OUT1 Right */ | ||
110 | 0x0039, /* R59 - Analogue OUT2 Left */ | ||
111 | 0x0039, /* R60 - Analogue OUT2 Right */ | ||
112 | 0x0100, /* R61 */ | ||
113 | 0x0139, /* R62 - Analogue OUT3 Left */ | ||
114 | 0x0139, /* R63 - Analogue OUT3 Right */ | ||
115 | 0x0000, /* R64 */ | ||
116 | 0x0000, /* R65 - Analogue SPK Output Control 0 */ | ||
117 | 0x0000, /* R66 */ | ||
118 | 0x0010, /* R67 - DC Servo 0 */ | ||
119 | 0x0100, /* R68 */ | ||
120 | 0x00A4, /* R69 - DC Servo 2 */ | ||
121 | 0x0807, /* R70 */ | ||
122 | 0x0000, /* R71 */ | ||
123 | 0x0000, /* R72 */ | ||
124 | 0x0000, /* R73 */ | ||
125 | 0x0000, /* R74 */ | ||
126 | 0x0000, /* R75 */ | ||
127 | 0x0000, /* R76 */ | ||
128 | 0x0000, /* R77 */ | ||
129 | 0x0000, /* R78 */ | ||
130 | 0x000E, /* R79 */ | ||
131 | 0x0000, /* R80 */ | ||
132 | 0x0000, /* R81 */ | ||
133 | 0x0000, /* R82 */ | ||
134 | 0x0000, /* R83 */ | ||
135 | 0x0000, /* R84 */ | ||
136 | 0x0000, /* R85 */ | ||
137 | 0x0000, /* R86 */ | ||
138 | 0x0006, /* R87 */ | ||
139 | 0x0000, /* R88 */ | ||
140 | 0x0000, /* R89 */ | ||
141 | 0x0000, /* R90 - Analogue HP 0 */ | ||
142 | 0x0060, /* R91 */ | ||
143 | 0x0000, /* R92 */ | ||
144 | 0x0000, /* R93 */ | ||
145 | 0x0000, /* R94 - Analogue Lineout 0 */ | ||
146 | 0x0060, /* R95 */ | ||
147 | 0x0000, /* R96 */ | ||
148 | 0x0000, /* R97 */ | ||
149 | 0x0000, /* R98 - Charge Pump 0 */ | ||
150 | 0x1F25, /* R99 */ | ||
151 | 0x2B19, /* R100 */ | ||
152 | 0x01C0, /* R101 */ | ||
153 | 0x01EF, /* R102 */ | ||
154 | 0x2B00, /* R103 */ | ||
155 | 0x0000, /* R104 - Class W 0 */ | ||
156 | 0x01C0, /* R105 */ | ||
157 | 0x1C10, /* R106 */ | ||
158 | 0x0000, /* R107 */ | ||
159 | 0x0000, /* R108 - Write Sequencer 0 */ | ||
160 | 0x0000, /* R109 - Write Sequencer 1 */ | ||
161 | 0x0000, /* R110 - Write Sequencer 2 */ | ||
162 | 0x0000, /* R111 - Write Sequencer 3 */ | ||
163 | 0x0000, /* R112 - Write Sequencer 4 */ | ||
164 | 0x0000, /* R113 */ | ||
165 | 0x0000, /* R114 - Control Interface */ | ||
166 | 0x0000, /* R115 */ | ||
167 | 0x00A8, /* R116 - GPIO Control 1 */ | ||
168 | 0x00A8, /* R117 - GPIO Control 2 */ | ||
169 | 0x00A8, /* R118 - GPIO Control 3 */ | ||
170 | 0x0220, /* R119 - GPIO Control 4 */ | ||
171 | 0x01A0, /* R120 - GPIO Control 5 */ | ||
172 | 0x0000, /* R121 - Interrupt Status 1 */ | ||
173 | 0xFFFF, /* R122 - Interrupt Status 1 Mask */ | ||
174 | 0x0000, /* R123 - Interrupt Polarity 1 */ | ||
175 | 0x0000, /* R124 */ | ||
176 | 0x0003, /* R125 */ | ||
177 | 0x0000, /* R126 - Interrupt Control */ | ||
178 | 0x0000, /* R127 */ | ||
179 | 0x0005, /* R128 */ | ||
180 | 0x0000, /* R129 - Control Interface Test 1 */ | ||
181 | 0x0000, /* R130 */ | ||
182 | 0x0000, /* R131 */ | ||
183 | 0x0000, /* R132 */ | ||
184 | 0x0000, /* R133 */ | ||
185 | 0x0000, /* R134 */ | ||
186 | 0x03FF, /* R135 */ | ||
187 | 0x0007, /* R136 */ | ||
188 | 0x0040, /* R137 */ | ||
189 | 0x0000, /* R138 */ | ||
190 | 0x0000, /* R139 */ | ||
191 | 0x0000, /* R140 */ | ||
192 | 0x0000, /* R141 */ | ||
193 | 0x0000, /* R142 */ | ||
194 | 0x0000, /* R143 */ | ||
195 | 0x0000, /* R144 */ | ||
196 | 0x0000, /* R145 */ | ||
197 | 0x0000, /* R146 */ | ||
198 | 0x0000, /* R147 */ | ||
199 | 0x4000, /* R148 */ | ||
200 | 0x6810, /* R149 - Charge Pump Test 1 */ | ||
201 | 0x0004, /* R150 */ | ||
202 | 0x0000, /* R151 */ | ||
203 | 0x0000, /* R152 */ | ||
204 | 0x0000, /* R153 */ | ||
205 | 0x0000, /* R154 */ | ||
206 | 0x0000, /* R155 */ | ||
207 | 0x0000, /* R156 */ | ||
208 | 0x0000, /* R157 */ | ||
209 | 0x0000, /* R158 */ | ||
210 | 0x0000, /* R159 */ | ||
211 | 0x0000, /* R160 */ | ||
212 | 0x0000, /* R161 */ | ||
213 | 0x0000, /* R162 */ | ||
214 | 0x0000, /* R163 */ | ||
215 | 0x0028, /* R164 - Clock Rate Test 4 */ | ||
216 | 0x0004, /* R165 */ | ||
217 | 0x0000, /* R166 */ | ||
218 | 0x0060, /* R167 */ | ||
219 | 0x0000, /* R168 */ | ||
220 | 0x0000, /* R169 */ | ||
221 | 0x0000, /* R170 */ | ||
222 | 0x0000, /* R171 */ | ||
223 | 0x0000, /* R172 - Analogue Output Bias 0 */ | ||
224 | }; | ||
225 | |||
226 | static unsigned int wm8903_read_reg_cache(struct snd_soc_codec *codec, | ||
227 | unsigned int reg) | ||
228 | { | ||
229 | u16 *cache = codec->reg_cache; | ||
230 | |||
231 | BUG_ON(reg >= ARRAY_SIZE(wm8903_reg_defaults)); | ||
232 | |||
233 | return cache[reg]; | ||
234 | } | ||
235 | |||
236 | static unsigned int wm8903_hw_read(struct snd_soc_codec *codec, u8 reg) | ||
237 | { | ||
238 | struct i2c_msg xfer[2]; | ||
239 | u16 data; | ||
240 | int ret; | ||
241 | struct i2c_client *client = codec->control_data; | ||
242 | |||
243 | /* Write register */ | ||
244 | xfer[0].addr = client->addr; | ||
245 | xfer[0].flags = 0; | ||
246 | xfer[0].len = 1; | ||
247 | xfer[0].buf = ® | ||
248 | |||
249 | /* Read data */ | ||
250 | xfer[1].addr = client->addr; | ||
251 | xfer[1].flags = I2C_M_RD; | ||
252 | xfer[1].len = 2; | ||
253 | xfer[1].buf = (u8 *)&data; | ||
254 | |||
255 | ret = i2c_transfer(client->adapter, xfer, 2); | ||
256 | if (ret != 2) { | ||
257 | pr_err("i2c_transfer returned %d\n", ret); | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | return (data >> 8) | ((data & 0xff) << 8); | ||
262 | } | ||
263 | |||
264 | static unsigned int wm8903_read(struct snd_soc_codec *codec, | ||
265 | unsigned int reg) | ||
266 | { | ||
267 | switch (reg) { | ||
268 | case WM8903_SW_RESET_AND_ID: | ||
269 | case WM8903_REVISION_NUMBER: | ||
270 | case WM8903_INTERRUPT_STATUS_1: | ||
271 | case WM8903_WRITE_SEQUENCER_4: | ||
272 | return wm8903_hw_read(codec, reg); | ||
273 | |||
274 | default: | ||
275 | return wm8903_read_reg_cache(codec, reg); | ||
276 | } | ||
277 | } | ||
278 | |||
279 | static void wm8903_write_reg_cache(struct snd_soc_codec *codec, | ||
280 | u16 reg, unsigned int value) | ||
281 | { | ||
282 | u16 *cache = codec->reg_cache; | ||
283 | |||
284 | BUG_ON(reg >= ARRAY_SIZE(wm8903_reg_defaults)); | ||
285 | |||
286 | switch (reg) { | ||
287 | case WM8903_SW_RESET_AND_ID: | ||
288 | case WM8903_REVISION_NUMBER: | ||
289 | break; | ||
290 | |||
291 | default: | ||
292 | cache[reg] = value; | ||
293 | break; | ||
294 | } | ||
295 | } | ||
296 | |||
297 | static int wm8903_write(struct snd_soc_codec *codec, unsigned int reg, | ||
298 | unsigned int value) | ||
299 | { | ||
300 | u8 data[3]; | ||
301 | |||
302 | wm8903_write_reg_cache(codec, reg, value); | ||
303 | |||
304 | /* Data format is 1 byte of address followed by 2 bytes of data */ | ||
305 | data[0] = reg; | ||
306 | data[1] = (value >> 8) & 0xff; | ||
307 | data[2] = value & 0xff; | ||
308 | |||
309 | if (codec->hw_write(codec->control_data, data, 3) == 2) | ||
310 | return 0; | ||
311 | else | ||
312 | return -EIO; | ||
313 | } | ||
314 | |||
315 | static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start) | ||
316 | { | ||
317 | u16 reg[5]; | ||
318 | struct i2c_client *i2c = codec->control_data; | ||
319 | |||
320 | BUG_ON(start > 48); | ||
321 | |||
322 | /* Enable the sequencer */ | ||
323 | reg[0] = wm8903_read(codec, WM8903_WRITE_SEQUENCER_0); | ||
324 | reg[0] |= WM8903_WSEQ_ENA; | ||
325 | wm8903_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]); | ||
326 | |||
327 | dev_dbg(&i2c->dev, "Starting sequence at %d\n", start); | ||
328 | |||
329 | wm8903_write(codec, WM8903_WRITE_SEQUENCER_3, | ||
330 | start | WM8903_WSEQ_START); | ||
331 | |||
332 | /* Wait for it to complete. If we have the interrupt wired up then | ||
333 | * we could block waiting for an interrupt, though polling may still | ||
334 | * be desirable for diagnostic purposes. | ||
335 | */ | ||
336 | do { | ||
337 | msleep(10); | ||
338 | |||
339 | reg[4] = wm8903_read(codec, WM8903_WRITE_SEQUENCER_4); | ||
340 | } while (reg[4] & WM8903_WSEQ_BUSY); | ||
341 | |||
342 | dev_dbg(&i2c->dev, "Sequence complete\n"); | ||
343 | |||
344 | /* Disable the sequencer again */ | ||
345 | wm8903_write(codec, WM8903_WRITE_SEQUENCER_0, | ||
346 | reg[0] & ~WM8903_WSEQ_ENA); | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache) | ||
352 | { | ||
353 | int i; | ||
354 | |||
355 | /* There really ought to be something better we can do here :/ */ | ||
356 | for (i = 0; i < ARRAY_SIZE(wm8903_reg_defaults); i++) | ||
357 | cache[i] = wm8903_hw_read(codec, i); | ||
358 | } | ||
359 | |||
360 | static void wm8903_reset(struct snd_soc_codec *codec) | ||
361 | { | ||
362 | wm8903_write(codec, WM8903_SW_RESET_AND_ID, 0); | ||
363 | } | ||
364 | |||
365 | #define WM8903_OUTPUT_SHORT 0x8 | ||
366 | #define WM8903_OUTPUT_OUT 0x4 | ||
367 | #define WM8903_OUTPUT_INT 0x2 | ||
368 | #define WM8903_OUTPUT_IN 0x1 | ||
369 | |||
370 | /* | ||
371 | * Event for headphone and line out amplifier power changes. Special | ||
372 | * power up/down sequences are required in order to maximise pop/click | ||
373 | * performance. | ||
374 | */ | ||
375 | static int wm8903_output_event(struct snd_soc_dapm_widget *w, | ||
376 | struct snd_kcontrol *kcontrol, int event) | ||
377 | { | ||
378 | struct snd_soc_codec *codec = w->codec; | ||
379 | struct wm8903_priv *wm8903 = codec->private_data; | ||
380 | struct i2c_client *i2c = codec->control_data; | ||
381 | u16 val; | ||
382 | u16 reg; | ||
383 | int shift; | ||
384 | u16 cp_reg = wm8903_read(codec, WM8903_CHARGE_PUMP_0); | ||
385 | |||
386 | switch (w->reg) { | ||
387 | case WM8903_POWER_MANAGEMENT_2: | ||
388 | reg = WM8903_ANALOGUE_HP_0; | ||
389 | break; | ||
390 | case WM8903_POWER_MANAGEMENT_3: | ||
391 | reg = WM8903_ANALOGUE_LINEOUT_0; | ||
392 | break; | ||
393 | default: | ||
394 | BUG(); | ||
395 | } | ||
396 | |||
397 | switch (w->shift) { | ||
398 | case 0: | ||
399 | shift = 0; | ||
400 | break; | ||
401 | case 1: | ||
402 | shift = 4; | ||
403 | break; | ||
404 | default: | ||
405 | BUG(); | ||
406 | } | ||
407 | |||
408 | if (event & SND_SOC_DAPM_PRE_PMU) { | ||
409 | val = wm8903_read(codec, reg); | ||
410 | |||
411 | /* Short the output */ | ||
412 | val &= ~(WM8903_OUTPUT_SHORT << shift); | ||
413 | wm8903_write(codec, reg, val); | ||
414 | |||
415 | wm8903->charge_pump_users++; | ||
416 | |||
417 | dev_dbg(&i2c->dev, "Charge pump use count now %d\n", | ||
418 | wm8903->charge_pump_users); | ||
419 | |||
420 | if (wm8903->charge_pump_users == 1) { | ||
421 | dev_dbg(&i2c->dev, "Enabling charge pump\n"); | ||
422 | wm8903_write(codec, WM8903_CHARGE_PUMP_0, | ||
423 | cp_reg | WM8903_CP_ENA); | ||
424 | mdelay(4); | ||
425 | } | ||
426 | } | ||
427 | |||
428 | if (event & SND_SOC_DAPM_POST_PMU) { | ||
429 | val = wm8903_read(codec, reg); | ||
430 | |||
431 | val |= (WM8903_OUTPUT_IN << shift); | ||
432 | wm8903_write(codec, reg, val); | ||
433 | |||
434 | val |= (WM8903_OUTPUT_INT << shift); | ||
435 | wm8903_write(codec, reg, val); | ||
436 | |||
437 | /* Turn on the output ENA_OUTP */ | ||
438 | val |= (WM8903_OUTPUT_OUT << shift); | ||
439 | wm8903_write(codec, reg, val); | ||
440 | |||
441 | /* Remove the short */ | ||
442 | val |= (WM8903_OUTPUT_SHORT << shift); | ||
443 | wm8903_write(codec, reg, val); | ||
444 | } | ||
445 | |||
446 | if (event & SND_SOC_DAPM_PRE_PMD) { | ||
447 | val = wm8903_read(codec, reg); | ||
448 | |||
449 | /* Short the output */ | ||
450 | val &= ~(WM8903_OUTPUT_SHORT << shift); | ||
451 | wm8903_write(codec, reg, val); | ||
452 | |||
453 | /* Then disable the intermediate and output stages */ | ||
454 | val &= ~((WM8903_OUTPUT_OUT | WM8903_OUTPUT_INT | | ||
455 | WM8903_OUTPUT_IN) << shift); | ||
456 | wm8903_write(codec, reg, val); | ||
457 | } | ||
458 | |||
459 | if (event & SND_SOC_DAPM_POST_PMD) { | ||
460 | wm8903->charge_pump_users--; | ||
461 | |||
462 | dev_dbg(&i2c->dev, "Charge pump use count now %d\n", | ||
463 | wm8903->charge_pump_users); | ||
464 | |||
465 | if (wm8903->charge_pump_users == 0) { | ||
466 | dev_dbg(&i2c->dev, "Disabling charge pump\n"); | ||
467 | wm8903_write(codec, WM8903_CHARGE_PUMP_0, | ||
468 | cp_reg & ~WM8903_CP_ENA); | ||
469 | } | ||
470 | } | ||
471 | |||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | /* | ||
476 | * When used with DAC outputs only the WM8903 charge pump supports | ||
477 | * operation in class W mode, providing very low power consumption | ||
478 | * when used with digital sources. Enable and disable this mode | ||
479 | * automatically depending on the mixer configuration. | ||
480 | * | ||
481 | * All the relevant controls are simple switches. | ||
482 | */ | ||
483 | static int wm8903_class_w_put(struct snd_kcontrol *kcontrol, | ||
484 | struct snd_ctl_elem_value *ucontrol) | ||
485 | { | ||
486 | struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); | ||
487 | struct snd_soc_codec *codec = widget->codec; | ||
488 | struct wm8903_priv *wm8903 = codec->private_data; | ||
489 | struct i2c_client *i2c = codec->control_data; | ||
490 | u16 reg; | ||
491 | int ret; | ||
492 | |||
493 | reg = wm8903_read(codec, WM8903_CLASS_W_0); | ||
494 | |||
495 | /* Turn it off if we're about to enable bypass */ | ||
496 | if (ucontrol->value.integer.value[0]) { | ||
497 | if (wm8903->class_w_users == 0) { | ||
498 | dev_dbg(&i2c->dev, "Disabling Class W\n"); | ||
499 | wm8903_write(codec, WM8903_CLASS_W_0, reg & | ||
500 | ~(WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V)); | ||
501 | } | ||
502 | wm8903->class_w_users++; | ||
503 | } | ||
504 | |||
505 | /* Implement the change */ | ||
506 | ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); | ||
507 | |||
508 | /* If we've just disabled the last bypass path turn Class W on */ | ||
509 | if (!ucontrol->value.integer.value[0]) { | ||
510 | if (wm8903->class_w_users == 1) { | ||
511 | dev_dbg(&i2c->dev, "Enabling Class W\n"); | ||
512 | wm8903_write(codec, WM8903_CLASS_W_0, reg | | ||
513 | WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V); | ||
514 | } | ||
515 | wm8903->class_w_users--; | ||
516 | } | ||
517 | |||
518 | dev_dbg(&i2c->dev, "Bypass use count now %d\n", | ||
519 | wm8903->class_w_users); | ||
520 | |||
521 | return ret; | ||
522 | } | ||
523 | |||
524 | #define SOC_DAPM_SINGLE_W(xname, reg, shift, max, invert) \ | ||
525 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | ||
526 | .info = snd_soc_info_volsw, \ | ||
527 | .get = snd_soc_dapm_get_volsw, .put = wm8903_class_w_put, \ | ||
528 | .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } | ||
529 | |||
530 | |||
531 | /* ALSA can only do steps of .01dB */ | ||
532 | static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); | ||
533 | |||
534 | static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0); | ||
535 | |||
536 | static const DECLARE_TLV_DB_SCALE(drc_tlv_thresh, 0, 75, 0); | ||
537 | static const DECLARE_TLV_DB_SCALE(drc_tlv_amp, -2250, 75, 0); | ||
538 | static const DECLARE_TLV_DB_SCALE(drc_tlv_min, 0, 600, 0); | ||
539 | static const DECLARE_TLV_DB_SCALE(drc_tlv_max, 1200, 600, 0); | ||
540 | static const DECLARE_TLV_DB_SCALE(drc_tlv_startup, -300, 50, 0); | ||
541 | |||
542 | static const char *drc_slope_text[] = { | ||
543 | "1", "1/2", "1/4", "1/8", "1/16", "0" | ||
544 | }; | ||
545 | |||
546 | static const struct soc_enum drc_slope_r0 = | ||
547 | SOC_ENUM_SINGLE(WM8903_DRC_2, 3, 6, drc_slope_text); | ||
548 | |||
549 | static const struct soc_enum drc_slope_r1 = | ||
550 | SOC_ENUM_SINGLE(WM8903_DRC_2, 0, 6, drc_slope_text); | ||
551 | |||
552 | static const char *drc_attack_text[] = { | ||
553 | "instantaneous", | ||
554 | "363us", "762us", "1.45ms", "2.9ms", "5.8ms", "11.6ms", "23.2ms", | ||
555 | "46.4ms", "92.8ms", "185.6ms" | ||
556 | }; | ||
557 | |||
558 | static const struct soc_enum drc_attack = | ||
559 | SOC_ENUM_SINGLE(WM8903_DRC_1, 12, 11, drc_attack_text); | ||
560 | |||
561 | static const char *drc_decay_text[] = { | ||
562 | "186ms", "372ms", "743ms", "1.49s", "2.97s", "5.94s", "11.89s", | ||
563 | "23.87s", "47.56s" | ||
564 | }; | ||
565 | |||
566 | static const struct soc_enum drc_decay = | ||
567 | SOC_ENUM_SINGLE(WM8903_DRC_1, 8, 9, drc_decay_text); | ||
568 | |||
569 | static const char *drc_ff_delay_text[] = { | ||
570 | "5 samples", "9 samples" | ||
571 | }; | ||
572 | |||
573 | static const struct soc_enum drc_ff_delay = | ||
574 | SOC_ENUM_SINGLE(WM8903_DRC_0, 5, 2, drc_ff_delay_text); | ||
575 | |||
576 | static const char *drc_qr_decay_text[] = { | ||
577 | "0.725ms", "1.45ms", "5.8ms" | ||
578 | }; | ||
579 | |||
580 | static const struct soc_enum drc_qr_decay = | ||
581 | SOC_ENUM_SINGLE(WM8903_DRC_1, 4, 3, drc_qr_decay_text); | ||
582 | |||
583 | static const char *drc_smoothing_text[] = { | ||
584 | "Low", "Medium", "High" | ||
585 | }; | ||
586 | |||
587 | static const struct soc_enum drc_smoothing = | ||
588 | SOC_ENUM_SINGLE(WM8903_DRC_0, 11, 3, drc_smoothing_text); | ||
589 | |||
590 | static const char *soft_mute_text[] = { | ||
591 | "Fast (fs/2)", "Slow (fs/32)" | ||
592 | }; | ||
593 | |||
594 | static const struct soc_enum soft_mute = | ||
595 | SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 10, 2, soft_mute_text); | ||
596 | |||
597 | static const char *mute_mode_text[] = { | ||
598 | "Hard", "Soft" | ||
599 | }; | ||
600 | |||
601 | static const struct soc_enum mute_mode = | ||
602 | SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 9, 2, mute_mode_text); | ||
603 | |||
604 | static const char *dac_deemphasis_text[] = { | ||
605 | "Disabled", "32kHz", "44.1kHz", "48kHz" | ||
606 | }; | ||
607 | |||
608 | static const struct soc_enum dac_deemphasis = | ||
609 | SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 1, 4, dac_deemphasis_text); | ||
610 | |||
611 | static const char *companding_text[] = { | ||
612 | "ulaw", "alaw" | ||
613 | }; | ||
614 | |||
615 | static const struct soc_enum dac_companding = | ||
616 | SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 0, 2, companding_text); | ||
617 | |||
618 | static const struct soc_enum adc_companding = | ||
619 | SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 2, 2, companding_text); | ||
620 | |||
621 | static const char *input_mode_text[] = { | ||
622 | "Single-Ended", "Differential Line", "Differential Mic" | ||
623 | }; | ||
624 | |||
625 | static const struct soc_enum linput_mode_enum = | ||
626 | SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 0, 3, input_mode_text); | ||
627 | |||
628 | static const struct soc_enum rinput_mode_enum = | ||
629 | SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 0, 3, input_mode_text); | ||
630 | |||
631 | static const char *linput_mux_text[] = { | ||
632 | "IN1L", "IN2L", "IN3L" | ||
633 | }; | ||
634 | |||
635 | static const struct soc_enum linput_enum = | ||
636 | SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 2, 3, linput_mux_text); | ||
637 | |||
638 | static const struct soc_enum linput_inv_enum = | ||
639 | SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 4, 3, linput_mux_text); | ||
640 | |||
641 | static const char *rinput_mux_text[] = { | ||
642 | "IN1R", "IN2R", "IN3R" | ||
643 | }; | ||
644 | |||
645 | static const struct soc_enum rinput_enum = | ||
646 | SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 2, 3, rinput_mux_text); | ||
647 | |||
648 | static const struct soc_enum rinput_inv_enum = | ||
649 | SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 4, 3, rinput_mux_text); | ||
650 | |||
651 | |||
652 | static const struct snd_kcontrol_new wm8903_snd_controls[] = { | ||
653 | |||
654 | /* Input PGAs - No TLV since the scale depends on PGA mode */ | ||
655 | SOC_SINGLE("Left Input PGA Switch", WM8903_ANALOGUE_LEFT_INPUT_0, | ||
656 | 7, 1, 1), | ||
657 | SOC_SINGLE("Left Input PGA Volume", WM8903_ANALOGUE_LEFT_INPUT_0, | ||
658 | 0, 31, 0), | ||
659 | SOC_SINGLE("Left Input PGA Common Mode Switch", WM8903_ANALOGUE_LEFT_INPUT_1, | ||
660 | 6, 1, 0), | ||
661 | |||
662 | SOC_SINGLE("Right Input PGA Switch", WM8903_ANALOGUE_RIGHT_INPUT_0, | ||
663 | 7, 1, 1), | ||
664 | SOC_SINGLE("Right Input PGA Volume", WM8903_ANALOGUE_RIGHT_INPUT_0, | ||
665 | 0, 31, 0), | ||
666 | SOC_SINGLE("Right Input PGA Common Mode Switch", WM8903_ANALOGUE_RIGHT_INPUT_1, | ||
667 | 6, 1, 0), | ||
668 | |||
669 | /* ADCs */ | ||
670 | SOC_SINGLE("DRC Switch", WM8903_DRC_0, 15, 1, 0), | ||
671 | SOC_ENUM("DRC Compressor Slope R0", drc_slope_r0), | ||
672 | SOC_ENUM("DRC Compressor Slope R1", drc_slope_r1), | ||
673 | SOC_SINGLE_TLV("DRC Compressor Threashold Volume", WM8903_DRC_3, 5, 124, 1, | ||
674 | drc_tlv_thresh), | ||
675 | SOC_SINGLE_TLV("DRC Volume", WM8903_DRC_3, 0, 30, 1, drc_tlv_amp), | ||
676 | SOC_SINGLE_TLV("DRC Minimum Gain Volume", WM8903_DRC_1, 2, 3, 1, drc_tlv_min), | ||
677 | SOC_SINGLE_TLV("DRC Maximum Gain Volume", WM8903_DRC_1, 0, 3, 0, drc_tlv_max), | ||
678 | SOC_ENUM("DRC Attack Rate", drc_attack), | ||
679 | SOC_ENUM("DRC Decay Rate", drc_decay), | ||
680 | SOC_ENUM("DRC FF Delay", drc_ff_delay), | ||
681 | SOC_SINGLE("DRC Anticlip Switch", WM8903_DRC_0, 1, 1, 0), | ||
682 | SOC_SINGLE("DRC QR Switch", WM8903_DRC_0, 2, 1, 0), | ||
683 | SOC_SINGLE_TLV("DRC QR Threashold Volume", WM8903_DRC_0, 6, 3, 0, drc_tlv_max), | ||
684 | SOC_ENUM("DRC QR Decay Rate", drc_qr_decay), | ||
685 | SOC_SINGLE("DRC Smoothing Switch", WM8903_DRC_0, 3, 1, 0), | ||
686 | SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8903_DRC_0, 0, 1, 0), | ||
687 | SOC_ENUM("DRC Smoothing Threashold", drc_smoothing), | ||
688 | SOC_SINGLE_TLV("DRC Startup Volume", WM8903_DRC_0, 6, 18, 0, drc_tlv_startup), | ||
689 | |||
690 | SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8903_ADC_DIGITAL_VOLUME_LEFT, | ||
691 | WM8903_ADC_DIGITAL_VOLUME_RIGHT, 1, 96, 0, digital_tlv), | ||
692 | SOC_ENUM("ADC Companding Mode", adc_companding), | ||
693 | SOC_SINGLE("ADC Companding Switch", WM8903_AUDIO_INTERFACE_0, 3, 1, 0), | ||
694 | |||
695 | /* DAC */ | ||
696 | SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8903_DAC_DIGITAL_VOLUME_LEFT, | ||
697 | WM8903_DAC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv), | ||
698 | SOC_ENUM("DAC Soft Mute Rate", soft_mute), | ||
699 | SOC_ENUM("DAC Mute Mode", mute_mode), | ||
700 | SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0), | ||
701 | SOC_ENUM("DAC De-emphasis", dac_deemphasis), | ||
702 | SOC_SINGLE("DAC Sloping Stopband Filter Switch", | ||
703 | WM8903_DAC_DIGITAL_1, 11, 1, 0), | ||
704 | SOC_ENUM("DAC Companding Mode", dac_companding), | ||
705 | SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0), | ||
706 | |||
707 | /* Headphones */ | ||
708 | SOC_DOUBLE_R("Headphone Switch", | ||
709 | WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT, | ||
710 | 8, 1, 1), | ||
711 | SOC_DOUBLE_R("Headphone ZC Switch", | ||
712 | WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT, | ||
713 | 6, 1, 0), | ||
714 | SOC_DOUBLE_R_TLV("Headphone Volume", | ||
715 | WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT, | ||
716 | 0, 63, 0, out_tlv), | ||
717 | |||
718 | /* Line out */ | ||
719 | SOC_DOUBLE_R("Line Out Switch", | ||
720 | WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT, | ||
721 | 8, 1, 1), | ||
722 | SOC_DOUBLE_R("Line Out ZC Switch", | ||
723 | WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT, | ||
724 | 6, 1, 0), | ||
725 | SOC_DOUBLE_R_TLV("Line Out Volume", | ||
726 | WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT, | ||
727 | 0, 63, 0, out_tlv), | ||
728 | |||
729 | /* Speaker */ | ||
730 | SOC_DOUBLE_R("Speaker Switch", | ||
731 | WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, 8, 1, 1), | ||
732 | SOC_DOUBLE_R("Speaker ZC Switch", | ||
733 | WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, 6, 1, 0), | ||
734 | SOC_DOUBLE_R_TLV("Speaker Volume", | ||
735 | WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, | ||
736 | 0, 63, 0, out_tlv), | ||
737 | }; | ||
738 | |||
739 | static int wm8903_add_controls(struct snd_soc_codec *codec) | ||
740 | { | ||
741 | int err, i; | ||
742 | |||
743 | for (i = 0; i < ARRAY_SIZE(wm8903_snd_controls); i++) { | ||
744 | err = snd_ctl_add(codec->card, | ||
745 | snd_soc_cnew(&wm8903_snd_controls[i], | ||
746 | codec, NULL)); | ||
747 | if (err < 0) | ||
748 | return err; | ||
749 | } | ||
750 | |||
751 | return 0; | ||
752 | } | ||
753 | |||
754 | static const struct snd_kcontrol_new linput_mode_mux = | ||
755 | SOC_DAPM_ENUM("Left Input Mode Mux", linput_mode_enum); | ||
756 | |||
757 | static const struct snd_kcontrol_new rinput_mode_mux = | ||
758 | SOC_DAPM_ENUM("Right Input Mode Mux", rinput_mode_enum); | ||
759 | |||
760 | static const struct snd_kcontrol_new linput_mux = | ||
761 | SOC_DAPM_ENUM("Left Input Mux", linput_enum); | ||
762 | |||
763 | static const struct snd_kcontrol_new linput_inv_mux = | ||
764 | SOC_DAPM_ENUM("Left Inverting Input Mux", linput_inv_enum); | ||
765 | |||
766 | static const struct snd_kcontrol_new rinput_mux = | ||
767 | SOC_DAPM_ENUM("Right Input Mux", rinput_enum); | ||
768 | |||
769 | static const struct snd_kcontrol_new rinput_inv_mux = | ||
770 | SOC_DAPM_ENUM("Right Inverting Input Mux", rinput_inv_enum); | ||
771 | |||
772 | static const struct snd_kcontrol_new left_output_mixer[] = { | ||
773 | SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0), | ||
774 | SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0), | ||
775 | SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0), | ||
776 | SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0), | ||
777 | }; | ||
778 | |||
779 | static const struct snd_kcontrol_new right_output_mixer[] = { | ||
780 | SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 3, 1, 0), | ||
781 | SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 2, 1, 0), | ||
782 | SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0), | ||
783 | SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0), | ||
784 | }; | ||
785 | |||
786 | static const struct snd_kcontrol_new left_speaker_mixer[] = { | ||
787 | SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 3, 1, 0), | ||
788 | SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 2, 1, 0), | ||
789 | SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 1, 1, 0), | ||
790 | SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, | ||
791 | 1, 1, 0), | ||
792 | }; | ||
793 | |||
794 | static const struct snd_kcontrol_new right_speaker_mixer[] = { | ||
795 | SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 3, 1, 0), | ||
796 | SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 2, 1, 0), | ||
797 | SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, | ||
798 | 1, 1, 0), | ||
799 | SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, | ||
800 | 1, 1, 0), | ||
801 | }; | ||
802 | |||
803 | static const struct snd_soc_dapm_widget wm8903_dapm_widgets[] = { | ||
804 | SND_SOC_DAPM_INPUT("IN1L"), | ||
805 | SND_SOC_DAPM_INPUT("IN1R"), | ||
806 | SND_SOC_DAPM_INPUT("IN2L"), | ||
807 | SND_SOC_DAPM_INPUT("IN2R"), | ||
808 | SND_SOC_DAPM_INPUT("IN3L"), | ||
809 | SND_SOC_DAPM_INPUT("IN3R"), | ||
810 | |||
811 | SND_SOC_DAPM_OUTPUT("HPOUTL"), | ||
812 | SND_SOC_DAPM_OUTPUT("HPOUTR"), | ||
813 | SND_SOC_DAPM_OUTPUT("LINEOUTL"), | ||
814 | SND_SOC_DAPM_OUTPUT("LINEOUTR"), | ||
815 | SND_SOC_DAPM_OUTPUT("LOP"), | ||
816 | SND_SOC_DAPM_OUTPUT("LON"), | ||
817 | SND_SOC_DAPM_OUTPUT("ROP"), | ||
818 | SND_SOC_DAPM_OUTPUT("RON"), | ||
819 | |||
820 | SND_SOC_DAPM_MICBIAS("Mic Bias", WM8903_MIC_BIAS_CONTROL_0, 0, 0), | ||
821 | |||
822 | SND_SOC_DAPM_MUX("Left Input Mux", SND_SOC_NOPM, 0, 0, &linput_mux), | ||
823 | SND_SOC_DAPM_MUX("Left Input Inverting Mux", SND_SOC_NOPM, 0, 0, | ||
824 | &linput_inv_mux), | ||
825 | SND_SOC_DAPM_MUX("Left Input Mode Mux", SND_SOC_NOPM, 0, 0, &linput_mode_mux), | ||
826 | |||
827 | SND_SOC_DAPM_MUX("Right Input Mux", SND_SOC_NOPM, 0, 0, &rinput_mux), | ||
828 | SND_SOC_DAPM_MUX("Right Input Inverting Mux", SND_SOC_NOPM, 0, 0, | ||
829 | &rinput_inv_mux), | ||
830 | SND_SOC_DAPM_MUX("Right Input Mode Mux", SND_SOC_NOPM, 0, 0, &rinput_mode_mux), | ||
831 | |||
832 | SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0), | ||
833 | SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0), | ||
834 | |||
835 | SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8903_POWER_MANAGEMENT_6, 1, 0), | ||
836 | SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8903_POWER_MANAGEMENT_6, 0, 0), | ||
837 | |||
838 | SND_SOC_DAPM_DAC("DACL", "Left Playback", WM8903_POWER_MANAGEMENT_6, 3, 0), | ||
839 | SND_SOC_DAPM_DAC("DACR", "Right Playback", WM8903_POWER_MANAGEMENT_6, 2, 0), | ||
840 | |||
841 | SND_SOC_DAPM_MIXER("Left Output Mixer", WM8903_POWER_MANAGEMENT_1, 1, 0, | ||
842 | left_output_mixer, ARRAY_SIZE(left_output_mixer)), | ||
843 | SND_SOC_DAPM_MIXER("Right Output Mixer", WM8903_POWER_MANAGEMENT_1, 0, 0, | ||
844 | right_output_mixer, ARRAY_SIZE(right_output_mixer)), | ||
845 | |||
846 | SND_SOC_DAPM_MIXER("Left Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 1, 0, | ||
847 | left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), | ||
848 | SND_SOC_DAPM_MIXER("Right Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 0, 0, | ||
849 | right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), | ||
850 | |||
851 | SND_SOC_DAPM_PGA_E("Left Headphone Output PGA", WM8903_POWER_MANAGEMENT_2, | ||
852 | 1, 0, NULL, 0, wm8903_output_event, | ||
853 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | | ||
854 | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), | ||
855 | SND_SOC_DAPM_PGA_E("Right Headphone Output PGA", WM8903_POWER_MANAGEMENT_2, | ||
856 | 0, 0, NULL, 0, wm8903_output_event, | ||
857 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | | ||
858 | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), | ||
859 | |||
860 | SND_SOC_DAPM_PGA_E("Left Line Output PGA", WM8903_POWER_MANAGEMENT_3, 1, 0, | ||
861 | NULL, 0, wm8903_output_event, | ||
862 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | | ||
863 | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), | ||
864 | SND_SOC_DAPM_PGA_E("Right Line Output PGA", WM8903_POWER_MANAGEMENT_3, 0, 0, | ||
865 | NULL, 0, wm8903_output_event, | ||
866 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | | ||
867 | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), | ||
868 | |||
869 | SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0, | ||
870 | NULL, 0), | ||
871 | SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0, | ||
872 | NULL, 0), | ||
873 | |||
874 | }; | ||
875 | |||
876 | static const struct snd_soc_dapm_route intercon[] = { | ||
877 | |||
878 | { "Left Input Mux", "IN1L", "IN1L" }, | ||
879 | { "Left Input Mux", "IN2L", "IN2L" }, | ||
880 | { "Left Input Mux", "IN3L", "IN3L" }, | ||
881 | |||
882 | { "Left Input Inverting Mux", "IN1L", "IN1L" }, | ||
883 | { "Left Input Inverting Mux", "IN2L", "IN2L" }, | ||
884 | { "Left Input Inverting Mux", "IN3L", "IN3L" }, | ||
885 | |||
886 | { "Right Input Mux", "IN1R", "IN1R" }, | ||
887 | { "Right Input Mux", "IN2R", "IN2R" }, | ||
888 | { "Right Input Mux", "IN3R", "IN3R" }, | ||
889 | |||
890 | { "Right Input Inverting Mux", "IN1R", "IN1R" }, | ||
891 | { "Right Input Inverting Mux", "IN2R", "IN2R" }, | ||
892 | { "Right Input Inverting Mux", "IN3R", "IN3R" }, | ||
893 | |||
894 | { "Left Input Mode Mux", "Single-Ended", "Left Input Inverting Mux" }, | ||
895 | { "Left Input Mode Mux", "Differential Line", | ||
896 | "Left Input Mux" }, | ||
897 | { "Left Input Mode Mux", "Differential Line", | ||
898 | "Left Input Inverting Mux" }, | ||
899 | { "Left Input Mode Mux", "Differential Mic", | ||
900 | "Left Input Mux" }, | ||
901 | { "Left Input Mode Mux", "Differential Mic", | ||
902 | "Left Input Inverting Mux" }, | ||
903 | |||
904 | { "Right Input Mode Mux", "Single-Ended", | ||
905 | "Right Input Inverting Mux" }, | ||
906 | { "Right Input Mode Mux", "Differential Line", | ||
907 | "Right Input Mux" }, | ||
908 | { "Right Input Mode Mux", "Differential Line", | ||
909 | "Right Input Inverting Mux" }, | ||
910 | { "Right Input Mode Mux", "Differential Mic", | ||
911 | "Right Input Mux" }, | ||
912 | { "Right Input Mode Mux", "Differential Mic", | ||
913 | "Right Input Inverting Mux" }, | ||
914 | |||
915 | { "Left Input PGA", NULL, "Left Input Mode Mux" }, | ||
916 | { "Right Input PGA", NULL, "Right Input Mode Mux" }, | ||
917 | |||
918 | { "ADCL", NULL, "Left Input PGA" }, | ||
919 | { "ADCR", NULL, "Right Input PGA" }, | ||
920 | |||
921 | { "Left Output Mixer", "Left Bypass Switch", "Left Input PGA" }, | ||
922 | { "Left Output Mixer", "Right Bypass Switch", "Right Input PGA" }, | ||
923 | { "Left Output Mixer", "DACL Switch", "DACL" }, | ||
924 | { "Left Output Mixer", "DACR Switch", "DACR" }, | ||
925 | |||
926 | { "Right Output Mixer", "Left Bypass Switch", "Left Input PGA" }, | ||
927 | { "Right Output Mixer", "Right Bypass Switch", "Right Input PGA" }, | ||
928 | { "Right Output Mixer", "DACL Switch", "DACL" }, | ||
929 | { "Right Output Mixer", "DACR Switch", "DACR" }, | ||
930 | |||
931 | { "Left Speaker Mixer", "Left Bypass Switch", "Left Input PGA" }, | ||
932 | { "Left Speaker Mixer", "Right Bypass Switch", "Right Input PGA" }, | ||
933 | { "Left Speaker Mixer", "DACL Switch", "DACL" }, | ||
934 | { "Left Speaker Mixer", "DACR Switch", "DACR" }, | ||
935 | |||
936 | { "Right Speaker Mixer", "Left Bypass Switch", "Left Input PGA" }, | ||
937 | { "Right Speaker Mixer", "Right Bypass Switch", "Right Input PGA" }, | ||
938 | { "Right Speaker Mixer", "DACL Switch", "DACL" }, | ||
939 | { "Right Speaker Mixer", "DACR Switch", "DACR" }, | ||
940 | |||
941 | { "Left Line Output PGA", NULL, "Left Output Mixer" }, | ||
942 | { "Right Line Output PGA", NULL, "Right Output Mixer" }, | ||
943 | |||
944 | { "Left Headphone Output PGA", NULL, "Left Output Mixer" }, | ||
945 | { "Right Headphone Output PGA", NULL, "Right Output Mixer" }, | ||
946 | |||
947 | { "Left Speaker PGA", NULL, "Left Speaker Mixer" }, | ||
948 | { "Right Speaker PGA", NULL, "Right Speaker Mixer" }, | ||
949 | |||
950 | { "HPOUTL", NULL, "Left Headphone Output PGA" }, | ||
951 | { "HPOUTR", NULL, "Right Headphone Output PGA" }, | ||
952 | |||
953 | { "LINEOUTL", NULL, "Left Line Output PGA" }, | ||
954 | { "LINEOUTR", NULL, "Right Line Output PGA" }, | ||
955 | |||
956 | { "LOP", NULL, "Left Speaker PGA" }, | ||
957 | { "LON", NULL, "Left Speaker PGA" }, | ||
958 | |||
959 | { "ROP", NULL, "Right Speaker PGA" }, | ||
960 | { "RON", NULL, "Right Speaker PGA" }, | ||
961 | }; | ||
962 | |||
963 | static int wm8903_add_widgets(struct snd_soc_codec *codec) | ||
964 | { | ||
965 | snd_soc_dapm_new_controls(codec, wm8903_dapm_widgets, | ||
966 | ARRAY_SIZE(wm8903_dapm_widgets)); | ||
967 | |||
968 | snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); | ||
969 | |||
970 | snd_soc_dapm_new_widgets(codec); | ||
971 | |||
972 | return 0; | ||
973 | } | ||
974 | |||
975 | static int wm8903_set_bias_level(struct snd_soc_codec *codec, | ||
976 | enum snd_soc_bias_level level) | ||
977 | { | ||
978 | struct i2c_client *i2c = codec->control_data; | ||
979 | u16 reg, reg2; | ||
980 | |||
981 | switch (level) { | ||
982 | case SND_SOC_BIAS_ON: | ||
983 | case SND_SOC_BIAS_PREPARE: | ||
984 | reg = wm8903_read(codec, WM8903_VMID_CONTROL_0); | ||
985 | reg &= ~(WM8903_VMID_RES_MASK); | ||
986 | reg |= WM8903_VMID_RES_50K; | ||
987 | wm8903_write(codec, WM8903_VMID_CONTROL_0, reg); | ||
988 | break; | ||
989 | |||
990 | case SND_SOC_BIAS_STANDBY: | ||
991 | if (codec->bias_level == SND_SOC_BIAS_OFF) { | ||
992 | wm8903_run_sequence(codec, 0); | ||
993 | wm8903_sync_reg_cache(codec, codec->reg_cache); | ||
994 | |||
995 | /* Enable low impedence charge pump output */ | ||
996 | reg = wm8903_read(codec, | ||
997 | WM8903_CONTROL_INTERFACE_TEST_1); | ||
998 | wm8903_write(codec, WM8903_CONTROL_INTERFACE_TEST_1, | ||
999 | reg | WM8903_TEST_KEY); | ||
1000 | reg2 = wm8903_read(codec, WM8903_CHARGE_PUMP_TEST_1); | ||
1001 | wm8903_write(codec, WM8903_CHARGE_PUMP_TEST_1, | ||
1002 | reg2 | WM8903_CP_SW_KELVIN_MODE_MASK); | ||
1003 | wm8903_write(codec, WM8903_CONTROL_INTERFACE_TEST_1, | ||
1004 | reg); | ||
1005 | |||
1006 | /* By default no bypass paths are enabled so | ||
1007 | * enable Class W support. | ||
1008 | */ | ||
1009 | dev_dbg(&i2c->dev, "Enabling Class W\n"); | ||
1010 | wm8903_write(codec, WM8903_CLASS_W_0, reg | | ||
1011 | WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V); | ||
1012 | } | ||
1013 | |||
1014 | reg = wm8903_read(codec, WM8903_VMID_CONTROL_0); | ||
1015 | reg &= ~(WM8903_VMID_RES_MASK); | ||
1016 | reg |= WM8903_VMID_RES_250K; | ||
1017 | wm8903_write(codec, WM8903_VMID_CONTROL_0, reg); | ||
1018 | break; | ||
1019 | |||
1020 | case SND_SOC_BIAS_OFF: | ||
1021 | wm8903_run_sequence(codec, 32); | ||
1022 | break; | ||
1023 | } | ||
1024 | |||
1025 | codec->bias_level = level; | ||
1026 | |||
1027 | return 0; | ||
1028 | } | ||
1029 | |||
1030 | static int wm8903_set_dai_sysclk(struct snd_soc_dai *codec_dai, | ||
1031 | int clk_id, unsigned int freq, int dir) | ||
1032 | { | ||
1033 | struct snd_soc_codec *codec = codec_dai->codec; | ||
1034 | struct wm8903_priv *wm8903 = codec->private_data; | ||
1035 | |||
1036 | wm8903->sysclk = freq; | ||
1037 | |||
1038 | return 0; | ||
1039 | } | ||
1040 | |||
1041 | static int wm8903_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
1042 | unsigned int fmt) | ||
1043 | { | ||
1044 | struct snd_soc_codec *codec = codec_dai->codec; | ||
1045 | u16 aif1 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_1); | ||
1046 | |||
1047 | aif1 &= ~(WM8903_LRCLK_DIR | WM8903_BCLK_DIR | WM8903_AIF_FMT_MASK | | ||
1048 | WM8903_AIF_LRCLK_INV | WM8903_AIF_BCLK_INV); | ||
1049 | |||
1050 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
1051 | case SND_SOC_DAIFMT_CBS_CFS: | ||
1052 | break; | ||
1053 | case SND_SOC_DAIFMT_CBS_CFM: | ||
1054 | aif1 |= WM8903_LRCLK_DIR; | ||
1055 | break; | ||
1056 | case SND_SOC_DAIFMT_CBM_CFM: | ||
1057 | aif1 |= WM8903_LRCLK_DIR | WM8903_BCLK_DIR; | ||
1058 | break; | ||
1059 | case SND_SOC_DAIFMT_CBM_CFS: | ||
1060 | aif1 |= WM8903_BCLK_DIR; | ||
1061 | break; | ||
1062 | default: | ||
1063 | return -EINVAL; | ||
1064 | } | ||
1065 | |||
1066 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
1067 | case SND_SOC_DAIFMT_DSP_A: | ||
1068 | aif1 |= 0x3; | ||
1069 | break; | ||
1070 | case SND_SOC_DAIFMT_DSP_B: | ||
1071 | aif1 |= 0x3 | WM8903_AIF_LRCLK_INV; | ||
1072 | break; | ||
1073 | case SND_SOC_DAIFMT_I2S: | ||
1074 | aif1 |= 0x2; | ||
1075 | break; | ||
1076 | case SND_SOC_DAIFMT_RIGHT_J: | ||
1077 | aif1 |= 0x1; | ||
1078 | break; | ||
1079 | case SND_SOC_DAIFMT_LEFT_J: | ||
1080 | break; | ||
1081 | default: | ||
1082 | return -EINVAL; | ||
1083 | } | ||
1084 | |||
1085 | /* Clock inversion */ | ||
1086 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
1087 | case SND_SOC_DAIFMT_DSP_A: | ||
1088 | case SND_SOC_DAIFMT_DSP_B: | ||
1089 | /* frame inversion not valid for DSP modes */ | ||
1090 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
1091 | case SND_SOC_DAIFMT_NB_NF: | ||
1092 | break; | ||
1093 | case SND_SOC_DAIFMT_IB_NF: | ||
1094 | aif1 |= WM8903_AIF_BCLK_INV; | ||
1095 | break; | ||
1096 | default: | ||
1097 | return -EINVAL; | ||
1098 | } | ||
1099 | break; | ||
1100 | case SND_SOC_DAIFMT_I2S: | ||
1101 | case SND_SOC_DAIFMT_RIGHT_J: | ||
1102 | case SND_SOC_DAIFMT_LEFT_J: | ||
1103 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
1104 | case SND_SOC_DAIFMT_NB_NF: | ||
1105 | break; | ||
1106 | case SND_SOC_DAIFMT_IB_IF: | ||
1107 | aif1 |= WM8903_AIF_BCLK_INV | WM8903_AIF_LRCLK_INV; | ||
1108 | break; | ||
1109 | case SND_SOC_DAIFMT_IB_NF: | ||
1110 | aif1 |= WM8903_AIF_BCLK_INV; | ||
1111 | break; | ||
1112 | case SND_SOC_DAIFMT_NB_IF: | ||
1113 | aif1 |= WM8903_AIF_LRCLK_INV; | ||
1114 | break; | ||
1115 | default: | ||
1116 | return -EINVAL; | ||
1117 | } | ||
1118 | break; | ||
1119 | default: | ||
1120 | return -EINVAL; | ||
1121 | } | ||
1122 | |||
1123 | wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1); | ||
1124 | |||
1125 | return 0; | ||
1126 | } | ||
1127 | |||
1128 | static int wm8903_digital_mute(struct snd_soc_dai *codec_dai, int mute) | ||
1129 | { | ||
1130 | struct snd_soc_codec *codec = codec_dai->codec; | ||
1131 | u16 reg; | ||
1132 | |||
1133 | reg = wm8903_read(codec, WM8903_DAC_DIGITAL_1); | ||
1134 | |||
1135 | if (mute) | ||
1136 | reg |= WM8903_DAC_MUTE; | ||
1137 | else | ||
1138 | reg &= ~WM8903_DAC_MUTE; | ||
1139 | |||
1140 | wm8903_write(codec, WM8903_DAC_DIGITAL_1, reg); | ||
1141 | |||
1142 | return 0; | ||
1143 | } | ||
1144 | |||
1145 | /* Lookup table for CLK_SYS/fs ratio. 256fs or more is recommended | ||
1146 | * for optimal performance so we list the lower rates first and match | ||
1147 | * on the last match we find. */ | ||
1148 | static struct { | ||
1149 | int div; | ||
1150 | int rate; | ||
1151 | int mode; | ||
1152 | int mclk_div; | ||
1153 | } clk_sys_ratios[] = { | ||
1154 | { 64, 0x0, 0x0, 1 }, | ||
1155 | { 68, 0x0, 0x1, 1 }, | ||
1156 | { 125, 0x0, 0x2, 1 }, | ||
1157 | { 128, 0x1, 0x0, 1 }, | ||
1158 | { 136, 0x1, 0x1, 1 }, | ||
1159 | { 192, 0x2, 0x0, 1 }, | ||
1160 | { 204, 0x2, 0x1, 1 }, | ||
1161 | |||
1162 | { 64, 0x0, 0x0, 2 }, | ||
1163 | { 68, 0x0, 0x1, 2 }, | ||
1164 | { 125, 0x0, 0x2, 2 }, | ||
1165 | { 128, 0x1, 0x0, 2 }, | ||
1166 | { 136, 0x1, 0x1, 2 }, | ||
1167 | { 192, 0x2, 0x0, 2 }, | ||
1168 | { 204, 0x2, 0x1, 2 }, | ||
1169 | |||
1170 | { 250, 0x2, 0x2, 1 }, | ||
1171 | { 256, 0x3, 0x0, 1 }, | ||
1172 | { 272, 0x3, 0x1, 1 }, | ||
1173 | { 384, 0x4, 0x0, 1 }, | ||
1174 | { 408, 0x4, 0x1, 1 }, | ||
1175 | { 375, 0x4, 0x2, 1 }, | ||
1176 | { 512, 0x5, 0x0, 1 }, | ||
1177 | { 544, 0x5, 0x1, 1 }, | ||
1178 | { 500, 0x5, 0x2, 1 }, | ||
1179 | { 768, 0x6, 0x0, 1 }, | ||
1180 | { 816, 0x6, 0x1, 1 }, | ||
1181 | { 750, 0x6, 0x2, 1 }, | ||
1182 | { 1024, 0x7, 0x0, 1 }, | ||
1183 | { 1088, 0x7, 0x1, 1 }, | ||
1184 | { 1000, 0x7, 0x2, 1 }, | ||
1185 | { 1408, 0x8, 0x0, 1 }, | ||
1186 | { 1496, 0x8, 0x1, 1 }, | ||
1187 | { 1536, 0x9, 0x0, 1 }, | ||
1188 | { 1632, 0x9, 0x1, 1 }, | ||
1189 | { 1500, 0x9, 0x2, 1 }, | ||
1190 | |||
1191 | { 250, 0x2, 0x2, 2 }, | ||
1192 | { 256, 0x3, 0x0, 2 }, | ||
1193 | { 272, 0x3, 0x1, 2 }, | ||
1194 | { 384, 0x4, 0x0, 2 }, | ||
1195 | { 408, 0x4, 0x1, 2 }, | ||
1196 | { 375, 0x4, 0x2, 2 }, | ||
1197 | { 512, 0x5, 0x0, 2 }, | ||
1198 | { 544, 0x5, 0x1, 2 }, | ||
1199 | { 500, 0x5, 0x2, 2 }, | ||
1200 | { 768, 0x6, 0x0, 2 }, | ||
1201 | { 816, 0x6, 0x1, 2 }, | ||
1202 | { 750, 0x6, 0x2, 2 }, | ||
1203 | { 1024, 0x7, 0x0, 2 }, | ||
1204 | { 1088, 0x7, 0x1, 2 }, | ||
1205 | { 1000, 0x7, 0x2, 2 }, | ||
1206 | { 1408, 0x8, 0x0, 2 }, | ||
1207 | { 1496, 0x8, 0x1, 2 }, | ||
1208 | { 1536, 0x9, 0x0, 2 }, | ||
1209 | { 1632, 0x9, 0x1, 2 }, | ||
1210 | { 1500, 0x9, 0x2, 2 }, | ||
1211 | }; | ||
1212 | |||
1213 | /* CLK_SYS/BCLK ratios - multiplied by 10 due to .5s */ | ||
1214 | static struct { | ||
1215 | int ratio; | ||
1216 | int div; | ||
1217 | } bclk_divs[] = { | ||
1218 | { 10, 0 }, | ||
1219 | { 15, 1 }, | ||
1220 | { 20, 2 }, | ||
1221 | { 30, 3 }, | ||
1222 | { 40, 4 }, | ||
1223 | { 50, 5 }, | ||
1224 | { 55, 6 }, | ||
1225 | { 60, 7 }, | ||
1226 | { 80, 8 }, | ||
1227 | { 100, 9 }, | ||
1228 | { 110, 10 }, | ||
1229 | { 120, 11 }, | ||
1230 | { 160, 12 }, | ||
1231 | { 200, 13 }, | ||
1232 | { 220, 14 }, | ||
1233 | { 240, 15 }, | ||
1234 | { 250, 16 }, | ||
1235 | { 300, 17 }, | ||
1236 | { 320, 18 }, | ||
1237 | { 440, 19 }, | ||
1238 | { 480, 20 }, | ||
1239 | }; | ||
1240 | |||
1241 | /* Sample rates for DSP */ | ||
1242 | static struct { | ||
1243 | int rate; | ||
1244 | int value; | ||
1245 | } sample_rates[] = { | ||
1246 | { 8000, 0 }, | ||
1247 | { 11025, 1 }, | ||
1248 | { 12000, 2 }, | ||
1249 | { 16000, 3 }, | ||
1250 | { 22050, 4 }, | ||
1251 | { 24000, 5 }, | ||
1252 | { 32000, 6 }, | ||
1253 | { 44100, 7 }, | ||
1254 | { 48000, 8 }, | ||
1255 | { 88200, 9 }, | ||
1256 | { 96000, 10 }, | ||
1257 | { 0, 0 }, | ||
1258 | }; | ||
1259 | |||
1260 | static int wm8903_startup(struct snd_pcm_substream *substream) | ||
1261 | { | ||
1262 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
1263 | struct snd_soc_device *socdev = rtd->socdev; | ||
1264 | struct snd_soc_codec *codec = socdev->codec; | ||
1265 | struct wm8903_priv *wm8903 = codec->private_data; | ||
1266 | struct i2c_client *i2c = codec->control_data; | ||
1267 | struct snd_pcm_runtime *master_runtime; | ||
1268 | |||
1269 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
1270 | wm8903->playback_active++; | ||
1271 | else | ||
1272 | wm8903->capture_active++; | ||
1273 | |||
1274 | /* The DAI has shared clocks so if we already have a playback or | ||
1275 | * capture going then constrain this substream to match it. | ||
1276 | */ | ||
1277 | if (wm8903->master_substream) { | ||
1278 | master_runtime = wm8903->master_substream->runtime; | ||
1279 | |||
1280 | dev_dbg(&i2c->dev, "Constraining to %d bits at %dHz\n", | ||
1281 | master_runtime->sample_bits, | ||
1282 | master_runtime->rate); | ||
1283 | |||
1284 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
1285 | SNDRV_PCM_HW_PARAM_RATE, | ||
1286 | master_runtime->rate, | ||
1287 | master_runtime->rate); | ||
1288 | |||
1289 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
1290 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, | ||
1291 | master_runtime->sample_bits, | ||
1292 | master_runtime->sample_bits); | ||
1293 | |||
1294 | wm8903->slave_substream = substream; | ||
1295 | } else | ||
1296 | wm8903->master_substream = substream; | ||
1297 | |||
1298 | return 0; | ||
1299 | } | ||
1300 | |||
1301 | static void wm8903_shutdown(struct snd_pcm_substream *substream) | ||
1302 | { | ||
1303 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
1304 | struct snd_soc_device *socdev = rtd->socdev; | ||
1305 | struct snd_soc_codec *codec = socdev->codec; | ||
1306 | struct wm8903_priv *wm8903 = codec->private_data; | ||
1307 | |||
1308 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
1309 | wm8903->playback_active--; | ||
1310 | else | ||
1311 | wm8903->capture_active--; | ||
1312 | |||
1313 | if (wm8903->master_substream == substream) | ||
1314 | wm8903->master_substream = wm8903->slave_substream; | ||
1315 | |||
1316 | wm8903->slave_substream = NULL; | ||
1317 | } | ||
1318 | |||
1319 | static int wm8903_hw_params(struct snd_pcm_substream *substream, | ||
1320 | struct snd_pcm_hw_params *params) | ||
1321 | { | ||
1322 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
1323 | struct snd_soc_device *socdev = rtd->socdev; | ||
1324 | struct snd_soc_codec *codec = socdev->codec; | ||
1325 | struct wm8903_priv *wm8903 = codec->private_data; | ||
1326 | struct i2c_client *i2c = codec->control_data; | ||
1327 | int fs = params_rate(params); | ||
1328 | int bclk; | ||
1329 | int bclk_div; | ||
1330 | int i; | ||
1331 | int dsp_config; | ||
1332 | int clk_config; | ||
1333 | int best_val; | ||
1334 | int cur_val; | ||
1335 | int clk_sys; | ||
1336 | |||
1337 | u16 aif1 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_1); | ||
1338 | u16 aif2 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_2); | ||
1339 | u16 aif3 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_3); | ||
1340 | u16 clock0 = wm8903_read(codec, WM8903_CLOCK_RATES_0); | ||
1341 | u16 clock1 = wm8903_read(codec, WM8903_CLOCK_RATES_1); | ||
1342 | |||
1343 | if (substream == wm8903->slave_substream) { | ||
1344 | dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n"); | ||
1345 | return 0; | ||
1346 | } | ||
1347 | |||
1348 | /* Configure sample rate logic for DSP - choose nearest rate */ | ||
1349 | dsp_config = 0; | ||
1350 | best_val = abs(sample_rates[dsp_config].rate - fs); | ||
1351 | for (i = 1; i < ARRAY_SIZE(sample_rates); i++) { | ||
1352 | cur_val = abs(sample_rates[i].rate - fs); | ||
1353 | if (cur_val <= best_val) { | ||
1354 | dsp_config = i; | ||
1355 | best_val = cur_val; | ||
1356 | } | ||
1357 | } | ||
1358 | |||
1359 | /* Constraints should stop us hitting this but let's make sure */ | ||
1360 | if (wm8903->capture_active) | ||
1361 | switch (sample_rates[dsp_config].rate) { | ||
1362 | case 88200: | ||
1363 | case 96000: | ||
1364 | dev_err(&i2c->dev, "%dHz unsupported by ADC\n", | ||
1365 | fs); | ||
1366 | return -EINVAL; | ||
1367 | |||
1368 | default: | ||
1369 | break; | ||
1370 | } | ||
1371 | |||
1372 | dev_dbg(&i2c->dev, "DSP fs = %dHz\n", sample_rates[dsp_config].rate); | ||
1373 | clock1 &= ~WM8903_SAMPLE_RATE_MASK; | ||
1374 | clock1 |= sample_rates[dsp_config].value; | ||
1375 | |||
1376 | aif1 &= ~WM8903_AIF_WL_MASK; | ||
1377 | bclk = 2 * fs; | ||
1378 | switch (params_format(params)) { | ||
1379 | case SNDRV_PCM_FORMAT_S16_LE: | ||
1380 | bclk *= 16; | ||
1381 | break; | ||
1382 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
1383 | bclk *= 20; | ||
1384 | aif1 |= 0x4; | ||
1385 | break; | ||
1386 | case SNDRV_PCM_FORMAT_S24_LE: | ||
1387 | bclk *= 24; | ||
1388 | aif1 |= 0x8; | ||
1389 | break; | ||
1390 | case SNDRV_PCM_FORMAT_S32_LE: | ||
1391 | bclk *= 32; | ||
1392 | aif1 |= 0xc; | ||
1393 | break; | ||
1394 | default: | ||
1395 | return -EINVAL; | ||
1396 | } | ||
1397 | |||
1398 | dev_dbg(&i2c->dev, "MCLK = %dHz, target sample rate = %dHz\n", | ||
1399 | wm8903->sysclk, fs); | ||
1400 | |||
1401 | /* We may not have an MCLK which allows us to generate exactly | ||
1402 | * the clock we want, particularly with USB derived inputs, so | ||
1403 | * approximate. | ||
1404 | */ | ||
1405 | clk_config = 0; | ||
1406 | best_val = abs((wm8903->sysclk / | ||
1407 | (clk_sys_ratios[0].mclk_div * | ||
1408 | clk_sys_ratios[0].div)) - fs); | ||
1409 | for (i = 1; i < ARRAY_SIZE(clk_sys_ratios); i++) { | ||
1410 | cur_val = abs((wm8903->sysclk / | ||
1411 | (clk_sys_ratios[i].mclk_div * | ||
1412 | clk_sys_ratios[i].div)) - fs); | ||
1413 | |||
1414 | if (cur_val <= best_val) { | ||
1415 | clk_config = i; | ||
1416 | best_val = cur_val; | ||
1417 | } | ||
1418 | } | ||
1419 | |||
1420 | if (clk_sys_ratios[clk_config].mclk_div == 2) { | ||
1421 | clock0 |= WM8903_MCLKDIV2; | ||
1422 | clk_sys = wm8903->sysclk / 2; | ||
1423 | } else { | ||
1424 | clock0 &= ~WM8903_MCLKDIV2; | ||
1425 | clk_sys = wm8903->sysclk; | ||
1426 | } | ||
1427 | |||
1428 | clock1 &= ~(WM8903_CLK_SYS_RATE_MASK | | ||
1429 | WM8903_CLK_SYS_MODE_MASK); | ||
1430 | clock1 |= clk_sys_ratios[clk_config].rate << WM8903_CLK_SYS_RATE_SHIFT; | ||
1431 | clock1 |= clk_sys_ratios[clk_config].mode << WM8903_CLK_SYS_MODE_SHIFT; | ||
1432 | |||
1433 | dev_dbg(&i2c->dev, "CLK_SYS_RATE=%x, CLK_SYS_MODE=%x div=%d\n", | ||
1434 | clk_sys_ratios[clk_config].rate, | ||
1435 | clk_sys_ratios[clk_config].mode, | ||
1436 | clk_sys_ratios[clk_config].div); | ||
1437 | |||
1438 | dev_dbg(&i2c->dev, "Actual CLK_SYS = %dHz\n", clk_sys); | ||
1439 | |||
1440 | /* We may not get quite the right frequency if using | ||
1441 | * approximate clocks so look for the closest match that is | ||
1442 | * higher than the target (we need to ensure that there enough | ||
1443 | * BCLKs to clock out the samples). | ||
1444 | */ | ||
1445 | bclk_div = 0; | ||
1446 | best_val = ((clk_sys * 10) / bclk_divs[0].ratio) - bclk; | ||
1447 | i = 1; | ||
1448 | while (i < ARRAY_SIZE(bclk_divs)) { | ||
1449 | cur_val = ((clk_sys * 10) / bclk_divs[i].ratio) - bclk; | ||
1450 | if (cur_val < 0) /* BCLK table is sorted */ | ||
1451 | break; | ||
1452 | bclk_div = i; | ||
1453 | best_val = cur_val; | ||
1454 | i++; | ||
1455 | } | ||
1456 | |||
1457 | aif2 &= ~WM8903_BCLK_DIV_MASK; | ||
1458 | aif3 &= ~WM8903_LRCLK_RATE_MASK; | ||
1459 | |||
1460 | dev_dbg(&i2c->dev, "BCLK ratio %d for %dHz - actual BCLK = %dHz\n", | ||
1461 | bclk_divs[bclk_div].ratio / 10, bclk, | ||
1462 | (clk_sys * 10) / bclk_divs[bclk_div].ratio); | ||
1463 | |||
1464 | aif2 |= bclk_divs[bclk_div].div; | ||
1465 | aif3 |= bclk / fs; | ||
1466 | |||
1467 | wm8903_write(codec, WM8903_CLOCK_RATES_0, clock0); | ||
1468 | wm8903_write(codec, WM8903_CLOCK_RATES_1, clock1); | ||
1469 | wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1); | ||
1470 | wm8903_write(codec, WM8903_AUDIO_INTERFACE_2, aif2); | ||
1471 | wm8903_write(codec, WM8903_AUDIO_INTERFACE_3, aif3); | ||
1472 | |||
1473 | return 0; | ||
1474 | } | ||
1475 | |||
1476 | #define WM8903_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\ | ||
1477 | SNDRV_PCM_RATE_11025 | \ | ||
1478 | SNDRV_PCM_RATE_16000 | \ | ||
1479 | SNDRV_PCM_RATE_22050 | \ | ||
1480 | SNDRV_PCM_RATE_32000 | \ | ||
1481 | SNDRV_PCM_RATE_44100 | \ | ||
1482 | SNDRV_PCM_RATE_48000 | \ | ||
1483 | SNDRV_PCM_RATE_88200 | \ | ||
1484 | SNDRV_PCM_RATE_96000) | ||
1485 | |||
1486 | #define WM8903_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\ | ||
1487 | SNDRV_PCM_RATE_11025 | \ | ||
1488 | SNDRV_PCM_RATE_16000 | \ | ||
1489 | SNDRV_PCM_RATE_22050 | \ | ||
1490 | SNDRV_PCM_RATE_32000 | \ | ||
1491 | SNDRV_PCM_RATE_44100 | \ | ||
1492 | SNDRV_PCM_RATE_48000) | ||
1493 | |||
1494 | #define WM8903_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ | ||
1495 | SNDRV_PCM_FMTBIT_S20_3LE |\ | ||
1496 | SNDRV_PCM_FMTBIT_S24_LE) | ||
1497 | |||
1498 | struct snd_soc_dai wm8903_dai = { | ||
1499 | .name = "WM8903", | ||
1500 | .playback = { | ||
1501 | .stream_name = "Playback", | ||
1502 | .channels_min = 2, | ||
1503 | .channels_max = 2, | ||
1504 | .rates = WM8903_PLAYBACK_RATES, | ||
1505 | .formats = WM8903_FORMATS, | ||
1506 | }, | ||
1507 | .capture = { | ||
1508 | .stream_name = "Capture", | ||
1509 | .channels_min = 2, | ||
1510 | .channels_max = 2, | ||
1511 | .rates = WM8903_CAPTURE_RATES, | ||
1512 | .formats = WM8903_FORMATS, | ||
1513 | }, | ||
1514 | .ops = { | ||
1515 | .startup = wm8903_startup, | ||
1516 | .shutdown = wm8903_shutdown, | ||
1517 | .hw_params = wm8903_hw_params, | ||
1518 | }, | ||
1519 | .dai_ops = { | ||
1520 | .digital_mute = wm8903_digital_mute, | ||
1521 | .set_fmt = wm8903_set_dai_fmt, | ||
1522 | .set_sysclk = wm8903_set_dai_sysclk | ||
1523 | } | ||
1524 | }; | ||
1525 | EXPORT_SYMBOL_GPL(wm8903_dai); | ||
1526 | |||
1527 | static int wm8903_suspend(struct platform_device *pdev, pm_message_t state) | ||
1528 | { | ||
1529 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1530 | struct snd_soc_codec *codec = socdev->codec; | ||
1531 | |||
1532 | wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
1533 | |||
1534 | return 0; | ||
1535 | } | ||
1536 | |||
1537 | static int wm8903_resume(struct platform_device *pdev) | ||
1538 | { | ||
1539 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1540 | struct snd_soc_codec *codec = socdev->codec; | ||
1541 | struct i2c_client *i2c = codec->control_data; | ||
1542 | int i; | ||
1543 | u16 *reg_cache = codec->reg_cache; | ||
1544 | u16 *tmp_cache = kmemdup(codec->reg_cache, sizeof(wm8903_reg_defaults), | ||
1545 | GFP_KERNEL); | ||
1546 | |||
1547 | /* Bring the codec back up to standby first to minimise pop/clicks */ | ||
1548 | wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
1549 | wm8903_set_bias_level(codec, codec->suspend_bias_level); | ||
1550 | |||
1551 | /* Sync back everything else */ | ||
1552 | if (tmp_cache) { | ||
1553 | for (i = 2; i < ARRAY_SIZE(wm8903_reg_defaults); i++) | ||
1554 | if (tmp_cache[i] != reg_cache[i]) | ||
1555 | wm8903_write(codec, i, tmp_cache[i]); | ||
1556 | } else { | ||
1557 | dev_err(&i2c->dev, "Failed to allocate temporary cache\n"); | ||
1558 | } | ||
1559 | |||
1560 | return 0; | ||
1561 | } | ||
1562 | |||
1563 | /* | ||
1564 | * initialise the WM8903 driver | ||
1565 | * register the mixer and dsp interfaces with the kernel | ||
1566 | */ | ||
1567 | static int wm8903_init(struct snd_soc_device *socdev) | ||
1568 | { | ||
1569 | struct snd_soc_codec *codec = socdev->codec; | ||
1570 | struct i2c_client *i2c = codec->control_data; | ||
1571 | int ret = 0; | ||
1572 | u16 val; | ||
1573 | |||
1574 | val = wm8903_hw_read(codec, WM8903_SW_RESET_AND_ID); | ||
1575 | if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) { | ||
1576 | dev_err(&i2c->dev, | ||
1577 | "Device with ID register %x is not a WM8903\n", val); | ||
1578 | return -ENODEV; | ||
1579 | } | ||
1580 | |||
1581 | codec->name = "WM8903"; | ||
1582 | codec->owner = THIS_MODULE; | ||
1583 | codec->read = wm8903_read; | ||
1584 | codec->write = wm8903_write; | ||
1585 | codec->bias_level = SND_SOC_BIAS_OFF; | ||
1586 | codec->set_bias_level = wm8903_set_bias_level; | ||
1587 | codec->dai = &wm8903_dai; | ||
1588 | codec->num_dai = 1; | ||
1589 | codec->reg_cache_size = ARRAY_SIZE(wm8903_reg_defaults); | ||
1590 | codec->reg_cache = kmemdup(wm8903_reg_defaults, | ||
1591 | sizeof(wm8903_reg_defaults), | ||
1592 | GFP_KERNEL); | ||
1593 | if (codec->reg_cache == NULL) { | ||
1594 | dev_err(&i2c->dev, "Failed to allocate register cache\n"); | ||
1595 | return -ENOMEM; | ||
1596 | } | ||
1597 | |||
1598 | val = wm8903_read(codec, WM8903_REVISION_NUMBER); | ||
1599 | dev_info(&i2c->dev, "WM8903 revision %d\n", | ||
1600 | val & WM8903_CHIP_REV_MASK); | ||
1601 | |||
1602 | wm8903_reset(codec); | ||
1603 | |||
1604 | /* register pcms */ | ||
1605 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
1606 | if (ret < 0) { | ||
1607 | dev_err(&i2c->dev, "failed to create pcms\n"); | ||
1608 | goto pcm_err; | ||
1609 | } | ||
1610 | |||
1611 | /* SYSCLK is required for pretty much anything */ | ||
1612 | wm8903_write(codec, WM8903_CLOCK_RATES_2, WM8903_CLK_SYS_ENA); | ||
1613 | |||
1614 | /* power on device */ | ||
1615 | wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
1616 | |||
1617 | /* Latch volume update bits */ | ||
1618 | val = wm8903_read(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT); | ||
1619 | val |= WM8903_ADCVU; | ||
1620 | wm8903_write(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT, val); | ||
1621 | wm8903_write(codec, WM8903_ADC_DIGITAL_VOLUME_RIGHT, val); | ||
1622 | |||
1623 | val = wm8903_read(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT); | ||
1624 | val |= WM8903_DACVU; | ||
1625 | wm8903_write(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT, val); | ||
1626 | wm8903_write(codec, WM8903_DAC_DIGITAL_VOLUME_RIGHT, val); | ||
1627 | |||
1628 | val = wm8903_read(codec, WM8903_ANALOGUE_OUT1_LEFT); | ||
1629 | val |= WM8903_HPOUTVU; | ||
1630 | wm8903_write(codec, WM8903_ANALOGUE_OUT1_LEFT, val); | ||
1631 | wm8903_write(codec, WM8903_ANALOGUE_OUT1_RIGHT, val); | ||
1632 | |||
1633 | val = wm8903_read(codec, WM8903_ANALOGUE_OUT2_LEFT); | ||
1634 | val |= WM8903_LINEOUTVU; | ||
1635 | wm8903_write(codec, WM8903_ANALOGUE_OUT2_LEFT, val); | ||
1636 | wm8903_write(codec, WM8903_ANALOGUE_OUT2_RIGHT, val); | ||
1637 | |||
1638 | val = wm8903_read(codec, WM8903_ANALOGUE_OUT3_LEFT); | ||
1639 | val |= WM8903_SPKVU; | ||
1640 | wm8903_write(codec, WM8903_ANALOGUE_OUT3_LEFT, val); | ||
1641 | wm8903_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val); | ||
1642 | |||
1643 | /* Enable DAC soft mute by default */ | ||
1644 | val = wm8903_read(codec, WM8903_DAC_DIGITAL_1); | ||
1645 | val |= WM8903_DAC_MUTEMODE; | ||
1646 | wm8903_write(codec, WM8903_DAC_DIGITAL_1, val); | ||
1647 | |||
1648 | wm8903_add_controls(codec); | ||
1649 | wm8903_add_widgets(codec); | ||
1650 | ret = snd_soc_register_card(socdev); | ||
1651 | if (ret < 0) { | ||
1652 | dev_err(&i2c->dev, "wm8903: failed to register card\n"); | ||
1653 | goto card_err; | ||
1654 | } | ||
1655 | |||
1656 | return ret; | ||
1657 | |||
1658 | card_err: | ||
1659 | snd_soc_free_pcms(socdev); | ||
1660 | snd_soc_dapm_free(socdev); | ||
1661 | pcm_err: | ||
1662 | kfree(codec->reg_cache); | ||
1663 | return ret; | ||
1664 | } | ||
1665 | |||
1666 | static struct snd_soc_device *wm8903_socdev; | ||
1667 | |||
1668 | static int wm8903_i2c_probe(struct i2c_client *i2c, | ||
1669 | const struct i2c_device_id *id) | ||
1670 | { | ||
1671 | struct snd_soc_device *socdev = wm8903_socdev; | ||
1672 | struct snd_soc_codec *codec = socdev->codec; | ||
1673 | int ret; | ||
1674 | |||
1675 | i2c_set_clientdata(i2c, codec); | ||
1676 | codec->control_data = i2c; | ||
1677 | |||
1678 | ret = wm8903_init(socdev); | ||
1679 | if (ret < 0) | ||
1680 | dev_err(&i2c->dev, "Device initialisation failed\n"); | ||
1681 | |||
1682 | return ret; | ||
1683 | } | ||
1684 | |||
1685 | static int wm8903_i2c_remove(struct i2c_client *client) | ||
1686 | { | ||
1687 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | ||
1688 | kfree(codec->reg_cache); | ||
1689 | return 0; | ||
1690 | } | ||
1691 | |||
1692 | /* i2c codec control layer */ | ||
1693 | static const struct i2c_device_id wm8903_i2c_id[] = { | ||
1694 | { "wm8903", 0 }, | ||
1695 | { } | ||
1696 | }; | ||
1697 | MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id); | ||
1698 | |||
1699 | static struct i2c_driver wm8903_i2c_driver = { | ||
1700 | .driver = { | ||
1701 | .name = "WM8903", | ||
1702 | .owner = THIS_MODULE, | ||
1703 | }, | ||
1704 | .probe = wm8903_i2c_probe, | ||
1705 | .remove = wm8903_i2c_remove, | ||
1706 | .id_table = wm8903_i2c_id, | ||
1707 | }; | ||
1708 | |||
1709 | static int wm8903_probe(struct platform_device *pdev) | ||
1710 | { | ||
1711 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1712 | struct wm8903_setup_data *setup; | ||
1713 | struct snd_soc_codec *codec; | ||
1714 | struct wm8903_priv *wm8903; | ||
1715 | struct i2c_board_info board_info; | ||
1716 | struct i2c_adapter *adapter; | ||
1717 | struct i2c_client *i2c_client; | ||
1718 | int ret = 0; | ||
1719 | |||
1720 | setup = socdev->codec_data; | ||
1721 | |||
1722 | if (!setup->i2c_address) { | ||
1723 | dev_err(&pdev->dev, "No codec address provided\n"); | ||
1724 | return -ENODEV; | ||
1725 | } | ||
1726 | |||
1727 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | ||
1728 | if (codec == NULL) | ||
1729 | return -ENOMEM; | ||
1730 | |||
1731 | wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL); | ||
1732 | if (wm8903 == NULL) { | ||
1733 | ret = -ENOMEM; | ||
1734 | goto err_codec; | ||
1735 | } | ||
1736 | |||
1737 | codec->private_data = wm8903; | ||
1738 | socdev->codec = codec; | ||
1739 | mutex_init(&codec->mutex); | ||
1740 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
1741 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
1742 | |||
1743 | wm8903_socdev = socdev; | ||
1744 | |||
1745 | codec->hw_write = (hw_write_t)i2c_master_send; | ||
1746 | ret = i2c_add_driver(&wm8903_i2c_driver); | ||
1747 | if (ret != 0) { | ||
1748 | dev_err(&pdev->dev, "can't add i2c driver\n"); | ||
1749 | goto err_priv; | ||
1750 | } else { | ||
1751 | memset(&board_info, 0, sizeof(board_info)); | ||
1752 | strlcpy(board_info.type, "wm8903", I2C_NAME_SIZE); | ||
1753 | board_info.addr = setup->i2c_address; | ||
1754 | |||
1755 | adapter = i2c_get_adapter(setup->i2c_bus); | ||
1756 | if (!adapter) { | ||
1757 | dev_err(&pdev->dev, "Can't get I2C bus %d\n", | ||
1758 | setup->i2c_bus); | ||
1759 | ret = -ENODEV; | ||
1760 | goto err_adapter; | ||
1761 | } | ||
1762 | |||
1763 | i2c_client = i2c_new_device(adapter, &board_info); | ||
1764 | i2c_put_adapter(adapter); | ||
1765 | if (i2c_client == NULL) { | ||
1766 | dev_err(&pdev->dev, | ||
1767 | "I2C driver registration failed\n"); | ||
1768 | ret = -ENODEV; | ||
1769 | goto err_adapter; | ||
1770 | } | ||
1771 | } | ||
1772 | |||
1773 | return ret; | ||
1774 | |||
1775 | err_adapter: | ||
1776 | i2c_del_driver(&wm8903_i2c_driver); | ||
1777 | err_priv: | ||
1778 | kfree(codec->private_data); | ||
1779 | err_codec: | ||
1780 | kfree(codec); | ||
1781 | return ret; | ||
1782 | } | ||
1783 | |||
1784 | /* power down chip */ | ||
1785 | static int wm8903_remove(struct platform_device *pdev) | ||
1786 | { | ||
1787 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1788 | struct snd_soc_codec *codec = socdev->codec; | ||
1789 | |||
1790 | if (codec->control_data) | ||
1791 | wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
1792 | |||
1793 | snd_soc_free_pcms(socdev); | ||
1794 | snd_soc_dapm_free(socdev); | ||
1795 | i2c_unregister_device(socdev->codec->control_data); | ||
1796 | i2c_del_driver(&wm8903_i2c_driver); | ||
1797 | kfree(codec->private_data); | ||
1798 | kfree(codec); | ||
1799 | |||
1800 | return 0; | ||
1801 | } | ||
1802 | |||
1803 | struct snd_soc_codec_device soc_codec_dev_wm8903 = { | ||
1804 | .probe = wm8903_probe, | ||
1805 | .remove = wm8903_remove, | ||
1806 | .suspend = wm8903_suspend, | ||
1807 | .resume = wm8903_resume, | ||
1808 | }; | ||
1809 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8903); | ||
1810 | |||
1811 | MODULE_DESCRIPTION("ASoC WM8903 driver"); | ||
1812 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.cm>"); | ||
1813 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h new file mode 100644 index 000000000000..cec622f2f660 --- /dev/null +++ b/sound/soc/codecs/wm8903.h | |||
@@ -0,0 +1,1463 @@ | |||
1 | /* | ||
2 | * wm8903.h - WM8903 audio codec interface | ||
3 | * | ||
4 | * Copyright 2008 Wolfson Microelectronics PLC. | ||
5 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | */ | ||
12 | |||
13 | #ifndef _WM8903_H | ||
14 | #define _WM8903_H | ||
15 | |||
16 | #include <linux/i2c.h> | ||
17 | |||
18 | extern struct snd_soc_dai wm8903_dai; | ||
19 | extern struct snd_soc_codec_device soc_codec_dev_wm8903; | ||
20 | |||
21 | struct wm8903_setup_data { | ||
22 | int i2c_bus; | ||
23 | int i2c_address; | ||
24 | }; | ||
25 | |||
26 | #define WM8903_MCLK_DIV_2 1 | ||
27 | #define WM8903_CLK_SYS 2 | ||
28 | #define WM8903_BCLK 3 | ||
29 | #define WM8903_LRCLK 4 | ||
30 | |||
31 | /* | ||
32 | * Register values. | ||
33 | */ | ||
34 | #define WM8903_SW_RESET_AND_ID 0x00 | ||
35 | #define WM8903_REVISION_NUMBER 0x01 | ||
36 | #define WM8903_BIAS_CONTROL_0 0x04 | ||
37 | #define WM8903_VMID_CONTROL_0 0x05 | ||
38 | #define WM8903_MIC_BIAS_CONTROL_0 0x06 | ||
39 | #define WM8903_ANALOGUE_DAC_0 0x08 | ||
40 | #define WM8903_ANALOGUE_ADC_0 0x0A | ||
41 | #define WM8903_POWER_MANAGEMENT_0 0x0C | ||
42 | #define WM8903_POWER_MANAGEMENT_1 0x0D | ||
43 | #define WM8903_POWER_MANAGEMENT_2 0x0E | ||
44 | #define WM8903_POWER_MANAGEMENT_3 0x0F | ||
45 | #define WM8903_POWER_MANAGEMENT_4 0x10 | ||
46 | #define WM8903_POWER_MANAGEMENT_5 0x11 | ||
47 | #define WM8903_POWER_MANAGEMENT_6 0x12 | ||
48 | #define WM8903_CLOCK_RATES_0 0x14 | ||
49 | #define WM8903_CLOCK_RATES_1 0x15 | ||
50 | #define WM8903_CLOCK_RATES_2 0x16 | ||
51 | #define WM8903_AUDIO_INTERFACE_0 0x18 | ||
52 | #define WM8903_AUDIO_INTERFACE_1 0x19 | ||
53 | #define WM8903_AUDIO_INTERFACE_2 0x1A | ||
54 | #define WM8903_AUDIO_INTERFACE_3 0x1B | ||
55 | #define WM8903_DAC_DIGITAL_VOLUME_LEFT 0x1E | ||
56 | #define WM8903_DAC_DIGITAL_VOLUME_RIGHT 0x1F | ||
57 | #define WM8903_DAC_DIGITAL_0 0x20 | ||
58 | #define WM8903_DAC_DIGITAL_1 0x21 | ||
59 | #define WM8903_ADC_DIGITAL_VOLUME_LEFT 0x24 | ||
60 | #define WM8903_ADC_DIGITAL_VOLUME_RIGHT 0x25 | ||
61 | #define WM8903_ADC_DIGITAL_0 0x26 | ||
62 | #define WM8903_DIGITAL_MICROPHONE_0 0x27 | ||
63 | #define WM8903_DRC_0 0x28 | ||
64 | #define WM8903_DRC_1 0x29 | ||
65 | #define WM8903_DRC_2 0x2A | ||
66 | #define WM8903_DRC_3 0x2B | ||
67 | #define WM8903_ANALOGUE_LEFT_INPUT_0 0x2C | ||
68 | #define WM8903_ANALOGUE_RIGHT_INPUT_0 0x2D | ||
69 | #define WM8903_ANALOGUE_LEFT_INPUT_1 0x2E | ||
70 | #define WM8903_ANALOGUE_RIGHT_INPUT_1 0x2F | ||
71 | #define WM8903_ANALOGUE_LEFT_MIX_0 0x32 | ||
72 | #define WM8903_ANALOGUE_RIGHT_MIX_0 0x33 | ||
73 | #define WM8903_ANALOGUE_SPK_MIX_LEFT_0 0x34 | ||
74 | #define WM8903_ANALOGUE_SPK_MIX_LEFT_1 0x35 | ||
75 | #define WM8903_ANALOGUE_SPK_MIX_RIGHT_0 0x36 | ||
76 | #define WM8903_ANALOGUE_SPK_MIX_RIGHT_1 0x37 | ||
77 | #define WM8903_ANALOGUE_OUT1_LEFT 0x39 | ||
78 | #define WM8903_ANALOGUE_OUT1_RIGHT 0x3A | ||
79 | #define WM8903_ANALOGUE_OUT2_LEFT 0x3B | ||
80 | #define WM8903_ANALOGUE_OUT2_RIGHT 0x3C | ||
81 | #define WM8903_ANALOGUE_OUT3_LEFT 0x3E | ||
82 | #define WM8903_ANALOGUE_OUT3_RIGHT 0x3F | ||
83 | #define WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0 0x41 | ||
84 | #define WM8903_DC_SERVO_0 0x43 | ||
85 | #define WM8903_DC_SERVO_2 0x45 | ||
86 | #define WM8903_ANALOGUE_HP_0 0x5A | ||
87 | #define WM8903_ANALOGUE_LINEOUT_0 0x5E | ||
88 | #define WM8903_CHARGE_PUMP_0 0x62 | ||
89 | #define WM8903_CLASS_W_0 0x68 | ||
90 | #define WM8903_WRITE_SEQUENCER_0 0x6C | ||
91 | #define WM8903_WRITE_SEQUENCER_1 0x6D | ||
92 | #define WM8903_WRITE_SEQUENCER_2 0x6E | ||
93 | #define WM8903_WRITE_SEQUENCER_3 0x6F | ||
94 | #define WM8903_WRITE_SEQUENCER_4 0x70 | ||
95 | #define WM8903_CONTROL_INTERFACE 0x72 | ||
96 | #define WM8903_GPIO_CONTROL_1 0x74 | ||
97 | #define WM8903_GPIO_CONTROL_2 0x75 | ||
98 | #define WM8903_GPIO_CONTROL_3 0x76 | ||
99 | #define WM8903_GPIO_CONTROL_4 0x77 | ||
100 | #define WM8903_GPIO_CONTROL_5 0x78 | ||
101 | #define WM8903_INTERRUPT_STATUS_1 0x79 | ||
102 | #define WM8903_INTERRUPT_STATUS_1_MASK 0x7A | ||
103 | #define WM8903_INTERRUPT_POLARITY_1 0x7B | ||
104 | #define WM8903_INTERRUPT_CONTROL 0x7E | ||
105 | #define WM8903_CONTROL_INTERFACE_TEST_1 0x81 | ||
106 | #define WM8903_CHARGE_PUMP_TEST_1 0x95 | ||
107 | #define WM8903_CLOCK_RATE_TEST_4 0xA4 | ||
108 | #define WM8903_ANALOGUE_OUTPUT_BIAS_0 0xAC | ||
109 | |||
110 | #define WM8903_REGISTER_COUNT 75 | ||
111 | #define WM8903_MAX_REGISTER 0xAC | ||
112 | |||
113 | /* | ||
114 | * Field Definitions. | ||
115 | */ | ||
116 | |||
117 | /* | ||
118 | * R0 (0x00) - SW Reset and ID | ||
119 | */ | ||
120 | #define WM8903_SW_RESET_DEV_ID1_MASK 0xFFFF /* SW_RESET_DEV_ID1 - [15:0] */ | ||
121 | #define WM8903_SW_RESET_DEV_ID1_SHIFT 0 /* SW_RESET_DEV_ID1 - [15:0] */ | ||
122 | #define WM8903_SW_RESET_DEV_ID1_WIDTH 16 /* SW_RESET_DEV_ID1 - [15:0] */ | ||
123 | |||
124 | /* | ||
125 | * R1 (0x01) - Revision Number | ||
126 | */ | ||
127 | #define WM8903_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */ | ||
128 | #define WM8903_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */ | ||
129 | #define WM8903_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */ | ||
130 | |||
131 | /* | ||
132 | * R4 (0x04) - Bias Control 0 | ||
133 | */ | ||
134 | #define WM8903_POBCTRL 0x0010 /* POBCTRL */ | ||
135 | #define WM8903_POBCTRL_MASK 0x0010 /* POBCTRL */ | ||
136 | #define WM8903_POBCTRL_SHIFT 4 /* POBCTRL */ | ||
137 | #define WM8903_POBCTRL_WIDTH 1 /* POBCTRL */ | ||
138 | #define WM8903_ISEL_MASK 0x000C /* ISEL - [3:2] */ | ||
139 | #define WM8903_ISEL_SHIFT 2 /* ISEL - [3:2] */ | ||
140 | #define WM8903_ISEL_WIDTH 2 /* ISEL - [3:2] */ | ||
141 | #define WM8903_STARTUP_BIAS_ENA 0x0002 /* STARTUP_BIAS_ENA */ | ||
142 | #define WM8903_STARTUP_BIAS_ENA_MASK 0x0002 /* STARTUP_BIAS_ENA */ | ||
143 | #define WM8903_STARTUP_BIAS_ENA_SHIFT 1 /* STARTUP_BIAS_ENA */ | ||
144 | #define WM8903_STARTUP_BIAS_ENA_WIDTH 1 /* STARTUP_BIAS_ENA */ | ||
145 | #define WM8903_BIAS_ENA 0x0001 /* BIAS_ENA */ | ||
146 | #define WM8903_BIAS_ENA_MASK 0x0001 /* BIAS_ENA */ | ||
147 | #define WM8903_BIAS_ENA_SHIFT 0 /* BIAS_ENA */ | ||
148 | #define WM8903_BIAS_ENA_WIDTH 1 /* BIAS_ENA */ | ||
149 | |||
150 | /* | ||
151 | * R5 (0x05) - VMID Control 0 | ||
152 | */ | ||
153 | #define WM8903_VMID_TIE_ENA 0x0080 /* VMID_TIE_ENA */ | ||
154 | #define WM8903_VMID_TIE_ENA_MASK 0x0080 /* VMID_TIE_ENA */ | ||
155 | #define WM8903_VMID_TIE_ENA_SHIFT 7 /* VMID_TIE_ENA */ | ||
156 | #define WM8903_VMID_TIE_ENA_WIDTH 1 /* VMID_TIE_ENA */ | ||
157 | #define WM8903_BUFIO_ENA 0x0040 /* BUFIO_ENA */ | ||
158 | #define WM8903_BUFIO_ENA_MASK 0x0040 /* BUFIO_ENA */ | ||
159 | #define WM8903_BUFIO_ENA_SHIFT 6 /* BUFIO_ENA */ | ||
160 | #define WM8903_BUFIO_ENA_WIDTH 1 /* BUFIO_ENA */ | ||
161 | #define WM8903_VMID_IO_ENA 0x0020 /* VMID_IO_ENA */ | ||
162 | #define WM8903_VMID_IO_ENA_MASK 0x0020 /* VMID_IO_ENA */ | ||
163 | #define WM8903_VMID_IO_ENA_SHIFT 5 /* VMID_IO_ENA */ | ||
164 | #define WM8903_VMID_IO_ENA_WIDTH 1 /* VMID_IO_ENA */ | ||
165 | #define WM8903_VMID_SOFT_MASK 0x0018 /* VMID_SOFT - [4:3] */ | ||
166 | #define WM8903_VMID_SOFT_SHIFT 3 /* VMID_SOFT - [4:3] */ | ||
167 | #define WM8903_VMID_SOFT_WIDTH 2 /* VMID_SOFT - [4:3] */ | ||
168 | #define WM8903_VMID_RES_MASK 0x0006 /* VMID_RES - [2:1] */ | ||
169 | #define WM8903_VMID_RES_SHIFT 1 /* VMID_RES - [2:1] */ | ||
170 | #define WM8903_VMID_RES_WIDTH 2 /* VMID_RES - [2:1] */ | ||
171 | #define WM8903_VMID_BUF_ENA 0x0001 /* VMID_BUF_ENA */ | ||
172 | #define WM8903_VMID_BUF_ENA_MASK 0x0001 /* VMID_BUF_ENA */ | ||
173 | #define WM8903_VMID_BUF_ENA_SHIFT 0 /* VMID_BUF_ENA */ | ||
174 | #define WM8903_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */ | ||
175 | |||
176 | #define WM8903_VMID_RES_50K 2 | ||
177 | #define WM8903_VMID_RES_250K 3 | ||
178 | #define WM8903_VMID_RES_5K 4 | ||
179 | |||
180 | /* | ||
181 | * R6 (0x06) - Mic Bias Control 0 | ||
182 | */ | ||
183 | #define WM8903_MICDET_HYST_ENA 0x0080 /* MICDET_HYST_ENA */ | ||
184 | #define WM8903_MICDET_HYST_ENA_MASK 0x0080 /* MICDET_HYST_ENA */ | ||
185 | #define WM8903_MICDET_HYST_ENA_SHIFT 7 /* MICDET_HYST_ENA */ | ||
186 | #define WM8903_MICDET_HYST_ENA_WIDTH 1 /* MICDET_HYST_ENA */ | ||
187 | #define WM8903_MICDET_THR_MASK 0x0070 /* MICDET_THR - [6:4] */ | ||
188 | #define WM8903_MICDET_THR_SHIFT 4 /* MICDET_THR - [6:4] */ | ||
189 | #define WM8903_MICDET_THR_WIDTH 3 /* MICDET_THR - [6:4] */ | ||
190 | #define WM8903_MICSHORT_THR_MASK 0x000C /* MICSHORT_THR - [3:2] */ | ||
191 | #define WM8903_MICSHORT_THR_SHIFT 2 /* MICSHORT_THR - [3:2] */ | ||
192 | #define WM8903_MICSHORT_THR_WIDTH 2 /* MICSHORT_THR - [3:2] */ | ||
193 | #define WM8903_MICDET_ENA 0x0002 /* MICDET_ENA */ | ||
194 | #define WM8903_MICDET_ENA_MASK 0x0002 /* MICDET_ENA */ | ||
195 | #define WM8903_MICDET_ENA_SHIFT 1 /* MICDET_ENA */ | ||
196 | #define WM8903_MICDET_ENA_WIDTH 1 /* MICDET_ENA */ | ||
197 | #define WM8903_MICBIAS_ENA 0x0001 /* MICBIAS_ENA */ | ||
198 | #define WM8903_MICBIAS_ENA_MASK 0x0001 /* MICBIAS_ENA */ | ||
199 | #define WM8903_MICBIAS_ENA_SHIFT 0 /* MICBIAS_ENA */ | ||
200 | #define WM8903_MICBIAS_ENA_WIDTH 1 /* MICBIAS_ENA */ | ||
201 | |||
202 | /* | ||
203 | * R8 (0x08) - Analogue DAC 0 | ||
204 | */ | ||
205 | #define WM8903_DACBIAS_SEL_MASK 0x0018 /* DACBIAS_SEL - [4:3] */ | ||
206 | #define WM8903_DACBIAS_SEL_SHIFT 3 /* DACBIAS_SEL - [4:3] */ | ||
207 | #define WM8903_DACBIAS_SEL_WIDTH 2 /* DACBIAS_SEL - [4:3] */ | ||
208 | #define WM8903_DACVMID_BIAS_SEL_MASK 0x0006 /* DACVMID_BIAS_SEL - [2:1] */ | ||
209 | #define WM8903_DACVMID_BIAS_SEL_SHIFT 1 /* DACVMID_BIAS_SEL - [2:1] */ | ||
210 | #define WM8903_DACVMID_BIAS_SEL_WIDTH 2 /* DACVMID_BIAS_SEL - [2:1] */ | ||
211 | |||
212 | /* | ||
213 | * R10 (0x0A) - Analogue ADC 0 | ||
214 | */ | ||
215 | #define WM8903_ADC_OSR128 0x0001 /* ADC_OSR128 */ | ||
216 | #define WM8903_ADC_OSR128_MASK 0x0001 /* ADC_OSR128 */ | ||
217 | #define WM8903_ADC_OSR128_SHIFT 0 /* ADC_OSR128 */ | ||
218 | #define WM8903_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */ | ||
219 | |||
220 | /* | ||
221 | * R12 (0x0C) - Power Management 0 | ||
222 | */ | ||
223 | #define WM8903_INL_ENA 0x0002 /* INL_ENA */ | ||
224 | #define WM8903_INL_ENA_MASK 0x0002 /* INL_ENA */ | ||
225 | #define WM8903_INL_ENA_SHIFT 1 /* INL_ENA */ | ||
226 | #define WM8903_INL_ENA_WIDTH 1 /* INL_ENA */ | ||
227 | #define WM8903_INR_ENA 0x0001 /* INR_ENA */ | ||
228 | #define WM8903_INR_ENA_MASK 0x0001 /* INR_ENA */ | ||
229 | #define WM8903_INR_ENA_SHIFT 0 /* INR_ENA */ | ||
230 | #define WM8903_INR_ENA_WIDTH 1 /* INR_ENA */ | ||
231 | |||
232 | /* | ||
233 | * R13 (0x0D) - Power Management 1 | ||
234 | */ | ||
235 | #define WM8903_MIXOUTL_ENA 0x0002 /* MIXOUTL_ENA */ | ||
236 | #define WM8903_MIXOUTL_ENA_MASK 0x0002 /* MIXOUTL_ENA */ | ||
237 | #define WM8903_MIXOUTL_ENA_SHIFT 1 /* MIXOUTL_ENA */ | ||
238 | #define WM8903_MIXOUTL_ENA_WIDTH 1 /* MIXOUTL_ENA */ | ||
239 | #define WM8903_MIXOUTR_ENA 0x0001 /* MIXOUTR_ENA */ | ||
240 | #define WM8903_MIXOUTR_ENA_MASK 0x0001 /* MIXOUTR_ENA */ | ||
241 | #define WM8903_MIXOUTR_ENA_SHIFT 0 /* MIXOUTR_ENA */ | ||
242 | #define WM8903_MIXOUTR_ENA_WIDTH 1 /* MIXOUTR_ENA */ | ||
243 | |||
244 | /* | ||
245 | * R14 (0x0E) - Power Management 2 | ||
246 | */ | ||
247 | #define WM8903_HPL_PGA_ENA 0x0002 /* HPL_PGA_ENA */ | ||
248 | #define WM8903_HPL_PGA_ENA_MASK 0x0002 /* HPL_PGA_ENA */ | ||
249 | #define WM8903_HPL_PGA_ENA_SHIFT 1 /* HPL_PGA_ENA */ | ||
250 | #define WM8903_HPL_PGA_ENA_WIDTH 1 /* HPL_PGA_ENA */ | ||
251 | #define WM8903_HPR_PGA_ENA 0x0001 /* HPR_PGA_ENA */ | ||
252 | #define WM8903_HPR_PGA_ENA_MASK 0x0001 /* HPR_PGA_ENA */ | ||
253 | #define WM8903_HPR_PGA_ENA_SHIFT 0 /* HPR_PGA_ENA */ | ||
254 | #define WM8903_HPR_PGA_ENA_WIDTH 1 /* HPR_PGA_ENA */ | ||
255 | |||
256 | /* | ||
257 | * R15 (0x0F) - Power Management 3 | ||
258 | */ | ||
259 | #define WM8903_LINEOUTL_PGA_ENA 0x0002 /* LINEOUTL_PGA_ENA */ | ||
260 | #define WM8903_LINEOUTL_PGA_ENA_MASK 0x0002 /* LINEOUTL_PGA_ENA */ | ||
261 | #define WM8903_LINEOUTL_PGA_ENA_SHIFT 1 /* LINEOUTL_PGA_ENA */ | ||
262 | #define WM8903_LINEOUTL_PGA_ENA_WIDTH 1 /* LINEOUTL_PGA_ENA */ | ||
263 | #define WM8903_LINEOUTR_PGA_ENA 0x0001 /* LINEOUTR_PGA_ENA */ | ||
264 | #define WM8903_LINEOUTR_PGA_ENA_MASK 0x0001 /* LINEOUTR_PGA_ENA */ | ||
265 | #define WM8903_LINEOUTR_PGA_ENA_SHIFT 0 /* LINEOUTR_PGA_ENA */ | ||
266 | #define WM8903_LINEOUTR_PGA_ENA_WIDTH 1 /* LINEOUTR_PGA_ENA */ | ||
267 | |||
268 | /* | ||
269 | * R16 (0x10) - Power Management 4 | ||
270 | */ | ||
271 | #define WM8903_MIXSPKL_ENA 0x0002 /* MIXSPKL_ENA */ | ||
272 | #define WM8903_MIXSPKL_ENA_MASK 0x0002 /* MIXSPKL_ENA */ | ||
273 | #define WM8903_MIXSPKL_ENA_SHIFT 1 /* MIXSPKL_ENA */ | ||
274 | #define WM8903_MIXSPKL_ENA_WIDTH 1 /* MIXSPKL_ENA */ | ||
275 | #define WM8903_MIXSPKR_ENA 0x0001 /* MIXSPKR_ENA */ | ||
276 | #define WM8903_MIXSPKR_ENA_MASK 0x0001 /* MIXSPKR_ENA */ | ||
277 | #define WM8903_MIXSPKR_ENA_SHIFT 0 /* MIXSPKR_ENA */ | ||
278 | #define WM8903_MIXSPKR_ENA_WIDTH 1 /* MIXSPKR_ENA */ | ||
279 | |||
280 | /* | ||
281 | * R17 (0x11) - Power Management 5 | ||
282 | */ | ||
283 | #define WM8903_SPKL_ENA 0x0002 /* SPKL_ENA */ | ||
284 | #define WM8903_SPKL_ENA_MASK 0x0002 /* SPKL_ENA */ | ||
285 | #define WM8903_SPKL_ENA_SHIFT 1 /* SPKL_ENA */ | ||
286 | #define WM8903_SPKL_ENA_WIDTH 1 /* SPKL_ENA */ | ||
287 | #define WM8903_SPKR_ENA 0x0001 /* SPKR_ENA */ | ||
288 | #define WM8903_SPKR_ENA_MASK 0x0001 /* SPKR_ENA */ | ||
289 | #define WM8903_SPKR_ENA_SHIFT 0 /* SPKR_ENA */ | ||
290 | #define WM8903_SPKR_ENA_WIDTH 1 /* SPKR_ENA */ | ||
291 | |||
292 | /* | ||
293 | * R18 (0x12) - Power Management 6 | ||
294 | */ | ||
295 | #define WM8903_DACL_ENA 0x0008 /* DACL_ENA */ | ||
296 | #define WM8903_DACL_ENA_MASK 0x0008 /* DACL_ENA */ | ||
297 | #define WM8903_DACL_ENA_SHIFT 3 /* DACL_ENA */ | ||
298 | #define WM8903_DACL_ENA_WIDTH 1 /* DACL_ENA */ | ||
299 | #define WM8903_DACR_ENA 0x0004 /* DACR_ENA */ | ||
300 | #define WM8903_DACR_ENA_MASK 0x0004 /* DACR_ENA */ | ||
301 | #define WM8903_DACR_ENA_SHIFT 2 /* DACR_ENA */ | ||
302 | #define WM8903_DACR_ENA_WIDTH 1 /* DACR_ENA */ | ||
303 | #define WM8903_ADCL_ENA 0x0002 /* ADCL_ENA */ | ||
304 | #define WM8903_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */ | ||
305 | #define WM8903_ADCL_ENA_SHIFT 1 /* ADCL_ENA */ | ||
306 | #define WM8903_ADCL_ENA_WIDTH 1 /* ADCL_ENA */ | ||
307 | #define WM8903_ADCR_ENA 0x0001 /* ADCR_ENA */ | ||
308 | #define WM8903_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */ | ||
309 | #define WM8903_ADCR_ENA_SHIFT 0 /* ADCR_ENA */ | ||
310 | #define WM8903_ADCR_ENA_WIDTH 1 /* ADCR_ENA */ | ||
311 | |||
312 | /* | ||
313 | * R20 (0x14) - Clock Rates 0 | ||
314 | */ | ||
315 | #define WM8903_MCLKDIV2 0x0001 /* MCLKDIV2 */ | ||
316 | #define WM8903_MCLKDIV2_MASK 0x0001 /* MCLKDIV2 */ | ||
317 | #define WM8903_MCLKDIV2_SHIFT 0 /* MCLKDIV2 */ | ||
318 | #define WM8903_MCLKDIV2_WIDTH 1 /* MCLKDIV2 */ | ||
319 | |||
320 | /* | ||
321 | * R21 (0x15) - Clock Rates 1 | ||
322 | */ | ||
323 | #define WM8903_CLK_SYS_RATE_MASK 0x3C00 /* CLK_SYS_RATE - [13:10] */ | ||
324 | #define WM8903_CLK_SYS_RATE_SHIFT 10 /* CLK_SYS_RATE - [13:10] */ | ||
325 | #define WM8903_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [13:10] */ | ||
326 | #define WM8903_CLK_SYS_MODE_MASK 0x0300 /* CLK_SYS_MODE - [9:8] */ | ||
327 | #define WM8903_CLK_SYS_MODE_SHIFT 8 /* CLK_SYS_MODE - [9:8] */ | ||
328 | #define WM8903_CLK_SYS_MODE_WIDTH 2 /* CLK_SYS_MODE - [9:8] */ | ||
329 | #define WM8903_SAMPLE_RATE_MASK 0x000F /* SAMPLE_RATE - [3:0] */ | ||
330 | #define WM8903_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [3:0] */ | ||
331 | #define WM8903_SAMPLE_RATE_WIDTH 4 /* SAMPLE_RATE - [3:0] */ | ||
332 | |||
333 | /* | ||
334 | * R22 (0x16) - Clock Rates 2 | ||
335 | */ | ||
336 | #define WM8903_CLK_SYS_ENA 0x0004 /* CLK_SYS_ENA */ | ||
337 | #define WM8903_CLK_SYS_ENA_MASK 0x0004 /* CLK_SYS_ENA */ | ||
338 | #define WM8903_CLK_SYS_ENA_SHIFT 2 /* CLK_SYS_ENA */ | ||
339 | #define WM8903_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */ | ||
340 | #define WM8903_CLK_DSP_ENA 0x0002 /* CLK_DSP_ENA */ | ||
341 | #define WM8903_CLK_DSP_ENA_MASK 0x0002 /* CLK_DSP_ENA */ | ||
342 | #define WM8903_CLK_DSP_ENA_SHIFT 1 /* CLK_DSP_ENA */ | ||
343 | #define WM8903_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */ | ||
344 | #define WM8903_TO_ENA 0x0001 /* TO_ENA */ | ||
345 | #define WM8903_TO_ENA_MASK 0x0001 /* TO_ENA */ | ||
346 | #define WM8903_TO_ENA_SHIFT 0 /* TO_ENA */ | ||
347 | #define WM8903_TO_ENA_WIDTH 1 /* TO_ENA */ | ||
348 | |||
349 | /* | ||
350 | * R24 (0x18) - Audio Interface 0 | ||
351 | */ | ||
352 | #define WM8903_DACL_DATINV 0x1000 /* DACL_DATINV */ | ||
353 | #define WM8903_DACL_DATINV_MASK 0x1000 /* DACL_DATINV */ | ||
354 | #define WM8903_DACL_DATINV_SHIFT 12 /* DACL_DATINV */ | ||
355 | #define WM8903_DACL_DATINV_WIDTH 1 /* DACL_DATINV */ | ||
356 | #define WM8903_DACR_DATINV 0x0800 /* DACR_DATINV */ | ||
357 | #define WM8903_DACR_DATINV_MASK 0x0800 /* DACR_DATINV */ | ||
358 | #define WM8903_DACR_DATINV_SHIFT 11 /* DACR_DATINV */ | ||
359 | #define WM8903_DACR_DATINV_WIDTH 1 /* DACR_DATINV */ | ||
360 | #define WM8903_DAC_BOOST_MASK 0x0600 /* DAC_BOOST - [10:9] */ | ||
361 | #define WM8903_DAC_BOOST_SHIFT 9 /* DAC_BOOST - [10:9] */ | ||
362 | #define WM8903_DAC_BOOST_WIDTH 2 /* DAC_BOOST - [10:9] */ | ||
363 | #define WM8903_LOOPBACK 0x0100 /* LOOPBACK */ | ||
364 | #define WM8903_LOOPBACK_MASK 0x0100 /* LOOPBACK */ | ||
365 | #define WM8903_LOOPBACK_SHIFT 8 /* LOOPBACK */ | ||
366 | #define WM8903_LOOPBACK_WIDTH 1 /* LOOPBACK */ | ||
367 | #define WM8903_AIFADCL_SRC 0x0080 /* AIFADCL_SRC */ | ||
368 | #define WM8903_AIFADCL_SRC_MASK 0x0080 /* AIFADCL_SRC */ | ||
369 | #define WM8903_AIFADCL_SRC_SHIFT 7 /* AIFADCL_SRC */ | ||
370 | #define WM8903_AIFADCL_SRC_WIDTH 1 /* AIFADCL_SRC */ | ||
371 | #define WM8903_AIFADCR_SRC 0x0040 /* AIFADCR_SRC */ | ||
372 | #define WM8903_AIFADCR_SRC_MASK 0x0040 /* AIFADCR_SRC */ | ||
373 | #define WM8903_AIFADCR_SRC_SHIFT 6 /* AIFADCR_SRC */ | ||
374 | #define WM8903_AIFADCR_SRC_WIDTH 1 /* AIFADCR_SRC */ | ||
375 | #define WM8903_AIFDACL_SRC 0x0020 /* AIFDACL_SRC */ | ||
376 | #define WM8903_AIFDACL_SRC_MASK 0x0020 /* AIFDACL_SRC */ | ||
377 | #define WM8903_AIFDACL_SRC_SHIFT 5 /* AIFDACL_SRC */ | ||
378 | #define WM8903_AIFDACL_SRC_WIDTH 1 /* AIFDACL_SRC */ | ||
379 | #define WM8903_AIFDACR_SRC 0x0010 /* AIFDACR_SRC */ | ||
380 | #define WM8903_AIFDACR_SRC_MASK 0x0010 /* AIFDACR_SRC */ | ||
381 | #define WM8903_AIFDACR_SRC_SHIFT 4 /* AIFDACR_SRC */ | ||
382 | #define WM8903_AIFDACR_SRC_WIDTH 1 /* AIFDACR_SRC */ | ||
383 | #define WM8903_ADC_COMP 0x0008 /* ADC_COMP */ | ||
384 | #define WM8903_ADC_COMP_MASK 0x0008 /* ADC_COMP */ | ||
385 | #define WM8903_ADC_COMP_SHIFT 3 /* ADC_COMP */ | ||
386 | #define WM8903_ADC_COMP_WIDTH 1 /* ADC_COMP */ | ||
387 | #define WM8903_ADC_COMPMODE 0x0004 /* ADC_COMPMODE */ | ||
388 | #define WM8903_ADC_COMPMODE_MASK 0x0004 /* ADC_COMPMODE */ | ||
389 | #define WM8903_ADC_COMPMODE_SHIFT 2 /* ADC_COMPMODE */ | ||
390 | #define WM8903_ADC_COMPMODE_WIDTH 1 /* ADC_COMPMODE */ | ||
391 | #define WM8903_DAC_COMP 0x0002 /* DAC_COMP */ | ||
392 | #define WM8903_DAC_COMP_MASK 0x0002 /* DAC_COMP */ | ||
393 | #define WM8903_DAC_COMP_SHIFT 1 /* DAC_COMP */ | ||
394 | #define WM8903_DAC_COMP_WIDTH 1 /* DAC_COMP */ | ||
395 | #define WM8903_DAC_COMPMODE 0x0001 /* DAC_COMPMODE */ | ||
396 | #define WM8903_DAC_COMPMODE_MASK 0x0001 /* DAC_COMPMODE */ | ||
397 | #define WM8903_DAC_COMPMODE_SHIFT 0 /* DAC_COMPMODE */ | ||
398 | #define WM8903_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */ | ||
399 | |||
400 | /* | ||
401 | * R25 (0x19) - Audio Interface 1 | ||
402 | */ | ||
403 | #define WM8903_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */ | ||
404 | #define WM8903_AIFDAC_TDM_MASK 0x2000 /* AIFDAC_TDM */ | ||
405 | #define WM8903_AIFDAC_TDM_SHIFT 13 /* AIFDAC_TDM */ | ||
406 | #define WM8903_AIFDAC_TDM_WIDTH 1 /* AIFDAC_TDM */ | ||
407 | #define WM8903_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */ | ||
408 | #define WM8903_AIFDAC_TDM_CHAN_MASK 0x1000 /* AIFDAC_TDM_CHAN */ | ||
409 | #define WM8903_AIFDAC_TDM_CHAN_SHIFT 12 /* AIFDAC_TDM_CHAN */ | ||
410 | #define WM8903_AIFDAC_TDM_CHAN_WIDTH 1 /* AIFDAC_TDM_CHAN */ | ||
411 | #define WM8903_AIFADC_TDM 0x0800 /* AIFADC_TDM */ | ||
412 | #define WM8903_AIFADC_TDM_MASK 0x0800 /* AIFADC_TDM */ | ||
413 | #define WM8903_AIFADC_TDM_SHIFT 11 /* AIFADC_TDM */ | ||
414 | #define WM8903_AIFADC_TDM_WIDTH 1 /* AIFADC_TDM */ | ||
415 | #define WM8903_AIFADC_TDM_CHAN 0x0400 /* AIFADC_TDM_CHAN */ | ||
416 | #define WM8903_AIFADC_TDM_CHAN_MASK 0x0400 /* AIFADC_TDM_CHAN */ | ||
417 | #define WM8903_AIFADC_TDM_CHAN_SHIFT 10 /* AIFADC_TDM_CHAN */ | ||
418 | #define WM8903_AIFADC_TDM_CHAN_WIDTH 1 /* AIFADC_TDM_CHAN */ | ||
419 | #define WM8903_LRCLK_DIR 0x0200 /* LRCLK_DIR */ | ||
420 | #define WM8903_LRCLK_DIR_MASK 0x0200 /* LRCLK_DIR */ | ||
421 | #define WM8903_LRCLK_DIR_SHIFT 9 /* LRCLK_DIR */ | ||
422 | #define WM8903_LRCLK_DIR_WIDTH 1 /* LRCLK_DIR */ | ||
423 | #define WM8903_AIF_BCLK_INV 0x0080 /* AIF_BCLK_INV */ | ||
424 | #define WM8903_AIF_BCLK_INV_MASK 0x0080 /* AIF_BCLK_INV */ | ||
425 | #define WM8903_AIF_BCLK_INV_SHIFT 7 /* AIF_BCLK_INV */ | ||
426 | #define WM8903_AIF_BCLK_INV_WIDTH 1 /* AIF_BCLK_INV */ | ||
427 | #define WM8903_BCLK_DIR 0x0040 /* BCLK_DIR */ | ||
428 | #define WM8903_BCLK_DIR_MASK 0x0040 /* BCLK_DIR */ | ||
429 | #define WM8903_BCLK_DIR_SHIFT 6 /* BCLK_DIR */ | ||
430 | #define WM8903_BCLK_DIR_WIDTH 1 /* BCLK_DIR */ | ||
431 | #define WM8903_AIF_LRCLK_INV 0x0010 /* AIF_LRCLK_INV */ | ||
432 | #define WM8903_AIF_LRCLK_INV_MASK 0x0010 /* AIF_LRCLK_INV */ | ||
433 | #define WM8903_AIF_LRCLK_INV_SHIFT 4 /* AIF_LRCLK_INV */ | ||
434 | #define WM8903_AIF_LRCLK_INV_WIDTH 1 /* AIF_LRCLK_INV */ | ||
435 | #define WM8903_AIF_WL_MASK 0x000C /* AIF_WL - [3:2] */ | ||
436 | #define WM8903_AIF_WL_SHIFT 2 /* AIF_WL - [3:2] */ | ||
437 | #define WM8903_AIF_WL_WIDTH 2 /* AIF_WL - [3:2] */ | ||
438 | #define WM8903_AIF_FMT_MASK 0x0003 /* AIF_FMT - [1:0] */ | ||
439 | #define WM8903_AIF_FMT_SHIFT 0 /* AIF_FMT - [1:0] */ | ||
440 | #define WM8903_AIF_FMT_WIDTH 2 /* AIF_FMT - [1:0] */ | ||
441 | |||
442 | /* | ||
443 | * R26 (0x1A) - Audio Interface 2 | ||
444 | */ | ||
445 | #define WM8903_BCLK_DIV_MASK 0x001F /* BCLK_DIV - [4:0] */ | ||
446 | #define WM8903_BCLK_DIV_SHIFT 0 /* BCLK_DIV - [4:0] */ | ||
447 | #define WM8903_BCLK_DIV_WIDTH 5 /* BCLK_DIV - [4:0] */ | ||
448 | |||
449 | /* | ||
450 | * R27 (0x1B) - Audio Interface 3 | ||
451 | */ | ||
452 | #define WM8903_LRCLK_RATE_MASK 0x07FF /* LRCLK_RATE - [10:0] */ | ||
453 | #define WM8903_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [10:0] */ | ||
454 | #define WM8903_LRCLK_RATE_WIDTH 11 /* LRCLK_RATE - [10:0] */ | ||
455 | |||
456 | /* | ||
457 | * R30 (0x1E) - DAC Digital Volume Left | ||
458 | */ | ||
459 | #define WM8903_DACVU 0x0100 /* DACVU */ | ||
460 | #define WM8903_DACVU_MASK 0x0100 /* DACVU */ | ||
461 | #define WM8903_DACVU_SHIFT 8 /* DACVU */ | ||
462 | #define WM8903_DACVU_WIDTH 1 /* DACVU */ | ||
463 | #define WM8903_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */ | ||
464 | #define WM8903_DACL_VOL_SHIFT 0 /* DACL_VOL - [7:0] */ | ||
465 | #define WM8903_DACL_VOL_WIDTH 8 /* DACL_VOL - [7:0] */ | ||
466 | |||
467 | /* | ||
468 | * R31 (0x1F) - DAC Digital Volume Right | ||
469 | */ | ||
470 | #define WM8903_DACVU 0x0100 /* DACVU */ | ||
471 | #define WM8903_DACVU_MASK 0x0100 /* DACVU */ | ||
472 | #define WM8903_DACVU_SHIFT 8 /* DACVU */ | ||
473 | #define WM8903_DACVU_WIDTH 1 /* DACVU */ | ||
474 | #define WM8903_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */ | ||
475 | #define WM8903_DACR_VOL_SHIFT 0 /* DACR_VOL - [7:0] */ | ||
476 | #define WM8903_DACR_VOL_WIDTH 8 /* DACR_VOL - [7:0] */ | ||
477 | |||
478 | /* | ||
479 | * R32 (0x20) - DAC Digital 0 | ||
480 | */ | ||
481 | #define WM8903_ADCL_DAC_SVOL_MASK 0x0F00 /* ADCL_DAC_SVOL - [11:8] */ | ||
482 | #define WM8903_ADCL_DAC_SVOL_SHIFT 8 /* ADCL_DAC_SVOL - [11:8] */ | ||
483 | #define WM8903_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [11:8] */ | ||
484 | #define WM8903_ADCR_DAC_SVOL_MASK 0x00F0 /* ADCR_DAC_SVOL - [7:4] */ | ||
485 | #define WM8903_ADCR_DAC_SVOL_SHIFT 4 /* ADCR_DAC_SVOL - [7:4] */ | ||
486 | #define WM8903_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [7:4] */ | ||
487 | #define WM8903_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */ | ||
488 | #define WM8903_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */ | ||
489 | #define WM8903_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */ | ||
490 | #define WM8903_ADC_TO_DACR_MASK 0x0003 /* ADC_TO_DACR - [1:0] */ | ||
491 | #define WM8903_ADC_TO_DACR_SHIFT 0 /* ADC_TO_DACR - [1:0] */ | ||
492 | #define WM8903_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [1:0] */ | ||
493 | |||
494 | /* | ||
495 | * R33 (0x21) - DAC Digital 1 | ||
496 | */ | ||
497 | #define WM8903_DAC_MONO 0x1000 /* DAC_MONO */ | ||
498 | #define WM8903_DAC_MONO_MASK 0x1000 /* DAC_MONO */ | ||
499 | #define WM8903_DAC_MONO_SHIFT 12 /* DAC_MONO */ | ||
500 | #define WM8903_DAC_MONO_WIDTH 1 /* DAC_MONO */ | ||
501 | #define WM8903_DAC_SB_FILT 0x0800 /* DAC_SB_FILT */ | ||
502 | #define WM8903_DAC_SB_FILT_MASK 0x0800 /* DAC_SB_FILT */ | ||
503 | #define WM8903_DAC_SB_FILT_SHIFT 11 /* DAC_SB_FILT */ | ||
504 | #define WM8903_DAC_SB_FILT_WIDTH 1 /* DAC_SB_FILT */ | ||
505 | #define WM8903_DAC_MUTERATE 0x0400 /* DAC_MUTERATE */ | ||
506 | #define WM8903_DAC_MUTERATE_MASK 0x0400 /* DAC_MUTERATE */ | ||
507 | #define WM8903_DAC_MUTERATE_SHIFT 10 /* DAC_MUTERATE */ | ||
508 | #define WM8903_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */ | ||
509 | #define WM8903_DAC_MUTEMODE 0x0200 /* DAC_MUTEMODE */ | ||
510 | #define WM8903_DAC_MUTEMODE_MASK 0x0200 /* DAC_MUTEMODE */ | ||
511 | #define WM8903_DAC_MUTEMODE_SHIFT 9 /* DAC_MUTEMODE */ | ||
512 | #define WM8903_DAC_MUTEMODE_WIDTH 1 /* DAC_MUTEMODE */ | ||
513 | #define WM8903_DAC_MUTE 0x0008 /* DAC_MUTE */ | ||
514 | #define WM8903_DAC_MUTE_MASK 0x0008 /* DAC_MUTE */ | ||
515 | #define WM8903_DAC_MUTE_SHIFT 3 /* DAC_MUTE */ | ||
516 | #define WM8903_DAC_MUTE_WIDTH 1 /* DAC_MUTE */ | ||
517 | #define WM8903_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */ | ||
518 | #define WM8903_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */ | ||
519 | #define WM8903_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */ | ||
520 | |||
521 | /* | ||
522 | * R36 (0x24) - ADC Digital Volume Left | ||
523 | */ | ||
524 | #define WM8903_ADCVU 0x0100 /* ADCVU */ | ||
525 | #define WM8903_ADCVU_MASK 0x0100 /* ADCVU */ | ||
526 | #define WM8903_ADCVU_SHIFT 8 /* ADCVU */ | ||
527 | #define WM8903_ADCVU_WIDTH 1 /* ADCVU */ | ||
528 | #define WM8903_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */ | ||
529 | #define WM8903_ADCL_VOL_SHIFT 0 /* ADCL_VOL - [7:0] */ | ||
530 | #define WM8903_ADCL_VOL_WIDTH 8 /* ADCL_VOL - [7:0] */ | ||
531 | |||
532 | /* | ||
533 | * R37 (0x25) - ADC Digital Volume Right | ||
534 | */ | ||
535 | #define WM8903_ADCVU 0x0100 /* ADCVU */ | ||
536 | #define WM8903_ADCVU_MASK 0x0100 /* ADCVU */ | ||
537 | #define WM8903_ADCVU_SHIFT 8 /* ADCVU */ | ||
538 | #define WM8903_ADCVU_WIDTH 1 /* ADCVU */ | ||
539 | #define WM8903_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */ | ||
540 | #define WM8903_ADCR_VOL_SHIFT 0 /* ADCR_VOL - [7:0] */ | ||
541 | #define WM8903_ADCR_VOL_WIDTH 8 /* ADCR_VOL - [7:0] */ | ||
542 | |||
543 | /* | ||
544 | * R38 (0x26) - ADC Digital 0 | ||
545 | */ | ||
546 | #define WM8903_ADC_HPF_CUT_MASK 0x0060 /* ADC_HPF_CUT - [6:5] */ | ||
547 | #define WM8903_ADC_HPF_CUT_SHIFT 5 /* ADC_HPF_CUT - [6:5] */ | ||
548 | #define WM8903_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [6:5] */ | ||
549 | #define WM8903_ADC_HPF_ENA 0x0010 /* ADC_HPF_ENA */ | ||
550 | #define WM8903_ADC_HPF_ENA_MASK 0x0010 /* ADC_HPF_ENA */ | ||
551 | #define WM8903_ADC_HPF_ENA_SHIFT 4 /* ADC_HPF_ENA */ | ||
552 | #define WM8903_ADC_HPF_ENA_WIDTH 1 /* ADC_HPF_ENA */ | ||
553 | #define WM8903_ADCL_DATINV 0x0002 /* ADCL_DATINV */ | ||
554 | #define WM8903_ADCL_DATINV_MASK 0x0002 /* ADCL_DATINV */ | ||
555 | #define WM8903_ADCL_DATINV_SHIFT 1 /* ADCL_DATINV */ | ||
556 | #define WM8903_ADCL_DATINV_WIDTH 1 /* ADCL_DATINV */ | ||
557 | #define WM8903_ADCR_DATINV 0x0001 /* ADCR_DATINV */ | ||
558 | #define WM8903_ADCR_DATINV_MASK 0x0001 /* ADCR_DATINV */ | ||
559 | #define WM8903_ADCR_DATINV_SHIFT 0 /* ADCR_DATINV */ | ||
560 | #define WM8903_ADCR_DATINV_WIDTH 1 /* ADCR_DATINV */ | ||
561 | |||
562 | /* | ||
563 | * R39 (0x27) - Digital Microphone 0 | ||
564 | */ | ||
565 | #define WM8903_DIGMIC_MODE_SEL 0x0100 /* DIGMIC_MODE_SEL */ | ||
566 | #define WM8903_DIGMIC_MODE_SEL_MASK 0x0100 /* DIGMIC_MODE_SEL */ | ||
567 | #define WM8903_DIGMIC_MODE_SEL_SHIFT 8 /* DIGMIC_MODE_SEL */ | ||
568 | #define WM8903_DIGMIC_MODE_SEL_WIDTH 1 /* DIGMIC_MODE_SEL */ | ||
569 | #define WM8903_DIGMIC_CLK_SEL_L_MASK 0x00C0 /* DIGMIC_CLK_SEL_L - [7:6] */ | ||
570 | #define WM8903_DIGMIC_CLK_SEL_L_SHIFT 6 /* DIGMIC_CLK_SEL_L - [7:6] */ | ||
571 | #define WM8903_DIGMIC_CLK_SEL_L_WIDTH 2 /* DIGMIC_CLK_SEL_L - [7:6] */ | ||
572 | #define WM8903_DIGMIC_CLK_SEL_R_MASK 0x0030 /* DIGMIC_CLK_SEL_R - [5:4] */ | ||
573 | #define WM8903_DIGMIC_CLK_SEL_R_SHIFT 4 /* DIGMIC_CLK_SEL_R - [5:4] */ | ||
574 | #define WM8903_DIGMIC_CLK_SEL_R_WIDTH 2 /* DIGMIC_CLK_SEL_R - [5:4] */ | ||
575 | #define WM8903_DIGMIC_CLK_SEL_RT_MASK 0x000C /* DIGMIC_CLK_SEL_RT - [3:2] */ | ||
576 | #define WM8903_DIGMIC_CLK_SEL_RT_SHIFT 2 /* DIGMIC_CLK_SEL_RT - [3:2] */ | ||
577 | #define WM8903_DIGMIC_CLK_SEL_RT_WIDTH 2 /* DIGMIC_CLK_SEL_RT - [3:2] */ | ||
578 | #define WM8903_DIGMIC_CLK_SEL_MASK 0x0003 /* DIGMIC_CLK_SEL - [1:0] */ | ||
579 | #define WM8903_DIGMIC_CLK_SEL_SHIFT 0 /* DIGMIC_CLK_SEL - [1:0] */ | ||
580 | #define WM8903_DIGMIC_CLK_SEL_WIDTH 2 /* DIGMIC_CLK_SEL - [1:0] */ | ||
581 | |||
582 | /* | ||
583 | * R40 (0x28) - DRC 0 | ||
584 | */ | ||
585 | #define WM8903_DRC_ENA 0x8000 /* DRC_ENA */ | ||
586 | #define WM8903_DRC_ENA_MASK 0x8000 /* DRC_ENA */ | ||
587 | #define WM8903_DRC_ENA_SHIFT 15 /* DRC_ENA */ | ||
588 | #define WM8903_DRC_ENA_WIDTH 1 /* DRC_ENA */ | ||
589 | #define WM8903_DRC_THRESH_HYST_MASK 0x1800 /* DRC_THRESH_HYST - [12:11] */ | ||
590 | #define WM8903_DRC_THRESH_HYST_SHIFT 11 /* DRC_THRESH_HYST - [12:11] */ | ||
591 | #define WM8903_DRC_THRESH_HYST_WIDTH 2 /* DRC_THRESH_HYST - [12:11] */ | ||
592 | #define WM8903_DRC_STARTUP_GAIN_MASK 0x07C0 /* DRC_STARTUP_GAIN - [10:6] */ | ||
593 | #define WM8903_DRC_STARTUP_GAIN_SHIFT 6 /* DRC_STARTUP_GAIN - [10:6] */ | ||
594 | #define WM8903_DRC_STARTUP_GAIN_WIDTH 5 /* DRC_STARTUP_GAIN - [10:6] */ | ||
595 | #define WM8903_DRC_FF_DELAY 0x0020 /* DRC_FF_DELAY */ | ||
596 | #define WM8903_DRC_FF_DELAY_MASK 0x0020 /* DRC_FF_DELAY */ | ||
597 | #define WM8903_DRC_FF_DELAY_SHIFT 5 /* DRC_FF_DELAY */ | ||
598 | #define WM8903_DRC_FF_DELAY_WIDTH 1 /* DRC_FF_DELAY */ | ||
599 | #define WM8903_DRC_SMOOTH_ENA 0x0008 /* DRC_SMOOTH_ENA */ | ||
600 | #define WM8903_DRC_SMOOTH_ENA_MASK 0x0008 /* DRC_SMOOTH_ENA */ | ||
601 | #define WM8903_DRC_SMOOTH_ENA_SHIFT 3 /* DRC_SMOOTH_ENA */ | ||
602 | #define WM8903_DRC_SMOOTH_ENA_WIDTH 1 /* DRC_SMOOTH_ENA */ | ||
603 | #define WM8903_DRC_QR_ENA 0x0004 /* DRC_QR_ENA */ | ||
604 | #define WM8903_DRC_QR_ENA_MASK 0x0004 /* DRC_QR_ENA */ | ||
605 | #define WM8903_DRC_QR_ENA_SHIFT 2 /* DRC_QR_ENA */ | ||
606 | #define WM8903_DRC_QR_ENA_WIDTH 1 /* DRC_QR_ENA */ | ||
607 | #define WM8903_DRC_ANTICLIP_ENA 0x0002 /* DRC_ANTICLIP_ENA */ | ||
608 | #define WM8903_DRC_ANTICLIP_ENA_MASK 0x0002 /* DRC_ANTICLIP_ENA */ | ||
609 | #define WM8903_DRC_ANTICLIP_ENA_SHIFT 1 /* DRC_ANTICLIP_ENA */ | ||
610 | #define WM8903_DRC_ANTICLIP_ENA_WIDTH 1 /* DRC_ANTICLIP_ENA */ | ||
611 | #define WM8903_DRC_HYST_ENA 0x0001 /* DRC_HYST_ENA */ | ||
612 | #define WM8903_DRC_HYST_ENA_MASK 0x0001 /* DRC_HYST_ENA */ | ||
613 | #define WM8903_DRC_HYST_ENA_SHIFT 0 /* DRC_HYST_ENA */ | ||
614 | #define WM8903_DRC_HYST_ENA_WIDTH 1 /* DRC_HYST_ENA */ | ||
615 | |||
616 | /* | ||
617 | * R41 (0x29) - DRC 1 | ||
618 | */ | ||
619 | #define WM8903_DRC_ATTACK_RATE_MASK 0xF000 /* DRC_ATTACK_RATE - [15:12] */ | ||
620 | #define WM8903_DRC_ATTACK_RATE_SHIFT 12 /* DRC_ATTACK_RATE - [15:12] */ | ||
621 | #define WM8903_DRC_ATTACK_RATE_WIDTH 4 /* DRC_ATTACK_RATE - [15:12] */ | ||
622 | #define WM8903_DRC_DECAY_RATE_MASK 0x0F00 /* DRC_DECAY_RATE - [11:8] */ | ||
623 | #define WM8903_DRC_DECAY_RATE_SHIFT 8 /* DRC_DECAY_RATE - [11:8] */ | ||
624 | #define WM8903_DRC_DECAY_RATE_WIDTH 4 /* DRC_DECAY_RATE - [11:8] */ | ||
625 | #define WM8903_DRC_THRESH_QR_MASK 0x00C0 /* DRC_THRESH_QR - [7:6] */ | ||
626 | #define WM8903_DRC_THRESH_QR_SHIFT 6 /* DRC_THRESH_QR - [7:6] */ | ||
627 | #define WM8903_DRC_THRESH_QR_WIDTH 2 /* DRC_THRESH_QR - [7:6] */ | ||
628 | #define WM8903_DRC_RATE_QR_MASK 0x0030 /* DRC_RATE_QR - [5:4] */ | ||
629 | #define WM8903_DRC_RATE_QR_SHIFT 4 /* DRC_RATE_QR - [5:4] */ | ||
630 | #define WM8903_DRC_RATE_QR_WIDTH 2 /* DRC_RATE_QR - [5:4] */ | ||
631 | #define WM8903_DRC_MINGAIN_MASK 0x000C /* DRC_MINGAIN - [3:2] */ | ||
632 | #define WM8903_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [3:2] */ | ||
633 | #define WM8903_DRC_MINGAIN_WIDTH 2 /* DRC_MINGAIN - [3:2] */ | ||
634 | #define WM8903_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */ | ||
635 | #define WM8903_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */ | ||
636 | #define WM8903_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */ | ||
637 | |||
638 | /* | ||
639 | * R42 (0x2A) - DRC 2 | ||
640 | */ | ||
641 | #define WM8903_DRC_R0_SLOPE_COMP_MASK 0x0038 /* DRC_R0_SLOPE_COMP - [5:3] */ | ||
642 | #define WM8903_DRC_R0_SLOPE_COMP_SHIFT 3 /* DRC_R0_SLOPE_COMP - [5:3] */ | ||
643 | #define WM8903_DRC_R0_SLOPE_COMP_WIDTH 3 /* DRC_R0_SLOPE_COMP - [5:3] */ | ||
644 | #define WM8903_DRC_R1_SLOPE_COMP_MASK 0x0007 /* DRC_R1_SLOPE_COMP - [2:0] */ | ||
645 | #define WM8903_DRC_R1_SLOPE_COMP_SHIFT 0 /* DRC_R1_SLOPE_COMP - [2:0] */ | ||
646 | #define WM8903_DRC_R1_SLOPE_COMP_WIDTH 3 /* DRC_R1_SLOPE_COMP - [2:0] */ | ||
647 | |||
648 | /* | ||
649 | * R43 (0x2B) - DRC 3 | ||
650 | */ | ||
651 | #define WM8903_DRC_THRESH_COMP_MASK 0x07E0 /* DRC_THRESH_COMP - [10:5] */ | ||
652 | #define WM8903_DRC_THRESH_COMP_SHIFT 5 /* DRC_THRESH_COMP - [10:5] */ | ||
653 | #define WM8903_DRC_THRESH_COMP_WIDTH 6 /* DRC_THRESH_COMP - [10:5] */ | ||
654 | #define WM8903_DRC_AMP_COMP_MASK 0x001F /* DRC_AMP_COMP - [4:0] */ | ||
655 | #define WM8903_DRC_AMP_COMP_SHIFT 0 /* DRC_AMP_COMP - [4:0] */ | ||
656 | #define WM8903_DRC_AMP_COMP_WIDTH 5 /* DRC_AMP_COMP - [4:0] */ | ||
657 | |||
658 | /* | ||
659 | * R44 (0x2C) - Analogue Left Input 0 | ||
660 | */ | ||
661 | #define WM8903_LINMUTE 0x0080 /* LINMUTE */ | ||
662 | #define WM8903_LINMUTE_MASK 0x0080 /* LINMUTE */ | ||
663 | #define WM8903_LINMUTE_SHIFT 7 /* LINMUTE */ | ||
664 | #define WM8903_LINMUTE_WIDTH 1 /* LINMUTE */ | ||
665 | #define WM8903_LIN_VOL_MASK 0x001F /* LIN_VOL - [4:0] */ | ||
666 | #define WM8903_LIN_VOL_SHIFT 0 /* LIN_VOL - [4:0] */ | ||
667 | #define WM8903_LIN_VOL_WIDTH 5 /* LIN_VOL - [4:0] */ | ||
668 | |||
669 | /* | ||
670 | * R45 (0x2D) - Analogue Right Input 0 | ||
671 | */ | ||
672 | #define WM8903_RINMUTE 0x0080 /* RINMUTE */ | ||
673 | #define WM8903_RINMUTE_MASK 0x0080 /* RINMUTE */ | ||
674 | #define WM8903_RINMUTE_SHIFT 7 /* RINMUTE */ | ||
675 | #define WM8903_RINMUTE_WIDTH 1 /* RINMUTE */ | ||
676 | #define WM8903_RIN_VOL_MASK 0x001F /* RIN_VOL - [4:0] */ | ||
677 | #define WM8903_RIN_VOL_SHIFT 0 /* RIN_VOL - [4:0] */ | ||
678 | #define WM8903_RIN_VOL_WIDTH 5 /* RIN_VOL - [4:0] */ | ||
679 | |||
680 | /* | ||
681 | * R46 (0x2E) - Analogue Left Input 1 | ||
682 | */ | ||
683 | #define WM8903_INL_CM_ENA 0x0040 /* INL_CM_ENA */ | ||
684 | #define WM8903_INL_CM_ENA_MASK 0x0040 /* INL_CM_ENA */ | ||
685 | #define WM8903_INL_CM_ENA_SHIFT 6 /* INL_CM_ENA */ | ||
686 | #define WM8903_INL_CM_ENA_WIDTH 1 /* INL_CM_ENA */ | ||
687 | #define WM8903_L_IP_SEL_N_MASK 0x0030 /* L_IP_SEL_N - [5:4] */ | ||
688 | #define WM8903_L_IP_SEL_N_SHIFT 4 /* L_IP_SEL_N - [5:4] */ | ||
689 | #define WM8903_L_IP_SEL_N_WIDTH 2 /* L_IP_SEL_N - [5:4] */ | ||
690 | #define WM8903_L_IP_SEL_P_MASK 0x000C /* L_IP_SEL_P - [3:2] */ | ||
691 | #define WM8903_L_IP_SEL_P_SHIFT 2 /* L_IP_SEL_P - [3:2] */ | ||
692 | #define WM8903_L_IP_SEL_P_WIDTH 2 /* L_IP_SEL_P - [3:2] */ | ||
693 | #define WM8903_L_MODE_MASK 0x0003 /* L_MODE - [1:0] */ | ||
694 | #define WM8903_L_MODE_SHIFT 0 /* L_MODE - [1:0] */ | ||
695 | #define WM8903_L_MODE_WIDTH 2 /* L_MODE - [1:0] */ | ||
696 | |||
697 | /* | ||
698 | * R47 (0x2F) - Analogue Right Input 1 | ||
699 | */ | ||
700 | #define WM8903_INR_CM_ENA 0x0040 /* INR_CM_ENA */ | ||
701 | #define WM8903_INR_CM_ENA_MASK 0x0040 /* INR_CM_ENA */ | ||
702 | #define WM8903_INR_CM_ENA_SHIFT 6 /* INR_CM_ENA */ | ||
703 | #define WM8903_INR_CM_ENA_WIDTH 1 /* INR_CM_ENA */ | ||
704 | #define WM8903_R_IP_SEL_N_MASK 0x0030 /* R_IP_SEL_N - [5:4] */ | ||
705 | #define WM8903_R_IP_SEL_N_SHIFT 4 /* R_IP_SEL_N - [5:4] */ | ||
706 | #define WM8903_R_IP_SEL_N_WIDTH 2 /* R_IP_SEL_N - [5:4] */ | ||
707 | #define WM8903_R_IP_SEL_P_MASK 0x000C /* R_IP_SEL_P - [3:2] */ | ||
708 | #define WM8903_R_IP_SEL_P_SHIFT 2 /* R_IP_SEL_P - [3:2] */ | ||
709 | #define WM8903_R_IP_SEL_P_WIDTH 2 /* R_IP_SEL_P - [3:2] */ | ||
710 | #define WM8903_R_MODE_MASK 0x0003 /* R_MODE - [1:0] */ | ||
711 | #define WM8903_R_MODE_SHIFT 0 /* R_MODE - [1:0] */ | ||
712 | #define WM8903_R_MODE_WIDTH 2 /* R_MODE - [1:0] */ | ||
713 | |||
714 | /* | ||
715 | * R50 (0x32) - Analogue Left Mix 0 | ||
716 | */ | ||
717 | #define WM8903_DACL_TO_MIXOUTL 0x0008 /* DACL_TO_MIXOUTL */ | ||
718 | #define WM8903_DACL_TO_MIXOUTL_MASK 0x0008 /* DACL_TO_MIXOUTL */ | ||
719 | #define WM8903_DACL_TO_MIXOUTL_SHIFT 3 /* DACL_TO_MIXOUTL */ | ||
720 | #define WM8903_DACL_TO_MIXOUTL_WIDTH 1 /* DACL_TO_MIXOUTL */ | ||
721 | #define WM8903_DACR_TO_MIXOUTL 0x0004 /* DACR_TO_MIXOUTL */ | ||
722 | #define WM8903_DACR_TO_MIXOUTL_MASK 0x0004 /* DACR_TO_MIXOUTL */ | ||
723 | #define WM8903_DACR_TO_MIXOUTL_SHIFT 2 /* DACR_TO_MIXOUTL */ | ||
724 | #define WM8903_DACR_TO_MIXOUTL_WIDTH 1 /* DACR_TO_MIXOUTL */ | ||
725 | #define WM8903_BYPASSL_TO_MIXOUTL 0x0002 /* BYPASSL_TO_MIXOUTL */ | ||
726 | #define WM8903_BYPASSL_TO_MIXOUTL_MASK 0x0002 /* BYPASSL_TO_MIXOUTL */ | ||
727 | #define WM8903_BYPASSL_TO_MIXOUTL_SHIFT 1 /* BYPASSL_TO_MIXOUTL */ | ||
728 | #define WM8903_BYPASSL_TO_MIXOUTL_WIDTH 1 /* BYPASSL_TO_MIXOUTL */ | ||
729 | #define WM8903_BYPASSR_TO_MIXOUTL 0x0001 /* BYPASSR_TO_MIXOUTL */ | ||
730 | #define WM8903_BYPASSR_TO_MIXOUTL_MASK 0x0001 /* BYPASSR_TO_MIXOUTL */ | ||
731 | #define WM8903_BYPASSR_TO_MIXOUTL_SHIFT 0 /* BYPASSR_TO_MIXOUTL */ | ||
732 | #define WM8903_BYPASSR_TO_MIXOUTL_WIDTH 1 /* BYPASSR_TO_MIXOUTL */ | ||
733 | |||
734 | /* | ||
735 | * R51 (0x33) - Analogue Right Mix 0 | ||
736 | */ | ||
737 | #define WM8903_DACL_TO_MIXOUTR 0x0008 /* DACL_TO_MIXOUTR */ | ||
738 | #define WM8903_DACL_TO_MIXOUTR_MASK 0x0008 /* DACL_TO_MIXOUTR */ | ||
739 | #define WM8903_DACL_TO_MIXOUTR_SHIFT 3 /* DACL_TO_MIXOUTR */ | ||
740 | #define WM8903_DACL_TO_MIXOUTR_WIDTH 1 /* DACL_TO_MIXOUTR */ | ||
741 | #define WM8903_DACR_TO_MIXOUTR 0x0004 /* DACR_TO_MIXOUTR */ | ||
742 | #define WM8903_DACR_TO_MIXOUTR_MASK 0x0004 /* DACR_TO_MIXOUTR */ | ||
743 | #define WM8903_DACR_TO_MIXOUTR_SHIFT 2 /* DACR_TO_MIXOUTR */ | ||
744 | #define WM8903_DACR_TO_MIXOUTR_WIDTH 1 /* DACR_TO_MIXOUTR */ | ||
745 | #define WM8903_BYPASSL_TO_MIXOUTR 0x0002 /* BYPASSL_TO_MIXOUTR */ | ||
746 | #define WM8903_BYPASSL_TO_MIXOUTR_MASK 0x0002 /* BYPASSL_TO_MIXOUTR */ | ||
747 | #define WM8903_BYPASSL_TO_MIXOUTR_SHIFT 1 /* BYPASSL_TO_MIXOUTR */ | ||
748 | #define WM8903_BYPASSL_TO_MIXOUTR_WIDTH 1 /* BYPASSL_TO_MIXOUTR */ | ||
749 | #define WM8903_BYPASSR_TO_MIXOUTR 0x0001 /* BYPASSR_TO_MIXOUTR */ | ||
750 | #define WM8903_BYPASSR_TO_MIXOUTR_MASK 0x0001 /* BYPASSR_TO_MIXOUTR */ | ||
751 | #define WM8903_BYPASSR_TO_MIXOUTR_SHIFT 0 /* BYPASSR_TO_MIXOUTR */ | ||
752 | #define WM8903_BYPASSR_TO_MIXOUTR_WIDTH 1 /* BYPASSR_TO_MIXOUTR */ | ||
753 | |||
754 | /* | ||
755 | * R52 (0x34) - Analogue Spk Mix Left 0 | ||
756 | */ | ||
757 | #define WM8903_DACL_TO_MIXSPKL 0x0008 /* DACL_TO_MIXSPKL */ | ||
758 | #define WM8903_DACL_TO_MIXSPKL_MASK 0x0008 /* DACL_TO_MIXSPKL */ | ||
759 | #define WM8903_DACL_TO_MIXSPKL_SHIFT 3 /* DACL_TO_MIXSPKL */ | ||
760 | #define WM8903_DACL_TO_MIXSPKL_WIDTH 1 /* DACL_TO_MIXSPKL */ | ||
761 | #define WM8903_DACR_TO_MIXSPKL 0x0004 /* DACR_TO_MIXSPKL */ | ||
762 | #define WM8903_DACR_TO_MIXSPKL_MASK 0x0004 /* DACR_TO_MIXSPKL */ | ||
763 | #define WM8903_DACR_TO_MIXSPKL_SHIFT 2 /* DACR_TO_MIXSPKL */ | ||
764 | #define WM8903_DACR_TO_MIXSPKL_WIDTH 1 /* DACR_TO_MIXSPKL */ | ||
765 | #define WM8903_BYPASSL_TO_MIXSPKL 0x0002 /* BYPASSL_TO_MIXSPKL */ | ||
766 | #define WM8903_BYPASSL_TO_MIXSPKL_MASK 0x0002 /* BYPASSL_TO_MIXSPKL */ | ||
767 | #define WM8903_BYPASSL_TO_MIXSPKL_SHIFT 1 /* BYPASSL_TO_MIXSPKL */ | ||
768 | #define WM8903_BYPASSL_TO_MIXSPKL_WIDTH 1 /* BYPASSL_TO_MIXSPKL */ | ||
769 | #define WM8903_BYPASSR_TO_MIXSPKL 0x0001 /* BYPASSR_TO_MIXSPKL */ | ||
770 | #define WM8903_BYPASSR_TO_MIXSPKL_MASK 0x0001 /* BYPASSR_TO_MIXSPKL */ | ||
771 | #define WM8903_BYPASSR_TO_MIXSPKL_SHIFT 0 /* BYPASSR_TO_MIXSPKL */ | ||
772 | #define WM8903_BYPASSR_TO_MIXSPKL_WIDTH 1 /* BYPASSR_TO_MIXSPKL */ | ||
773 | |||
774 | /* | ||
775 | * R53 (0x35) - Analogue Spk Mix Left 1 | ||
776 | */ | ||
777 | #define WM8903_DACL_MIXSPKL_VOL 0x0008 /* DACL_MIXSPKL_VOL */ | ||
778 | #define WM8903_DACL_MIXSPKL_VOL_MASK 0x0008 /* DACL_MIXSPKL_VOL */ | ||
779 | #define WM8903_DACL_MIXSPKL_VOL_SHIFT 3 /* DACL_MIXSPKL_VOL */ | ||
780 | #define WM8903_DACL_MIXSPKL_VOL_WIDTH 1 /* DACL_MIXSPKL_VOL */ | ||
781 | #define WM8903_DACR_MIXSPKL_VOL 0x0004 /* DACR_MIXSPKL_VOL */ | ||
782 | #define WM8903_DACR_MIXSPKL_VOL_MASK 0x0004 /* DACR_MIXSPKL_VOL */ | ||
783 | #define WM8903_DACR_MIXSPKL_VOL_SHIFT 2 /* DACR_MIXSPKL_VOL */ | ||
784 | #define WM8903_DACR_MIXSPKL_VOL_WIDTH 1 /* DACR_MIXSPKL_VOL */ | ||
785 | #define WM8903_BYPASSL_MIXSPKL_VOL 0x0002 /* BYPASSL_MIXSPKL_VOL */ | ||
786 | #define WM8903_BYPASSL_MIXSPKL_VOL_MASK 0x0002 /* BYPASSL_MIXSPKL_VOL */ | ||
787 | #define WM8903_BYPASSL_MIXSPKL_VOL_SHIFT 1 /* BYPASSL_MIXSPKL_VOL */ | ||
788 | #define WM8903_BYPASSL_MIXSPKL_VOL_WIDTH 1 /* BYPASSL_MIXSPKL_VOL */ | ||
789 | #define WM8903_BYPASSR_MIXSPKL_VOL 0x0001 /* BYPASSR_MIXSPKL_VOL */ | ||
790 | #define WM8903_BYPASSR_MIXSPKL_VOL_MASK 0x0001 /* BYPASSR_MIXSPKL_VOL */ | ||
791 | #define WM8903_BYPASSR_MIXSPKL_VOL_SHIFT 0 /* BYPASSR_MIXSPKL_VOL */ | ||
792 | #define WM8903_BYPASSR_MIXSPKL_VOL_WIDTH 1 /* BYPASSR_MIXSPKL_VOL */ | ||
793 | |||
794 | /* | ||
795 | * R54 (0x36) - Analogue Spk Mix Right 0 | ||
796 | */ | ||
797 | #define WM8903_DACL_TO_MIXSPKR 0x0008 /* DACL_TO_MIXSPKR */ | ||
798 | #define WM8903_DACL_TO_MIXSPKR_MASK 0x0008 /* DACL_TO_MIXSPKR */ | ||
799 | #define WM8903_DACL_TO_MIXSPKR_SHIFT 3 /* DACL_TO_MIXSPKR */ | ||
800 | #define WM8903_DACL_TO_MIXSPKR_WIDTH 1 /* DACL_TO_MIXSPKR */ | ||
801 | #define WM8903_DACR_TO_MIXSPKR 0x0004 /* DACR_TO_MIXSPKR */ | ||
802 | #define WM8903_DACR_TO_MIXSPKR_MASK 0x0004 /* DACR_TO_MIXSPKR */ | ||
803 | #define WM8903_DACR_TO_MIXSPKR_SHIFT 2 /* DACR_TO_MIXSPKR */ | ||
804 | #define WM8903_DACR_TO_MIXSPKR_WIDTH 1 /* DACR_TO_MIXSPKR */ | ||
805 | #define WM8903_BYPASSL_TO_MIXSPKR 0x0002 /* BYPASSL_TO_MIXSPKR */ | ||
806 | #define WM8903_BYPASSL_TO_MIXSPKR_MASK 0x0002 /* BYPASSL_TO_MIXSPKR */ | ||
807 | #define WM8903_BYPASSL_TO_MIXSPKR_SHIFT 1 /* BYPASSL_TO_MIXSPKR */ | ||
808 | #define WM8903_BYPASSL_TO_MIXSPKR_WIDTH 1 /* BYPASSL_TO_MIXSPKR */ | ||
809 | #define WM8903_BYPASSR_TO_MIXSPKR 0x0001 /* BYPASSR_TO_MIXSPKR */ | ||
810 | #define WM8903_BYPASSR_TO_MIXSPKR_MASK 0x0001 /* BYPASSR_TO_MIXSPKR */ | ||
811 | #define WM8903_BYPASSR_TO_MIXSPKR_SHIFT 0 /* BYPASSR_TO_MIXSPKR */ | ||
812 | #define WM8903_BYPASSR_TO_MIXSPKR_WIDTH 1 /* BYPASSR_TO_MIXSPKR */ | ||
813 | |||
814 | /* | ||
815 | * R55 (0x37) - Analogue Spk Mix Right 1 | ||
816 | */ | ||
817 | #define WM8903_DACL_MIXSPKR_VOL 0x0008 /* DACL_MIXSPKR_VOL */ | ||
818 | #define WM8903_DACL_MIXSPKR_VOL_MASK 0x0008 /* DACL_MIXSPKR_VOL */ | ||
819 | #define WM8903_DACL_MIXSPKR_VOL_SHIFT 3 /* DACL_MIXSPKR_VOL */ | ||
820 | #define WM8903_DACL_MIXSPKR_VOL_WIDTH 1 /* DACL_MIXSPKR_VOL */ | ||
821 | #define WM8903_DACR_MIXSPKR_VOL 0x0004 /* DACR_MIXSPKR_VOL */ | ||
822 | #define WM8903_DACR_MIXSPKR_VOL_MASK 0x0004 /* DACR_MIXSPKR_VOL */ | ||
823 | #define WM8903_DACR_MIXSPKR_VOL_SHIFT 2 /* DACR_MIXSPKR_VOL */ | ||
824 | #define WM8903_DACR_MIXSPKR_VOL_WIDTH 1 /* DACR_MIXSPKR_VOL */ | ||
825 | #define WM8903_BYPASSL_MIXSPKR_VOL 0x0002 /* BYPASSL_MIXSPKR_VOL */ | ||
826 | #define WM8903_BYPASSL_MIXSPKR_VOL_MASK 0x0002 /* BYPASSL_MIXSPKR_VOL */ | ||
827 | #define WM8903_BYPASSL_MIXSPKR_VOL_SHIFT 1 /* BYPASSL_MIXSPKR_VOL */ | ||
828 | #define WM8903_BYPASSL_MIXSPKR_VOL_WIDTH 1 /* BYPASSL_MIXSPKR_VOL */ | ||
829 | #define WM8903_BYPASSR_MIXSPKR_VOL 0x0001 /* BYPASSR_MIXSPKR_VOL */ | ||
830 | #define WM8903_BYPASSR_MIXSPKR_VOL_MASK 0x0001 /* BYPASSR_MIXSPKR_VOL */ | ||
831 | #define WM8903_BYPASSR_MIXSPKR_VOL_SHIFT 0 /* BYPASSR_MIXSPKR_VOL */ | ||
832 | #define WM8903_BYPASSR_MIXSPKR_VOL_WIDTH 1 /* BYPASSR_MIXSPKR_VOL */ | ||
833 | |||
834 | /* | ||
835 | * R57 (0x39) - Analogue OUT1 Left | ||
836 | */ | ||
837 | #define WM8903_HPL_MUTE 0x0100 /* HPL_MUTE */ | ||
838 | #define WM8903_HPL_MUTE_MASK 0x0100 /* HPL_MUTE */ | ||
839 | #define WM8903_HPL_MUTE_SHIFT 8 /* HPL_MUTE */ | ||
840 | #define WM8903_HPL_MUTE_WIDTH 1 /* HPL_MUTE */ | ||
841 | #define WM8903_HPOUTVU 0x0080 /* HPOUTVU */ | ||
842 | #define WM8903_HPOUTVU_MASK 0x0080 /* HPOUTVU */ | ||
843 | #define WM8903_HPOUTVU_SHIFT 7 /* HPOUTVU */ | ||
844 | #define WM8903_HPOUTVU_WIDTH 1 /* HPOUTVU */ | ||
845 | #define WM8903_HPOUTLZC 0x0040 /* HPOUTLZC */ | ||
846 | #define WM8903_HPOUTLZC_MASK 0x0040 /* HPOUTLZC */ | ||
847 | #define WM8903_HPOUTLZC_SHIFT 6 /* HPOUTLZC */ | ||
848 | #define WM8903_HPOUTLZC_WIDTH 1 /* HPOUTLZC */ | ||
849 | #define WM8903_HPOUTL_VOL_MASK 0x003F /* HPOUTL_VOL - [5:0] */ | ||
850 | #define WM8903_HPOUTL_VOL_SHIFT 0 /* HPOUTL_VOL - [5:0] */ | ||
851 | #define WM8903_HPOUTL_VOL_WIDTH 6 /* HPOUTL_VOL - [5:0] */ | ||
852 | |||
853 | /* | ||
854 | * R58 (0x3A) - Analogue OUT1 Right | ||
855 | */ | ||
856 | #define WM8903_HPR_MUTE 0x0100 /* HPR_MUTE */ | ||
857 | #define WM8903_HPR_MUTE_MASK 0x0100 /* HPR_MUTE */ | ||
858 | #define WM8903_HPR_MUTE_SHIFT 8 /* HPR_MUTE */ | ||
859 | #define WM8903_HPR_MUTE_WIDTH 1 /* HPR_MUTE */ | ||
860 | #define WM8903_HPOUTVU 0x0080 /* HPOUTVU */ | ||
861 | #define WM8903_HPOUTVU_MASK 0x0080 /* HPOUTVU */ | ||
862 | #define WM8903_HPOUTVU_SHIFT 7 /* HPOUTVU */ | ||
863 | #define WM8903_HPOUTVU_WIDTH 1 /* HPOUTVU */ | ||
864 | #define WM8903_HPOUTRZC 0x0040 /* HPOUTRZC */ | ||
865 | #define WM8903_HPOUTRZC_MASK 0x0040 /* HPOUTRZC */ | ||
866 | #define WM8903_HPOUTRZC_SHIFT 6 /* HPOUTRZC */ | ||
867 | #define WM8903_HPOUTRZC_WIDTH 1 /* HPOUTRZC */ | ||
868 | #define WM8903_HPOUTR_VOL_MASK 0x003F /* HPOUTR_VOL - [5:0] */ | ||
869 | #define WM8903_HPOUTR_VOL_SHIFT 0 /* HPOUTR_VOL - [5:0] */ | ||
870 | #define WM8903_HPOUTR_VOL_WIDTH 6 /* HPOUTR_VOL - [5:0] */ | ||
871 | |||
872 | /* | ||
873 | * R59 (0x3B) - Analogue OUT2 Left | ||
874 | */ | ||
875 | #define WM8903_LINEOUTL_MUTE 0x0100 /* LINEOUTL_MUTE */ | ||
876 | #define WM8903_LINEOUTL_MUTE_MASK 0x0100 /* LINEOUTL_MUTE */ | ||
877 | #define WM8903_LINEOUTL_MUTE_SHIFT 8 /* LINEOUTL_MUTE */ | ||
878 | #define WM8903_LINEOUTL_MUTE_WIDTH 1 /* LINEOUTL_MUTE */ | ||
879 | #define WM8903_LINEOUTVU 0x0080 /* LINEOUTVU */ | ||
880 | #define WM8903_LINEOUTVU_MASK 0x0080 /* LINEOUTVU */ | ||
881 | #define WM8903_LINEOUTVU_SHIFT 7 /* LINEOUTVU */ | ||
882 | #define WM8903_LINEOUTVU_WIDTH 1 /* LINEOUTVU */ | ||
883 | #define WM8903_LINEOUTLZC 0x0040 /* LINEOUTLZC */ | ||
884 | #define WM8903_LINEOUTLZC_MASK 0x0040 /* LINEOUTLZC */ | ||
885 | #define WM8903_LINEOUTLZC_SHIFT 6 /* LINEOUTLZC */ | ||
886 | #define WM8903_LINEOUTLZC_WIDTH 1 /* LINEOUTLZC */ | ||
887 | #define WM8903_LINEOUTL_VOL_MASK 0x003F /* LINEOUTL_VOL - [5:0] */ | ||
888 | #define WM8903_LINEOUTL_VOL_SHIFT 0 /* LINEOUTL_VOL - [5:0] */ | ||
889 | #define WM8903_LINEOUTL_VOL_WIDTH 6 /* LINEOUTL_VOL - [5:0] */ | ||
890 | |||
891 | /* | ||
892 | * R60 (0x3C) - Analogue OUT2 Right | ||
893 | */ | ||
894 | #define WM8903_LINEOUTR_MUTE 0x0100 /* LINEOUTR_MUTE */ | ||
895 | #define WM8903_LINEOUTR_MUTE_MASK 0x0100 /* LINEOUTR_MUTE */ | ||
896 | #define WM8903_LINEOUTR_MUTE_SHIFT 8 /* LINEOUTR_MUTE */ | ||
897 | #define WM8903_LINEOUTR_MUTE_WIDTH 1 /* LINEOUTR_MUTE */ | ||
898 | #define WM8903_LINEOUTVU 0x0080 /* LINEOUTVU */ | ||
899 | #define WM8903_LINEOUTVU_MASK 0x0080 /* LINEOUTVU */ | ||
900 | #define WM8903_LINEOUTVU_SHIFT 7 /* LINEOUTVU */ | ||
901 | #define WM8903_LINEOUTVU_WIDTH 1 /* LINEOUTVU */ | ||
902 | #define WM8903_LINEOUTRZC 0x0040 /* LINEOUTRZC */ | ||
903 | #define WM8903_LINEOUTRZC_MASK 0x0040 /* LINEOUTRZC */ | ||
904 | #define WM8903_LINEOUTRZC_SHIFT 6 /* LINEOUTRZC */ | ||
905 | #define WM8903_LINEOUTRZC_WIDTH 1 /* LINEOUTRZC */ | ||
906 | #define WM8903_LINEOUTR_VOL_MASK 0x003F /* LINEOUTR_VOL - [5:0] */ | ||
907 | #define WM8903_LINEOUTR_VOL_SHIFT 0 /* LINEOUTR_VOL - [5:0] */ | ||
908 | #define WM8903_LINEOUTR_VOL_WIDTH 6 /* LINEOUTR_VOL - [5:0] */ | ||
909 | |||
910 | /* | ||
911 | * R62 (0x3E) - Analogue OUT3 Left | ||
912 | */ | ||
913 | #define WM8903_SPKL_MUTE 0x0100 /* SPKL_MUTE */ | ||
914 | #define WM8903_SPKL_MUTE_MASK 0x0100 /* SPKL_MUTE */ | ||
915 | #define WM8903_SPKL_MUTE_SHIFT 8 /* SPKL_MUTE */ | ||
916 | #define WM8903_SPKL_MUTE_WIDTH 1 /* SPKL_MUTE */ | ||
917 | #define WM8903_SPKVU 0x0080 /* SPKVU */ | ||
918 | #define WM8903_SPKVU_MASK 0x0080 /* SPKVU */ | ||
919 | #define WM8903_SPKVU_SHIFT 7 /* SPKVU */ | ||
920 | #define WM8903_SPKVU_WIDTH 1 /* SPKVU */ | ||
921 | #define WM8903_SPKLZC 0x0040 /* SPKLZC */ | ||
922 | #define WM8903_SPKLZC_MASK 0x0040 /* SPKLZC */ | ||
923 | #define WM8903_SPKLZC_SHIFT 6 /* SPKLZC */ | ||
924 | #define WM8903_SPKLZC_WIDTH 1 /* SPKLZC */ | ||
925 | #define WM8903_SPKL_VOL_MASK 0x003F /* SPKL_VOL - [5:0] */ | ||
926 | #define WM8903_SPKL_VOL_SHIFT 0 /* SPKL_VOL - [5:0] */ | ||
927 | #define WM8903_SPKL_VOL_WIDTH 6 /* SPKL_VOL - [5:0] */ | ||
928 | |||
929 | /* | ||
930 | * R63 (0x3F) - Analogue OUT3 Right | ||
931 | */ | ||
932 | #define WM8903_SPKR_MUTE 0x0100 /* SPKR_MUTE */ | ||
933 | #define WM8903_SPKR_MUTE_MASK 0x0100 /* SPKR_MUTE */ | ||
934 | #define WM8903_SPKR_MUTE_SHIFT 8 /* SPKR_MUTE */ | ||
935 | #define WM8903_SPKR_MUTE_WIDTH 1 /* SPKR_MUTE */ | ||
936 | #define WM8903_SPKVU 0x0080 /* SPKVU */ | ||
937 | #define WM8903_SPKVU_MASK 0x0080 /* SPKVU */ | ||
938 | #define WM8903_SPKVU_SHIFT 7 /* SPKVU */ | ||
939 | #define WM8903_SPKVU_WIDTH 1 /* SPKVU */ | ||
940 | #define WM8903_SPKRZC 0x0040 /* SPKRZC */ | ||
941 | #define WM8903_SPKRZC_MASK 0x0040 /* SPKRZC */ | ||
942 | #define WM8903_SPKRZC_SHIFT 6 /* SPKRZC */ | ||
943 | #define WM8903_SPKRZC_WIDTH 1 /* SPKRZC */ | ||
944 | #define WM8903_SPKR_VOL_MASK 0x003F /* SPKR_VOL - [5:0] */ | ||
945 | #define WM8903_SPKR_VOL_SHIFT 0 /* SPKR_VOL - [5:0] */ | ||
946 | #define WM8903_SPKR_VOL_WIDTH 6 /* SPKR_VOL - [5:0] */ | ||
947 | |||
948 | /* | ||
949 | * R65 (0x41) - Analogue SPK Output Control 0 | ||
950 | */ | ||
951 | #define WM8903_SPK_DISCHARGE 0x0002 /* SPK_DISCHARGE */ | ||
952 | #define WM8903_SPK_DISCHARGE_MASK 0x0002 /* SPK_DISCHARGE */ | ||
953 | #define WM8903_SPK_DISCHARGE_SHIFT 1 /* SPK_DISCHARGE */ | ||
954 | #define WM8903_SPK_DISCHARGE_WIDTH 1 /* SPK_DISCHARGE */ | ||
955 | #define WM8903_VROI 0x0001 /* VROI */ | ||
956 | #define WM8903_VROI_MASK 0x0001 /* VROI */ | ||
957 | #define WM8903_VROI_SHIFT 0 /* VROI */ | ||
958 | #define WM8903_VROI_WIDTH 1 /* VROI */ | ||
959 | |||
960 | /* | ||
961 | * R67 (0x43) - DC Servo 0 | ||
962 | */ | ||
963 | #define WM8903_DCS_MASTER_ENA 0x0010 /* DCS_MASTER_ENA */ | ||
964 | #define WM8903_DCS_MASTER_ENA_MASK 0x0010 /* DCS_MASTER_ENA */ | ||
965 | #define WM8903_DCS_MASTER_ENA_SHIFT 4 /* DCS_MASTER_ENA */ | ||
966 | #define WM8903_DCS_MASTER_ENA_WIDTH 1 /* DCS_MASTER_ENA */ | ||
967 | #define WM8903_DCS_ENA_MASK 0x000F /* DCS_ENA - [3:0] */ | ||
968 | #define WM8903_DCS_ENA_SHIFT 0 /* DCS_ENA - [3:0] */ | ||
969 | #define WM8903_DCS_ENA_WIDTH 4 /* DCS_ENA - [3:0] */ | ||
970 | |||
971 | /* | ||
972 | * R69 (0x45) - DC Servo 2 | ||
973 | */ | ||
974 | #define WM8903_DCS_MODE_MASK 0x0003 /* DCS_MODE - [1:0] */ | ||
975 | #define WM8903_DCS_MODE_SHIFT 0 /* DCS_MODE - [1:0] */ | ||
976 | #define WM8903_DCS_MODE_WIDTH 2 /* DCS_MODE - [1:0] */ | ||
977 | |||
978 | /* | ||
979 | * R90 (0x5A) - Analogue HP 0 | ||
980 | */ | ||
981 | #define WM8903_HPL_RMV_SHORT 0x0080 /* HPL_RMV_SHORT */ | ||
982 | #define WM8903_HPL_RMV_SHORT_MASK 0x0080 /* HPL_RMV_SHORT */ | ||
983 | #define WM8903_HPL_RMV_SHORT_SHIFT 7 /* HPL_RMV_SHORT */ | ||
984 | #define WM8903_HPL_RMV_SHORT_WIDTH 1 /* HPL_RMV_SHORT */ | ||
985 | #define WM8903_HPL_ENA_OUTP 0x0040 /* HPL_ENA_OUTP */ | ||
986 | #define WM8903_HPL_ENA_OUTP_MASK 0x0040 /* HPL_ENA_OUTP */ | ||
987 | #define WM8903_HPL_ENA_OUTP_SHIFT 6 /* HPL_ENA_OUTP */ | ||
988 | #define WM8903_HPL_ENA_OUTP_WIDTH 1 /* HPL_ENA_OUTP */ | ||
989 | #define WM8903_HPL_ENA_DLY 0x0020 /* HPL_ENA_DLY */ | ||
990 | #define WM8903_HPL_ENA_DLY_MASK 0x0020 /* HPL_ENA_DLY */ | ||
991 | #define WM8903_HPL_ENA_DLY_SHIFT 5 /* HPL_ENA_DLY */ | ||
992 | #define WM8903_HPL_ENA_DLY_WIDTH 1 /* HPL_ENA_DLY */ | ||
993 | #define WM8903_HPL_ENA 0x0010 /* HPL_ENA */ | ||
994 | #define WM8903_HPL_ENA_MASK 0x0010 /* HPL_ENA */ | ||
995 | #define WM8903_HPL_ENA_SHIFT 4 /* HPL_ENA */ | ||
996 | #define WM8903_HPL_ENA_WIDTH 1 /* HPL_ENA */ | ||
997 | #define WM8903_HPR_RMV_SHORT 0x0008 /* HPR_RMV_SHORT */ | ||
998 | #define WM8903_HPR_RMV_SHORT_MASK 0x0008 /* HPR_RMV_SHORT */ | ||
999 | #define WM8903_HPR_RMV_SHORT_SHIFT 3 /* HPR_RMV_SHORT */ | ||
1000 | #define WM8903_HPR_RMV_SHORT_WIDTH 1 /* HPR_RMV_SHORT */ | ||
1001 | #define WM8903_HPR_ENA_OUTP 0x0004 /* HPR_ENA_OUTP */ | ||
1002 | #define WM8903_HPR_ENA_OUTP_MASK 0x0004 /* HPR_ENA_OUTP */ | ||
1003 | #define WM8903_HPR_ENA_OUTP_SHIFT 2 /* HPR_ENA_OUTP */ | ||
1004 | #define WM8903_HPR_ENA_OUTP_WIDTH 1 /* HPR_ENA_OUTP */ | ||
1005 | #define WM8903_HPR_ENA_DLY 0x0002 /* HPR_ENA_DLY */ | ||
1006 | #define WM8903_HPR_ENA_DLY_MASK 0x0002 /* HPR_ENA_DLY */ | ||
1007 | #define WM8903_HPR_ENA_DLY_SHIFT 1 /* HPR_ENA_DLY */ | ||
1008 | #define WM8903_HPR_ENA_DLY_WIDTH 1 /* HPR_ENA_DLY */ | ||
1009 | #define WM8903_HPR_ENA 0x0001 /* HPR_ENA */ | ||
1010 | #define WM8903_HPR_ENA_MASK 0x0001 /* HPR_ENA */ | ||
1011 | #define WM8903_HPR_ENA_SHIFT 0 /* HPR_ENA */ | ||
1012 | #define WM8903_HPR_ENA_WIDTH 1 /* HPR_ENA */ | ||
1013 | |||
1014 | /* | ||
1015 | * R94 (0x5E) - Analogue Lineout 0 | ||
1016 | */ | ||
1017 | #define WM8903_LINEOUTL_RMV_SHORT 0x0080 /* LINEOUTL_RMV_SHORT */ | ||
1018 | #define WM8903_LINEOUTL_RMV_SHORT_MASK 0x0080 /* LINEOUTL_RMV_SHORT */ | ||
1019 | #define WM8903_LINEOUTL_RMV_SHORT_SHIFT 7 /* LINEOUTL_RMV_SHORT */ | ||
1020 | #define WM8903_LINEOUTL_RMV_SHORT_WIDTH 1 /* LINEOUTL_RMV_SHORT */ | ||
1021 | #define WM8903_LINEOUTL_ENA_OUTP 0x0040 /* LINEOUTL_ENA_OUTP */ | ||
1022 | #define WM8903_LINEOUTL_ENA_OUTP_MASK 0x0040 /* LINEOUTL_ENA_OUTP */ | ||
1023 | #define WM8903_LINEOUTL_ENA_OUTP_SHIFT 6 /* LINEOUTL_ENA_OUTP */ | ||
1024 | #define WM8903_LINEOUTL_ENA_OUTP_WIDTH 1 /* LINEOUTL_ENA_OUTP */ | ||
1025 | #define WM8903_LINEOUTL_ENA_DLY 0x0020 /* LINEOUTL_ENA_DLY */ | ||
1026 | #define WM8903_LINEOUTL_ENA_DLY_MASK 0x0020 /* LINEOUTL_ENA_DLY */ | ||
1027 | #define WM8903_LINEOUTL_ENA_DLY_SHIFT 5 /* LINEOUTL_ENA_DLY */ | ||
1028 | #define WM8903_LINEOUTL_ENA_DLY_WIDTH 1 /* LINEOUTL_ENA_DLY */ | ||
1029 | #define WM8903_LINEOUTL_ENA 0x0010 /* LINEOUTL_ENA */ | ||
1030 | #define WM8903_LINEOUTL_ENA_MASK 0x0010 /* LINEOUTL_ENA */ | ||
1031 | #define WM8903_LINEOUTL_ENA_SHIFT 4 /* LINEOUTL_ENA */ | ||
1032 | #define WM8903_LINEOUTL_ENA_WIDTH 1 /* LINEOUTL_ENA */ | ||
1033 | #define WM8903_LINEOUTR_RMV_SHORT 0x0008 /* LINEOUTR_RMV_SHORT */ | ||
1034 | #define WM8903_LINEOUTR_RMV_SHORT_MASK 0x0008 /* LINEOUTR_RMV_SHORT */ | ||
1035 | #define WM8903_LINEOUTR_RMV_SHORT_SHIFT 3 /* LINEOUTR_RMV_SHORT */ | ||
1036 | #define WM8903_LINEOUTR_RMV_SHORT_WIDTH 1 /* LINEOUTR_RMV_SHORT */ | ||
1037 | #define WM8903_LINEOUTR_ENA_OUTP 0x0004 /* LINEOUTR_ENA_OUTP */ | ||
1038 | #define WM8903_LINEOUTR_ENA_OUTP_MASK 0x0004 /* LINEOUTR_ENA_OUTP */ | ||
1039 | #define WM8903_LINEOUTR_ENA_OUTP_SHIFT 2 /* LINEOUTR_ENA_OUTP */ | ||
1040 | #define WM8903_LINEOUTR_ENA_OUTP_WIDTH 1 /* LINEOUTR_ENA_OUTP */ | ||
1041 | #define WM8903_LINEOUTR_ENA_DLY 0x0002 /* LINEOUTR_ENA_DLY */ | ||
1042 | #define WM8903_LINEOUTR_ENA_DLY_MASK 0x0002 /* LINEOUTR_ENA_DLY */ | ||
1043 | #define WM8903_LINEOUTR_ENA_DLY_SHIFT 1 /* LINEOUTR_ENA_DLY */ | ||
1044 | #define WM8903_LINEOUTR_ENA_DLY_WIDTH 1 /* LINEOUTR_ENA_DLY */ | ||
1045 | #define WM8903_LINEOUTR_ENA 0x0001 /* LINEOUTR_ENA */ | ||
1046 | #define WM8903_LINEOUTR_ENA_MASK 0x0001 /* LINEOUTR_ENA */ | ||
1047 | #define WM8903_LINEOUTR_ENA_SHIFT 0 /* LINEOUTR_ENA */ | ||
1048 | #define WM8903_LINEOUTR_ENA_WIDTH 1 /* LINEOUTR_ENA */ | ||
1049 | |||
1050 | /* | ||
1051 | * R98 (0x62) - Charge Pump 0 | ||
1052 | */ | ||
1053 | #define WM8903_CP_ENA 0x0001 /* CP_ENA */ | ||
1054 | #define WM8903_CP_ENA_MASK 0x0001 /* CP_ENA */ | ||
1055 | #define WM8903_CP_ENA_SHIFT 0 /* CP_ENA */ | ||
1056 | #define WM8903_CP_ENA_WIDTH 1 /* CP_ENA */ | ||
1057 | |||
1058 | /* | ||
1059 | * R104 (0x68) - Class W 0 | ||
1060 | */ | ||
1061 | #define WM8903_CP_DYN_FREQ 0x0002 /* CP_DYN_FREQ */ | ||
1062 | #define WM8903_CP_DYN_FREQ_MASK 0x0002 /* CP_DYN_FREQ */ | ||
1063 | #define WM8903_CP_DYN_FREQ_SHIFT 1 /* CP_DYN_FREQ */ | ||
1064 | #define WM8903_CP_DYN_FREQ_WIDTH 1 /* CP_DYN_FREQ */ | ||
1065 | #define WM8903_CP_DYN_V 0x0001 /* CP_DYN_V */ | ||
1066 | #define WM8903_CP_DYN_V_MASK 0x0001 /* CP_DYN_V */ | ||
1067 | #define WM8903_CP_DYN_V_SHIFT 0 /* CP_DYN_V */ | ||
1068 | #define WM8903_CP_DYN_V_WIDTH 1 /* CP_DYN_V */ | ||
1069 | |||
1070 | /* | ||
1071 | * R108 (0x6C) - Write Sequencer 0 | ||
1072 | */ | ||
1073 | #define WM8903_WSEQ_ENA 0x0100 /* WSEQ_ENA */ | ||
1074 | #define WM8903_WSEQ_ENA_MASK 0x0100 /* WSEQ_ENA */ | ||
1075 | #define WM8903_WSEQ_ENA_SHIFT 8 /* WSEQ_ENA */ | ||
1076 | #define WM8903_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ | ||
1077 | #define WM8903_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */ | ||
1078 | #define WM8903_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */ | ||
1079 | #define WM8903_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */ | ||
1080 | |||
1081 | /* | ||
1082 | * R109 (0x6D) - Write Sequencer 1 | ||
1083 | */ | ||
1084 | #define WM8903_WSEQ_DATA_WIDTH_MASK 0x7000 /* WSEQ_DATA_WIDTH - [14:12] */ | ||
1085 | #define WM8903_WSEQ_DATA_WIDTH_SHIFT 12 /* WSEQ_DATA_WIDTH - [14:12] */ | ||
1086 | #define WM8903_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [14:12] */ | ||
1087 | #define WM8903_WSEQ_DATA_START_MASK 0x0F00 /* WSEQ_DATA_START - [11:8] */ | ||
1088 | #define WM8903_WSEQ_DATA_START_SHIFT 8 /* WSEQ_DATA_START - [11:8] */ | ||
1089 | #define WM8903_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [11:8] */ | ||
1090 | #define WM8903_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */ | ||
1091 | #define WM8903_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */ | ||
1092 | #define WM8903_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */ | ||
1093 | |||
1094 | /* | ||
1095 | * R110 (0x6E) - Write Sequencer 2 | ||
1096 | */ | ||
1097 | #define WM8903_WSEQ_EOS 0x4000 /* WSEQ_EOS */ | ||
1098 | #define WM8903_WSEQ_EOS_MASK 0x4000 /* WSEQ_EOS */ | ||
1099 | #define WM8903_WSEQ_EOS_SHIFT 14 /* WSEQ_EOS */ | ||
1100 | #define WM8903_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */ | ||
1101 | #define WM8903_WSEQ_DELAY_MASK 0x0F00 /* WSEQ_DELAY - [11:8] */ | ||
1102 | #define WM8903_WSEQ_DELAY_SHIFT 8 /* WSEQ_DELAY - [11:8] */ | ||
1103 | #define WM8903_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [11:8] */ | ||
1104 | #define WM8903_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */ | ||
1105 | #define WM8903_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */ | ||
1106 | #define WM8903_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */ | ||
1107 | |||
1108 | /* | ||
1109 | * R111 (0x6F) - Write Sequencer 3 | ||
1110 | */ | ||
1111 | #define WM8903_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */ | ||
1112 | #define WM8903_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */ | ||
1113 | #define WM8903_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */ | ||
1114 | #define WM8903_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ | ||
1115 | #define WM8903_WSEQ_START 0x0100 /* WSEQ_START */ | ||
1116 | #define WM8903_WSEQ_START_MASK 0x0100 /* WSEQ_START */ | ||
1117 | #define WM8903_WSEQ_START_SHIFT 8 /* WSEQ_START */ | ||
1118 | #define WM8903_WSEQ_START_WIDTH 1 /* WSEQ_START */ | ||
1119 | #define WM8903_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */ | ||
1120 | #define WM8903_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */ | ||
1121 | #define WM8903_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */ | ||
1122 | |||
1123 | /* | ||
1124 | * R112 (0x70) - Write Sequencer 4 | ||
1125 | */ | ||
1126 | #define WM8903_WSEQ_CURRENT_INDEX_MASK 0x03F0 /* WSEQ_CURRENT_INDEX - [9:4] */ | ||
1127 | #define WM8903_WSEQ_CURRENT_INDEX_SHIFT 4 /* WSEQ_CURRENT_INDEX - [9:4] */ | ||
1128 | #define WM8903_WSEQ_CURRENT_INDEX_WIDTH 6 /* WSEQ_CURRENT_INDEX - [9:4] */ | ||
1129 | #define WM8903_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */ | ||
1130 | #define WM8903_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */ | ||
1131 | #define WM8903_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */ | ||
1132 | #define WM8903_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ | ||
1133 | |||
1134 | /* | ||
1135 | * R114 (0x72) - Control Interface | ||
1136 | */ | ||
1137 | #define WM8903_MASK_WRITE_ENA 0x0001 /* MASK_WRITE_ENA */ | ||
1138 | #define WM8903_MASK_WRITE_ENA_MASK 0x0001 /* MASK_WRITE_ENA */ | ||
1139 | #define WM8903_MASK_WRITE_ENA_SHIFT 0 /* MASK_WRITE_ENA */ | ||
1140 | #define WM8903_MASK_WRITE_ENA_WIDTH 1 /* MASK_WRITE_ENA */ | ||
1141 | |||
1142 | /* | ||
1143 | * R116 (0x74) - GPIO Control 1 | ||
1144 | */ | ||
1145 | #define WM8903_GP1_FN_MASK 0x1F00 /* GP1_FN - [12:8] */ | ||
1146 | #define WM8903_GP1_FN_SHIFT 8 /* GP1_FN - [12:8] */ | ||
1147 | #define WM8903_GP1_FN_WIDTH 5 /* GP1_FN - [12:8] */ | ||
1148 | #define WM8903_GP1_DIR 0x0080 /* GP1_DIR */ | ||
1149 | #define WM8903_GP1_DIR_MASK 0x0080 /* GP1_DIR */ | ||
1150 | #define WM8903_GP1_DIR_SHIFT 7 /* GP1_DIR */ | ||
1151 | #define WM8903_GP1_DIR_WIDTH 1 /* GP1_DIR */ | ||
1152 | #define WM8903_GP1_OP_CFG 0x0040 /* GP1_OP_CFG */ | ||
1153 | #define WM8903_GP1_OP_CFG_MASK 0x0040 /* GP1_OP_CFG */ | ||
1154 | #define WM8903_GP1_OP_CFG_SHIFT 6 /* GP1_OP_CFG */ | ||
1155 | #define WM8903_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */ | ||
1156 | #define WM8903_GP1_IP_CFG 0x0020 /* GP1_IP_CFG */ | ||
1157 | #define WM8903_GP1_IP_CFG_MASK 0x0020 /* GP1_IP_CFG */ | ||
1158 | #define WM8903_GP1_IP_CFG_SHIFT 5 /* GP1_IP_CFG */ | ||
1159 | #define WM8903_GP1_IP_CFG_WIDTH 1 /* GP1_IP_CFG */ | ||
1160 | #define WM8903_GP1_LVL 0x0010 /* GP1_LVL */ | ||
1161 | #define WM8903_GP1_LVL_MASK 0x0010 /* GP1_LVL */ | ||
1162 | #define WM8903_GP1_LVL_SHIFT 4 /* GP1_LVL */ | ||
1163 | #define WM8903_GP1_LVL_WIDTH 1 /* GP1_LVL */ | ||
1164 | #define WM8903_GP1_PD 0x0008 /* GP1_PD */ | ||
1165 | #define WM8903_GP1_PD_MASK 0x0008 /* GP1_PD */ | ||
1166 | #define WM8903_GP1_PD_SHIFT 3 /* GP1_PD */ | ||
1167 | #define WM8903_GP1_PD_WIDTH 1 /* GP1_PD */ | ||
1168 | #define WM8903_GP1_PU 0x0004 /* GP1_PU */ | ||
1169 | #define WM8903_GP1_PU_MASK 0x0004 /* GP1_PU */ | ||
1170 | #define WM8903_GP1_PU_SHIFT 2 /* GP1_PU */ | ||
1171 | #define WM8903_GP1_PU_WIDTH 1 /* GP1_PU */ | ||
1172 | #define WM8903_GP1_INTMODE 0x0002 /* GP1_INTMODE */ | ||
1173 | #define WM8903_GP1_INTMODE_MASK 0x0002 /* GP1_INTMODE */ | ||
1174 | #define WM8903_GP1_INTMODE_SHIFT 1 /* GP1_INTMODE */ | ||
1175 | #define WM8903_GP1_INTMODE_WIDTH 1 /* GP1_INTMODE */ | ||
1176 | #define WM8903_GP1_DB 0x0001 /* GP1_DB */ | ||
1177 | #define WM8903_GP1_DB_MASK 0x0001 /* GP1_DB */ | ||
1178 | #define WM8903_GP1_DB_SHIFT 0 /* GP1_DB */ | ||
1179 | #define WM8903_GP1_DB_WIDTH 1 /* GP1_DB */ | ||
1180 | |||
1181 | /* | ||
1182 | * R117 (0x75) - GPIO Control 2 | ||
1183 | */ | ||
1184 | #define WM8903_GP2_FN_MASK 0x1F00 /* GP2_FN - [12:8] */ | ||
1185 | #define WM8903_GP2_FN_SHIFT 8 /* GP2_FN - [12:8] */ | ||
1186 | #define WM8903_GP2_FN_WIDTH 5 /* GP2_FN - [12:8] */ | ||
1187 | #define WM8903_GP2_DIR 0x0080 /* GP2_DIR */ | ||
1188 | #define WM8903_GP2_DIR_MASK 0x0080 /* GP2_DIR */ | ||
1189 | #define WM8903_GP2_DIR_SHIFT 7 /* GP2_DIR */ | ||
1190 | #define WM8903_GP2_DIR_WIDTH 1 /* GP2_DIR */ | ||
1191 | #define WM8903_GP2_OP_CFG 0x0040 /* GP2_OP_CFG */ | ||
1192 | #define WM8903_GP2_OP_CFG_MASK 0x0040 /* GP2_OP_CFG */ | ||
1193 | #define WM8903_GP2_OP_CFG_SHIFT 6 /* GP2_OP_CFG */ | ||
1194 | #define WM8903_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */ | ||
1195 | #define WM8903_GP2_IP_CFG 0x0020 /* GP2_IP_CFG */ | ||
1196 | #define WM8903_GP2_IP_CFG_MASK 0x0020 /* GP2_IP_CFG */ | ||
1197 | #define WM8903_GP2_IP_CFG_SHIFT 5 /* GP2_IP_CFG */ | ||
1198 | #define WM8903_GP2_IP_CFG_WIDTH 1 /* GP2_IP_CFG */ | ||
1199 | #define WM8903_GP2_LVL 0x0010 /* GP2_LVL */ | ||
1200 | #define WM8903_GP2_LVL_MASK 0x0010 /* GP2_LVL */ | ||
1201 | #define WM8903_GP2_LVL_SHIFT 4 /* GP2_LVL */ | ||
1202 | #define WM8903_GP2_LVL_WIDTH 1 /* GP2_LVL */ | ||
1203 | #define WM8903_GP2_PD 0x0008 /* GP2_PD */ | ||
1204 | #define WM8903_GP2_PD_MASK 0x0008 /* GP2_PD */ | ||
1205 | #define WM8903_GP2_PD_SHIFT 3 /* GP2_PD */ | ||
1206 | #define WM8903_GP2_PD_WIDTH 1 /* GP2_PD */ | ||
1207 | #define WM8903_GP2_PU 0x0004 /* GP2_PU */ | ||
1208 | #define WM8903_GP2_PU_MASK 0x0004 /* GP2_PU */ | ||
1209 | #define WM8903_GP2_PU_SHIFT 2 /* GP2_PU */ | ||
1210 | #define WM8903_GP2_PU_WIDTH 1 /* GP2_PU */ | ||
1211 | #define WM8903_GP2_INTMODE 0x0002 /* GP2_INTMODE */ | ||
1212 | #define WM8903_GP2_INTMODE_MASK 0x0002 /* GP2_INTMODE */ | ||
1213 | #define WM8903_GP2_INTMODE_SHIFT 1 /* GP2_INTMODE */ | ||
1214 | #define WM8903_GP2_INTMODE_WIDTH 1 /* GP2_INTMODE */ | ||
1215 | #define WM8903_GP2_DB 0x0001 /* GP2_DB */ | ||
1216 | #define WM8903_GP2_DB_MASK 0x0001 /* GP2_DB */ | ||
1217 | #define WM8903_GP2_DB_SHIFT 0 /* GP2_DB */ | ||
1218 | #define WM8903_GP2_DB_WIDTH 1 /* GP2_DB */ | ||
1219 | |||
1220 | /* | ||
1221 | * R118 (0x76) - GPIO Control 3 | ||
1222 | */ | ||
1223 | #define WM8903_GP3_FN_MASK 0x1F00 /* GP3_FN - [12:8] */ | ||
1224 | #define WM8903_GP3_FN_SHIFT 8 /* GP3_FN - [12:8] */ | ||
1225 | #define WM8903_GP3_FN_WIDTH 5 /* GP3_FN - [12:8] */ | ||
1226 | #define WM8903_GP3_DIR 0x0080 /* GP3_DIR */ | ||
1227 | #define WM8903_GP3_DIR_MASK 0x0080 /* GP3_DIR */ | ||
1228 | #define WM8903_GP3_DIR_SHIFT 7 /* GP3_DIR */ | ||
1229 | #define WM8903_GP3_DIR_WIDTH 1 /* GP3_DIR */ | ||
1230 | #define WM8903_GP3_OP_CFG 0x0040 /* GP3_OP_CFG */ | ||
1231 | #define WM8903_GP3_OP_CFG_MASK 0x0040 /* GP3_OP_CFG */ | ||
1232 | #define WM8903_GP3_OP_CFG_SHIFT 6 /* GP3_OP_CFG */ | ||
1233 | #define WM8903_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */ | ||
1234 | #define WM8903_GP3_IP_CFG 0x0020 /* GP3_IP_CFG */ | ||
1235 | #define WM8903_GP3_IP_CFG_MASK 0x0020 /* GP3_IP_CFG */ | ||
1236 | #define WM8903_GP3_IP_CFG_SHIFT 5 /* GP3_IP_CFG */ | ||
1237 | #define WM8903_GP3_IP_CFG_WIDTH 1 /* GP3_IP_CFG */ | ||
1238 | #define WM8903_GP3_LVL 0x0010 /* GP3_LVL */ | ||
1239 | #define WM8903_GP3_LVL_MASK 0x0010 /* GP3_LVL */ | ||
1240 | #define WM8903_GP3_LVL_SHIFT 4 /* GP3_LVL */ | ||
1241 | #define WM8903_GP3_LVL_WIDTH 1 /* GP3_LVL */ | ||
1242 | #define WM8903_GP3_PD 0x0008 /* GP3_PD */ | ||
1243 | #define WM8903_GP3_PD_MASK 0x0008 /* GP3_PD */ | ||
1244 | #define WM8903_GP3_PD_SHIFT 3 /* GP3_PD */ | ||
1245 | #define WM8903_GP3_PD_WIDTH 1 /* GP3_PD */ | ||
1246 | #define WM8903_GP3_PU 0x0004 /* GP3_PU */ | ||
1247 | #define WM8903_GP3_PU_MASK 0x0004 /* GP3_PU */ | ||
1248 | #define WM8903_GP3_PU_SHIFT 2 /* GP3_PU */ | ||
1249 | #define WM8903_GP3_PU_WIDTH 1 /* GP3_PU */ | ||
1250 | #define WM8903_GP3_INTMODE 0x0002 /* GP3_INTMODE */ | ||
1251 | #define WM8903_GP3_INTMODE_MASK 0x0002 /* GP3_INTMODE */ | ||
1252 | #define WM8903_GP3_INTMODE_SHIFT 1 /* GP3_INTMODE */ | ||
1253 | #define WM8903_GP3_INTMODE_WIDTH 1 /* GP3_INTMODE */ | ||
1254 | #define WM8903_GP3_DB 0x0001 /* GP3_DB */ | ||
1255 | #define WM8903_GP3_DB_MASK 0x0001 /* GP3_DB */ | ||
1256 | #define WM8903_GP3_DB_SHIFT 0 /* GP3_DB */ | ||
1257 | #define WM8903_GP3_DB_WIDTH 1 /* GP3_DB */ | ||
1258 | |||
1259 | /* | ||
1260 | * R119 (0x77) - GPIO Control 4 | ||
1261 | */ | ||
1262 | #define WM8903_GP4_FN_MASK 0x1F00 /* GP4_FN - [12:8] */ | ||
1263 | #define WM8903_GP4_FN_SHIFT 8 /* GP4_FN - [12:8] */ | ||
1264 | #define WM8903_GP4_FN_WIDTH 5 /* GP4_FN - [12:8] */ | ||
1265 | #define WM8903_GP4_DIR 0x0080 /* GP4_DIR */ | ||
1266 | #define WM8903_GP4_DIR_MASK 0x0080 /* GP4_DIR */ | ||
1267 | #define WM8903_GP4_DIR_SHIFT 7 /* GP4_DIR */ | ||
1268 | #define WM8903_GP4_DIR_WIDTH 1 /* GP4_DIR */ | ||
1269 | #define WM8903_GP4_OP_CFG 0x0040 /* GP4_OP_CFG */ | ||
1270 | #define WM8903_GP4_OP_CFG_MASK 0x0040 /* GP4_OP_CFG */ | ||
1271 | #define WM8903_GP4_OP_CFG_SHIFT 6 /* GP4_OP_CFG */ | ||
1272 | #define WM8903_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */ | ||
1273 | #define WM8903_GP4_IP_CFG 0x0020 /* GP4_IP_CFG */ | ||
1274 | #define WM8903_GP4_IP_CFG_MASK 0x0020 /* GP4_IP_CFG */ | ||
1275 | #define WM8903_GP4_IP_CFG_SHIFT 5 /* GP4_IP_CFG */ | ||
1276 | #define WM8903_GP4_IP_CFG_WIDTH 1 /* GP4_IP_CFG */ | ||
1277 | #define WM8903_GP4_LVL 0x0010 /* GP4_LVL */ | ||
1278 | #define WM8903_GP4_LVL_MASK 0x0010 /* GP4_LVL */ | ||
1279 | #define WM8903_GP4_LVL_SHIFT 4 /* GP4_LVL */ | ||
1280 | #define WM8903_GP4_LVL_WIDTH 1 /* GP4_LVL */ | ||
1281 | #define WM8903_GP4_PD 0x0008 /* GP4_PD */ | ||
1282 | #define WM8903_GP4_PD_MASK 0x0008 /* GP4_PD */ | ||
1283 | #define WM8903_GP4_PD_SHIFT 3 /* GP4_PD */ | ||
1284 | #define WM8903_GP4_PD_WIDTH 1 /* GP4_PD */ | ||
1285 | #define WM8903_GP4_PU 0x0004 /* GP4_PU */ | ||
1286 | #define WM8903_GP4_PU_MASK 0x0004 /* GP4_PU */ | ||
1287 | #define WM8903_GP4_PU_SHIFT 2 /* GP4_PU */ | ||
1288 | #define WM8903_GP4_PU_WIDTH 1 /* GP4_PU */ | ||
1289 | #define WM8903_GP4_INTMODE 0x0002 /* GP4_INTMODE */ | ||
1290 | #define WM8903_GP4_INTMODE_MASK 0x0002 /* GP4_INTMODE */ | ||
1291 | #define WM8903_GP4_INTMODE_SHIFT 1 /* GP4_INTMODE */ | ||
1292 | #define WM8903_GP4_INTMODE_WIDTH 1 /* GP4_INTMODE */ | ||
1293 | #define WM8903_GP4_DB 0x0001 /* GP4_DB */ | ||
1294 | #define WM8903_GP4_DB_MASK 0x0001 /* GP4_DB */ | ||
1295 | #define WM8903_GP4_DB_SHIFT 0 /* GP4_DB */ | ||
1296 | #define WM8903_GP4_DB_WIDTH 1 /* GP4_DB */ | ||
1297 | |||
1298 | /* | ||
1299 | * R120 (0x78) - GPIO Control 5 | ||
1300 | */ | ||
1301 | #define WM8903_GP5_FN_MASK 0x1F00 /* GP5_FN - [12:8] */ | ||
1302 | #define WM8903_GP5_FN_SHIFT 8 /* GP5_FN - [12:8] */ | ||
1303 | #define WM8903_GP5_FN_WIDTH 5 /* GP5_FN - [12:8] */ | ||
1304 | #define WM8903_GP5_DIR 0x0080 /* GP5_DIR */ | ||
1305 | #define WM8903_GP5_DIR_MASK 0x0080 /* GP5_DIR */ | ||
1306 | #define WM8903_GP5_DIR_SHIFT 7 /* GP5_DIR */ | ||
1307 | #define WM8903_GP5_DIR_WIDTH 1 /* GP5_DIR */ | ||
1308 | #define WM8903_GP5_OP_CFG 0x0040 /* GP5_OP_CFG */ | ||
1309 | #define WM8903_GP5_OP_CFG_MASK 0x0040 /* GP5_OP_CFG */ | ||
1310 | #define WM8903_GP5_OP_CFG_SHIFT 6 /* GP5_OP_CFG */ | ||
1311 | #define WM8903_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */ | ||
1312 | #define WM8903_GP5_IP_CFG 0x0020 /* GP5_IP_CFG */ | ||
1313 | #define WM8903_GP5_IP_CFG_MASK 0x0020 /* GP5_IP_CFG */ | ||
1314 | #define WM8903_GP5_IP_CFG_SHIFT 5 /* GP5_IP_CFG */ | ||
1315 | #define WM8903_GP5_IP_CFG_WIDTH 1 /* GP5_IP_CFG */ | ||
1316 | #define WM8903_GP5_LVL 0x0010 /* GP5_LVL */ | ||
1317 | #define WM8903_GP5_LVL_MASK 0x0010 /* GP5_LVL */ | ||
1318 | #define WM8903_GP5_LVL_SHIFT 4 /* GP5_LVL */ | ||
1319 | #define WM8903_GP5_LVL_WIDTH 1 /* GP5_LVL */ | ||
1320 | #define WM8903_GP5_PD 0x0008 /* GP5_PD */ | ||
1321 | #define WM8903_GP5_PD_MASK 0x0008 /* GP5_PD */ | ||
1322 | #define WM8903_GP5_PD_SHIFT 3 /* GP5_PD */ | ||
1323 | #define WM8903_GP5_PD_WIDTH 1 /* GP5_PD */ | ||
1324 | #define WM8903_GP5_PU 0x0004 /* GP5_PU */ | ||
1325 | #define WM8903_GP5_PU_MASK 0x0004 /* GP5_PU */ | ||
1326 | #define WM8903_GP5_PU_SHIFT 2 /* GP5_PU */ | ||
1327 | #define WM8903_GP5_PU_WIDTH 1 /* GP5_PU */ | ||
1328 | #define WM8903_GP5_INTMODE 0x0002 /* GP5_INTMODE */ | ||
1329 | #define WM8903_GP5_INTMODE_MASK 0x0002 /* GP5_INTMODE */ | ||
1330 | #define WM8903_GP5_INTMODE_SHIFT 1 /* GP5_INTMODE */ | ||
1331 | #define WM8903_GP5_INTMODE_WIDTH 1 /* GP5_INTMODE */ | ||
1332 | #define WM8903_GP5_DB 0x0001 /* GP5_DB */ | ||
1333 | #define WM8903_GP5_DB_MASK 0x0001 /* GP5_DB */ | ||
1334 | #define WM8903_GP5_DB_SHIFT 0 /* GP5_DB */ | ||
1335 | #define WM8903_GP5_DB_WIDTH 1 /* GP5_DB */ | ||
1336 | |||
1337 | /* | ||
1338 | * R121 (0x79) - Interrupt Status 1 | ||
1339 | */ | ||
1340 | #define WM8903_MICSHRT_EINT 0x8000 /* MICSHRT_EINT */ | ||
1341 | #define WM8903_MICSHRT_EINT_MASK 0x8000 /* MICSHRT_EINT */ | ||
1342 | #define WM8903_MICSHRT_EINT_SHIFT 15 /* MICSHRT_EINT */ | ||
1343 | #define WM8903_MICSHRT_EINT_WIDTH 1 /* MICSHRT_EINT */ | ||
1344 | #define WM8903_MICDET_EINT 0x4000 /* MICDET_EINT */ | ||
1345 | #define WM8903_MICDET_EINT_MASK 0x4000 /* MICDET_EINT */ | ||
1346 | #define WM8903_MICDET_EINT_SHIFT 14 /* MICDET_EINT */ | ||
1347 | #define WM8903_MICDET_EINT_WIDTH 1 /* MICDET_EINT */ | ||
1348 | #define WM8903_WSEQ_BUSY_EINT 0x2000 /* WSEQ_BUSY_EINT */ | ||
1349 | #define WM8903_WSEQ_BUSY_EINT_MASK 0x2000 /* WSEQ_BUSY_EINT */ | ||
1350 | #define WM8903_WSEQ_BUSY_EINT_SHIFT 13 /* WSEQ_BUSY_EINT */ | ||
1351 | #define WM8903_WSEQ_BUSY_EINT_WIDTH 1 /* WSEQ_BUSY_EINT */ | ||
1352 | #define WM8903_GP5_EINT 0x0010 /* GP5_EINT */ | ||
1353 | #define WM8903_GP5_EINT_MASK 0x0010 /* GP5_EINT */ | ||
1354 | #define WM8903_GP5_EINT_SHIFT 4 /* GP5_EINT */ | ||
1355 | #define WM8903_GP5_EINT_WIDTH 1 /* GP5_EINT */ | ||
1356 | #define WM8903_GP4_EINT 0x0008 /* GP4_EINT */ | ||
1357 | #define WM8903_GP4_EINT_MASK 0x0008 /* GP4_EINT */ | ||
1358 | #define WM8903_GP4_EINT_SHIFT 3 /* GP4_EINT */ | ||
1359 | #define WM8903_GP4_EINT_WIDTH 1 /* GP4_EINT */ | ||
1360 | #define WM8903_GP3_EINT 0x0004 /* GP3_EINT */ | ||
1361 | #define WM8903_GP3_EINT_MASK 0x0004 /* GP3_EINT */ | ||
1362 | #define WM8903_GP3_EINT_SHIFT 2 /* GP3_EINT */ | ||
1363 | #define WM8903_GP3_EINT_WIDTH 1 /* GP3_EINT */ | ||
1364 | #define WM8903_GP2_EINT 0x0002 /* GP2_EINT */ | ||
1365 | #define WM8903_GP2_EINT_MASK 0x0002 /* GP2_EINT */ | ||
1366 | #define WM8903_GP2_EINT_SHIFT 1 /* GP2_EINT */ | ||
1367 | #define WM8903_GP2_EINT_WIDTH 1 /* GP2_EINT */ | ||
1368 | #define WM8903_GP1_EINT 0x0001 /* GP1_EINT */ | ||
1369 | #define WM8903_GP1_EINT_MASK 0x0001 /* GP1_EINT */ | ||
1370 | #define WM8903_GP1_EINT_SHIFT 0 /* GP1_EINT */ | ||
1371 | #define WM8903_GP1_EINT_WIDTH 1 /* GP1_EINT */ | ||
1372 | |||
1373 | /* | ||
1374 | * R122 (0x7A) - Interrupt Status 1 Mask | ||
1375 | */ | ||
1376 | #define WM8903_IM_MICSHRT_EINT 0x8000 /* IM_MICSHRT_EINT */ | ||
1377 | #define WM8903_IM_MICSHRT_EINT_MASK 0x8000 /* IM_MICSHRT_EINT */ | ||
1378 | #define WM8903_IM_MICSHRT_EINT_SHIFT 15 /* IM_MICSHRT_EINT */ | ||
1379 | #define WM8903_IM_MICSHRT_EINT_WIDTH 1 /* IM_MICSHRT_EINT */ | ||
1380 | #define WM8903_IM_MICDET_EINT 0x4000 /* IM_MICDET_EINT */ | ||
1381 | #define WM8903_IM_MICDET_EINT_MASK 0x4000 /* IM_MICDET_EINT */ | ||
1382 | #define WM8903_IM_MICDET_EINT_SHIFT 14 /* IM_MICDET_EINT */ | ||
1383 | #define WM8903_IM_MICDET_EINT_WIDTH 1 /* IM_MICDET_EINT */ | ||
1384 | #define WM8903_IM_WSEQ_BUSY_EINT 0x2000 /* IM_WSEQ_BUSY_EINT */ | ||
1385 | #define WM8903_IM_WSEQ_BUSY_EINT_MASK 0x2000 /* IM_WSEQ_BUSY_EINT */ | ||
1386 | #define WM8903_IM_WSEQ_BUSY_EINT_SHIFT 13 /* IM_WSEQ_BUSY_EINT */ | ||
1387 | #define WM8903_IM_WSEQ_BUSY_EINT_WIDTH 1 /* IM_WSEQ_BUSY_EINT */ | ||
1388 | #define WM8903_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */ | ||
1389 | #define WM8903_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */ | ||
1390 | #define WM8903_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */ | ||
1391 | #define WM8903_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */ | ||
1392 | #define WM8903_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */ | ||
1393 | #define WM8903_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */ | ||
1394 | #define WM8903_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */ | ||
1395 | #define WM8903_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */ | ||
1396 | #define WM8903_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */ | ||
1397 | #define WM8903_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */ | ||
1398 | #define WM8903_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */ | ||
1399 | #define WM8903_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */ | ||
1400 | #define WM8903_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */ | ||
1401 | #define WM8903_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */ | ||
1402 | #define WM8903_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */ | ||
1403 | #define WM8903_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */ | ||
1404 | #define WM8903_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */ | ||
1405 | #define WM8903_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */ | ||
1406 | #define WM8903_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */ | ||
1407 | #define WM8903_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */ | ||
1408 | |||
1409 | /* | ||
1410 | * R123 (0x7B) - Interrupt Polarity 1 | ||
1411 | */ | ||
1412 | #define WM8903_MICSHRT_INV 0x8000 /* MICSHRT_INV */ | ||
1413 | #define WM8903_MICSHRT_INV_MASK 0x8000 /* MICSHRT_INV */ | ||
1414 | #define WM8903_MICSHRT_INV_SHIFT 15 /* MICSHRT_INV */ | ||
1415 | #define WM8903_MICSHRT_INV_WIDTH 1 /* MICSHRT_INV */ | ||
1416 | #define WM8903_MICDET_INV 0x4000 /* MICDET_INV */ | ||
1417 | #define WM8903_MICDET_INV_MASK 0x4000 /* MICDET_INV */ | ||
1418 | #define WM8903_MICDET_INV_SHIFT 14 /* MICDET_INV */ | ||
1419 | #define WM8903_MICDET_INV_WIDTH 1 /* MICDET_INV */ | ||
1420 | |||
1421 | /* | ||
1422 | * R126 (0x7E) - Interrupt Control | ||
1423 | */ | ||
1424 | #define WM8903_IRQ_POL 0x0001 /* IRQ_POL */ | ||
1425 | #define WM8903_IRQ_POL_MASK 0x0001 /* IRQ_POL */ | ||
1426 | #define WM8903_IRQ_POL_SHIFT 0 /* IRQ_POL */ | ||
1427 | #define WM8903_IRQ_POL_WIDTH 1 /* IRQ_POL */ | ||
1428 | |||
1429 | /* | ||
1430 | * R129 (0x81) - Control Interface Test 1 | ||
1431 | */ | ||
1432 | #define WM8903_USER_KEY 0x0002 /* USER_KEY */ | ||
1433 | #define WM8903_USER_KEY_MASK 0x0002 /* USER_KEY */ | ||
1434 | #define WM8903_USER_KEY_SHIFT 1 /* USER_KEY */ | ||
1435 | #define WM8903_USER_KEY_WIDTH 1 /* USER_KEY */ | ||
1436 | #define WM8903_TEST_KEY 0x0001 /* TEST_KEY */ | ||
1437 | #define WM8903_TEST_KEY_MASK 0x0001 /* TEST_KEY */ | ||
1438 | #define WM8903_TEST_KEY_SHIFT 0 /* TEST_KEY */ | ||
1439 | #define WM8903_TEST_KEY_WIDTH 1 /* TEST_KEY */ | ||
1440 | |||
1441 | /* | ||
1442 | * R149 (0x95) - Charge Pump Test 1 | ||
1443 | */ | ||
1444 | #define WM8903_CP_SW_KELVIN_MODE_MASK 0x0006 /* CP_SW_KELVIN_MODE - [2:1] */ | ||
1445 | #define WM8903_CP_SW_KELVIN_MODE_SHIFT 1 /* CP_SW_KELVIN_MODE - [2:1] */ | ||
1446 | #define WM8903_CP_SW_KELVIN_MODE_WIDTH 2 /* CP_SW_KELVIN_MODE - [2:1] */ | ||
1447 | |||
1448 | /* | ||
1449 | * R164 (0xA4) - Clock Rate Test 4 | ||
1450 | */ | ||
1451 | #define WM8903_ADC_DIG_MIC 0x0200 /* ADC_DIG_MIC */ | ||
1452 | #define WM8903_ADC_DIG_MIC_MASK 0x0200 /* ADC_DIG_MIC */ | ||
1453 | #define WM8903_ADC_DIG_MIC_SHIFT 9 /* ADC_DIG_MIC */ | ||
1454 | #define WM8903_ADC_DIG_MIC_WIDTH 1 /* ADC_DIG_MIC */ | ||
1455 | |||
1456 | /* | ||
1457 | * R172 (0xAC) - Analogue Output Bias 0 | ||
1458 | */ | ||
1459 | #define WM8903_PGA_BIAS_MASK 0x0070 /* PGA_BIAS - [6:4] */ | ||
1460 | #define WM8903_PGA_BIAS_SHIFT 4 /* PGA_BIAS - [6:4] */ | ||
1461 | #define WM8903_PGA_BIAS_WIDTH 3 /* PGA_BIAS - [6:4] */ | ||
1462 | |||
1463 | #endif | ||
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c new file mode 100644 index 000000000000..f41a578ddd4f --- /dev/null +++ b/sound/soc/codecs/wm8971.c | |||
@@ -0,0 +1,941 @@ | |||
1 | /* | ||
2 | * wm8971.c -- WM8971 ALSA SoC Audio driver | ||
3 | * | ||
4 | * Copyright 2005 Lab126, Inc. | ||
5 | * | ||
6 | * Author: Kenneth Kiraly <kiraly@lab126.com> | ||
7 | * | ||
8 | * Based on wm8753.c by Liam Girdwood | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/moduleparam.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/pm.h> | ||
21 | #include <linux/i2c.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <sound/core.h> | ||
24 | #include <sound/pcm.h> | ||
25 | #include <sound/pcm_params.h> | ||
26 | #include <sound/soc.h> | ||
27 | #include <sound/soc-dapm.h> | ||
28 | #include <sound/initval.h> | ||
29 | |||
30 | #include "wm8971.h" | ||
31 | |||
32 | #define WM8971_VERSION "0.9" | ||
33 | |||
34 | #define WM8971_REG_COUNT 43 | ||
35 | |||
36 | static struct workqueue_struct *wm8971_workq = NULL; | ||
37 | |||
38 | /* codec private data */ | ||
39 | struct wm8971_priv { | ||
40 | unsigned int sysclk; | ||
41 | }; | ||
42 | |||
43 | /* | ||
44 | * wm8971 register cache | ||
45 | * We can't read the WM8971 register space when we | ||
46 | * are using 2 wire for device control, so we cache them instead. | ||
47 | */ | ||
48 | static const u16 wm8971_reg[] = { | ||
49 | 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */ | ||
50 | 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */ | ||
51 | 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */ | ||
52 | 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */ | ||
53 | 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */ | ||
54 | 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */ | ||
55 | 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */ | ||
56 | 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */ | ||
57 | 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */ | ||
58 | 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */ | ||
59 | 0x0079, 0x0079, 0x0079, /* 40 */ | ||
60 | }; | ||
61 | |||
62 | static inline unsigned int wm8971_read_reg_cache(struct snd_soc_codec *codec, | ||
63 | unsigned int reg) | ||
64 | { | ||
65 | u16 *cache = codec->reg_cache; | ||
66 | if (reg < WM8971_REG_COUNT) | ||
67 | return cache[reg]; | ||
68 | |||
69 | return -1; | ||
70 | } | ||
71 | |||
72 | static inline void wm8971_write_reg_cache(struct snd_soc_codec *codec, | ||
73 | unsigned int reg, unsigned int value) | ||
74 | { | ||
75 | u16 *cache = codec->reg_cache; | ||
76 | if (reg < WM8971_REG_COUNT) | ||
77 | cache[reg] = value; | ||
78 | } | ||
79 | |||
80 | static int wm8971_write(struct snd_soc_codec *codec, unsigned int reg, | ||
81 | unsigned int value) | ||
82 | { | ||
83 | u8 data[2]; | ||
84 | |||
85 | /* data is | ||
86 | * D15..D9 WM8753 register offset | ||
87 | * D8...D0 register data | ||
88 | */ | ||
89 | data[0] = (reg << 1) | ((value >> 8) & 0x0001); | ||
90 | data[1] = value & 0x00ff; | ||
91 | |||
92 | wm8971_write_reg_cache (codec, reg, value); | ||
93 | if (codec->hw_write(codec->control_data, data, 2) == 2) | ||
94 | return 0; | ||
95 | else | ||
96 | return -EIO; | ||
97 | } | ||
98 | |||
99 | #define wm8971_reset(c) wm8971_write(c, WM8971_RESET, 0) | ||
100 | |||
101 | /* WM8971 Controls */ | ||
102 | static const char *wm8971_bass[] = { "Linear Control", "Adaptive Boost" }; | ||
103 | static const char *wm8971_bass_filter[] = { "130Hz @ 48kHz", | ||
104 | "200Hz @ 48kHz" }; | ||
105 | static const char *wm8971_treble[] = { "8kHz", "4kHz" }; | ||
106 | static const char *wm8971_alc_func[] = { "Off", "Right", "Left", "Stereo" }; | ||
107 | static const char *wm8971_ng_type[] = { "Constant PGA Gain", | ||
108 | "Mute ADC Output" }; | ||
109 | static const char *wm8971_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" }; | ||
110 | static const char *wm8971_mono_mux[] = {"Stereo", "Mono (Left)", | ||
111 | "Mono (Right)", "Digital Mono"}; | ||
112 | static const char *wm8971_dac_phase[] = { "Non Inverted", "Inverted" }; | ||
113 | static const char *wm8971_lline_mux[] = {"Line", "NC", "NC", "PGA", | ||
114 | "Differential"}; | ||
115 | static const char *wm8971_rline_mux[] = {"Line", "Mic", "NC", "PGA", | ||
116 | "Differential"}; | ||
117 | static const char *wm8971_lpga_sel[] = {"Line", "NC", "NC", "Differential"}; | ||
118 | static const char *wm8971_rpga_sel[] = {"Line", "Mic", "NC", "Differential"}; | ||
119 | static const char *wm8971_adcpol[] = {"Normal", "L Invert", "R Invert", | ||
120 | "L + R Invert"}; | ||
121 | |||
122 | static const struct soc_enum wm8971_enum[] = { | ||
123 | SOC_ENUM_SINGLE(WM8971_BASS, 7, 2, wm8971_bass), /* 0 */ | ||
124 | SOC_ENUM_SINGLE(WM8971_BASS, 6, 2, wm8971_bass_filter), | ||
125 | SOC_ENUM_SINGLE(WM8971_TREBLE, 6, 2, wm8971_treble), | ||
126 | SOC_ENUM_SINGLE(WM8971_ALC1, 7, 4, wm8971_alc_func), | ||
127 | SOC_ENUM_SINGLE(WM8971_NGATE, 1, 2, wm8971_ng_type), /* 4 */ | ||
128 | SOC_ENUM_SINGLE(WM8971_ADCDAC, 1, 4, wm8971_deemp), | ||
129 | SOC_ENUM_SINGLE(WM8971_ADCTL1, 4, 4, wm8971_mono_mux), | ||
130 | SOC_ENUM_SINGLE(WM8971_ADCTL1, 1, 2, wm8971_dac_phase), | ||
131 | SOC_ENUM_SINGLE(WM8971_LOUTM1, 0, 5, wm8971_lline_mux), /* 8 */ | ||
132 | SOC_ENUM_SINGLE(WM8971_ROUTM1, 0, 5, wm8971_rline_mux), | ||
133 | SOC_ENUM_SINGLE(WM8971_LADCIN, 6, 4, wm8971_lpga_sel), | ||
134 | SOC_ENUM_SINGLE(WM8971_RADCIN, 6, 4, wm8971_rpga_sel), | ||
135 | SOC_ENUM_SINGLE(WM8971_ADCDAC, 5, 4, wm8971_adcpol), /* 12 */ | ||
136 | SOC_ENUM_SINGLE(WM8971_ADCIN, 6, 4, wm8971_mono_mux), | ||
137 | }; | ||
138 | |||
139 | static const struct snd_kcontrol_new wm8971_snd_controls[] = { | ||
140 | SOC_DOUBLE_R("Capture Volume", WM8971_LINVOL, WM8971_RINVOL, 0, 63, 0), | ||
141 | SOC_DOUBLE_R("Capture ZC Switch", WM8971_LINVOL, WM8971_RINVOL, | ||
142 | 6, 1, 0), | ||
143 | SOC_DOUBLE_R("Capture Switch", WM8971_LINVOL, WM8971_RINVOL, 7, 1, 1), | ||
144 | |||
145 | SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8971_LOUT1V, | ||
146 | WM8971_ROUT1V, 7, 1, 0), | ||
147 | SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8971_LOUT2V, | ||
148 | WM8971_ROUT2V, 7, 1, 0), | ||
149 | SOC_SINGLE("Mono Playback ZC Switch", WM8971_MOUTV, 7, 1, 0), | ||
150 | |||
151 | SOC_DOUBLE_R("PCM Volume", WM8971_LDAC, WM8971_RDAC, 0, 255, 0), | ||
152 | |||
153 | SOC_DOUBLE_R("Bypass Left Playback Volume", WM8971_LOUTM1, | ||
154 | WM8971_LOUTM2, 4, 7, 1), | ||
155 | SOC_DOUBLE_R("Bypass Right Playback Volume", WM8971_ROUTM1, | ||
156 | WM8971_ROUTM2, 4, 7, 1), | ||
157 | SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8971_MOUTM1, | ||
158 | WM8971_MOUTM2, 4, 7, 1), | ||
159 | |||
160 | SOC_DOUBLE_R("Headphone Playback Volume", WM8971_LOUT1V, | ||
161 | WM8971_ROUT1V, 0, 127, 0), | ||
162 | SOC_DOUBLE_R("Speaker Playback Volume", WM8971_LOUT2V, | ||
163 | WM8971_ROUT2V, 0, 127, 0), | ||
164 | |||
165 | SOC_ENUM("Bass Boost", wm8971_enum[0]), | ||
166 | SOC_ENUM("Bass Filter", wm8971_enum[1]), | ||
167 | SOC_SINGLE("Bass Volume", WM8971_BASS, 0, 7, 1), | ||
168 | |||
169 | SOC_SINGLE("Treble Volume", WM8971_TREBLE, 0, 7, 0), | ||
170 | SOC_ENUM("Treble Cut-off", wm8971_enum[2]), | ||
171 | |||
172 | SOC_SINGLE("Capture Filter Switch", WM8971_ADCDAC, 0, 1, 1), | ||
173 | |||
174 | SOC_SINGLE("ALC Target Volume", WM8971_ALC1, 0, 7, 0), | ||
175 | SOC_SINGLE("ALC Max Volume", WM8971_ALC1, 4, 7, 0), | ||
176 | |||
177 | SOC_SINGLE("ALC Capture Target Volume", WM8971_ALC1, 0, 7, 0), | ||
178 | SOC_SINGLE("ALC Capture Max Volume", WM8971_ALC1, 4, 7, 0), | ||
179 | SOC_ENUM("ALC Capture Function", wm8971_enum[3]), | ||
180 | SOC_SINGLE("ALC Capture ZC Switch", WM8971_ALC2, 7, 1, 0), | ||
181 | SOC_SINGLE("ALC Capture Hold Time", WM8971_ALC2, 0, 15, 0), | ||
182 | SOC_SINGLE("ALC Capture Decay Time", WM8971_ALC3, 4, 15, 0), | ||
183 | SOC_SINGLE("ALC Capture Attack Time", WM8971_ALC3, 0, 15, 0), | ||
184 | SOC_SINGLE("ALC Capture NG Threshold", WM8971_NGATE, 3, 31, 0), | ||
185 | SOC_ENUM("ALC Capture NG Type", wm8971_enum[4]), | ||
186 | SOC_SINGLE("ALC Capture NG Switch", WM8971_NGATE, 0, 1, 0), | ||
187 | |||
188 | SOC_SINGLE("Capture 6dB Attenuate", WM8971_ADCDAC, 8, 1, 0), | ||
189 | SOC_SINGLE("Playback 6dB Attenuate", WM8971_ADCDAC, 7, 1, 0), | ||
190 | |||
191 | SOC_ENUM("Playback De-emphasis", wm8971_enum[5]), | ||
192 | SOC_ENUM("Playback Function", wm8971_enum[6]), | ||
193 | SOC_ENUM("Playback Phase", wm8971_enum[7]), | ||
194 | |||
195 | SOC_DOUBLE_R("Mic Boost", WM8971_LADCIN, WM8971_RADCIN, 4, 3, 0), | ||
196 | }; | ||
197 | |||
198 | /* add non-DAPM controls */ | ||
199 | static int wm8971_add_controls(struct snd_soc_codec *codec) | ||
200 | { | ||
201 | int err, i; | ||
202 | |||
203 | for (i = 0; i < ARRAY_SIZE(wm8971_snd_controls); i++) { | ||
204 | err = snd_ctl_add(codec->card, | ||
205 | snd_soc_cnew(&wm8971_snd_controls[i], | ||
206 | codec, NULL)); | ||
207 | if (err < 0) | ||
208 | return err; | ||
209 | } | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | /* | ||
214 | * DAPM Controls | ||
215 | */ | ||
216 | |||
217 | /* Left Mixer */ | ||
218 | static const struct snd_kcontrol_new wm8971_left_mixer_controls[] = { | ||
219 | SOC_DAPM_SINGLE("Playback Switch", WM8971_LOUTM1, 8, 1, 0), | ||
220 | SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_LOUTM1, 7, 1, 0), | ||
221 | SOC_DAPM_SINGLE("Right Playback Switch", WM8971_LOUTM2, 8, 1, 0), | ||
222 | SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_LOUTM2, 7, 1, 0), | ||
223 | }; | ||
224 | |||
225 | /* Right Mixer */ | ||
226 | static const struct snd_kcontrol_new wm8971_right_mixer_controls[] = { | ||
227 | SOC_DAPM_SINGLE("Left Playback Switch", WM8971_ROUTM1, 8, 1, 0), | ||
228 | SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_ROUTM1, 7, 1, 0), | ||
229 | SOC_DAPM_SINGLE("Playback Switch", WM8971_ROUTM2, 8, 1, 0), | ||
230 | SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_ROUTM2, 7, 1, 0), | ||
231 | }; | ||
232 | |||
233 | /* Mono Mixer */ | ||
234 | static const struct snd_kcontrol_new wm8971_mono_mixer_controls[] = { | ||
235 | SOC_DAPM_SINGLE("Left Playback Switch", WM8971_MOUTM1, 8, 1, 0), | ||
236 | SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_MOUTM1, 7, 1, 0), | ||
237 | SOC_DAPM_SINGLE("Right Playback Switch", WM8971_MOUTM2, 8, 1, 0), | ||
238 | SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_MOUTM2, 7, 1, 0), | ||
239 | }; | ||
240 | |||
241 | /* Left Line Mux */ | ||
242 | static const struct snd_kcontrol_new wm8971_left_line_controls = | ||
243 | SOC_DAPM_ENUM("Route", wm8971_enum[8]); | ||
244 | |||
245 | /* Right Line Mux */ | ||
246 | static const struct snd_kcontrol_new wm8971_right_line_controls = | ||
247 | SOC_DAPM_ENUM("Route", wm8971_enum[9]); | ||
248 | |||
249 | /* Left PGA Mux */ | ||
250 | static const struct snd_kcontrol_new wm8971_left_pga_controls = | ||
251 | SOC_DAPM_ENUM("Route", wm8971_enum[10]); | ||
252 | |||
253 | /* Right PGA Mux */ | ||
254 | static const struct snd_kcontrol_new wm8971_right_pga_controls = | ||
255 | SOC_DAPM_ENUM("Route", wm8971_enum[11]); | ||
256 | |||
257 | /* Mono ADC Mux */ | ||
258 | static const struct snd_kcontrol_new wm8971_monomux_controls = | ||
259 | SOC_DAPM_ENUM("Route", wm8971_enum[13]); | ||
260 | |||
261 | static const struct snd_soc_dapm_widget wm8971_dapm_widgets[] = { | ||
262 | SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, | ||
263 | &wm8971_left_mixer_controls[0], | ||
264 | ARRAY_SIZE(wm8971_left_mixer_controls)), | ||
265 | SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, | ||
266 | &wm8971_right_mixer_controls[0], | ||
267 | ARRAY_SIZE(wm8971_right_mixer_controls)), | ||
268 | SND_SOC_DAPM_MIXER("Mono Mixer", WM8971_PWR2, 2, 0, | ||
269 | &wm8971_mono_mixer_controls[0], | ||
270 | ARRAY_SIZE(wm8971_mono_mixer_controls)), | ||
271 | |||
272 | SND_SOC_DAPM_PGA("Right Out 2", WM8971_PWR2, 3, 0, NULL, 0), | ||
273 | SND_SOC_DAPM_PGA("Left Out 2", WM8971_PWR2, 4, 0, NULL, 0), | ||
274 | SND_SOC_DAPM_PGA("Right Out 1", WM8971_PWR2, 5, 0, NULL, 0), | ||
275 | SND_SOC_DAPM_PGA("Left Out 1", WM8971_PWR2, 6, 0, NULL, 0), | ||
276 | SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8971_PWR2, 7, 0), | ||
277 | SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8971_PWR2, 8, 0), | ||
278 | SND_SOC_DAPM_PGA("Mono Out 1", WM8971_PWR2, 2, 0, NULL, 0), | ||
279 | |||
280 | SND_SOC_DAPM_MICBIAS("Mic Bias", WM8971_PWR1, 1, 0), | ||
281 | SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8971_PWR1, 2, 0), | ||
282 | SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8971_PWR1, 3, 0), | ||
283 | |||
284 | SND_SOC_DAPM_MUX("Left PGA Mux", WM8971_PWR1, 5, 0, | ||
285 | &wm8971_left_pga_controls), | ||
286 | SND_SOC_DAPM_MUX("Right PGA Mux", WM8971_PWR1, 4, 0, | ||
287 | &wm8971_right_pga_controls), | ||
288 | SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, | ||
289 | &wm8971_left_line_controls), | ||
290 | SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, | ||
291 | &wm8971_right_line_controls), | ||
292 | |||
293 | SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, | ||
294 | &wm8971_monomux_controls), | ||
295 | SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, | ||
296 | &wm8971_monomux_controls), | ||
297 | |||
298 | SND_SOC_DAPM_OUTPUT("LOUT1"), | ||
299 | SND_SOC_DAPM_OUTPUT("ROUT1"), | ||
300 | SND_SOC_DAPM_OUTPUT("LOUT2"), | ||
301 | SND_SOC_DAPM_OUTPUT("ROUT2"), | ||
302 | SND_SOC_DAPM_OUTPUT("MONO"), | ||
303 | |||
304 | SND_SOC_DAPM_INPUT("LINPUT1"), | ||
305 | SND_SOC_DAPM_INPUT("RINPUT1"), | ||
306 | SND_SOC_DAPM_INPUT("MIC"), | ||
307 | }; | ||
308 | |||
309 | static const struct snd_soc_dapm_route audio_map[] = { | ||
310 | /* left mixer */ | ||
311 | {"Left Mixer", "Playback Switch", "Left DAC"}, | ||
312 | {"Left Mixer", "Left Bypass Switch", "Left Line Mux"}, | ||
313 | {"Left Mixer", "Right Playback Switch", "Right DAC"}, | ||
314 | {"Left Mixer", "Right Bypass Switch", "Right Line Mux"}, | ||
315 | |||
316 | /* right mixer */ | ||
317 | {"Right Mixer", "Left Playback Switch", "Left DAC"}, | ||
318 | {"Right Mixer", "Left Bypass Switch", "Left Line Mux"}, | ||
319 | {"Right Mixer", "Playback Switch", "Right DAC"}, | ||
320 | {"Right Mixer", "Right Bypass Switch", "Right Line Mux"}, | ||
321 | |||
322 | /* left out 1 */ | ||
323 | {"Left Out 1", NULL, "Left Mixer"}, | ||
324 | {"LOUT1", NULL, "Left Out 1"}, | ||
325 | |||
326 | /* left out 2 */ | ||
327 | {"Left Out 2", NULL, "Left Mixer"}, | ||
328 | {"LOUT2", NULL, "Left Out 2"}, | ||
329 | |||
330 | /* right out 1 */ | ||
331 | {"Right Out 1", NULL, "Right Mixer"}, | ||
332 | {"ROUT1", NULL, "Right Out 1"}, | ||
333 | |||
334 | /* right out 2 */ | ||
335 | {"Right Out 2", NULL, "Right Mixer"}, | ||
336 | {"ROUT2", NULL, "Right Out 2"}, | ||
337 | |||
338 | /* mono mixer */ | ||
339 | {"Mono Mixer", "Left Playback Switch", "Left DAC"}, | ||
340 | {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"}, | ||
341 | {"Mono Mixer", "Right Playback Switch", "Right DAC"}, | ||
342 | {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"}, | ||
343 | |||
344 | /* mono out */ | ||
345 | {"Mono Out", NULL, "Mono Mixer"}, | ||
346 | {"MONO1", NULL, "Mono Out"}, | ||
347 | |||
348 | /* Left Line Mux */ | ||
349 | {"Left Line Mux", "Line", "LINPUT1"}, | ||
350 | {"Left Line Mux", "PGA", "Left PGA Mux"}, | ||
351 | {"Left Line Mux", "Differential", "Differential Mux"}, | ||
352 | |||
353 | /* Right Line Mux */ | ||
354 | {"Right Line Mux", "Line", "RINPUT1"}, | ||
355 | {"Right Line Mux", "Mic", "MIC"}, | ||
356 | {"Right Line Mux", "PGA", "Right PGA Mux"}, | ||
357 | {"Right Line Mux", "Differential", "Differential Mux"}, | ||
358 | |||
359 | /* Left PGA Mux */ | ||
360 | {"Left PGA Mux", "Line", "LINPUT1"}, | ||
361 | {"Left PGA Mux", "Differential", "Differential Mux"}, | ||
362 | |||
363 | /* Right PGA Mux */ | ||
364 | {"Right PGA Mux", "Line", "RINPUT1"}, | ||
365 | {"Right PGA Mux", "Differential", "Differential Mux"}, | ||
366 | |||
367 | /* Differential Mux */ | ||
368 | {"Differential Mux", "Line", "LINPUT1"}, | ||
369 | {"Differential Mux", "Line", "RINPUT1"}, | ||
370 | |||
371 | /* Left ADC Mux */ | ||
372 | {"Left ADC Mux", "Stereo", "Left PGA Mux"}, | ||
373 | {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"}, | ||
374 | {"Left ADC Mux", "Digital Mono", "Left PGA Mux"}, | ||
375 | |||
376 | /* Right ADC Mux */ | ||
377 | {"Right ADC Mux", "Stereo", "Right PGA Mux"}, | ||
378 | {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"}, | ||
379 | {"Right ADC Mux", "Digital Mono", "Right PGA Mux"}, | ||
380 | |||
381 | /* ADC */ | ||
382 | {"Left ADC", NULL, "Left ADC Mux"}, | ||
383 | {"Right ADC", NULL, "Right ADC Mux"}, | ||
384 | }; | ||
385 | |||
386 | static int wm8971_add_widgets(struct snd_soc_codec *codec) | ||
387 | { | ||
388 | snd_soc_dapm_new_controls(codec, wm8971_dapm_widgets, | ||
389 | ARRAY_SIZE(wm8971_dapm_widgets)); | ||
390 | |||
391 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
392 | |||
393 | snd_soc_dapm_new_widgets(codec); | ||
394 | |||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | struct _coeff_div { | ||
399 | u32 mclk; | ||
400 | u32 rate; | ||
401 | u16 fs; | ||
402 | u8 sr:5; | ||
403 | u8 usb:1; | ||
404 | }; | ||
405 | |||
406 | /* codec hifi mclk clock divider coefficients */ | ||
407 | static const struct _coeff_div coeff_div[] = { | ||
408 | /* 8k */ | ||
409 | {12288000, 8000, 1536, 0x6, 0x0}, | ||
410 | {11289600, 8000, 1408, 0x16, 0x0}, | ||
411 | {18432000, 8000, 2304, 0x7, 0x0}, | ||
412 | {16934400, 8000, 2112, 0x17, 0x0}, | ||
413 | {12000000, 8000, 1500, 0x6, 0x1}, | ||
414 | |||
415 | /* 11.025k */ | ||
416 | {11289600, 11025, 1024, 0x18, 0x0}, | ||
417 | {16934400, 11025, 1536, 0x19, 0x0}, | ||
418 | {12000000, 11025, 1088, 0x19, 0x1}, | ||
419 | |||
420 | /* 16k */ | ||
421 | {12288000, 16000, 768, 0xa, 0x0}, | ||
422 | {18432000, 16000, 1152, 0xb, 0x0}, | ||
423 | {12000000, 16000, 750, 0xa, 0x1}, | ||
424 | |||
425 | /* 22.05k */ | ||
426 | {11289600, 22050, 512, 0x1a, 0x0}, | ||
427 | {16934400, 22050, 768, 0x1b, 0x0}, | ||
428 | {12000000, 22050, 544, 0x1b, 0x1}, | ||
429 | |||
430 | /* 32k */ | ||
431 | {12288000, 32000, 384, 0xc, 0x0}, | ||
432 | {18432000, 32000, 576, 0xd, 0x0}, | ||
433 | {12000000, 32000, 375, 0xa, 0x1}, | ||
434 | |||
435 | /* 44.1k */ | ||
436 | {11289600, 44100, 256, 0x10, 0x0}, | ||
437 | {16934400, 44100, 384, 0x11, 0x0}, | ||
438 | {12000000, 44100, 272, 0x11, 0x1}, | ||
439 | |||
440 | /* 48k */ | ||
441 | {12288000, 48000, 256, 0x0, 0x0}, | ||
442 | {18432000, 48000, 384, 0x1, 0x0}, | ||
443 | {12000000, 48000, 250, 0x0, 0x1}, | ||
444 | |||
445 | /* 88.2k */ | ||
446 | {11289600, 88200, 128, 0x1e, 0x0}, | ||
447 | {16934400, 88200, 192, 0x1f, 0x0}, | ||
448 | {12000000, 88200, 136, 0x1f, 0x1}, | ||
449 | |||
450 | /* 96k */ | ||
451 | {12288000, 96000, 128, 0xe, 0x0}, | ||
452 | {18432000, 96000, 192, 0xf, 0x0}, | ||
453 | {12000000, 96000, 125, 0xe, 0x1}, | ||
454 | }; | ||
455 | |||
456 | static int get_coeff(int mclk, int rate) | ||
457 | { | ||
458 | int i; | ||
459 | |||
460 | for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { | ||
461 | if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) | ||
462 | return i; | ||
463 | } | ||
464 | return -EINVAL; | ||
465 | } | ||
466 | |||
467 | static int wm8971_set_dai_sysclk(struct snd_soc_dai *codec_dai, | ||
468 | int clk_id, unsigned int freq, int dir) | ||
469 | { | ||
470 | struct snd_soc_codec *codec = codec_dai->codec; | ||
471 | struct wm8971_priv *wm8971 = codec->private_data; | ||
472 | |||
473 | switch (freq) { | ||
474 | case 11289600: | ||
475 | case 12000000: | ||
476 | case 12288000: | ||
477 | case 16934400: | ||
478 | case 18432000: | ||
479 | wm8971->sysclk = freq; | ||
480 | return 0; | ||
481 | } | ||
482 | return -EINVAL; | ||
483 | } | ||
484 | |||
485 | static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
486 | unsigned int fmt) | ||
487 | { | ||
488 | struct snd_soc_codec *codec = codec_dai->codec; | ||
489 | u16 iface = 0; | ||
490 | |||
491 | /* set master/slave audio interface */ | ||
492 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
493 | case SND_SOC_DAIFMT_CBM_CFM: | ||
494 | iface = 0x0040; | ||
495 | break; | ||
496 | case SND_SOC_DAIFMT_CBS_CFS: | ||
497 | break; | ||
498 | default: | ||
499 | return -EINVAL; | ||
500 | } | ||
501 | |||
502 | /* interface format */ | ||
503 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
504 | case SND_SOC_DAIFMT_I2S: | ||
505 | iface |= 0x0002; | ||
506 | break; | ||
507 | case SND_SOC_DAIFMT_RIGHT_J: | ||
508 | break; | ||
509 | case SND_SOC_DAIFMT_LEFT_J: | ||
510 | iface |= 0x0001; | ||
511 | break; | ||
512 | case SND_SOC_DAIFMT_DSP_A: | ||
513 | iface |= 0x0003; | ||
514 | break; | ||
515 | case SND_SOC_DAIFMT_DSP_B: | ||
516 | iface |= 0x0013; | ||
517 | break; | ||
518 | default: | ||
519 | return -EINVAL; | ||
520 | } | ||
521 | |||
522 | /* clock inversion */ | ||
523 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
524 | case SND_SOC_DAIFMT_NB_NF: | ||
525 | break; | ||
526 | case SND_SOC_DAIFMT_IB_IF: | ||
527 | iface |= 0x0090; | ||
528 | break; | ||
529 | case SND_SOC_DAIFMT_IB_NF: | ||
530 | iface |= 0x0080; | ||
531 | break; | ||
532 | case SND_SOC_DAIFMT_NB_IF: | ||
533 | iface |= 0x0010; | ||
534 | break; | ||
535 | default: | ||
536 | return -EINVAL; | ||
537 | } | ||
538 | |||
539 | wm8971_write(codec, WM8971_IFACE, iface); | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream, | ||
544 | struct snd_pcm_hw_params *params) | ||
545 | { | ||
546 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
547 | struct snd_soc_device *socdev = rtd->socdev; | ||
548 | struct snd_soc_codec *codec = socdev->codec; | ||
549 | struct wm8971_priv *wm8971 = codec->private_data; | ||
550 | u16 iface = wm8971_read_reg_cache(codec, WM8971_IFACE) & 0x1f3; | ||
551 | u16 srate = wm8971_read_reg_cache(codec, WM8971_SRATE) & 0x1c0; | ||
552 | int coeff = get_coeff(wm8971->sysclk, params_rate(params)); | ||
553 | |||
554 | /* bit size */ | ||
555 | switch (params_format(params)) { | ||
556 | case SNDRV_PCM_FORMAT_S16_LE: | ||
557 | break; | ||
558 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
559 | iface |= 0x0004; | ||
560 | break; | ||
561 | case SNDRV_PCM_FORMAT_S24_LE: | ||
562 | iface |= 0x0008; | ||
563 | break; | ||
564 | case SNDRV_PCM_FORMAT_S32_LE: | ||
565 | iface |= 0x000c; | ||
566 | break; | ||
567 | } | ||
568 | |||
569 | /* set iface & srate */ | ||
570 | wm8971_write(codec, WM8971_IFACE, iface); | ||
571 | if (coeff >= 0) | ||
572 | wm8971_write(codec, WM8971_SRATE, srate | | ||
573 | (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb); | ||
574 | |||
575 | return 0; | ||
576 | } | ||
577 | |||
578 | static int wm8971_mute(struct snd_soc_dai *dai, int mute) | ||
579 | { | ||
580 | struct snd_soc_codec *codec = dai->codec; | ||
581 | u16 mute_reg = wm8971_read_reg_cache(codec, WM8971_ADCDAC) & 0xfff7; | ||
582 | |||
583 | if (mute) | ||
584 | wm8971_write(codec, WM8971_ADCDAC, mute_reg | 0x8); | ||
585 | else | ||
586 | wm8971_write(codec, WM8971_ADCDAC, mute_reg); | ||
587 | return 0; | ||
588 | } | ||
589 | |||
590 | static int wm8971_set_bias_level(struct snd_soc_codec *codec, | ||
591 | enum snd_soc_bias_level level) | ||
592 | { | ||
593 | u16 pwr_reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e; | ||
594 | |||
595 | switch (level) { | ||
596 | case SND_SOC_BIAS_ON: | ||
597 | /* set vmid to 50k and unmute dac */ | ||
598 | wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x00c1); | ||
599 | break; | ||
600 | case SND_SOC_BIAS_PREPARE: | ||
601 | break; | ||
602 | case SND_SOC_BIAS_STANDBY: | ||
603 | /* mute dac and set vmid to 500k, enable VREF */ | ||
604 | wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x0140); | ||
605 | break; | ||
606 | case SND_SOC_BIAS_OFF: | ||
607 | wm8971_write(codec, WM8971_PWR1, 0x0001); | ||
608 | break; | ||
609 | } | ||
610 | codec->bias_level = level; | ||
611 | return 0; | ||
612 | } | ||
613 | |||
614 | #define WM8971_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ | ||
615 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ | ||
616 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) | ||
617 | |||
618 | #define WM8971_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ | ||
619 | SNDRV_PCM_FMTBIT_S24_LE) | ||
620 | |||
621 | struct snd_soc_dai wm8971_dai = { | ||
622 | .name = "WM8971", | ||
623 | .playback = { | ||
624 | .stream_name = "Playback", | ||
625 | .channels_min = 1, | ||
626 | .channels_max = 2, | ||
627 | .rates = WM8971_RATES, | ||
628 | .formats = WM8971_FORMATS,}, | ||
629 | .capture = { | ||
630 | .stream_name = "Capture", | ||
631 | .channels_min = 1, | ||
632 | .channels_max = 2, | ||
633 | .rates = WM8971_RATES, | ||
634 | .formats = WM8971_FORMATS,}, | ||
635 | .ops = { | ||
636 | .hw_params = wm8971_pcm_hw_params, | ||
637 | }, | ||
638 | .dai_ops = { | ||
639 | .digital_mute = wm8971_mute, | ||
640 | .set_fmt = wm8971_set_dai_fmt, | ||
641 | .set_sysclk = wm8971_set_dai_sysclk, | ||
642 | }, | ||
643 | }; | ||
644 | EXPORT_SYMBOL_GPL(wm8971_dai); | ||
645 | |||
646 | static void wm8971_work(struct work_struct *work) | ||
647 | { | ||
648 | struct snd_soc_codec *codec = | ||
649 | container_of(work, struct snd_soc_codec, delayed_work.work); | ||
650 | wm8971_set_bias_level(codec, codec->bias_level); | ||
651 | } | ||
652 | |||
653 | static int wm8971_suspend(struct platform_device *pdev, pm_message_t state) | ||
654 | { | ||
655 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
656 | struct snd_soc_codec *codec = socdev->codec; | ||
657 | |||
658 | wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | static int wm8971_resume(struct platform_device *pdev) | ||
663 | { | ||
664 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
665 | struct snd_soc_codec *codec = socdev->codec; | ||
666 | int i; | ||
667 | u8 data[2]; | ||
668 | u16 *cache = codec->reg_cache; | ||
669 | u16 reg; | ||
670 | |||
671 | /* Sync reg_cache with the hardware */ | ||
672 | for (i = 0; i < ARRAY_SIZE(wm8971_reg); i++) { | ||
673 | if (i + 1 == WM8971_RESET) | ||
674 | continue; | ||
675 | data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); | ||
676 | data[1] = cache[i] & 0x00ff; | ||
677 | codec->hw_write(codec->control_data, data, 2); | ||
678 | } | ||
679 | |||
680 | wm8971_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
681 | |||
682 | /* charge wm8971 caps */ | ||
683 | if (codec->suspend_bias_level == SND_SOC_BIAS_ON) { | ||
684 | reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e; | ||
685 | wm8971_write(codec, WM8971_PWR1, reg | 0x01c0); | ||
686 | codec->bias_level = SND_SOC_BIAS_ON; | ||
687 | queue_delayed_work(wm8971_workq, &codec->delayed_work, | ||
688 | msecs_to_jiffies(1000)); | ||
689 | } | ||
690 | |||
691 | return 0; | ||
692 | } | ||
693 | |||
694 | static int wm8971_init(struct snd_soc_device *socdev) | ||
695 | { | ||
696 | struct snd_soc_codec *codec = socdev->codec; | ||
697 | int reg, ret = 0; | ||
698 | |||
699 | codec->name = "WM8971"; | ||
700 | codec->owner = THIS_MODULE; | ||
701 | codec->read = wm8971_read_reg_cache; | ||
702 | codec->write = wm8971_write; | ||
703 | codec->set_bias_level = wm8971_set_bias_level; | ||
704 | codec->dai = &wm8971_dai; | ||
705 | codec->reg_cache_size = ARRAY_SIZE(wm8971_reg); | ||
706 | codec->num_dai = 1; | ||
707 | codec->reg_cache = kmemdup(wm8971_reg, sizeof(wm8971_reg), GFP_KERNEL); | ||
708 | |||
709 | if (codec->reg_cache == NULL) | ||
710 | return -ENOMEM; | ||
711 | |||
712 | wm8971_reset(codec); | ||
713 | |||
714 | /* register pcms */ | ||
715 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
716 | if (ret < 0) { | ||
717 | printk(KERN_ERR "wm8971: failed to create pcms\n"); | ||
718 | goto pcm_err; | ||
719 | } | ||
720 | |||
721 | /* charge output caps - set vmid to 5k for quick power up */ | ||
722 | reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e; | ||
723 | wm8971_write(codec, WM8971_PWR1, reg | 0x01c0); | ||
724 | codec->bias_level = SND_SOC_BIAS_STANDBY; | ||
725 | queue_delayed_work(wm8971_workq, &codec->delayed_work, | ||
726 | msecs_to_jiffies(1000)); | ||
727 | |||
728 | /* set the update bits */ | ||
729 | reg = wm8971_read_reg_cache(codec, WM8971_LDAC); | ||
730 | wm8971_write(codec, WM8971_LDAC, reg | 0x0100); | ||
731 | reg = wm8971_read_reg_cache(codec, WM8971_RDAC); | ||
732 | wm8971_write(codec, WM8971_RDAC, reg | 0x0100); | ||
733 | |||
734 | reg = wm8971_read_reg_cache(codec, WM8971_LOUT1V); | ||
735 | wm8971_write(codec, WM8971_LOUT1V, reg | 0x0100); | ||
736 | reg = wm8971_read_reg_cache(codec, WM8971_ROUT1V); | ||
737 | wm8971_write(codec, WM8971_ROUT1V, reg | 0x0100); | ||
738 | |||
739 | reg = wm8971_read_reg_cache(codec, WM8971_LOUT2V); | ||
740 | wm8971_write(codec, WM8971_LOUT2V, reg | 0x0100); | ||
741 | reg = wm8971_read_reg_cache(codec, WM8971_ROUT2V); | ||
742 | wm8971_write(codec, WM8971_ROUT2V, reg | 0x0100); | ||
743 | |||
744 | reg = wm8971_read_reg_cache(codec, WM8971_LINVOL); | ||
745 | wm8971_write(codec, WM8971_LINVOL, reg | 0x0100); | ||
746 | reg = wm8971_read_reg_cache(codec, WM8971_RINVOL); | ||
747 | wm8971_write(codec, WM8971_RINVOL, reg | 0x0100); | ||
748 | |||
749 | wm8971_add_controls(codec); | ||
750 | wm8971_add_widgets(codec); | ||
751 | ret = snd_soc_register_card(socdev); | ||
752 | if (ret < 0) { | ||
753 | printk(KERN_ERR "wm8971: failed to register card\n"); | ||
754 | goto card_err; | ||
755 | } | ||
756 | return ret; | ||
757 | |||
758 | card_err: | ||
759 | snd_soc_free_pcms(socdev); | ||
760 | snd_soc_dapm_free(socdev); | ||
761 | pcm_err: | ||
762 | kfree(codec->reg_cache); | ||
763 | return ret; | ||
764 | } | ||
765 | |||
766 | /* If the i2c layer weren't so broken, we could pass this kind of data | ||
767 | around */ | ||
768 | static struct snd_soc_device *wm8971_socdev; | ||
769 | |||
770 | #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) | ||
771 | |||
772 | static int wm8971_i2c_probe(struct i2c_client *i2c, | ||
773 | const struct i2c_device_id *id) | ||
774 | { | ||
775 | struct snd_soc_device *socdev = wm8971_socdev; | ||
776 | struct snd_soc_codec *codec = socdev->codec; | ||
777 | int ret; | ||
778 | |||
779 | i2c_set_clientdata(i2c, codec); | ||
780 | |||
781 | codec->control_data = i2c; | ||
782 | |||
783 | ret = wm8971_init(socdev); | ||
784 | if (ret < 0) | ||
785 | pr_err("failed to initialise WM8971\n"); | ||
786 | |||
787 | return ret; | ||
788 | } | ||
789 | |||
790 | static int wm8971_i2c_remove(struct i2c_client *client) | ||
791 | { | ||
792 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | ||
793 | kfree(codec->reg_cache); | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | static const struct i2c_device_id wm8971_i2c_id[] = { | ||
798 | { "wm8971", 0 }, | ||
799 | { } | ||
800 | }; | ||
801 | MODULE_DEVICE_TABLE(i2c, wm8971_i2c_id); | ||
802 | |||
803 | static struct i2c_driver wm8971_i2c_driver = { | ||
804 | .driver = { | ||
805 | .name = "WM8971 I2C Codec", | ||
806 | .owner = THIS_MODULE, | ||
807 | }, | ||
808 | .probe = wm8971_i2c_probe, | ||
809 | .remove = wm8971_i2c_remove, | ||
810 | .id_table = wm8971_i2c_id, | ||
811 | }; | ||
812 | |||
813 | static int wm8971_add_i2c_device(struct platform_device *pdev, | ||
814 | const struct wm8971_setup_data *setup) | ||
815 | { | ||
816 | struct i2c_board_info info; | ||
817 | struct i2c_adapter *adapter; | ||
818 | struct i2c_client *client; | ||
819 | int ret; | ||
820 | |||
821 | ret = i2c_add_driver(&wm8971_i2c_driver); | ||
822 | if (ret != 0) { | ||
823 | dev_err(&pdev->dev, "can't add i2c driver\n"); | ||
824 | return ret; | ||
825 | } | ||
826 | |||
827 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
828 | info.addr = setup->i2c_address; | ||
829 | strlcpy(info.type, "wm8971", I2C_NAME_SIZE); | ||
830 | |||
831 | adapter = i2c_get_adapter(setup->i2c_bus); | ||
832 | if (!adapter) { | ||
833 | dev_err(&pdev->dev, "can't get i2c adapter %d\n", | ||
834 | setup->i2c_bus); | ||
835 | goto err_driver; | ||
836 | } | ||
837 | |||
838 | client = i2c_new_device(adapter, &info); | ||
839 | i2c_put_adapter(adapter); | ||
840 | if (!client) { | ||
841 | dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", | ||
842 | (unsigned int)info.addr); | ||
843 | goto err_driver; | ||
844 | } | ||
845 | |||
846 | return 0; | ||
847 | |||
848 | err_driver: | ||
849 | i2c_del_driver(&wm8971_i2c_driver); | ||
850 | return -ENODEV; | ||
851 | } | ||
852 | |||
853 | #endif | ||
854 | |||
855 | static int wm8971_probe(struct platform_device *pdev) | ||
856 | { | ||
857 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
858 | struct wm8971_setup_data *setup; | ||
859 | struct snd_soc_codec *codec; | ||
860 | struct wm8971_priv *wm8971; | ||
861 | int ret = 0; | ||
862 | |||
863 | pr_info("WM8971 Audio Codec %s", WM8971_VERSION); | ||
864 | |||
865 | setup = socdev->codec_data; | ||
866 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | ||
867 | if (codec == NULL) | ||
868 | return -ENOMEM; | ||
869 | |||
870 | wm8971 = kzalloc(sizeof(struct wm8971_priv), GFP_KERNEL); | ||
871 | if (wm8971 == NULL) { | ||
872 | kfree(codec); | ||
873 | return -ENOMEM; | ||
874 | } | ||
875 | |||
876 | codec->private_data = wm8971; | ||
877 | socdev->codec = codec; | ||
878 | mutex_init(&codec->mutex); | ||
879 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
880 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
881 | wm8971_socdev = socdev; | ||
882 | |||
883 | INIT_DELAYED_WORK(&codec->delayed_work, wm8971_work); | ||
884 | wm8971_workq = create_workqueue("wm8971"); | ||
885 | if (wm8971_workq == NULL) { | ||
886 | kfree(codec->private_data); | ||
887 | kfree(codec); | ||
888 | return -ENOMEM; | ||
889 | } | ||
890 | |||
891 | #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) | ||
892 | if (setup->i2c_address) { | ||
893 | codec->hw_write = (hw_write_t)i2c_master_send; | ||
894 | ret = wm8971_add_i2c_device(pdev, setup); | ||
895 | } | ||
896 | #endif | ||
897 | /* Add other interfaces here */ | ||
898 | |||
899 | if (ret != 0) { | ||
900 | destroy_workqueue(wm8971_workq); | ||
901 | kfree(codec->private_data); | ||
902 | kfree(codec); | ||
903 | } | ||
904 | |||
905 | return ret; | ||
906 | } | ||
907 | |||
908 | /* power down chip */ | ||
909 | static int wm8971_remove(struct platform_device *pdev) | ||
910 | { | ||
911 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
912 | struct snd_soc_codec *codec = socdev->codec; | ||
913 | |||
914 | if (codec->control_data) | ||
915 | wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
916 | if (wm8971_workq) | ||
917 | destroy_workqueue(wm8971_workq); | ||
918 | snd_soc_free_pcms(socdev); | ||
919 | snd_soc_dapm_free(socdev); | ||
920 | #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) | ||
921 | i2c_unregister_device(codec->control_data); | ||
922 | i2c_del_driver(&wm8971_i2c_driver); | ||
923 | #endif | ||
924 | kfree(codec->private_data); | ||
925 | kfree(codec); | ||
926 | |||
927 | return 0; | ||
928 | } | ||
929 | |||
930 | struct snd_soc_codec_device soc_codec_dev_wm8971 = { | ||
931 | .probe = wm8971_probe, | ||
932 | .remove = wm8971_remove, | ||
933 | .suspend = wm8971_suspend, | ||
934 | .resume = wm8971_resume, | ||
935 | }; | ||
936 | |||
937 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8971); | ||
938 | |||
939 | MODULE_DESCRIPTION("ASoC WM8971 driver"); | ||
940 | MODULE_AUTHOR("Lab126"); | ||
941 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/wm8971.h b/sound/soc/codecs/wm8971.h new file mode 100644 index 000000000000..ef4f08f9f344 --- /dev/null +++ b/sound/soc/codecs/wm8971.h | |||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | * wm8971.h -- audio driver for WM8971 | ||
3 | * | ||
4 | * Copyright 2005 Lab126, Inc. | ||
5 | * | ||
6 | * Author: Kenneth Kiraly <kiraly@lab126.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #ifndef _WM8971_H | ||
16 | #define _WM8971_H | ||
17 | |||
18 | #define WM8971_LINVOL 0x00 | ||
19 | #define WM8971_RINVOL 0x01 | ||
20 | #define WM8971_LOUT1V 0x02 | ||
21 | #define WM8971_ROUT1V 0x03 | ||
22 | #define WM8971_ADCDAC 0x05 | ||
23 | #define WM8971_IFACE 0x07 | ||
24 | #define WM8971_SRATE 0x08 | ||
25 | #define WM8971_LDAC 0x0a | ||
26 | #define WM8971_RDAC 0x0b | ||
27 | #define WM8971_BASS 0x0c | ||
28 | #define WM8971_TREBLE 0x0d | ||
29 | #define WM8971_RESET 0x0f | ||
30 | #define WM8971_ALC1 0x11 | ||
31 | #define WM8971_ALC2 0x12 | ||
32 | #define WM8971_ALC3 0x13 | ||
33 | #define WM8971_NGATE 0x14 | ||
34 | #define WM8971_LADC 0x15 | ||
35 | #define WM8971_RADC 0x16 | ||
36 | #define WM8971_ADCTL1 0x17 | ||
37 | #define WM8971_ADCTL2 0x18 | ||
38 | #define WM8971_PWR1 0x19 | ||
39 | #define WM8971_PWR2 0x1a | ||
40 | #define WM8971_ADCTL3 0x1b | ||
41 | #define WM8971_ADCIN 0x1f | ||
42 | #define WM8971_LADCIN 0x20 | ||
43 | #define WM8971_RADCIN 0x21 | ||
44 | #define WM8971_LOUTM1 0x22 | ||
45 | #define WM8971_LOUTM2 0x23 | ||
46 | #define WM8971_ROUTM1 0x24 | ||
47 | #define WM8971_ROUTM2 0x25 | ||
48 | #define WM8971_MOUTM1 0x26 | ||
49 | #define WM8971_MOUTM2 0x27 | ||
50 | #define WM8971_LOUT2V 0x28 | ||
51 | #define WM8971_ROUT2V 0x29 | ||
52 | #define WM8971_MOUTV 0x2A | ||
53 | |||
54 | #define WM8971_SYSCLK 0 | ||
55 | |||
56 | struct wm8971_setup_data { | ||
57 | int i2c_bus; | ||
58 | unsigned short i2c_address; | ||
59 | }; | ||
60 | |||
61 | extern struct snd_soc_dai wm8971_dai; | ||
62 | extern struct snd_soc_codec_device soc_codec_dev_wm8971; | ||
63 | |||
64 | #endif | ||
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index dd995ef448b4..572d22b0880b 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c | |||
@@ -30,7 +30,6 @@ | |||
30 | 30 | ||
31 | #include "wm8990.h" | 31 | #include "wm8990.h" |
32 | 32 | ||
33 | #define AUDIO_NAME "wm8990" | ||
34 | #define WM8990_VERSION "0.2" | 33 | #define WM8990_VERSION "0.2" |
35 | 34 | ||
36 | /* codec private data */ | 35 | /* codec private data */ |
@@ -1477,81 +1476,86 @@ static struct snd_soc_device *wm8990_socdev; | |||
1477 | * low = 0x34 | 1476 | * low = 0x34 |
1478 | * high = 0x36 | 1477 | * high = 0x36 |
1479 | */ | 1478 | */ |
1480 | static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; | ||
1481 | 1479 | ||
1482 | /* Magic definition of all other variables and things */ | 1480 | static int wm8990_i2c_probe(struct i2c_client *i2c, |
1483 | I2C_CLIENT_INSMOD; | 1481 | const struct i2c_device_id *id) |
1484 | |||
1485 | static struct i2c_driver wm8990_i2c_driver; | ||
1486 | static struct i2c_client client_template; | ||
1487 | |||
1488 | static int wm8990_codec_probe(struct i2c_adapter *adap, int addr, int kind) | ||
1489 | { | 1482 | { |
1490 | struct snd_soc_device *socdev = wm8990_socdev; | 1483 | struct snd_soc_device *socdev = wm8990_socdev; |
1491 | struct wm8990_setup_data *setup = socdev->codec_data; | ||
1492 | struct snd_soc_codec *codec = socdev->codec; | 1484 | struct snd_soc_codec *codec = socdev->codec; |
1493 | struct i2c_client *i2c; | ||
1494 | int ret; | 1485 | int ret; |
1495 | 1486 | ||
1496 | if (addr != setup->i2c_address) | ||
1497 | return -ENODEV; | ||
1498 | |||
1499 | client_template.adapter = adap; | ||
1500 | client_template.addr = addr; | ||
1501 | |||
1502 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | ||
1503 | if (i2c == NULL) | ||
1504 | return -ENOMEM; | ||
1505 | |||
1506 | i2c_set_clientdata(i2c, codec); | 1487 | i2c_set_clientdata(i2c, codec); |
1507 | codec->control_data = i2c; | 1488 | codec->control_data = i2c; |
1508 | 1489 | ||
1509 | ret = i2c_attach_client(i2c); | ||
1510 | if (ret < 0) { | ||
1511 | pr_err("failed to attach codec at addr %x\n", addr); | ||
1512 | goto err; | ||
1513 | } | ||
1514 | |||
1515 | ret = wm8990_init(socdev); | 1490 | ret = wm8990_init(socdev); |
1516 | if (ret < 0) { | 1491 | if (ret < 0) |
1517 | pr_err("failed to initialise WM8990\n"); | 1492 | pr_err("failed to initialise WM8990\n"); |
1518 | goto err; | ||
1519 | } | ||
1520 | return ret; | ||
1521 | 1493 | ||
1522 | err: | ||
1523 | kfree(i2c); | ||
1524 | return ret; | 1494 | return ret; |
1525 | } | 1495 | } |
1526 | 1496 | ||
1527 | static int wm8990_i2c_detach(struct i2c_client *client) | 1497 | static int wm8990_i2c_remove(struct i2c_client *client) |
1528 | { | 1498 | { |
1529 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | 1499 | struct snd_soc_codec *codec = i2c_get_clientdata(client); |
1530 | i2c_detach_client(client); | ||
1531 | kfree(codec->reg_cache); | 1500 | kfree(codec->reg_cache); |
1532 | kfree(client); | ||
1533 | return 0; | 1501 | return 0; |
1534 | } | 1502 | } |
1535 | 1503 | ||
1536 | static int wm8990_i2c_attach(struct i2c_adapter *adap) | 1504 | static const struct i2c_device_id wm8990_i2c_id[] = { |
1537 | { | 1505 | { "wm8990", 0 }, |
1538 | return i2c_probe(adap, &addr_data, wm8990_codec_probe); | 1506 | { } |
1539 | } | 1507 | }; |
1508 | MODULE_DEVICE_TABLE(i2c, wm8990_i2c_id); | ||
1540 | 1509 | ||
1541 | static struct i2c_driver wm8990_i2c_driver = { | 1510 | static struct i2c_driver wm8990_i2c_driver = { |
1542 | .driver = { | 1511 | .driver = { |
1543 | .name = "WM8990 I2C Codec", | 1512 | .name = "WM8990 I2C Codec", |
1544 | .owner = THIS_MODULE, | 1513 | .owner = THIS_MODULE, |
1545 | }, | 1514 | }, |
1546 | .attach_adapter = wm8990_i2c_attach, | 1515 | .probe = wm8990_i2c_probe, |
1547 | .detach_client = wm8990_i2c_detach, | 1516 | .remove = wm8990_i2c_remove, |
1548 | .command = NULL, | 1517 | .id_table = wm8990_i2c_id, |
1549 | }; | 1518 | }; |
1550 | 1519 | ||
1551 | static struct i2c_client client_template = { | 1520 | static int wm8990_add_i2c_device(struct platform_device *pdev, |
1552 | .name = "WM8990", | 1521 | const struct wm8990_setup_data *setup) |
1553 | .driver = &wm8990_i2c_driver, | 1522 | { |
1554 | }; | 1523 | struct i2c_board_info info; |
1524 | struct i2c_adapter *adapter; | ||
1525 | struct i2c_client *client; | ||
1526 | int ret; | ||
1527 | |||
1528 | ret = i2c_add_driver(&wm8990_i2c_driver); | ||
1529 | if (ret != 0) { | ||
1530 | dev_err(&pdev->dev, "can't add i2c driver\n"); | ||
1531 | return ret; | ||
1532 | } | ||
1533 | |||
1534 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
1535 | info.addr = setup->i2c_address; | ||
1536 | strlcpy(info.type, "wm8990", I2C_NAME_SIZE); | ||
1537 | |||
1538 | adapter = i2c_get_adapter(setup->i2c_bus); | ||
1539 | if (!adapter) { | ||
1540 | dev_err(&pdev->dev, "can't get i2c adapter %d\n", | ||
1541 | setup->i2c_bus); | ||
1542 | goto err_driver; | ||
1543 | } | ||
1544 | |||
1545 | client = i2c_new_device(adapter, &info); | ||
1546 | i2c_put_adapter(adapter); | ||
1547 | if (!client) { | ||
1548 | dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", | ||
1549 | (unsigned int)info.addr); | ||
1550 | goto err_driver; | ||
1551 | } | ||
1552 | |||
1553 | return 0; | ||
1554 | |||
1555 | err_driver: | ||
1556 | i2c_del_driver(&wm8990_i2c_driver); | ||
1557 | return -ENODEV; | ||
1558 | } | ||
1555 | #endif | 1559 | #endif |
1556 | 1560 | ||
1557 | static int wm8990_probe(struct platform_device *pdev) | 1561 | static int wm8990_probe(struct platform_device *pdev) |
@@ -1560,7 +1564,7 @@ static int wm8990_probe(struct platform_device *pdev) | |||
1560 | struct wm8990_setup_data *setup; | 1564 | struct wm8990_setup_data *setup; |
1561 | struct snd_soc_codec *codec; | 1565 | struct snd_soc_codec *codec; |
1562 | struct wm8990_priv *wm8990; | 1566 | struct wm8990_priv *wm8990; |
1563 | int ret = 0; | 1567 | int ret; |
1564 | 1568 | ||
1565 | pr_info("WM8990 Audio Codec %s\n", WM8990_VERSION); | 1569 | pr_info("WM8990 Audio Codec %s\n", WM8990_VERSION); |
1566 | 1570 | ||
@@ -1582,16 +1586,13 @@ static int wm8990_probe(struct platform_device *pdev) | |||
1582 | INIT_LIST_HEAD(&codec->dapm_paths); | 1586 | INIT_LIST_HEAD(&codec->dapm_paths); |
1583 | wm8990_socdev = socdev; | 1587 | wm8990_socdev = socdev; |
1584 | 1588 | ||
1589 | ret = -ENODEV; | ||
1590 | |||
1585 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 1591 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
1586 | if (setup->i2c_address) { | 1592 | if (setup->i2c_address) { |
1587 | normal_i2c[0] = setup->i2c_address; | ||
1588 | codec->hw_write = (hw_write_t)i2c_master_send; | 1593 | codec->hw_write = (hw_write_t)i2c_master_send; |
1589 | ret = i2c_add_driver(&wm8990_i2c_driver); | 1594 | ret = wm8990_add_i2c_device(pdev, setup); |
1590 | if (ret != 0) | ||
1591 | printk(KERN_ERR "can't add i2c driver"); | ||
1592 | } | 1595 | } |
1593 | #else | ||
1594 | /* Add other interfaces here */ | ||
1595 | #endif | 1596 | #endif |
1596 | 1597 | ||
1597 | if (ret != 0) { | 1598 | if (ret != 0) { |
@@ -1612,6 +1613,7 @@ static int wm8990_remove(struct platform_device *pdev) | |||
1612 | snd_soc_free_pcms(socdev); | 1613 | snd_soc_free_pcms(socdev); |
1613 | snd_soc_dapm_free(socdev); | 1614 | snd_soc_dapm_free(socdev); |
1614 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 1615 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
1616 | i2c_unregister_device(codec->control_data); | ||
1615 | i2c_del_driver(&wm8990_i2c_driver); | 1617 | i2c_del_driver(&wm8990_i2c_driver); |
1616 | #endif | 1618 | #endif |
1617 | kfree(codec->private_data); | 1619 | kfree(codec->private_data); |
diff --git a/sound/soc/codecs/wm8990.h b/sound/soc/codecs/wm8990.h index 0a08325d5443..0e192f3b0788 100644 --- a/sound/soc/codecs/wm8990.h +++ b/sound/soc/codecs/wm8990.h | |||
@@ -827,6 +827,7 @@ | |||
827 | #define WM8990_AINRMUX_PWR_BIT 3 | 827 | #define WM8990_AINRMUX_PWR_BIT 3 |
828 | 828 | ||
829 | struct wm8990_setup_data { | 829 | struct wm8990_setup_data { |
830 | unsigned i2c_bus; | ||
830 | unsigned short i2c_address; | 831 | unsigned short i2c_address; |
831 | }; | 832 | }; |
832 | 833 | ||
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 2f1c91b1d556..ffb471e420e2 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c | |||
@@ -2,8 +2,7 @@ | |||
2 | * wm9712.c -- ALSA Soc WM9712 codec support | 2 | * wm9712.c -- ALSA Soc WM9712 codec support |
3 | * | 3 | * |
4 | * Copyright 2006 Wolfson Microelectronics PLC. | 4 | * Copyright 2006 Wolfson Microelectronics PLC. |
5 | * Author: Liam Girdwood | 5 | * Author: Liam Girdwood <lrg@slimlogic.co.uk> |
6 | * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com | ||
7 | * | 6 | * |
8 | * This program is free software; you can redistribute it and/or modify it | 7 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the | 8 | * under the terms of the GNU General Public License as published by the |
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 38d1fe0971fc..aba402b3c999 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c | |||
@@ -2,8 +2,7 @@ | |||
2 | * wm9713.c -- ALSA Soc WM9713 codec support | 2 | * wm9713.c -- ALSA Soc WM9713 codec support |
3 | * | 3 | * |
4 | * Copyright 2006 Wolfson Microelectronics PLC. | 4 | * Copyright 2006 Wolfson Microelectronics PLC. |
5 | * Author: Liam Girdwood | 5 | * Author: Liam Girdwood <lrg@slimlogic.co.uk> |
6 | * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com | ||
7 | * | 6 | * |
8 | * This program is free software; you can redistribute it and/or modify it | 7 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the | 8 | * under the terms of the GNU General Public License as published by the |
@@ -419,8 +418,12 @@ SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), | |||
419 | SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), | 418 | SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), |
420 | SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1), | 419 | SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1), |
421 | SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1), | 420 | SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1), |
422 | SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_EXTENDED_MID, 5, 1), | 421 | SND_SOC_DAPM_PGA("Left ADC", AC97_EXTENDED_MID, 5, 1, NULL, 0), |
423 | SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_EXTENDED_MID, 4, 1), | 422 | SND_SOC_DAPM_PGA("Right ADC", AC97_EXTENDED_MID, 4, 1, NULL, 0), |
423 | SND_SOC_DAPM_ADC("Left HiFi ADC", "Left HiFi Capture", SND_SOC_NOPM, 0, 0), | ||
424 | SND_SOC_DAPM_ADC("Right HiFi ADC", "Right HiFi Capture", SND_SOC_NOPM, 0, 0), | ||
425 | SND_SOC_DAPM_ADC("Left Voice ADC", "Left Voice Capture", SND_SOC_NOPM, 0, 0), | ||
426 | SND_SOC_DAPM_ADC("Right Voice ADC", "Right Voice Capture", SND_SOC_NOPM, 0, 0), | ||
424 | SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0), | 427 | SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0), |
425 | SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0), | 428 | SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0), |
426 | SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0), | 429 | SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0), |
@@ -583,9 +586,13 @@ static const struct snd_soc_dapm_route audio_map[] = { | |||
583 | 586 | ||
584 | /* left ADC */ | 587 | /* left ADC */ |
585 | {"Left ADC", NULL, "Left Capture Source"}, | 588 | {"Left ADC", NULL, "Left Capture Source"}, |
589 | {"Left Voice ADC", NULL, "Left ADC"}, | ||
590 | {"Left HiFi ADC", NULL, "Left ADC"}, | ||
586 | 591 | ||
587 | /* right ADC */ | 592 | /* right ADC */ |
588 | {"Right ADC", NULL, "Right Capture Source"}, | 593 | {"Right ADC", NULL, "Right Capture Source"}, |
594 | {"Right Voice ADC", NULL, "Right ADC"}, | ||
595 | {"Right HiFi ADC", NULL, "Right ADC"}, | ||
589 | 596 | ||
590 | /* mic */ | 597 | /* mic */ |
591 | {"Mic A Pre Amp", NULL, "Mic A Source"}, | 598 | {"Mic A Pre Amp", NULL, "Mic A Source"}, |
@@ -949,17 +956,17 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream, | |||
949 | 956 | ||
950 | static void wm9713_voiceshutdown(struct snd_pcm_substream *substream) | 957 | static void wm9713_voiceshutdown(struct snd_pcm_substream *substream) |
951 | { | 958 | { |
952 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 959 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
953 | struct snd_soc_device *socdev = rtd->socdev; | 960 | struct snd_soc_device *socdev = rtd->socdev; |
954 | struct snd_soc_codec *codec = socdev->codec; | 961 | struct snd_soc_codec *codec = socdev->codec; |
955 | u16 status; | 962 | u16 status; |
956 | 963 | ||
957 | /* Gracefully shut down the voice interface. */ | 964 | /* Gracefully shut down the voice interface. */ |
958 | status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000; | 965 | status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000; |
959 | ac97_write(codec, AC97_HANDSET_RATE, 0x0280); | 966 | ac97_write(codec, AC97_HANDSET_RATE, 0x0280); |
960 | schedule_timeout_interruptible(msecs_to_jiffies(1)); | 967 | schedule_timeout_interruptible(msecs_to_jiffies(1)); |
961 | ac97_write(codec, AC97_HANDSET_RATE, 0x0F80); | 968 | ac97_write(codec, AC97_HANDSET_RATE, 0x0F80); |
962 | ac97_write(codec, AC97_EXTENDED_MID, status); | 969 | ac97_write(codec, AC97_EXTENDED_MID, status); |
963 | } | 970 | } |
964 | 971 | ||
965 | static int ac97_hifi_prepare(struct snd_pcm_substream *substream) | 972 | static int ac97_hifi_prepare(struct snd_pcm_substream *substream) |
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 65fdbd81a379..9e6062cd6b59 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * ASoC driver for TI DAVINCI EVM platform | 2 | * ASoC driver for TI DAVINCI EVM platform |
3 | * | 3 | * |
4 | * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> | 4 | * Author: Vladimir Barinov, <vbarinov@embeddedalley.com> |
5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> | 5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
@@ -136,6 +136,7 @@ static struct snd_soc_machine snd_soc_machine_evm = { | |||
136 | 136 | ||
137 | /* evm audio private data */ | 137 | /* evm audio private data */ |
138 | static struct aic3x_setup_data evm_aic3x_setup = { | 138 | static struct aic3x_setup_data evm_aic3x_setup = { |
139 | .i2c_bus = 0, | ||
139 | .i2c_address = 0x1b, | 140 | .i2c_address = 0x1b, |
140 | }; | 141 | }; |
141 | 142 | ||
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 5ebf1ff71c4c..abb5fedb0b1e 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor | 2 | * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor |
3 | * | 3 | * |
4 | * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> | 4 | * Author: Vladimir Barinov, <vbarinov@embeddedalley.com> |
5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> | 5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
@@ -256,7 +256,7 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, | |||
256 | mcbsp_word_length = DAVINCI_MCBSP_WORD_32; | 256 | mcbsp_word_length = DAVINCI_MCBSP_WORD_32; |
257 | break; | 257 | break; |
258 | default: | 258 | default: |
259 | printk(KERN_WARNING "davinci-i2s: unsupported PCM format"); | 259 | printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n"); |
260 | return -EINVAL; | 260 | return -EINVAL; |
261 | } | 261 | } |
262 | 262 | ||
diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/davinci/davinci-i2s.h index c5b091807eec..241648ce8873 100644 --- a/sound/soc/davinci/davinci-i2s.h +++ b/sound/soc/davinci/davinci-i2s.h | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor | 2 | * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor |
3 | * | 3 | * |
4 | * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> | 4 | * Author: Vladimir Barinov, <vbarinov@embeddedalley.com> |
5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> | 5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 6a5e56a782bb..76feaa657375 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * ALSA PCM interface for the TI DAVINCI processor | 2 | * ALSA PCM interface for the TI DAVINCI processor |
3 | * | 3 | * |
4 | * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> | 4 | * Author: Vladimir Barinov, <vbarinov@embeddedalley.com> |
5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> | 5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h index 8d6a45e75a6e..62cb4eb07e34 100644 --- a/sound/soc/davinci/davinci-pcm.h +++ b/sound/soc/davinci/davinci-pcm.h | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * ALSA PCM interface for the TI DAVINCI processor | 2 | * ALSA PCM interface for the TI DAVINCI processor |
3 | * | 3 | * |
4 | * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> | 4 | * Author: Vladimir Barinov, <vbarinov@embeddedalley.com> |
5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> | 5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 3368ace60977..bba9546ba5f5 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig | |||
@@ -1,3 +1,6 @@ | |||
1 | config SND_SOC_OF_SIMPLE | ||
2 | tristate | ||
3 | |||
1 | config SND_SOC_MPC8610 | 4 | config SND_SOC_MPC8610 |
2 | bool "ALSA SoC support for the MPC8610 SOC" | 5 | bool "ALSA SoC support for the MPC8610 SOC" |
3 | depends on MPC8610_HPCD | 6 | depends on MPC8610_HPCD |
@@ -14,3 +17,10 @@ config SND_SOC_MPC8610_HPCD | |||
14 | default y if MPC8610_HPCD | 17 | default y if MPC8610_HPCD |
15 | help | 18 | help |
16 | Say Y if you want to enable audio on the Freescale MPC8610 HPCD. | 19 | Say Y if you want to enable audio on the Freescale MPC8610 HPCD. |
20 | |||
21 | config SND_SOC_MPC5200_I2S | ||
22 | tristate "Freescale MPC5200 PSC in I2S mode driver" | ||
23 | select SND_SOC_OF_SIMPLE | ||
24 | depends on SND_SOC && PPC_MPC52xx | ||
25 | help | ||
26 | Say Y here to support the MPC5200 PSCs in I2S mode. | ||
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 62f680a4a776..035da4afec34 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile | |||
@@ -1,6 +1,11 @@ | |||
1 | # Simple machine driver that extracts configuration from the OF device tree | ||
2 | obj-$(CONFIG_SND_SOC_OF_SIMPLE) += soc-of-simple.o | ||
3 | |||
1 | # MPC8610 HPCD Machine Support | 4 | # MPC8610 HPCD Machine Support |
2 | obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o | 5 | obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o |
3 | 6 | ||
4 | # MPC8610 Platform Support | 7 | # MPC8610 Platform Support |
5 | obj-$(CONFIG_SND_SOC_MPC8610) += fsl_ssi.o fsl_dma.o | 8 | obj-$(CONFIG_SND_SOC_MPC8610) += fsl_ssi.o fsl_dma.o |
6 | 9 | ||
10 | obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o | ||
11 | |||
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c new file mode 100644 index 000000000000..86923299bc10 --- /dev/null +++ b/sound/soc/fsl/mpc5200_psc_i2s.c | |||
@@ -0,0 +1,884 @@ | |||
1 | /* | ||
2 | * Freescale MPC5200 PSC in I2S mode | ||
3 | * ALSA SoC Digital Audio Interface (DAI) driver | ||
4 | * | ||
5 | * Copyright (C) 2008 Secret Lab Technologies Ltd. | ||
6 | */ | ||
7 | |||
8 | #include <linux/init.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/interrupt.h> | ||
11 | #include <linux/device.h> | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/of_device.h> | ||
14 | #include <linux/of_platform.h> | ||
15 | #include <linux/dma-mapping.h> | ||
16 | |||
17 | #include <sound/core.h> | ||
18 | #include <sound/pcm.h> | ||
19 | #include <sound/pcm_params.h> | ||
20 | #include <sound/initval.h> | ||
21 | #include <sound/soc.h> | ||
22 | #include <sound/soc-of-simple.h> | ||
23 | |||
24 | #include <sysdev/bestcomm/bestcomm.h> | ||
25 | #include <sysdev/bestcomm/gen_bd.h> | ||
26 | #include <asm/mpc52xx_psc.h> | ||
27 | |||
28 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); | ||
29 | MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver"); | ||
30 | MODULE_LICENSE("GPL"); | ||
31 | |||
32 | /** | ||
33 | * PSC_I2S_RATES: sample rates supported by the I2S | ||
34 | * | ||
35 | * This driver currently only supports the PSC running in I2S slave mode, | ||
36 | * which means the codec determines the sample rate. Therefore, we tell | ||
37 | * ALSA that we support all rates and let the codec driver decide what rates | ||
38 | * are really supported. | ||
39 | */ | ||
40 | #define PSC_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \ | ||
41 | SNDRV_PCM_RATE_CONTINUOUS) | ||
42 | |||
43 | /** | ||
44 | * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode | ||
45 | */ | ||
46 | #define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \ | ||
47 | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE | \ | ||
48 | SNDRV_PCM_FMTBIT_S32_BE) | ||
49 | |||
50 | /** | ||
51 | * psc_i2s_stream - Data specific to a single stream (playback or capture) | ||
52 | * @active: flag indicating if the stream is active | ||
53 | * @psc_i2s: pointer back to parent psc_i2s data structure | ||
54 | * @bcom_task: bestcomm task structure | ||
55 | * @irq: irq number for bestcomm task | ||
56 | * @period_start: physical address of start of DMA region | ||
57 | * @period_end: physical address of end of DMA region | ||
58 | * @period_next_pt: physical address of next DMA buffer to enqueue | ||
59 | * @period_bytes: size of DMA period in bytes | ||
60 | */ | ||
61 | struct psc_i2s_stream { | ||
62 | int active; | ||
63 | struct psc_i2s *psc_i2s; | ||
64 | struct bcom_task *bcom_task; | ||
65 | int irq; | ||
66 | struct snd_pcm_substream *stream; | ||
67 | dma_addr_t period_start; | ||
68 | dma_addr_t period_end; | ||
69 | dma_addr_t period_next_pt; | ||
70 | dma_addr_t period_current_pt; | ||
71 | int period_bytes; | ||
72 | }; | ||
73 | |||
74 | /** | ||
75 | * psc_i2s - Private driver data | ||
76 | * @name: short name for this device ("PSC0", "PSC1", etc) | ||
77 | * @psc_regs: pointer to the PSC's registers | ||
78 | * @fifo_regs: pointer to the PSC's FIFO registers | ||
79 | * @irq: IRQ of this PSC | ||
80 | * @dev: struct device pointer | ||
81 | * @dai: the CPU DAI for this device | ||
82 | * @sicr: Base value used in serial interface control register; mode is ORed | ||
83 | * with this value. | ||
84 | * @playback: Playback stream context data | ||
85 | * @capture: Capture stream context data | ||
86 | */ | ||
87 | struct psc_i2s { | ||
88 | char name[32]; | ||
89 | struct mpc52xx_psc __iomem *psc_regs; | ||
90 | struct mpc52xx_psc_fifo __iomem *fifo_regs; | ||
91 | unsigned int irq; | ||
92 | struct device *dev; | ||
93 | struct snd_soc_dai dai; | ||
94 | spinlock_t lock; | ||
95 | u32 sicr; | ||
96 | |||
97 | /* per-stream data */ | ||
98 | struct psc_i2s_stream playback; | ||
99 | struct psc_i2s_stream capture; | ||
100 | |||
101 | /* Statistics */ | ||
102 | struct { | ||
103 | int overrun_count; | ||
104 | int underrun_count; | ||
105 | } stats; | ||
106 | }; | ||
107 | |||
108 | /* | ||
109 | * Interrupt handlers | ||
110 | */ | ||
111 | static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s) | ||
112 | { | ||
113 | struct psc_i2s *psc_i2s = _psc_i2s; | ||
114 | struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs; | ||
115 | u16 isr; | ||
116 | |||
117 | isr = in_be16(®s->mpc52xx_psc_isr); | ||
118 | |||
119 | /* Playback underrun error */ | ||
120 | if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) | ||
121 | psc_i2s->stats.underrun_count++; | ||
122 | |||
123 | /* Capture overrun error */ | ||
124 | if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) | ||
125 | psc_i2s->stats.overrun_count++; | ||
126 | |||
127 | out_8(®s->command, 4 << 4); /* reset the error status */ | ||
128 | |||
129 | return IRQ_HANDLED; | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer | ||
134 | * @s: pointer to stream private data structure | ||
135 | * | ||
136 | * Enqueues another audio period buffer into the bestcomm queue. | ||
137 | * | ||
138 | * Note: The routine must only be called when there is space available in | ||
139 | * the queue. Otherwise the enqueue will fail and the audio ring buffer | ||
140 | * will get out of sync | ||
141 | */ | ||
142 | static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s) | ||
143 | { | ||
144 | struct bcom_bd *bd; | ||
145 | |||
146 | /* Prepare and enqueue the next buffer descriptor */ | ||
147 | bd = bcom_prepare_next_buffer(s->bcom_task); | ||
148 | bd->status = s->period_bytes; | ||
149 | bd->data[0] = s->period_next_pt; | ||
150 | bcom_submit_next_buffer(s->bcom_task, NULL); | ||
151 | |||
152 | /* Update for next period */ | ||
153 | s->period_next_pt += s->period_bytes; | ||
154 | if (s->period_next_pt >= s->period_end) | ||
155 | s->period_next_pt = s->period_start; | ||
156 | } | ||
157 | |||
158 | /* Bestcomm DMA irq handler */ | ||
159 | static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream) | ||
160 | { | ||
161 | struct psc_i2s_stream *s = _psc_i2s_stream; | ||
162 | |||
163 | /* For each finished period, dequeue the completed period buffer | ||
164 | * and enqueue a new one in it's place. */ | ||
165 | while (bcom_buffer_done(s->bcom_task)) { | ||
166 | bcom_retrieve_buffer(s->bcom_task, NULL, NULL); | ||
167 | s->period_current_pt += s->period_bytes; | ||
168 | if (s->period_current_pt >= s->period_end) | ||
169 | s->period_current_pt = s->period_start; | ||
170 | psc_i2s_bcom_enqueue_next_buffer(s); | ||
171 | bcom_enable(s->bcom_task); | ||
172 | } | ||
173 | |||
174 | /* If the stream is active, then also inform the PCM middle layer | ||
175 | * of the period finished event. */ | ||
176 | if (s->active) | ||
177 | snd_pcm_period_elapsed(s->stream); | ||
178 | |||
179 | return IRQ_HANDLED; | ||
180 | } | ||
181 | |||
182 | /** | ||
183 | * psc_i2s_startup: create a new substream | ||
184 | * | ||
185 | * This is the first function called when a stream is opened. | ||
186 | * | ||
187 | * If this is the first stream open, then grab the IRQ and program most of | ||
188 | * the PSC registers. | ||
189 | */ | ||
190 | static int psc_i2s_startup(struct snd_pcm_substream *substream) | ||
191 | { | ||
192 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
193 | struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; | ||
194 | int rc; | ||
195 | |||
196 | dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream); | ||
197 | |||
198 | if (!psc_i2s->playback.active && | ||
199 | !psc_i2s->capture.active) { | ||
200 | /* Setup the IRQs */ | ||
201 | rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED, | ||
202 | "psc-i2s-status", psc_i2s); | ||
203 | rc |= request_irq(psc_i2s->capture.irq, | ||
204 | &psc_i2s_bcom_irq, IRQF_SHARED, | ||
205 | "psc-i2s-capture", &psc_i2s->capture); | ||
206 | rc |= request_irq(psc_i2s->playback.irq, | ||
207 | &psc_i2s_bcom_irq, IRQF_SHARED, | ||
208 | "psc-i2s-playback", &psc_i2s->playback); | ||
209 | if (rc) { | ||
210 | free_irq(psc_i2s->irq, psc_i2s); | ||
211 | free_irq(psc_i2s->capture.irq, | ||
212 | &psc_i2s->capture); | ||
213 | free_irq(psc_i2s->playback.irq, | ||
214 | &psc_i2s->playback); | ||
215 | return -ENODEV; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | static int psc_i2s_hw_params(struct snd_pcm_substream *substream, | ||
223 | struct snd_pcm_hw_params *params) | ||
224 | { | ||
225 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
226 | struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; | ||
227 | u32 mode; | ||
228 | |||
229 | dev_dbg(psc_i2s->dev, "%s(substream=%p) p_size=%i p_bytes=%i" | ||
230 | " periods=%i buffer_size=%i buffer_bytes=%i\n", | ||
231 | __func__, substream, params_period_size(params), | ||
232 | params_period_bytes(params), params_periods(params), | ||
233 | params_buffer_size(params), params_buffer_bytes(params)); | ||
234 | |||
235 | switch (params_format(params)) { | ||
236 | case SNDRV_PCM_FORMAT_S8: | ||
237 | mode = MPC52xx_PSC_SICR_SIM_CODEC_8; | ||
238 | break; | ||
239 | case SNDRV_PCM_FORMAT_S16_BE: | ||
240 | mode = MPC52xx_PSC_SICR_SIM_CODEC_16; | ||
241 | break; | ||
242 | case SNDRV_PCM_FORMAT_S24_BE: | ||
243 | mode = MPC52xx_PSC_SICR_SIM_CODEC_24; | ||
244 | break; | ||
245 | case SNDRV_PCM_FORMAT_S32_BE: | ||
246 | mode = MPC52xx_PSC_SICR_SIM_CODEC_32; | ||
247 | break; | ||
248 | default: | ||
249 | dev_dbg(psc_i2s->dev, "invalid format\n"); | ||
250 | return -EINVAL; | ||
251 | } | ||
252 | out_be32(&psc_i2s->psc_regs->sicr, psc_i2s->sicr | mode); | ||
253 | |||
254 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | static int psc_i2s_hw_free(struct snd_pcm_substream *substream) | ||
260 | { | ||
261 | snd_pcm_set_runtime_buffer(substream, NULL); | ||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * psc_i2s_trigger: start and stop the DMA transfer. | ||
267 | * | ||
268 | * This function is called by ALSA to start, stop, pause, and resume the DMA | ||
269 | * transfer of data. | ||
270 | */ | ||
271 | static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd) | ||
272 | { | ||
273 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
274 | struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; | ||
275 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
276 | struct psc_i2s_stream *s; | ||
277 | struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs; | ||
278 | u16 imr; | ||
279 | u8 psc_cmd; | ||
280 | long flags; | ||
281 | |||
282 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
283 | s = &psc_i2s->capture; | ||
284 | else | ||
285 | s = &psc_i2s->playback; | ||
286 | |||
287 | dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)" | ||
288 | " stream_id=%i\n", | ||
289 | substream, cmd, substream->pstr->stream); | ||
290 | |||
291 | switch (cmd) { | ||
292 | case SNDRV_PCM_TRIGGER_START: | ||
293 | s->period_bytes = frames_to_bytes(runtime, | ||
294 | runtime->period_size); | ||
295 | s->period_start = virt_to_phys(runtime->dma_area); | ||
296 | s->period_end = s->period_start + | ||
297 | (s->period_bytes * runtime->periods); | ||
298 | s->period_next_pt = s->period_start; | ||
299 | s->period_current_pt = s->period_start; | ||
300 | s->active = 1; | ||
301 | |||
302 | /* First; reset everything */ | ||
303 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
304 | out_8(®s->command, MPC52xx_PSC_RST_RX); | ||
305 | out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); | ||
306 | } else { | ||
307 | out_8(®s->command, MPC52xx_PSC_RST_TX); | ||
308 | out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); | ||
309 | } | ||
310 | |||
311 | /* Next, fill up the bestcomm bd queue and enable DMA. | ||
312 | * This will begin filling the PSC's fifo. */ | ||
313 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
314 | bcom_gen_bd_rx_reset(s->bcom_task); | ||
315 | else | ||
316 | bcom_gen_bd_tx_reset(s->bcom_task); | ||
317 | while (!bcom_queue_full(s->bcom_task)) | ||
318 | psc_i2s_bcom_enqueue_next_buffer(s); | ||
319 | bcom_enable(s->bcom_task); | ||
320 | |||
321 | /* Due to errata in the i2s mode; need to line up enabling | ||
322 | * the transmitter with a transition on the frame sync | ||
323 | * line */ | ||
324 | |||
325 | spin_lock_irqsave(&psc_i2s->lock, flags); | ||
326 | /* first make sure it is low */ | ||
327 | while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0) | ||
328 | ; | ||
329 | /* then wait for the transition to high */ | ||
330 | while ((in_8(®s->ipcr_acr.ipcr) & 0x80) == 0) | ||
331 | ; | ||
332 | /* Finally, enable the PSC. | ||
333 | * Receiver must always be enabled; even when we only want | ||
334 | * transmit. (see 15.3.2.3 of MPC5200B User's Guide) */ | ||
335 | psc_cmd = MPC52xx_PSC_RX_ENABLE; | ||
336 | if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
337 | psc_cmd |= MPC52xx_PSC_TX_ENABLE; | ||
338 | out_8(®s->command, psc_cmd); | ||
339 | spin_unlock_irqrestore(&psc_i2s->lock, flags); | ||
340 | |||
341 | break; | ||
342 | |||
343 | case SNDRV_PCM_TRIGGER_STOP: | ||
344 | /* Turn off the PSC */ | ||
345 | s->active = 0; | ||
346 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
347 | if (!psc_i2s->playback.active) { | ||
348 | out_8(®s->command, 2 << 4); /* reset rx */ | ||
349 | out_8(®s->command, 3 << 4); /* reset tx */ | ||
350 | out_8(®s->command, 4 << 4); /* reset err */ | ||
351 | } | ||
352 | } else { | ||
353 | out_8(®s->command, 3 << 4); /* reset tx */ | ||
354 | out_8(®s->command, 4 << 4); /* reset err */ | ||
355 | if (!psc_i2s->capture.active) | ||
356 | out_8(®s->command, 2 << 4); /* reset rx */ | ||
357 | } | ||
358 | |||
359 | bcom_disable(s->bcom_task); | ||
360 | while (!bcom_queue_empty(s->bcom_task)) | ||
361 | bcom_retrieve_buffer(s->bcom_task, NULL, NULL); | ||
362 | |||
363 | break; | ||
364 | |||
365 | default: | ||
366 | dev_dbg(psc_i2s->dev, "invalid command\n"); | ||
367 | return -EINVAL; | ||
368 | } | ||
369 | |||
370 | /* Update interrupt enable settings */ | ||
371 | imr = 0; | ||
372 | if (psc_i2s->playback.active) | ||
373 | imr |= MPC52xx_PSC_IMR_TXEMP; | ||
374 | if (psc_i2s->capture.active) | ||
375 | imr |= MPC52xx_PSC_IMR_ORERR; | ||
376 | out_be16(®s->isr_imr.imr, imr); | ||
377 | |||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | /** | ||
382 | * psc_i2s_shutdown: shutdown the data transfer on a stream | ||
383 | * | ||
384 | * Shutdown the PSC if there are no other substreams open. | ||
385 | */ | ||
386 | static void psc_i2s_shutdown(struct snd_pcm_substream *substream) | ||
387 | { | ||
388 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
389 | struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; | ||
390 | |||
391 | dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream); | ||
392 | |||
393 | /* | ||
394 | * If this is the last active substream, disable the PSC and release | ||
395 | * the IRQ. | ||
396 | */ | ||
397 | if (!psc_i2s->playback.active && | ||
398 | !psc_i2s->capture.active) { | ||
399 | |||
400 | /* Disable all interrupts and reset the PSC */ | ||
401 | out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0); | ||
402 | out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset tx */ | ||
403 | out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset rx */ | ||
404 | out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */ | ||
405 | out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */ | ||
406 | |||
407 | /* Release irqs */ | ||
408 | free_irq(psc_i2s->irq, psc_i2s); | ||
409 | free_irq(psc_i2s->capture.irq, &psc_i2s->capture); | ||
410 | free_irq(psc_i2s->playback.irq, &psc_i2s->playback); | ||
411 | } | ||
412 | } | ||
413 | |||
414 | /** | ||
415 | * psc_i2s_set_sysclk: set the clock frequency and direction | ||
416 | * | ||
417 | * This function is called by the machine driver to tell us what the clock | ||
418 | * frequency and direction are. | ||
419 | * | ||
420 | * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN), | ||
421 | * and we don't care about the frequency. Return an error if the direction | ||
422 | * is not SND_SOC_CLOCK_IN. | ||
423 | * | ||
424 | * @clk_id: reserved, should be zero | ||
425 | * @freq: the frequency of the given clock ID, currently ignored | ||
426 | * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master) | ||
427 | */ | ||
428 | static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, | ||
429 | int clk_id, unsigned int freq, int dir) | ||
430 | { | ||
431 | struct psc_i2s *psc_i2s = cpu_dai->private_data; | ||
432 | dev_dbg(psc_i2s->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n", | ||
433 | cpu_dai, dir); | ||
434 | return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL; | ||
435 | } | ||
436 | |||
437 | /** | ||
438 | * psc_i2s_set_fmt: set the serial format. | ||
439 | * | ||
440 | * This function is called by the machine driver to tell us what serial | ||
441 | * format to use. | ||
442 | * | ||
443 | * This driver only supports I2S mode. Return an error if the format is | ||
444 | * not SND_SOC_DAIFMT_I2S. | ||
445 | * | ||
446 | * @format: one of SND_SOC_DAIFMT_xxx | ||
447 | */ | ||
448 | static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) | ||
449 | { | ||
450 | struct psc_i2s *psc_i2s = cpu_dai->private_data; | ||
451 | dev_dbg(psc_i2s->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n", | ||
452 | cpu_dai, format); | ||
453 | return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL; | ||
454 | } | ||
455 | |||
456 | /* --------------------------------------------------------------------- | ||
457 | * ALSA SoC Bindings | ||
458 | * | ||
459 | * - Digital Audio Interface (DAI) template | ||
460 | * - create/destroy dai hooks | ||
461 | */ | ||
462 | |||
463 | /** | ||
464 | * psc_i2s_dai_template: template CPU Digital Audio Interface | ||
465 | */ | ||
466 | static struct snd_soc_dai psc_i2s_dai_template = { | ||
467 | .type = SND_SOC_DAI_I2S, | ||
468 | .playback = { | ||
469 | .channels_min = 2, | ||
470 | .channels_max = 2, | ||
471 | .rates = PSC_I2S_RATES, | ||
472 | .formats = PSC_I2S_FORMATS, | ||
473 | }, | ||
474 | .capture = { | ||
475 | .channels_min = 2, | ||
476 | .channels_max = 2, | ||
477 | .rates = PSC_I2S_RATES, | ||
478 | .formats = PSC_I2S_FORMATS, | ||
479 | }, | ||
480 | .ops = { | ||
481 | .startup = psc_i2s_startup, | ||
482 | .hw_params = psc_i2s_hw_params, | ||
483 | .hw_free = psc_i2s_hw_free, | ||
484 | .shutdown = psc_i2s_shutdown, | ||
485 | .trigger = psc_i2s_trigger, | ||
486 | }, | ||
487 | .dai_ops = { | ||
488 | .set_sysclk = psc_i2s_set_sysclk, | ||
489 | .set_fmt = psc_i2s_set_fmt, | ||
490 | }, | ||
491 | }; | ||
492 | |||
493 | /* --------------------------------------------------------------------- | ||
494 | * The PSC I2S 'ASoC platform' driver | ||
495 | * | ||
496 | * Can be referenced by an 'ASoC machine' driver | ||
497 | * This driver only deals with the audio bus; it doesn't have any | ||
498 | * interaction with the attached codec | ||
499 | */ | ||
500 | |||
501 | static const struct snd_pcm_hardware psc_i2s_pcm_hardware = { | ||
502 | .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | | ||
503 | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, | ||
504 | .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | | ||
505 | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, | ||
506 | .rate_min = 8000, | ||
507 | .rate_max = 48000, | ||
508 | .channels_min = 2, | ||
509 | .channels_max = 2, | ||
510 | .period_bytes_max = 1024 * 1024, | ||
511 | .period_bytes_min = 32, | ||
512 | .periods_min = 2, | ||
513 | .periods_max = 256, | ||
514 | .buffer_bytes_max = 2 * 1024 * 1024, | ||
515 | .fifo_size = 0, | ||
516 | }; | ||
517 | |||
518 | static int psc_i2s_pcm_open(struct snd_pcm_substream *substream) | ||
519 | { | ||
520 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
521 | struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; | ||
522 | struct psc_i2s_stream *s; | ||
523 | |||
524 | dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream); | ||
525 | |||
526 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
527 | s = &psc_i2s->capture; | ||
528 | else | ||
529 | s = &psc_i2s->playback; | ||
530 | |||
531 | snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware); | ||
532 | |||
533 | s->stream = substream; | ||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | static int psc_i2s_pcm_close(struct snd_pcm_substream *substream) | ||
538 | { | ||
539 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
540 | struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; | ||
541 | struct psc_i2s_stream *s; | ||
542 | |||
543 | dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream); | ||
544 | |||
545 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
546 | s = &psc_i2s->capture; | ||
547 | else | ||
548 | s = &psc_i2s->playback; | ||
549 | |||
550 | s->stream = NULL; | ||
551 | return 0; | ||
552 | } | ||
553 | |||
554 | static snd_pcm_uframes_t | ||
555 | psc_i2s_pcm_pointer(struct snd_pcm_substream *substream) | ||
556 | { | ||
557 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
558 | struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; | ||
559 | struct psc_i2s_stream *s; | ||
560 | dma_addr_t count; | ||
561 | |||
562 | if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
563 | s = &psc_i2s->capture; | ||
564 | else | ||
565 | s = &psc_i2s->playback; | ||
566 | |||
567 | count = s->period_current_pt - s->period_start; | ||
568 | |||
569 | return bytes_to_frames(substream->runtime, count); | ||
570 | } | ||
571 | |||
572 | static struct snd_pcm_ops psc_i2s_pcm_ops = { | ||
573 | .open = psc_i2s_pcm_open, | ||
574 | .close = psc_i2s_pcm_close, | ||
575 | .ioctl = snd_pcm_lib_ioctl, | ||
576 | .pointer = psc_i2s_pcm_pointer, | ||
577 | }; | ||
578 | |||
579 | static u64 psc_i2s_pcm_dmamask = 0xffffffff; | ||
580 | static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, | ||
581 | struct snd_pcm *pcm) | ||
582 | { | ||
583 | struct snd_soc_pcm_runtime *rtd = pcm->private_data; | ||
584 | size_t size = psc_i2s_pcm_hardware.buffer_bytes_max; | ||
585 | int rc = 0; | ||
586 | |||
587 | dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n", | ||
588 | card, dai, pcm); | ||
589 | |||
590 | if (!card->dev->dma_mask) | ||
591 | card->dev->dma_mask = &psc_i2s_pcm_dmamask; | ||
592 | if (!card->dev->coherent_dma_mask) | ||
593 | card->dev->coherent_dma_mask = 0xffffffff; | ||
594 | |||
595 | if (pcm->streams[0].substream) { | ||
596 | rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, | ||
597 | &pcm->streams[0].substream->dma_buffer); | ||
598 | if (rc) | ||
599 | goto playback_alloc_err; | ||
600 | } | ||
601 | |||
602 | if (pcm->streams[1].substream) { | ||
603 | rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, | ||
604 | &pcm->streams[1].substream->dma_buffer); | ||
605 | if (rc) | ||
606 | goto capture_alloc_err; | ||
607 | } | ||
608 | |||
609 | return 0; | ||
610 | |||
611 | capture_alloc_err: | ||
612 | if (pcm->streams[0].substream) | ||
613 | snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); | ||
614 | playback_alloc_err: | ||
615 | dev_err(card->dev, "Cannot allocate buffer(s)\n"); | ||
616 | return -ENOMEM; | ||
617 | } | ||
618 | |||
619 | static void psc_i2s_pcm_free(struct snd_pcm *pcm) | ||
620 | { | ||
621 | struct snd_soc_pcm_runtime *rtd = pcm->private_data; | ||
622 | struct snd_pcm_substream *substream; | ||
623 | int stream; | ||
624 | |||
625 | dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=%p)\n", pcm); | ||
626 | |||
627 | for (stream = 0; stream < 2; stream++) { | ||
628 | substream = pcm->streams[stream].substream; | ||
629 | if (substream) { | ||
630 | snd_dma_free_pages(&substream->dma_buffer); | ||
631 | substream->dma_buffer.area = NULL; | ||
632 | substream->dma_buffer.addr = 0; | ||
633 | } | ||
634 | } | ||
635 | } | ||
636 | |||
637 | struct snd_soc_platform psc_i2s_pcm_soc_platform = { | ||
638 | .name = "mpc5200-psc-audio", | ||
639 | .pcm_ops = &psc_i2s_pcm_ops, | ||
640 | .pcm_new = &psc_i2s_pcm_new, | ||
641 | .pcm_free = &psc_i2s_pcm_free, | ||
642 | }; | ||
643 | |||
644 | /* --------------------------------------------------------------------- | ||
645 | * Sysfs attributes for debugging | ||
646 | */ | ||
647 | |||
648 | static ssize_t psc_i2s_status_show(struct device *dev, | ||
649 | struct device_attribute *attr, char *buf) | ||
650 | { | ||
651 | struct psc_i2s *psc_i2s = dev_get_drvdata(dev); | ||
652 | |||
653 | return sprintf(buf, "status=%.4x sicr=%.8x rfnum=%i rfstat=0x%.4x " | ||
654 | "tfnum=%i tfstat=0x%.4x\n", | ||
655 | in_be16(&psc_i2s->psc_regs->sr_csr.status), | ||
656 | in_be32(&psc_i2s->psc_regs->sicr), | ||
657 | in_be16(&psc_i2s->fifo_regs->rfnum) & 0x1ff, | ||
658 | in_be16(&psc_i2s->fifo_regs->rfstat), | ||
659 | in_be16(&psc_i2s->fifo_regs->tfnum) & 0x1ff, | ||
660 | in_be16(&psc_i2s->fifo_regs->tfstat)); | ||
661 | } | ||
662 | |||
663 | static int *psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s, const char *name) | ||
664 | { | ||
665 | if (strcmp(name, "playback_underrun") == 0) | ||
666 | return &psc_i2s->stats.underrun_count; | ||
667 | if (strcmp(name, "capture_overrun") == 0) | ||
668 | return &psc_i2s->stats.overrun_count; | ||
669 | |||
670 | return NULL; | ||
671 | } | ||
672 | |||
673 | static ssize_t psc_i2s_stat_show(struct device *dev, | ||
674 | struct device_attribute *attr, char *buf) | ||
675 | { | ||
676 | struct psc_i2s *psc_i2s = dev_get_drvdata(dev); | ||
677 | int *attrib; | ||
678 | |||
679 | attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name); | ||
680 | if (!attrib) | ||
681 | return 0; | ||
682 | |||
683 | return sprintf(buf, "%i\n", *attrib); | ||
684 | } | ||
685 | |||
686 | static ssize_t psc_i2s_stat_store(struct device *dev, | ||
687 | struct device_attribute *attr, | ||
688 | const char *buf, | ||
689 | size_t count) | ||
690 | { | ||
691 | struct psc_i2s *psc_i2s = dev_get_drvdata(dev); | ||
692 | int *attrib; | ||
693 | |||
694 | attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name); | ||
695 | if (!attrib) | ||
696 | return 0; | ||
697 | |||
698 | *attrib = simple_strtoul(buf, NULL, 0); | ||
699 | return count; | ||
700 | } | ||
701 | |||
702 | DEVICE_ATTR(status, 0644, psc_i2s_status_show, NULL); | ||
703 | DEVICE_ATTR(playback_underrun, 0644, psc_i2s_stat_show, psc_i2s_stat_store); | ||
704 | DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show, psc_i2s_stat_store); | ||
705 | |||
706 | /* --------------------------------------------------------------------- | ||
707 | * OF platform bus binding code: | ||
708 | * - Probe/remove operations | ||
709 | * - OF device match table | ||
710 | */ | ||
711 | static int __devinit psc_i2s_of_probe(struct of_device *op, | ||
712 | const struct of_device_id *match) | ||
713 | { | ||
714 | phys_addr_t fifo; | ||
715 | struct psc_i2s *psc_i2s; | ||
716 | struct resource res; | ||
717 | int size, psc_id, irq, rc; | ||
718 | const __be32 *prop; | ||
719 | void __iomem *regs; | ||
720 | |||
721 | dev_dbg(&op->dev, "probing psc i2s device\n"); | ||
722 | |||
723 | /* Get the PSC ID */ | ||
724 | prop = of_get_property(op->node, "cell-index", &size); | ||
725 | if (!prop || size < sizeof *prop) | ||
726 | return -ENODEV; | ||
727 | psc_id = be32_to_cpu(*prop); | ||
728 | |||
729 | /* Fetch the registers and IRQ of the PSC */ | ||
730 | irq = irq_of_parse_and_map(op->node, 0); | ||
731 | if (of_address_to_resource(op->node, 0, &res)) { | ||
732 | dev_err(&op->dev, "Missing reg property\n"); | ||
733 | return -ENODEV; | ||
734 | } | ||
735 | regs = ioremap(res.start, 1 + res.end - res.start); | ||
736 | if (!regs) { | ||
737 | dev_err(&op->dev, "Could not map registers\n"); | ||
738 | return -ENODEV; | ||
739 | } | ||
740 | |||
741 | /* Allocate and initialize the driver private data */ | ||
742 | psc_i2s = kzalloc(sizeof *psc_i2s, GFP_KERNEL); | ||
743 | if (!psc_i2s) { | ||
744 | iounmap(regs); | ||
745 | return -ENOMEM; | ||
746 | } | ||
747 | spin_lock_init(&psc_i2s->lock); | ||
748 | psc_i2s->irq = irq; | ||
749 | psc_i2s->psc_regs = regs; | ||
750 | psc_i2s->fifo_regs = regs + sizeof *psc_i2s->psc_regs; | ||
751 | psc_i2s->dev = &op->dev; | ||
752 | psc_i2s->playback.psc_i2s = psc_i2s; | ||
753 | psc_i2s->capture.psc_i2s = psc_i2s; | ||
754 | snprintf(psc_i2s->name, sizeof psc_i2s->name, "PSC%u", psc_id+1); | ||
755 | |||
756 | /* Fill out the CPU DAI structure */ | ||
757 | memcpy(&psc_i2s->dai, &psc_i2s_dai_template, sizeof psc_i2s->dai); | ||
758 | psc_i2s->dai.private_data = psc_i2s; | ||
759 | psc_i2s->dai.name = psc_i2s->name; | ||
760 | psc_i2s->dai.id = psc_id; | ||
761 | |||
762 | /* Find the address of the fifo data registers and setup the | ||
763 | * DMA tasks */ | ||
764 | fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); | ||
765 | psc_i2s->capture.bcom_task = | ||
766 | bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512); | ||
767 | psc_i2s->playback.bcom_task = | ||
768 | bcom_psc_gen_bd_tx_init(psc_id, 10, fifo); | ||
769 | if (!psc_i2s->capture.bcom_task || | ||
770 | !psc_i2s->playback.bcom_task) { | ||
771 | dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); | ||
772 | iounmap(regs); | ||
773 | kfree(psc_i2s); | ||
774 | return -ENODEV; | ||
775 | } | ||
776 | |||
777 | /* Disable all interrupts and reset the PSC */ | ||
778 | out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0); | ||
779 | out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset transmitter */ | ||
780 | out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset receiver */ | ||
781 | out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */ | ||
782 | out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */ | ||
783 | |||
784 | /* Configure the serial interface mode; defaulting to CODEC8 mode */ | ||
785 | psc_i2s->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S | | ||
786 | MPC52xx_PSC_SICR_CLKPOL; | ||
787 | if (of_get_property(op->node, "fsl,cellslave", NULL)) | ||
788 | psc_i2s->sicr |= MPC52xx_PSC_SICR_CELLSLAVE | | ||
789 | MPC52xx_PSC_SICR_GENCLK; | ||
790 | out_be32(&psc_i2s->psc_regs->sicr, | ||
791 | psc_i2s->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8); | ||
792 | |||
793 | /* Check for the codec handle. If it is not present then we | ||
794 | * are done */ | ||
795 | if (!of_get_property(op->node, "codec-handle", NULL)) | ||
796 | return 0; | ||
797 | |||
798 | /* Set up mode register; | ||
799 | * First write: RxRdy (FIFO Alarm) generates rx FIFO irq | ||
800 | * Second write: register Normal mode for non loopback | ||
801 | */ | ||
802 | out_8(&psc_i2s->psc_regs->mode, 0); | ||
803 | out_8(&psc_i2s->psc_regs->mode, 0); | ||
804 | |||
805 | /* Set the TX and RX fifo alarm thresholds */ | ||
806 | out_be16(&psc_i2s->fifo_regs->rfalarm, 0x100); | ||
807 | out_8(&psc_i2s->fifo_regs->rfcntl, 0x4); | ||
808 | out_be16(&psc_i2s->fifo_regs->tfalarm, 0x100); | ||
809 | out_8(&psc_i2s->fifo_regs->tfcntl, 0x7); | ||
810 | |||
811 | /* Lookup the IRQ numbers */ | ||
812 | psc_i2s->playback.irq = | ||
813 | bcom_get_task_irq(psc_i2s->playback.bcom_task); | ||
814 | psc_i2s->capture.irq = | ||
815 | bcom_get_task_irq(psc_i2s->capture.bcom_task); | ||
816 | |||
817 | /* Save what we've done so it can be found again later */ | ||
818 | dev_set_drvdata(&op->dev, psc_i2s); | ||
819 | |||
820 | /* Register the SYSFS files */ | ||
821 | rc = device_create_file(psc_i2s->dev, &dev_attr_status); | ||
822 | rc = device_create_file(psc_i2s->dev, &dev_attr_capture_overrun); | ||
823 | rc = device_create_file(psc_i2s->dev, &dev_attr_playback_underrun); | ||
824 | if (rc) | ||
825 | dev_info(psc_i2s->dev, "error creating sysfs files\n"); | ||
826 | |||
827 | /* Tell the ASoC OF helpers about it */ | ||
828 | of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node, | ||
829 | &psc_i2s->dai); | ||
830 | |||
831 | return 0; | ||
832 | } | ||
833 | |||
834 | static int __devexit psc_i2s_of_remove(struct of_device *op) | ||
835 | { | ||
836 | struct psc_i2s *psc_i2s = dev_get_drvdata(&op->dev); | ||
837 | |||
838 | dev_dbg(&op->dev, "psc_i2s_remove()\n"); | ||
839 | |||
840 | bcom_gen_bd_rx_release(psc_i2s->capture.bcom_task); | ||
841 | bcom_gen_bd_tx_release(psc_i2s->playback.bcom_task); | ||
842 | |||
843 | iounmap(psc_i2s->psc_regs); | ||
844 | iounmap(psc_i2s->fifo_regs); | ||
845 | kfree(psc_i2s); | ||
846 | dev_set_drvdata(&op->dev, NULL); | ||
847 | |||
848 | return 0; | ||
849 | } | ||
850 | |||
851 | /* Match table for of_platform binding */ | ||
852 | static struct of_device_id psc_i2s_match[] __devinitdata = { | ||
853 | { .compatible = "fsl,mpc5200-psc-i2s", }, | ||
854 | {} | ||
855 | }; | ||
856 | MODULE_DEVICE_TABLE(of, psc_i2s_match); | ||
857 | |||
858 | static struct of_platform_driver psc_i2s_driver = { | ||
859 | .match_table = psc_i2s_match, | ||
860 | .probe = psc_i2s_of_probe, | ||
861 | .remove = __devexit_p(psc_i2s_of_remove), | ||
862 | .driver = { | ||
863 | .name = "mpc5200-psc-i2s", | ||
864 | .owner = THIS_MODULE, | ||
865 | }, | ||
866 | }; | ||
867 | |||
868 | /* --------------------------------------------------------------------- | ||
869 | * Module setup and teardown; simply register the of_platform driver | ||
870 | * for the PSC in I2S mode. | ||
871 | */ | ||
872 | static int __init psc_i2s_init(void) | ||
873 | { | ||
874 | return of_register_platform_driver(&psc_i2s_driver); | ||
875 | } | ||
876 | module_init(psc_i2s_init); | ||
877 | |||
878 | static void __exit psc_i2s_exit(void) | ||
879 | { | ||
880 | of_unregister_platform_driver(&psc_i2s_driver); | ||
881 | } | ||
882 | module_exit(psc_i2s_exit); | ||
883 | |||
884 | |||
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 4bdc9d8fc90e..94f89debde1f 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c | |||
@@ -68,10 +68,6 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) | |||
68 | guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, | 68 | guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, |
69 | machine_data->dma_channel_id[1], 0); | 69 | machine_data->dma_channel_id[1], 0); |
70 | 70 | ||
71 | guts_set_pmuxcr_dma(machine_data->guts, 1, 0, 0); | ||
72 | guts_set_pmuxcr_dma(machine_data->guts, 1, 3, 0); | ||
73 | guts_set_pmuxcr_dma(machine_data->guts, 0, 3, 0); | ||
74 | |||
75 | switch (machine_data->ssi_id) { | 71 | switch (machine_data->ssi_id) { |
76 | case 0: | 72 | case 0: |
77 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 73 | clrsetbits_be32(&machine_data->guts->pmuxcr, |
@@ -230,6 +226,8 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, | |||
230 | struct fsl_ssi_info ssi_info; | 226 | struct fsl_ssi_info ssi_info; |
231 | struct fsl_dma_info dma_info; | 227 | struct fsl_dma_info dma_info; |
232 | int ret = -ENODEV; | 228 | int ret = -ENODEV; |
229 | unsigned int playback_dma_channel; | ||
230 | unsigned int capture_dma_channel; | ||
233 | 231 | ||
234 | machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL); | 232 | machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL); |
235 | if (!machine_data) | 233 | if (!machine_data) |
@@ -381,8 +379,9 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, | |||
381 | goto error; | 379 | goto error; |
382 | } | 380 | } |
383 | 381 | ||
384 | /* Find the DMA channels to use. For now, we always use the first DMA | 382 | /* Find the DMA channels to use. Both SSIs need to use the same DMA |
385 | controller. */ | 383 | * controller, so let's use DMA#1. |
384 | */ | ||
386 | for_each_compatible_node(dma_np, NULL, "fsl,mpc8610-dma") { | 385 | for_each_compatible_node(dma_np, NULL, "fsl,mpc8610-dma") { |
387 | iprop = of_get_property(dma_np, "cell-index", NULL); | 386 | iprop = of_get_property(dma_np, "cell-index", NULL); |
388 | if (iprop && (*iprop == 0)) { | 387 | if (iprop && (*iprop == 0)) { |
@@ -397,14 +396,19 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, | |||
397 | } | 396 | } |
398 | machine_data->dma_id = *iprop; | 397 | machine_data->dma_id = *iprop; |
399 | 398 | ||
399 | /* SSI1 needs to use DMA Channels 0 and 1, and SSI2 needs to use DMA | ||
400 | * channels 2 and 3. This is just how the MPC8610 is wired | ||
401 | * internally. | ||
402 | */ | ||
403 | playback_dma_channel = (machine_data->ssi_id == 0) ? 0 : 2; | ||
404 | capture_dma_channel = (machine_data->ssi_id == 0) ? 1 : 3; | ||
405 | |||
400 | /* | 406 | /* |
401 | * Find the DMA channels to use. For now, we always use DMA channel 0 | 407 | * Find the DMA channels to use. |
402 | * for playback, and DMA channel 1 for capture. | ||
403 | */ | 408 | */ |
404 | while ((dma_channel_np = of_get_next_child(dma_np, dma_channel_np))) { | 409 | while ((dma_channel_np = of_get_next_child(dma_np, dma_channel_np))) { |
405 | iprop = of_get_property(dma_channel_np, "cell-index", NULL); | 410 | iprop = of_get_property(dma_channel_np, "cell-index", NULL); |
406 | /* Is it DMA channel 0? */ | 411 | if (iprop && (*iprop == playback_dma_channel)) { |
407 | if (iprop && (*iprop == 0)) { | ||
408 | /* dma_channel[0] and dma_irq[0] are for playback */ | 412 | /* dma_channel[0] and dma_irq[0] are for playback */ |
409 | dma_info.dma_channel[0] = of_iomap(dma_channel_np, 0); | 413 | dma_info.dma_channel[0] = of_iomap(dma_channel_np, 0); |
410 | dma_info.dma_irq[0] = | 414 | dma_info.dma_irq[0] = |
@@ -412,7 +416,7 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, | |||
412 | machine_data->dma_channel_id[0] = *iprop; | 416 | machine_data->dma_channel_id[0] = *iprop; |
413 | continue; | 417 | continue; |
414 | } | 418 | } |
415 | if (iprop && (*iprop == 1)) { | 419 | if (iprop && (*iprop == capture_dma_channel)) { |
416 | /* dma_channel[1] and dma_irq[1] are for capture */ | 420 | /* dma_channel[1] and dma_irq[1] are for capture */ |
417 | dma_info.dma_channel[1] = of_iomap(dma_channel_np, 0); | 421 | dma_info.dma_channel[1] = of_iomap(dma_channel_np, 0); |
418 | dma_info.dma_irq[1] = | 422 | dma_info.dma_irq[1] = |
diff --git a/sound/soc/fsl/soc-of-simple.c b/sound/soc/fsl/soc-of-simple.c new file mode 100644 index 000000000000..0382fdac51cd --- /dev/null +++ b/sound/soc/fsl/soc-of-simple.c | |||
@@ -0,0 +1,171 @@ | |||
1 | /* | ||
2 | * OF helpers for ALSA SoC Layer | ||
3 | * | ||
4 | * Copyright (C) 2008, Secret Lab Technologies Ltd. | ||
5 | */ | ||
6 | |||
7 | #include <linux/module.h> | ||
8 | #include <linux/moduleparam.h> | ||
9 | #include <linux/init.h> | ||
10 | #include <linux/delay.h> | ||
11 | #include <linux/pm.h> | ||
12 | #include <linux/bitops.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <sound/core.h> | ||
16 | #include <sound/pcm.h> | ||
17 | #include <sound/pcm_params.h> | ||
18 | #include <sound/soc.h> | ||
19 | #include <sound/soc-of-simple.h> | ||
20 | #include <sound/initval.h> | ||
21 | |||
22 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); | ||
23 | MODULE_LICENSE("GPL"); | ||
24 | MODULE_DESCRIPTION("ALSA SoC OpenFirmware bindings"); | ||
25 | |||
26 | static DEFINE_MUTEX(of_snd_soc_mutex); | ||
27 | static LIST_HEAD(of_snd_soc_device_list); | ||
28 | static int of_snd_soc_next_index; | ||
29 | |||
30 | struct of_snd_soc_device { | ||
31 | int id; | ||
32 | struct list_head list; | ||
33 | struct snd_soc_device device; | ||
34 | struct snd_soc_machine machine; | ||
35 | struct snd_soc_dai_link dai_link; | ||
36 | struct platform_device *pdev; | ||
37 | struct device_node *platform_node; | ||
38 | struct device_node *codec_node; | ||
39 | }; | ||
40 | |||
41 | static struct snd_soc_ops of_snd_soc_ops = { | ||
42 | }; | ||
43 | |||
44 | static struct of_snd_soc_device * | ||
45 | of_snd_soc_get_device(struct device_node *codec_node) | ||
46 | { | ||
47 | struct of_snd_soc_device *of_soc; | ||
48 | |||
49 | list_for_each_entry(of_soc, &of_snd_soc_device_list, list) { | ||
50 | if (of_soc->codec_node == codec_node) | ||
51 | return of_soc; | ||
52 | } | ||
53 | |||
54 | of_soc = kzalloc(sizeof(struct of_snd_soc_device), GFP_KERNEL); | ||
55 | if (!of_soc) | ||
56 | return NULL; | ||
57 | |||
58 | /* Initialize the structure and add it to the global list */ | ||
59 | of_soc->codec_node = codec_node; | ||
60 | of_soc->id = of_snd_soc_next_index++; | ||
61 | of_soc->machine.dai_link = &of_soc->dai_link; | ||
62 | of_soc->machine.num_links = 1; | ||
63 | of_soc->device.machine = &of_soc->machine; | ||
64 | of_soc->dai_link.ops = &of_snd_soc_ops; | ||
65 | list_add(&of_soc->list, &of_snd_soc_device_list); | ||
66 | |||
67 | return of_soc; | ||
68 | } | ||
69 | |||
70 | static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc) | ||
71 | { | ||
72 | struct platform_device *pdev; | ||
73 | int rc; | ||
74 | |||
75 | /* Only register the device if both the codec and platform have | ||
76 | * been registered */ | ||
77 | if ((!of_soc->device.codec_data) || (!of_soc->platform_node)) | ||
78 | return; | ||
79 | |||
80 | pr_info("platform<-->codec match achieved; registering machine\n"); | ||
81 | |||
82 | pdev = platform_device_alloc("soc-audio", of_soc->id); | ||
83 | if (!pdev) { | ||
84 | pr_err("of_soc: platform_device_alloc() failed\n"); | ||
85 | return; | ||
86 | } | ||
87 | |||
88 | pdev->dev.platform_data = of_soc; | ||
89 | platform_set_drvdata(pdev, &of_soc->device); | ||
90 | of_soc->device.dev = &pdev->dev; | ||
91 | |||
92 | /* The ASoC device is complete; register it */ | ||
93 | rc = platform_device_add(pdev); | ||
94 | if (rc) { | ||
95 | pr_err("of_soc: platform_device_add() failed\n"); | ||
96 | return; | ||
97 | } | ||
98 | |||
99 | } | ||
100 | |||
101 | int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev, | ||
102 | void *codec_data, struct snd_soc_dai *dai, | ||
103 | struct device_node *node) | ||
104 | { | ||
105 | struct of_snd_soc_device *of_soc; | ||
106 | int rc = 0; | ||
107 | |||
108 | pr_info("registering ASoC codec driver: %s\n", node->full_name); | ||
109 | |||
110 | mutex_lock(&of_snd_soc_mutex); | ||
111 | of_soc = of_snd_soc_get_device(node); | ||
112 | if (!of_soc) { | ||
113 | rc = -ENOMEM; | ||
114 | goto out; | ||
115 | } | ||
116 | |||
117 | /* Store the codec data */ | ||
118 | of_soc->device.codec_data = codec_data; | ||
119 | of_soc->device.codec_dev = codec_dev; | ||
120 | of_soc->dai_link.name = (char *)node->name; | ||
121 | of_soc->dai_link.stream_name = (char *)node->name; | ||
122 | of_soc->dai_link.codec_dai = dai; | ||
123 | |||
124 | /* Now try to register the SoC device */ | ||
125 | of_snd_soc_register_device(of_soc); | ||
126 | |||
127 | out: | ||
128 | mutex_unlock(&of_snd_soc_mutex); | ||
129 | return rc; | ||
130 | } | ||
131 | EXPORT_SYMBOL_GPL(of_snd_soc_register_codec); | ||
132 | |||
133 | int of_snd_soc_register_platform(struct snd_soc_platform *platform, | ||
134 | struct device_node *node, | ||
135 | struct snd_soc_dai *cpu_dai) | ||
136 | { | ||
137 | struct of_snd_soc_device *of_soc; | ||
138 | struct device_node *codec_node; | ||
139 | const phandle *handle; | ||
140 | int len, rc = 0; | ||
141 | |||
142 | pr_info("registering ASoC platform driver: %s\n", node->full_name); | ||
143 | |||
144 | handle = of_get_property(node, "codec-handle", &len); | ||
145 | if (!handle || len < sizeof(handle)) | ||
146 | return -ENODEV; | ||
147 | codec_node = of_find_node_by_phandle(*handle); | ||
148 | if (!codec_node) | ||
149 | return -ENODEV; | ||
150 | pr_info("looking for codec: %s\n", codec_node->full_name); | ||
151 | |||
152 | mutex_lock(&of_snd_soc_mutex); | ||
153 | of_soc = of_snd_soc_get_device(codec_node); | ||
154 | if (!of_soc) { | ||
155 | rc = -ENOMEM; | ||
156 | goto out; | ||
157 | } | ||
158 | |||
159 | of_soc->platform_node = node; | ||
160 | of_soc->dai_link.cpu_dai = cpu_dai; | ||
161 | of_soc->device.platform = platform; | ||
162 | of_soc->machine.name = of_soc->dai_link.cpu_dai->name; | ||
163 | |||
164 | /* Now try to register the SoC device */ | ||
165 | of_snd_soc_register_device(of_soc); | ||
166 | |||
167 | out: | ||
168 | mutex_unlock(&of_snd_soc_mutex); | ||
169 | return rc; | ||
170 | } | ||
171 | EXPORT_SYMBOL_GPL(of_snd_soc_register_platform); | ||
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index aea27e70043c..8b7766b998d7 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig | |||
@@ -13,3 +13,11 @@ config SND_OMAP_SOC_N810 | |||
13 | select SND_SOC_TLV320AIC3X | 13 | select SND_SOC_TLV320AIC3X |
14 | help | 14 | help |
15 | Say Y if you want to add support for SoC audio on Nokia N810. | 15 | Say Y if you want to add support for SoC audio on Nokia N810. |
16 | |||
17 | config SND_OMAP_SOC_OSK5912 | ||
18 | tristate "SoC Audio support for omap osk5912" | ||
19 | depends on SND_OMAP_SOC && MACH_OMAP_OSK | ||
20 | select SND_OMAP_SOC_MCBSP | ||
21 | select SND_SOC_TLV320AIC23 | ||
22 | help | ||
23 | Say Y if you want to add support for SoC audio on osk5912. | ||
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index d8d8d58075e3..e09d1f297f64 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile | |||
@@ -7,5 +7,7 @@ obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o | |||
7 | 7 | ||
8 | # OMAP Machine Support | 8 | # OMAP Machine Support |
9 | snd-soc-n810-objs := n810.o | 9 | snd-soc-n810-objs := n810.o |
10 | snd-soc-osk5912-objs := osk5912.o | ||
10 | 11 | ||
11 | obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o | 12 | obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o |
13 | obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o | ||
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index 87d0ed01f65a..fae3ad36e0bf 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c | |||
@@ -247,9 +247,9 @@ static int n810_aic33_init(struct snd_soc_codec *codec) | |||
247 | int i, err; | 247 | int i, err; |
248 | 248 | ||
249 | /* Not connected */ | 249 | /* Not connected */ |
250 | snd_soc_dapm_disable_pin(codec, "MONO_LOUT"); | 250 | snd_soc_dapm_nc_pin(codec, "MONO_LOUT"); |
251 | snd_soc_dapm_disable_pin(codec, "HPLCOM"); | 251 | snd_soc_dapm_nc_pin(codec, "HPLCOM"); |
252 | snd_soc_dapm_disable_pin(codec, "HPRCOM"); | 252 | snd_soc_dapm_nc_pin(codec, "HPRCOM"); |
253 | 253 | ||
254 | /* Add N810 specific controls */ | 254 | /* Add N810 specific controls */ |
255 | for (i = 0; i < ARRAY_SIZE(aic33_n810_controls); i++) { | 255 | for (i = 0; i < ARRAY_SIZE(aic33_n810_controls); i++) { |
@@ -290,6 +290,7 @@ static struct snd_soc_machine snd_soc_machine_n810 = { | |||
290 | 290 | ||
291 | /* Audio private data */ | 291 | /* Audio private data */ |
292 | static struct aic3x_setup_data n810_aic33_setup = { | 292 | static struct aic3x_setup_data n810_aic33_setup = { |
293 | .i2c_bus = 2, | ||
293 | .i2c_address = 0x18, | 294 | .i2c_address = 0x18, |
294 | .gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED, | 295 | .gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED, |
295 | .gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT, | 296 | .gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT, |
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 35310e16d7f3..0a063a98a661 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c | |||
@@ -59,12 +59,7 @@ static struct omap_mcbsp_data mcbsp_data[NUM_LINKS]; | |||
59 | * Stream DMA parameters. DMA request line and port address are set runtime | 59 | * Stream DMA parameters. DMA request line and port address are set runtime |
60 | * since they are different between OMAP1 and later OMAPs | 60 | * since they are different between OMAP1 and later OMAPs |
61 | */ | 61 | */ |
62 | static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2] = { | 62 | static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2]; |
63 | { | ||
64 | { .name = "I2S PCM Stereo out", }, | ||
65 | { .name = "I2S PCM Stereo in", }, | ||
66 | }, | ||
67 | }; | ||
68 | 63 | ||
69 | #if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) | 64 | #if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) |
70 | static const int omap1_dma_reqs[][2] = { | 65 | static const int omap1_dma_reqs[][2] = { |
@@ -84,11 +79,22 @@ static const unsigned long omap1_mcbsp_port[][2] = { | |||
84 | static const int omap1_dma_reqs[][2] = {}; | 79 | static const int omap1_dma_reqs[][2] = {}; |
85 | static const unsigned long omap1_mcbsp_port[][2] = {}; | 80 | static const unsigned long omap1_mcbsp_port[][2] = {}; |
86 | #endif | 81 | #endif |
87 | #if defined(CONFIG_ARCH_OMAP2420) | 82 | |
88 | static const int omap2420_dma_reqs[][2] = { | 83 | #if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) |
84 | static const int omap24xx_dma_reqs[][2] = { | ||
89 | { OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX }, | 85 | { OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX }, |
90 | { OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX }, | 86 | { OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX }, |
87 | #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) | ||
88 | { OMAP24XX_DMA_MCBSP3_TX, OMAP24XX_DMA_MCBSP3_RX }, | ||
89 | { OMAP24XX_DMA_MCBSP4_TX, OMAP24XX_DMA_MCBSP4_RX }, | ||
90 | { OMAP24XX_DMA_MCBSP5_TX, OMAP24XX_DMA_MCBSP5_RX }, | ||
91 | #endif | ||
91 | }; | 92 | }; |
93 | #else | ||
94 | static const int omap24xx_dma_reqs[][2] = {}; | ||
95 | #endif | ||
96 | |||
97 | #if defined(CONFIG_ARCH_OMAP2420) | ||
92 | static const unsigned long omap2420_mcbsp_port[][2] = { | 98 | static const unsigned long omap2420_mcbsp_port[][2] = { |
93 | { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1, | 99 | { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1, |
94 | OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 }, | 100 | OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 }, |
@@ -96,10 +102,43 @@ static const unsigned long omap2420_mcbsp_port[][2] = { | |||
96 | OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 }, | 102 | OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 }, |
97 | }; | 103 | }; |
98 | #else | 104 | #else |
99 | static const int omap2420_dma_reqs[][2] = {}; | ||
100 | static const unsigned long omap2420_mcbsp_port[][2] = {}; | 105 | static const unsigned long omap2420_mcbsp_port[][2] = {}; |
101 | #endif | 106 | #endif |
102 | 107 | ||
108 | #if defined(CONFIG_ARCH_OMAP2430) | ||
109 | static const unsigned long omap2430_mcbsp_port[][2] = { | ||
110 | { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, | ||
111 | OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, | ||
112 | { OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR, | ||
113 | OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR }, | ||
114 | { OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DXR, | ||
115 | OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DRR }, | ||
116 | { OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DXR, | ||
117 | OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DRR }, | ||
118 | { OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DXR, | ||
119 | OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DRR }, | ||
120 | }; | ||
121 | #else | ||
122 | static const unsigned long omap2430_mcbsp_port[][2] = {}; | ||
123 | #endif | ||
124 | |||
125 | #if defined(CONFIG_ARCH_OMAP34XX) | ||
126 | static const unsigned long omap34xx_mcbsp_port[][2] = { | ||
127 | { OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, | ||
128 | OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, | ||
129 | { OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR, | ||
130 | OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR }, | ||
131 | { OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DXR, | ||
132 | OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DRR }, | ||
133 | { OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DXR, | ||
134 | OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DRR }, | ||
135 | { OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DXR, | ||
136 | OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DRR }, | ||
137 | }; | ||
138 | #else | ||
139 | static const unsigned long omap34xx_mcbsp_port[][2] = {}; | ||
140 | #endif | ||
141 | |||
103 | static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream) | 142 | static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream) |
104 | { | 143 | { |
105 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 144 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
@@ -167,14 +206,19 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
167 | dma = omap1_dma_reqs[bus_id][substream->stream]; | 206 | dma = omap1_dma_reqs[bus_id][substream->stream]; |
168 | port = omap1_mcbsp_port[bus_id][substream->stream]; | 207 | port = omap1_mcbsp_port[bus_id][substream->stream]; |
169 | } else if (cpu_is_omap2420()) { | 208 | } else if (cpu_is_omap2420()) { |
170 | dma = omap2420_dma_reqs[bus_id][substream->stream]; | 209 | dma = omap24xx_dma_reqs[bus_id][substream->stream]; |
171 | port = omap2420_mcbsp_port[bus_id][substream->stream]; | 210 | port = omap2420_mcbsp_port[bus_id][substream->stream]; |
211 | } else if (cpu_is_omap2430()) { | ||
212 | dma = omap24xx_dma_reqs[bus_id][substream->stream]; | ||
213 | port = omap2430_mcbsp_port[bus_id][substream->stream]; | ||
214 | } else if (cpu_is_omap343x()) { | ||
215 | dma = omap24xx_dma_reqs[bus_id][substream->stream]; | ||
216 | port = omap34xx_mcbsp_port[bus_id][substream->stream]; | ||
172 | } else { | 217 | } else { |
173 | /* | ||
174 | * TODO: Add support for 2430 and 3430 | ||
175 | */ | ||
176 | return -ENODEV; | 218 | return -ENODEV; |
177 | } | 219 | } |
220 | omap_mcbsp_dai_dma_params[id][substream->stream].name = | ||
221 | substream->stream ? "Audio Capture" : "Audio Playback"; | ||
178 | omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; | 222 | omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; |
179 | omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; | 223 | omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; |
180 | cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream]; | 224 | cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream]; |
@@ -245,6 +289,11 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, | |||
245 | regs->rcr2 |= RDATDLY(1); | 289 | regs->rcr2 |= RDATDLY(1); |
246 | regs->xcr2 |= XDATDLY(1); | 290 | regs->xcr2 |= XDATDLY(1); |
247 | break; | 291 | break; |
292 | case SND_SOC_DAIFMT_DSP_A: | ||
293 | /* 0-bit data delay */ | ||
294 | regs->rcr2 |= RDATDLY(0); | ||
295 | regs->xcr2 |= XDATDLY(0); | ||
296 | break; | ||
248 | default: | 297 | default: |
249 | /* Unsupported data format */ | 298 | /* Unsupported data format */ |
250 | return -EINVAL; | 299 | return -EINVAL; |
@@ -310,7 +359,7 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data, | |||
310 | int clk_id) | 359 | int clk_id) |
311 | { | 360 | { |
312 | int sel_bit; | 361 | int sel_bit; |
313 | u16 reg; | 362 | u16 reg, reg_devconf1 = OMAP243X_CONTROL_DEVCONF1; |
314 | 363 | ||
315 | if (cpu_class_is_omap1()) { | 364 | if (cpu_class_is_omap1()) { |
316 | /* OMAP1's can use only external source clock */ | 365 | /* OMAP1's can use only external source clock */ |
@@ -320,6 +369,12 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data, | |||
320 | return 0; | 369 | return 0; |
321 | } | 370 | } |
322 | 371 | ||
372 | if (cpu_is_omap2420() && mcbsp_data->bus_id > 1) | ||
373 | return -EINVAL; | ||
374 | |||
375 | if (cpu_is_omap343x()) | ||
376 | reg_devconf1 = OMAP343X_CONTROL_DEVCONF1; | ||
377 | |||
323 | switch (mcbsp_data->bus_id) { | 378 | switch (mcbsp_data->bus_id) { |
324 | case 0: | 379 | case 0: |
325 | reg = OMAP2_CONTROL_DEVCONF0; | 380 | reg = OMAP2_CONTROL_DEVCONF0; |
@@ -329,20 +384,26 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data, | |||
329 | reg = OMAP2_CONTROL_DEVCONF0; | 384 | reg = OMAP2_CONTROL_DEVCONF0; |
330 | sel_bit = 6; | 385 | sel_bit = 6; |
331 | break; | 386 | break; |
332 | /* TODO: Support for ports 3 - 5 in OMAP2430 and OMAP34xx */ | 387 | case 2: |
388 | reg = reg_devconf1; | ||
389 | sel_bit = 0; | ||
390 | break; | ||
391 | case 3: | ||
392 | reg = reg_devconf1; | ||
393 | sel_bit = 2; | ||
394 | break; | ||
395 | case 4: | ||
396 | reg = reg_devconf1; | ||
397 | sel_bit = 4; | ||
398 | break; | ||
333 | default: | 399 | default: |
334 | return -EINVAL; | 400 | return -EINVAL; |
335 | } | 401 | } |
336 | 402 | ||
337 | if (cpu_class_is_omap2()) { | 403 | if (clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK) |
338 | if (clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK) { | 404 | omap_ctrl_writel(omap_ctrl_readl(reg) & ~(1 << sel_bit), reg); |
339 | omap_ctrl_writel(omap_ctrl_readl(reg) & | 405 | else |
340 | ~(1 << sel_bit), reg); | 406 | omap_ctrl_writel(omap_ctrl_readl(reg) | (1 << sel_bit), reg); |
341 | } else { | ||
342 | omap_ctrl_writel(omap_ctrl_readl(reg) | | ||
343 | (1 << sel_bit), reg); | ||
344 | } | ||
345 | } | ||
346 | 407 | ||
347 | return 0; | 408 | return 0; |
348 | } | 409 | } |
@@ -376,37 +437,49 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | |||
376 | return err; | 437 | return err; |
377 | } | 438 | } |
378 | 439 | ||
379 | struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS] = { | 440 | #define OMAP_MCBSP_DAI_BUILDER(link_id) \ |
380 | { | 441 | { \ |
381 | .name = "omap-mcbsp-dai", | 442 | .name = "omap-mcbsp-dai-(link_id)", \ |
382 | .id = 0, | 443 | .id = (link_id), \ |
383 | .type = SND_SOC_DAI_I2S, | 444 | .type = SND_SOC_DAI_I2S, \ |
384 | .playback = { | 445 | .playback = { \ |
385 | .channels_min = 2, | 446 | .channels_min = 2, \ |
386 | .channels_max = 2, | 447 | .channels_max = 2, \ |
387 | .rates = OMAP_MCBSP_RATES, | 448 | .rates = OMAP_MCBSP_RATES, \ |
388 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | 449 | .formats = SNDRV_PCM_FMTBIT_S16_LE, \ |
389 | }, | 450 | }, \ |
390 | .capture = { | 451 | .capture = { \ |
391 | .channels_min = 2, | 452 | .channels_min = 2, \ |
392 | .channels_max = 2, | 453 | .channels_max = 2, \ |
393 | .rates = OMAP_MCBSP_RATES, | 454 | .rates = OMAP_MCBSP_RATES, \ |
394 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | 455 | .formats = SNDRV_PCM_FMTBIT_S16_LE, \ |
395 | }, | 456 | }, \ |
396 | .ops = { | 457 | .ops = { \ |
397 | .startup = omap_mcbsp_dai_startup, | 458 | .startup = omap_mcbsp_dai_startup, \ |
398 | .shutdown = omap_mcbsp_dai_shutdown, | 459 | .shutdown = omap_mcbsp_dai_shutdown, \ |
399 | .trigger = omap_mcbsp_dai_trigger, | 460 | .trigger = omap_mcbsp_dai_trigger, \ |
400 | .hw_params = omap_mcbsp_dai_hw_params, | 461 | .hw_params = omap_mcbsp_dai_hw_params, \ |
401 | }, | 462 | }, \ |
402 | .dai_ops = { | 463 | .dai_ops = { \ |
403 | .set_fmt = omap_mcbsp_dai_set_dai_fmt, | 464 | .set_fmt = omap_mcbsp_dai_set_dai_fmt, \ |
404 | .set_clkdiv = omap_mcbsp_dai_set_clkdiv, | 465 | .set_clkdiv = omap_mcbsp_dai_set_clkdiv, \ |
405 | .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, | 466 | .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, \ |
406 | }, | 467 | }, \ |
407 | .private_data = &mcbsp_data[0].bus_id, | 468 | .private_data = &mcbsp_data[(link_id)].bus_id, \ |
408 | }, | 469 | } |
470 | |||
471 | struct snd_soc_dai omap_mcbsp_dai[] = { | ||
472 | OMAP_MCBSP_DAI_BUILDER(0), | ||
473 | OMAP_MCBSP_DAI_BUILDER(1), | ||
474 | #if NUM_LINKS >= 3 | ||
475 | OMAP_MCBSP_DAI_BUILDER(2), | ||
476 | #endif | ||
477 | #if NUM_LINKS == 5 | ||
478 | OMAP_MCBSP_DAI_BUILDER(3), | ||
479 | OMAP_MCBSP_DAI_BUILDER(4), | ||
480 | #endif | ||
409 | }; | 481 | }; |
482 | |||
410 | EXPORT_SYMBOL_GPL(omap_mcbsp_dai); | 483 | EXPORT_SYMBOL_GPL(omap_mcbsp_dai); |
411 | 484 | ||
412 | MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>"); | 485 | MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>"); |
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h index ed8afb550671..df7ad13ba73d 100644 --- a/sound/soc/omap/omap-mcbsp.h +++ b/sound/soc/omap/omap-mcbsp.h | |||
@@ -38,11 +38,17 @@ enum omap_mcbsp_div { | |||
38 | OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */ | 38 | OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */ |
39 | }; | 39 | }; |
40 | 40 | ||
41 | /* | 41 | #if defined(CONFIG_ARCH_OMAP2420) |
42 | * REVISIT: Preparation for the ASoC v2. Let the number of available links to | 42 | #define NUM_LINKS 2 |
43 | * be same than number of McBSP ports found in OMAP(s) we are compiling for. | 43 | #endif |
44 | */ | 44 | #if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) |
45 | #define NUM_LINKS 1 | 45 | #undef NUM_LINKS |
46 | #define NUM_LINKS 3 | ||
47 | #endif | ||
48 | #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) | ||
49 | #undef NUM_LINKS | ||
50 | #define NUM_LINKS 5 | ||
51 | #endif | ||
46 | 52 | ||
47 | extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS]; | 53 | extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS]; |
48 | 54 | ||
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 690bfeaec4a0..e9084fdd2082 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c | |||
@@ -97,7 +97,7 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream, | |||
97 | prtd->dma_data = dma_data; | 97 | prtd->dma_data = dma_data; |
98 | err = omap_request_dma(dma_data->dma_req, dma_data->name, | 98 | err = omap_request_dma(dma_data->dma_req, dma_data->name, |
99 | omap_pcm_dma_irq, substream, &prtd->dma_ch); | 99 | omap_pcm_dma_irq, substream, &prtd->dma_ch); |
100 | if (!cpu_is_omap1510()) { | 100 | if (!err & !cpu_is_omap1510()) { |
101 | /* | 101 | /* |
102 | * Link channel with itself so DMA doesn't need any | 102 | * Link channel with itself so DMA doesn't need any |
103 | * reprogramming while looping the buffer | 103 | * reprogramming while looping the buffer |
@@ -147,12 +147,14 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) | |||
147 | dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC; | 147 | dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC; |
148 | dma_params.src_start = runtime->dma_addr; | 148 | dma_params.src_start = runtime->dma_addr; |
149 | dma_params.dst_start = dma_data->port_addr; | 149 | dma_params.dst_start = dma_data->port_addr; |
150 | dma_params.dst_port = OMAP_DMA_PORT_MPUI; | ||
150 | } else { | 151 | } else { |
151 | dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT; | 152 | dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT; |
152 | dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC; | 153 | dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC; |
153 | dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC; | 154 | dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC; |
154 | dma_params.src_start = dma_data->port_addr; | 155 | dma_params.src_start = dma_data->port_addr; |
155 | dma_params.dst_start = runtime->dma_addr; | 156 | dma_params.dst_start = runtime->dma_addr; |
157 | dma_params.src_port = OMAP_DMA_PORT_MPUI; | ||
156 | } | 158 | } |
157 | /* | 159 | /* |
158 | * Set DMA transfer frame size equal to ALSA period size and frame | 160 | * Set DMA transfer frame size equal to ALSA period size and frame |
diff --git a/sound/soc/omap/osk5912.c b/sound/soc/omap/osk5912.c new file mode 100644 index 000000000000..0fe733796898 --- /dev/null +++ b/sound/soc/omap/osk5912.c | |||
@@ -0,0 +1,232 @@ | |||
1 | /* | ||
2 | * osk5912.c -- SoC audio for OSK 5912 | ||
3 | * | ||
4 | * Copyright (C) 2008 Mistral Solutions | ||
5 | * | ||
6 | * Contact: Arun KS <arunks@mistralsolutions.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
20 | * 02110-1301 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <linux/clk.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/pcm.h> | ||
28 | #include <sound/soc.h> | ||
29 | #include <sound/soc-dapm.h> | ||
30 | |||
31 | #include <asm/mach-types.h> | ||
32 | #include <mach/hardware.h> | ||
33 | #include <linux/gpio.h> | ||
34 | #include <mach/mcbsp.h> | ||
35 | |||
36 | #include "omap-mcbsp.h" | ||
37 | #include "omap-pcm.h" | ||
38 | #include "../codecs/tlv320aic23.h" | ||
39 | |||
40 | #define CODEC_CLOCK 12000000 | ||
41 | |||
42 | static struct clk *tlv320aic23_mclk; | ||
43 | |||
44 | static int osk_startup(struct snd_pcm_substream *substream) | ||
45 | { | ||
46 | return clk_enable(tlv320aic23_mclk); | ||
47 | } | ||
48 | |||
49 | static void osk_shutdown(struct snd_pcm_substream *substream) | ||
50 | { | ||
51 | clk_disable(tlv320aic23_mclk); | ||
52 | } | ||
53 | |||
54 | static int osk_hw_params(struct snd_pcm_substream *substream, | ||
55 | struct snd_pcm_hw_params *params) | ||
56 | { | ||
57 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
58 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
59 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
60 | int err; | ||
61 | |||
62 | /* Set codec DAI configuration */ | ||
63 | err = snd_soc_dai_set_fmt(codec_dai, | ||
64 | SND_SOC_DAIFMT_DSP_A | | ||
65 | SND_SOC_DAIFMT_NB_IF | | ||
66 | SND_SOC_DAIFMT_CBM_CFM); | ||
67 | if (err < 0) { | ||
68 | printk(KERN_ERR "can't set codec DAI configuration\n"); | ||
69 | return err; | ||
70 | } | ||
71 | |||
72 | /* Set cpu DAI configuration */ | ||
73 | err = snd_soc_dai_set_fmt(cpu_dai, | ||
74 | SND_SOC_DAIFMT_DSP_A | | ||
75 | SND_SOC_DAIFMT_NB_IF | | ||
76 | SND_SOC_DAIFMT_CBM_CFM); | ||
77 | if (err < 0) { | ||
78 | printk(KERN_ERR "can't set cpu DAI configuration\n"); | ||
79 | return err; | ||
80 | } | ||
81 | |||
82 | /* Set the codec system clock for DAC and ADC */ | ||
83 | err = | ||
84 | snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN); | ||
85 | |||
86 | if (err < 0) { | ||
87 | printk(KERN_ERR "can't set codec system clock\n"); | ||
88 | return err; | ||
89 | } | ||
90 | |||
91 | return err; | ||
92 | } | ||
93 | |||
94 | static struct snd_soc_ops osk_ops = { | ||
95 | .startup = osk_startup, | ||
96 | .hw_params = osk_hw_params, | ||
97 | .shutdown = osk_shutdown, | ||
98 | }; | ||
99 | |||
100 | static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { | ||
101 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | ||
102 | SND_SOC_DAPM_LINE("Line In", NULL), | ||
103 | SND_SOC_DAPM_MIC("Mic Jack", NULL), | ||
104 | }; | ||
105 | |||
106 | static const struct snd_soc_dapm_route audio_map[] = { | ||
107 | {"Headphone Jack", NULL, "LHPOUT"}, | ||
108 | {"Headphone Jack", NULL, "RHPOUT"}, | ||
109 | |||
110 | {"LLINEIN", NULL, "Line In"}, | ||
111 | {"RLINEIN", NULL, "Line In"}, | ||
112 | |||
113 | {"MICIN", NULL, "Mic Jack"}, | ||
114 | }; | ||
115 | |||
116 | static int osk_tlv320aic23_init(struct snd_soc_codec *codec) | ||
117 | { | ||
118 | |||
119 | /* Add osk5912 specific widgets */ | ||
120 | snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, | ||
121 | ARRAY_SIZE(tlv320aic23_dapm_widgets)); | ||
122 | |||
123 | /* Set up osk5912 specific audio path audio_map */ | ||
124 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
125 | |||
126 | snd_soc_dapm_enable_pin(codec, "Headphone Jack"); | ||
127 | snd_soc_dapm_enable_pin(codec, "Line In"); | ||
128 | snd_soc_dapm_enable_pin(codec, "Mic Jack"); | ||
129 | |||
130 | snd_soc_dapm_sync(codec); | ||
131 | |||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | /* Digital audio interface glue - connects codec <--> CPU */ | ||
136 | static struct snd_soc_dai_link osk_dai = { | ||
137 | .name = "TLV320AIC23", | ||
138 | .stream_name = "AIC23", | ||
139 | .cpu_dai = &omap_mcbsp_dai[0], | ||
140 | .codec_dai = &tlv320aic23_dai, | ||
141 | .init = osk_tlv320aic23_init, | ||
142 | .ops = &osk_ops, | ||
143 | }; | ||
144 | |||
145 | /* Audio machine driver */ | ||
146 | static struct snd_soc_machine snd_soc_machine_osk = { | ||
147 | .name = "OSK5912", | ||
148 | .dai_link = &osk_dai, | ||
149 | .num_links = 1, | ||
150 | }; | ||
151 | |||
152 | /* Audio subsystem */ | ||
153 | static struct snd_soc_device osk_snd_devdata = { | ||
154 | .machine = &snd_soc_machine_osk, | ||
155 | .platform = &omap_soc_platform, | ||
156 | .codec_dev = &soc_codec_dev_tlv320aic23, | ||
157 | }; | ||
158 | |||
159 | static struct platform_device *osk_snd_device; | ||
160 | |||
161 | static int __init osk_soc_init(void) | ||
162 | { | ||
163 | int err; | ||
164 | u32 curRate; | ||
165 | struct device *dev; | ||
166 | |||
167 | if (!(machine_is_omap_osk())) | ||
168 | return -ENODEV; | ||
169 | |||
170 | osk_snd_device = platform_device_alloc("soc-audio", -1); | ||
171 | if (!osk_snd_device) | ||
172 | return -ENOMEM; | ||
173 | |||
174 | platform_set_drvdata(osk_snd_device, &osk_snd_devdata); | ||
175 | osk_snd_devdata.dev = &osk_snd_device->dev; | ||
176 | *(unsigned int *)osk_dai.cpu_dai->private_data = 0; /* McBSP1 */ | ||
177 | err = platform_device_add(osk_snd_device); | ||
178 | if (err) | ||
179 | goto err1; | ||
180 | |||
181 | dev = &osk_snd_device->dev; | ||
182 | |||
183 | tlv320aic23_mclk = clk_get(dev, "mclk"); | ||
184 | if (IS_ERR(tlv320aic23_mclk)) { | ||
185 | printk(KERN_ERR "Could not get mclk clock\n"); | ||
186 | return -ENODEV; | ||
187 | } | ||
188 | |||
189 | if (clk_get_usecount(tlv320aic23_mclk) > 0) { | ||
190 | /* MCLK is already in use */ | ||
191 | printk(KERN_WARNING | ||
192 | "MCLK in use at %d Hz. We change it to %d Hz\n", | ||
193 | (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK); | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | * Configure 12 MHz output on MCLK. | ||
198 | */ | ||
199 | curRate = (uint) clk_get_rate(tlv320aic23_mclk); | ||
200 | if (curRate != CODEC_CLOCK) { | ||
201 | if (clk_set_rate(tlv320aic23_mclk, CODEC_CLOCK)) { | ||
202 | printk(KERN_ERR "Cannot set MCLK for AIC23 CODEC\n"); | ||
203 | err = -ECANCELED; | ||
204 | goto err1; | ||
205 | } | ||
206 | } | ||
207 | |||
208 | printk(KERN_INFO "MCLK = %d [%d], usecount = %d\n", | ||
209 | (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK, | ||
210 | clk_get_usecount(tlv320aic23_mclk)); | ||
211 | |||
212 | return 0; | ||
213 | err1: | ||
214 | clk_put(tlv320aic23_mclk); | ||
215 | platform_device_del(osk_snd_device); | ||
216 | platform_device_put(osk_snd_device); | ||
217 | |||
218 | return err; | ||
219 | |||
220 | } | ||
221 | |||
222 | static void __exit osk_soc_exit(void) | ||
223 | { | ||
224 | platform_device_unregister(osk_snd_device); | ||
225 | } | ||
226 | |||
227 | module_init(osk_soc_init); | ||
228 | module_exit(osk_soc_exit); | ||
229 | |||
230 | MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>"); | ||
231 | MODULE_DESCRIPTION("ALSA SoC OSK 5912"); | ||
232 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 9212c37a33b8..f8c1cdd940ac 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig | |||
@@ -1,6 +1,7 @@ | |||
1 | config SND_PXA2XX_SOC | 1 | config SND_PXA2XX_SOC |
2 | tristate "SoC Audio for the Intel PXA2xx chip" | 2 | tristate "SoC Audio for the Intel PXA2xx chip" |
3 | depends on ARCH_PXA | 3 | depends on ARCH_PXA |
4 | select SND_PXA2XX_LIB | ||
4 | help | 5 | help |
5 | Say Y or M if you want to add support for codecs attached to | 6 | Say Y or M if you want to add support for codecs attached to |
6 | the PXA2xx AC97, I2S or SSP interface. You will also need | 7 | the PXA2xx AC97, I2S or SSP interface. You will also need |
@@ -13,6 +14,8 @@ config SND_PXA2XX_AC97 | |||
13 | config SND_PXA2XX_SOC_AC97 | 14 | config SND_PXA2XX_SOC_AC97 |
14 | tristate | 15 | tristate |
15 | select AC97_BUS | 16 | select AC97_BUS |
17 | select SND_ARM | ||
18 | select SND_PXA2XX_LIB_AC97 | ||
16 | select SND_SOC_AC97_BUS | 19 | select SND_SOC_AC97_BUS |
17 | 20 | ||
18 | config SND_PXA2XX_SOC_I2S | 21 | config SND_PXA2XX_SOC_I2S |
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 0a53f72077fd..2718eaf7895f 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c | |||
@@ -4,7 +4,7 @@ | |||
4 | * Copyright 2005 Wolfson Microelectronics PLC. | 4 | * Copyright 2005 Wolfson Microelectronics PLC. |
5 | * Copyright 2005 Openedhand Ltd. | 5 | * Copyright 2005 Openedhand Ltd. |
6 | * | 6 | * |
7 | * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> | 7 | * Authors: Liam Girdwood <lrg@slimlogic.co.uk> |
8 | * Richard Purdie <richard@openedhand.com> | 8 | * Richard Purdie <richard@openedhand.com> |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify it | 10 | * This program is free software; you can redistribute it and/or modify it |
@@ -18,13 +18,13 @@ | |||
18 | #include <linux/timer.h> | 18 | #include <linux/timer.h> |
19 | #include <linux/interrupt.h> | 19 | #include <linux/interrupt.h> |
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/gpio.h> | ||
21 | #include <sound/core.h> | 22 | #include <sound/core.h> |
22 | #include <sound/pcm.h> | 23 | #include <sound/pcm.h> |
23 | #include <sound/soc.h> | 24 | #include <sound/soc.h> |
24 | #include <sound/soc-dapm.h> | 25 | #include <sound/soc-dapm.h> |
25 | 26 | ||
26 | #include <asm/mach-types.h> | 27 | #include <asm/mach-types.h> |
27 | #include <asm/hardware/scoop.h> | ||
28 | #include <mach/pxa-regs.h> | 28 | #include <mach/pxa-regs.h> |
29 | #include <mach/hardware.h> | 29 | #include <mach/hardware.h> |
30 | #include <mach/corgi.h> | 30 | #include <mach/corgi.h> |
@@ -54,8 +54,8 @@ static void corgi_ext_control(struct snd_soc_codec *codec) | |||
54 | switch (corgi_jack_func) { | 54 | switch (corgi_jack_func) { |
55 | case CORGI_HP: | 55 | case CORGI_HP: |
56 | /* set = unmute headphone */ | 56 | /* set = unmute headphone */ |
57 | set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); | 57 | gpio_set_value(CORGI_GPIO_MUTE_L, 1); |
58 | set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); | 58 | gpio_set_value(CORGI_GPIO_MUTE_R, 1); |
59 | snd_soc_dapm_disable_pin(codec, "Mic Jack"); | 59 | snd_soc_dapm_disable_pin(codec, "Mic Jack"); |
60 | snd_soc_dapm_disable_pin(codec, "Line Jack"); | 60 | snd_soc_dapm_disable_pin(codec, "Line Jack"); |
61 | snd_soc_dapm_enable_pin(codec, "Headphone Jack"); | 61 | snd_soc_dapm_enable_pin(codec, "Headphone Jack"); |
@@ -63,24 +63,24 @@ static void corgi_ext_control(struct snd_soc_codec *codec) | |||
63 | break; | 63 | break; |
64 | case CORGI_MIC: | 64 | case CORGI_MIC: |
65 | /* reset = mute headphone */ | 65 | /* reset = mute headphone */ |
66 | reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); | 66 | gpio_set_value(CORGI_GPIO_MUTE_L, 0); |
67 | reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); | 67 | gpio_set_value(CORGI_GPIO_MUTE_R, 0); |
68 | snd_soc_dapm_enable_pin(codec, "Mic Jack"); | 68 | snd_soc_dapm_enable_pin(codec, "Mic Jack"); |
69 | snd_soc_dapm_disable_pin(codec, "Line Jack"); | 69 | snd_soc_dapm_disable_pin(codec, "Line Jack"); |
70 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); | 70 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); |
71 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); | 71 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); |
72 | break; | 72 | break; |
73 | case CORGI_LINE: | 73 | case CORGI_LINE: |
74 | reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); | 74 | gpio_set_value(CORGI_GPIO_MUTE_L, 0); |
75 | reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); | 75 | gpio_set_value(CORGI_GPIO_MUTE_R, 0); |
76 | snd_soc_dapm_disable_pin(codec, "Mic Jack"); | 76 | snd_soc_dapm_disable_pin(codec, "Mic Jack"); |
77 | snd_soc_dapm_enable_pin(codec, "Line Jack"); | 77 | snd_soc_dapm_enable_pin(codec, "Line Jack"); |
78 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); | 78 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); |
79 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); | 79 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); |
80 | break; | 80 | break; |
81 | case CORGI_HEADSET: | 81 | case CORGI_HEADSET: |
82 | reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); | 82 | gpio_set_value(CORGI_GPIO_MUTE_L, 0); |
83 | set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); | 83 | gpio_set_value(CORGI_GPIO_MUTE_R, 1); |
84 | snd_soc_dapm_enable_pin(codec, "Mic Jack"); | 84 | snd_soc_dapm_enable_pin(codec, "Mic Jack"); |
85 | snd_soc_dapm_disable_pin(codec, "Line Jack"); | 85 | snd_soc_dapm_disable_pin(codec, "Line Jack"); |
86 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); | 86 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); |
@@ -114,8 +114,8 @@ static int corgi_shutdown(struct snd_pcm_substream *substream) | |||
114 | struct snd_soc_codec *codec = rtd->socdev->codec; | 114 | struct snd_soc_codec *codec = rtd->socdev->codec; |
115 | 115 | ||
116 | /* set = unmute headphone */ | 116 | /* set = unmute headphone */ |
117 | set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); | 117 | gpio_set_value(CORGI_GPIO_MUTE_L, 1); |
118 | set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); | 118 | gpio_set_value(CORGI_GPIO_MUTE_R, 1); |
119 | return 0; | 119 | return 0; |
120 | } | 120 | } |
121 | 121 | ||
@@ -218,22 +218,14 @@ static int corgi_set_spk(struct snd_kcontrol *kcontrol, | |||
218 | static int corgi_amp_event(struct snd_soc_dapm_widget *w, | 218 | static int corgi_amp_event(struct snd_soc_dapm_widget *w, |
219 | struct snd_kcontrol *k, int event) | 219 | struct snd_kcontrol *k, int event) |
220 | { | 220 | { |
221 | if (SND_SOC_DAPM_EVENT_ON(event)) | 221 | gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event)); |
222 | set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON); | ||
223 | else | ||
224 | reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON); | ||
225 | |||
226 | return 0; | 222 | return 0; |
227 | } | 223 | } |
228 | 224 | ||
229 | static int corgi_mic_event(struct snd_soc_dapm_widget *w, | 225 | static int corgi_mic_event(struct snd_soc_dapm_widget *w, |
230 | struct snd_kcontrol *k, int event) | 226 | struct snd_kcontrol *k, int event) |
231 | { | 227 | { |
232 | if (SND_SOC_DAPM_EVENT_ON(event)) | 228 | gpio_set_value(CORGI_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event)); |
233 | set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS); | ||
234 | else | ||
235 | reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS); | ||
236 | |||
237 | return 0; | 229 | return 0; |
238 | } | 230 | } |
239 | 231 | ||
@@ -289,8 +281,8 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec) | |||
289 | { | 281 | { |
290 | int i, err; | 282 | int i, err; |
291 | 283 | ||
292 | snd_soc_dapm_disable_pin(codec, "LLINEIN"); | 284 | snd_soc_dapm_nc_pin(codec, "LLINEIN"); |
293 | snd_soc_dapm_disable_pin(codec, "RLINEIN"); | 285 | snd_soc_dapm_nc_pin(codec, "RLINEIN"); |
294 | 286 | ||
295 | /* Add corgi specific controls */ | 287 | /* Add corgi specific controls */ |
296 | for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) { | 288 | for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) { |
@@ -330,6 +322,7 @@ static struct snd_soc_machine snd_soc_machine_corgi = { | |||
330 | 322 | ||
331 | /* corgi audio private data */ | 323 | /* corgi audio private data */ |
332 | static struct wm8731_setup_data corgi_wm8731_setup = { | 324 | static struct wm8731_setup_data corgi_wm8731_setup = { |
325 | .i2c_bus = 0, | ||
333 | .i2c_address = 0x1b, | 326 | .i2c_address = 0x1b, |
334 | }; | 327 | }; |
335 | 328 | ||
diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c index d9c3f7b28be2..e6ff6929ab4b 100644 --- a/sound/soc/pxa/em-x270.c +++ b/sound/soc/pxa/em-x270.c | |||
@@ -9,7 +9,7 @@ | |||
9 | * Copyright 2005 Wolfson Microelectronics PLC. | 9 | * Copyright 2005 Wolfson Microelectronics PLC. |
10 | * Copyright 2005 Openedhand Ltd. | 10 | * Copyright 2005 Openedhand Ltd. |
11 | * | 11 | * |
12 | * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> | 12 | * Authors: Liam Girdwood <lrg@slimlogic.co.uk> |
13 | * Richard Purdie <richard@openedhand.com> | 13 | * Richard Purdie <richard@openedhand.com> |
14 | * | 14 | * |
15 | * This program is free software; you can redistribute it and/or modify it | 15 | * This program is free software; you can redistribute it and/or modify it |
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index a4697f7e2921..4d9930c52789 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c | |||
@@ -4,7 +4,7 @@ | |||
4 | * Copyright 2005 Wolfson Microelectronics PLC. | 4 | * Copyright 2005 Wolfson Microelectronics PLC. |
5 | * Copyright 2005 Openedhand Ltd. | 5 | * Copyright 2005 Openedhand Ltd. |
6 | * | 6 | * |
7 | * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> | 7 | * Authors: Liam Girdwood <lrg@slimlogic.co.uk> |
8 | * Richard Purdie <richard@openedhand.com> | 8 | * Richard Purdie <richard@openedhand.com> |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify it | 10 | * This program is free software; you can redistribute it and/or modify it |
@@ -242,8 +242,8 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec) | |||
242 | { | 242 | { |
243 | int i, err; | 243 | int i, err; |
244 | 244 | ||
245 | snd_soc_dapm_disable_pin(codec, "LLINEIN"); | 245 | snd_soc_dapm_nc_pin(codec, "LLINEIN"); |
246 | snd_soc_dapm_disable_pin(codec, "RLINEIN"); | 246 | snd_soc_dapm_nc_pin(codec, "RLINEIN"); |
247 | snd_soc_dapm_enable_pin(codec, "MICIN"); | 247 | snd_soc_dapm_enable_pin(codec, "MICIN"); |
248 | 248 | ||
249 | /* Add poodle specific controls */ | 249 | /* Add poodle specific controls */ |
@@ -284,6 +284,7 @@ static struct snd_soc_machine snd_soc_machine_poodle = { | |||
284 | 284 | ||
285 | /* poodle audio private data */ | 285 | /* poodle audio private data */ |
286 | static struct wm8731_setup_data poodle_wm8731_setup = { | 286 | static struct wm8731_setup_data poodle_wm8731_setup = { |
287 | .i2c_bus = 0, | ||
287 | .i2c_address = 0x1b, | 288 | .i2c_address = 0x1b, |
288 | }; | 289 | }; |
289 | 290 | ||
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index d94a495bd6bd..a7a3a9c5c6ff 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c | |||
@@ -13,225 +13,30 @@ | |||
13 | #include <linux/init.h> | 13 | #include <linux/init.h> |
14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
15 | #include <linux/platform_device.h> | 15 | #include <linux/platform_device.h> |
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/wait.h> | ||
18 | #include <linux/clk.h> | ||
19 | #include <linux/delay.h> | ||
20 | 16 | ||
21 | #include <sound/core.h> | 17 | #include <sound/core.h> |
22 | #include <sound/pcm.h> | ||
23 | #include <sound/ac97_codec.h> | 18 | #include <sound/ac97_codec.h> |
24 | #include <sound/initval.h> | ||
25 | #include <sound/soc.h> | 19 | #include <sound/soc.h> |
20 | #include <sound/pxa2xx-lib.h> | ||
26 | 21 | ||
27 | #include <asm/irq.h> | ||
28 | #include <linux/mutex.h> | ||
29 | #include <mach/hardware.h> | 22 | #include <mach/hardware.h> |
30 | #include <mach/pxa-regs.h> | 23 | #include <mach/pxa-regs.h> |
31 | #include <mach/pxa2xx-gpio.h> | ||
32 | #include <mach/audio.h> | ||
33 | 24 | ||
34 | #include "pxa2xx-pcm.h" | 25 | #include "pxa2xx-pcm.h" |
35 | #include "pxa2xx-ac97.h" | 26 | #include "pxa2xx-ac97.h" |
36 | 27 | ||
37 | static DEFINE_MUTEX(car_mutex); | ||
38 | static DECLARE_WAIT_QUEUE_HEAD(gsr_wq); | ||
39 | static volatile long gsr_bits; | ||
40 | static struct clk *ac97_clk; | ||
41 | #ifdef CONFIG_PXA27x | ||
42 | static struct clk *ac97conf_clk; | ||
43 | #endif | ||
44 | |||
45 | /* | ||
46 | * Beware PXA27x bugs: | ||
47 | * | ||
48 | * o Slot 12 read from modem space will hang controller. | ||
49 | * o CDONE, SDONE interrupt fails after any slot 12 IO. | ||
50 | * | ||
51 | * We therefore have an hybrid approach for waiting on SDONE (interrupt or | ||
52 | * 1 jiffy timeout if interrupt never comes). | ||
53 | */ | ||
54 | |||
55 | static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, | ||
56 | unsigned short reg) | ||
57 | { | ||
58 | unsigned short val = -1; | ||
59 | volatile u32 *reg_addr; | ||
60 | |||
61 | mutex_lock(&car_mutex); | ||
62 | |||
63 | /* set up primary or secondary codec/modem space */ | ||
64 | #if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) | ||
65 | reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; | ||
66 | #else | ||
67 | if (reg == AC97_GPIO_STATUS) | ||
68 | reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; | ||
69 | else | ||
70 | reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; | ||
71 | #endif | ||
72 | reg_addr += (reg >> 1); | ||
73 | |||
74 | #ifndef CONFIG_PXA27x | ||
75 | if (reg == AC97_GPIO_STATUS) { | ||
76 | /* read from controller cache */ | ||
77 | val = *reg_addr; | ||
78 | goto out; | ||
79 | } | ||
80 | #endif | ||
81 | |||
82 | /* start read access across the ac97 link */ | ||
83 | GSR = GSR_CDONE | GSR_SDONE; | ||
84 | gsr_bits = 0; | ||
85 | val = *reg_addr; | ||
86 | |||
87 | wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); | ||
88 | if (!((GSR | gsr_bits) & GSR_SDONE)) { | ||
89 | printk(KERN_ERR "%s: read error (ac97_reg=%x GSR=%#lx)\n", | ||
90 | __func__, reg, GSR | gsr_bits); | ||
91 | val = -1; | ||
92 | goto out; | ||
93 | } | ||
94 | |||
95 | /* valid data now */ | ||
96 | GSR = GSR_CDONE | GSR_SDONE; | ||
97 | gsr_bits = 0; | ||
98 | val = *reg_addr; | ||
99 | /* but we've just started another cycle... */ | ||
100 | wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); | ||
101 | |||
102 | out: mutex_unlock(&car_mutex); | ||
103 | return val; | ||
104 | } | ||
105 | |||
106 | static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, | ||
107 | unsigned short val) | ||
108 | { | ||
109 | volatile u32 *reg_addr; | ||
110 | |||
111 | mutex_lock(&car_mutex); | ||
112 | |||
113 | /* set up primary or secondary codec/modem space */ | ||
114 | #if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) | ||
115 | reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; | ||
116 | #else | ||
117 | if (reg == AC97_GPIO_STATUS) | ||
118 | reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; | ||
119 | else | ||
120 | reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; | ||
121 | #endif | ||
122 | reg_addr += (reg >> 1); | ||
123 | |||
124 | GSR = GSR_CDONE | GSR_SDONE; | ||
125 | gsr_bits = 0; | ||
126 | *reg_addr = val; | ||
127 | wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1); | ||
128 | if (!((GSR | gsr_bits) & GSR_CDONE)) | ||
129 | printk(KERN_ERR "%s: write error (ac97_reg=%x GSR=%#lx)\n", | ||
130 | __func__, reg, GSR | gsr_bits); | ||
131 | |||
132 | mutex_unlock(&car_mutex); | ||
133 | } | ||
134 | |||
135 | static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) | 28 | static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) |
136 | { | 29 | { |
137 | #ifdef CONFIG_PXA3xx | 30 | pxa2xx_ac97_try_warm_reset(ac97); |
138 | int timeout = 100; | ||
139 | #endif | ||
140 | gsr_bits = 0; | ||
141 | |||
142 | #ifdef CONFIG_PXA27x | ||
143 | /* warm reset broken on Bulverde, | ||
144 | so manually keep AC97 reset high */ | ||
145 | pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH); | ||
146 | udelay(10); | ||
147 | GCR |= GCR_WARM_RST; | ||
148 | pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); | ||
149 | udelay(500); | ||
150 | #elif defined(CONFIG_PXA3xx) | ||
151 | /* Can't use interrupts */ | ||
152 | GCR |= GCR_WARM_RST; | ||
153 | while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) | ||
154 | mdelay(1); | ||
155 | #else | ||
156 | GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN; | ||
157 | wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); | ||
158 | #endif | ||
159 | |||
160 | if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) | ||
161 | printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n", | ||
162 | __func__, gsr_bits); | ||
163 | 31 | ||
164 | GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); | 32 | pxa2xx_ac97_finish_reset(ac97); |
165 | GCR |= GCR_SDONE_IE|GCR_CDONE_IE; | ||
166 | } | 33 | } |
167 | 34 | ||
168 | static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) | 35 | static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) |
169 | { | 36 | { |
170 | #ifdef CONFIG_PXA3xx | 37 | pxa2xx_ac97_try_cold_reset(ac97); |
171 | int timeout = 1000; | ||
172 | |||
173 | /* Hold CLKBPB for 100us */ | ||
174 | GCR = 0; | ||
175 | GCR = GCR_CLKBPB; | ||
176 | udelay(100); | ||
177 | GCR = 0; | ||
178 | #endif | ||
179 | 38 | ||
180 | GCR &= GCR_COLD_RST; /* clear everything but nCRST */ | 39 | pxa2xx_ac97_finish_reset(ac97); |
181 | GCR &= ~GCR_COLD_RST; /* then assert nCRST */ | ||
182 | |||
183 | gsr_bits = 0; | ||
184 | #ifdef CONFIG_PXA27x | ||
185 | /* PXA27x Developers Manual section 13.5.2.2.1 */ | ||
186 | clk_enable(ac97conf_clk); | ||
187 | udelay(5); | ||
188 | clk_disable(ac97conf_clk); | ||
189 | GCR = GCR_COLD_RST; | ||
190 | udelay(50); | ||
191 | #elif defined(CONFIG_PXA3xx) | ||
192 | /* Can't use interrupts on PXA3xx */ | ||
193 | GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); | ||
194 | |||
195 | GCR = GCR_WARM_RST | GCR_COLD_RST; | ||
196 | while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--) | ||
197 | mdelay(10); | ||
198 | #else | ||
199 | GCR = GCR_COLD_RST; | ||
200 | GCR |= GCR_CDONE_IE|GCR_SDONE_IE; | ||
201 | wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); | ||
202 | #endif | ||
203 | |||
204 | if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) | ||
205 | printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n", | ||
206 | __func__, gsr_bits); | ||
207 | |||
208 | GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); | ||
209 | GCR |= GCR_SDONE_IE|GCR_CDONE_IE; | ||
210 | } | ||
211 | |||
212 | static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id) | ||
213 | { | ||
214 | long status; | ||
215 | |||
216 | status = GSR; | ||
217 | if (status) { | ||
218 | GSR = status; | ||
219 | gsr_bits |= status; | ||
220 | wake_up(&gsr_wq); | ||
221 | |||
222 | #ifdef CONFIG_PXA27x | ||
223 | /* Although we don't use those we still need to clear them | ||
224 | since they tend to spuriously trigger when MMC is used | ||
225 | (hardware bug? go figure)... */ | ||
226 | MISR = MISR_EOC; | ||
227 | PISR = PISR_EOC; | ||
228 | MCSR = MCSR_EOC; | ||
229 | #endif | ||
230 | |||
231 | return IRQ_HANDLED; | ||
232 | } | ||
233 | |||
234 | return IRQ_NONE; | ||
235 | } | 40 | } |
236 | 41 | ||
237 | struct snd_ac97_bus_ops soc_ac97_ops = { | 42 | struct snd_ac97_bus_ops soc_ac97_ops = { |
@@ -244,7 +49,7 @@ struct snd_ac97_bus_ops soc_ac97_ops = { | |||
244 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = { | 49 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = { |
245 | .name = "AC97 PCM Stereo out", | 50 | .name = "AC97 PCM Stereo out", |
246 | .dev_addr = __PREG(PCDR), | 51 | .dev_addr = __PREG(PCDR), |
247 | .drcmr = &DRCMRTXPCDR, | 52 | .drcmr = &DRCMR(12), |
248 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | | 53 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | |
249 | DCMD_BURST32 | DCMD_WIDTH4, | 54 | DCMD_BURST32 | DCMD_WIDTH4, |
250 | }; | 55 | }; |
@@ -252,7 +57,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = { | |||
252 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = { | 57 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = { |
253 | .name = "AC97 PCM Stereo in", | 58 | .name = "AC97 PCM Stereo in", |
254 | .dev_addr = __PREG(PCDR), | 59 | .dev_addr = __PREG(PCDR), |
255 | .drcmr = &DRCMRRXPCDR, | 60 | .drcmr = &DRCMR(11), |
256 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | 61 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | |
257 | DCMD_BURST32 | DCMD_WIDTH4, | 62 | DCMD_BURST32 | DCMD_WIDTH4, |
258 | }; | 63 | }; |
@@ -260,7 +65,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = { | |||
260 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = { | 65 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = { |
261 | .name = "AC97 Aux PCM (Slot 5) Mono out", | 66 | .name = "AC97 Aux PCM (Slot 5) Mono out", |
262 | .dev_addr = __PREG(MODR), | 67 | .dev_addr = __PREG(MODR), |
263 | .drcmr = &DRCMRTXMODR, | 68 | .drcmr = &DRCMR(10), |
264 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | | 69 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | |
265 | DCMD_BURST16 | DCMD_WIDTH2, | 70 | DCMD_BURST16 | DCMD_WIDTH2, |
266 | }; | 71 | }; |
@@ -268,7 +73,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = { | |||
268 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = { | 73 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = { |
269 | .name = "AC97 Aux PCM (Slot 5) Mono in", | 74 | .name = "AC97 Aux PCM (Slot 5) Mono in", |
270 | .dev_addr = __PREG(MODR), | 75 | .dev_addr = __PREG(MODR), |
271 | .drcmr = &DRCMRRXMODR, | 76 | .drcmr = &DRCMR(9), |
272 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | 77 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | |
273 | DCMD_BURST16 | DCMD_WIDTH2, | 78 | DCMD_BURST16 | DCMD_WIDTH2, |
274 | }; | 79 | }; |
@@ -276,7 +81,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = { | |||
276 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = { | 81 | static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = { |
277 | .name = "AC97 Mic PCM (Slot 6) Mono in", | 82 | .name = "AC97 Mic PCM (Slot 6) Mono in", |
278 | .dev_addr = __PREG(MCDR), | 83 | .dev_addr = __PREG(MCDR), |
279 | .drcmr = &DRCMRRXMCDR, | 84 | .drcmr = &DRCMR(8), |
280 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | 85 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | |
281 | DCMD_BURST16 | DCMD_WIDTH2, | 86 | DCMD_BURST16 | DCMD_WIDTH2, |
282 | }; | 87 | }; |
@@ -285,24 +90,13 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = { | |||
285 | static int pxa2xx_ac97_suspend(struct platform_device *pdev, | 90 | static int pxa2xx_ac97_suspend(struct platform_device *pdev, |
286 | struct snd_soc_dai *dai) | 91 | struct snd_soc_dai *dai) |
287 | { | 92 | { |
288 | GCR |= GCR_ACLINK_OFF; | 93 | return pxa2xx_ac97_hw_suspend(); |
289 | clk_disable(ac97_clk); | ||
290 | return 0; | ||
291 | } | 94 | } |
292 | 95 | ||
293 | static int pxa2xx_ac97_resume(struct platform_device *pdev, | 96 | static int pxa2xx_ac97_resume(struct platform_device *pdev, |
294 | struct snd_soc_dai *dai) | 97 | struct snd_soc_dai *dai) |
295 | { | 98 | { |
296 | pxa_gpio_mode(GPIO31_SYNC_AC97_MD); | 99 | return pxa2xx_ac97_hw_resume(); |
297 | pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); | ||
298 | pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); | ||
299 | pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); | ||
300 | #ifdef CONFIG_PXA27x | ||
301 | /* Use GPIO 113 as AC97 Reset on Bulverde */ | ||
302 | pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); | ||
303 | #endif | ||
304 | clk_enable(ac97_clk); | ||
305 | return 0; | ||
306 | } | 100 | } |
307 | 101 | ||
308 | #else | 102 | #else |
@@ -313,61 +107,13 @@ static int pxa2xx_ac97_resume(struct platform_device *pdev, | |||
313 | static int pxa2xx_ac97_probe(struct platform_device *pdev, | 107 | static int pxa2xx_ac97_probe(struct platform_device *pdev, |
314 | struct snd_soc_dai *dai) | 108 | struct snd_soc_dai *dai) |
315 | { | 109 | { |
316 | int ret; | 110 | return pxa2xx_ac97_hw_probe(pdev); |
317 | |||
318 | ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL); | ||
319 | if (ret < 0) | ||
320 | goto err; | ||
321 | |||
322 | pxa_gpio_mode(GPIO31_SYNC_AC97_MD); | ||
323 | pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); | ||
324 | pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); | ||
325 | pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); | ||
326 | #ifdef CONFIG_PXA27x | ||
327 | /* Use GPIO 113 as AC97 Reset on Bulverde */ | ||
328 | pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); | ||
329 | |||
330 | ac97conf_clk = clk_get(&pdev->dev, "AC97CONFCLK"); | ||
331 | if (IS_ERR(ac97conf_clk)) { | ||
332 | ret = PTR_ERR(ac97conf_clk); | ||
333 | ac97conf_clk = NULL; | ||
334 | goto err_irq; | ||
335 | } | ||
336 | #endif | ||
337 | ac97_clk = clk_get(&pdev->dev, "AC97CLK"); | ||
338 | if (IS_ERR(ac97_clk)) { | ||
339 | ret = PTR_ERR(ac97_clk); | ||
340 | ac97_clk = NULL; | ||
341 | goto err_irq; | ||
342 | } | ||
343 | clk_enable(ac97_clk); | ||
344 | return 0; | ||
345 | |||
346 | err_irq: | ||
347 | GCR |= GCR_ACLINK_OFF; | ||
348 | #ifdef CONFIG_PXA27x | ||
349 | if (ac97conf_clk) { | ||
350 | clk_put(ac97conf_clk); | ||
351 | ac97conf_clk = NULL; | ||
352 | } | ||
353 | #endif | ||
354 | free_irq(IRQ_AC97, NULL); | ||
355 | err: | ||
356 | return ret; | ||
357 | } | 111 | } |
358 | 112 | ||
359 | static void pxa2xx_ac97_remove(struct platform_device *pdev, | 113 | static void pxa2xx_ac97_remove(struct platform_device *pdev, |
360 | struct snd_soc_dai *dai) | 114 | struct snd_soc_dai *dai) |
361 | { | 115 | { |
362 | GCR |= GCR_ACLINK_OFF; | 116 | pxa2xx_ac97_hw_remove(pdev); |
363 | free_irq(IRQ_AC97, NULL); | ||
364 | #ifdef CONFIG_PXA27x | ||
365 | clk_put(ac97conf_clk); | ||
366 | ac97conf_clk = NULL; | ||
367 | #endif | ||
368 | clk_disable(ac97_clk); | ||
369 | clk_put(ac97_clk); | ||
370 | ac97_clk = NULL; | ||
371 | } | 117 | } |
372 | 118 | ||
373 | static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, | 119 | static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, |
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index c796b1882776..e758034db5c3 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Copyright 2005 Wolfson Microelectronics PLC. | 4 | * Copyright 2005 Wolfson Microelectronics PLC. |
5 | * Author: Liam Girdwood | 5 | * Author: Liam Girdwood |
6 | * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com | 6 | * lrg@slimlogic.co.uk |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify it | 8 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the | 9 | * under the terms of the GNU General Public License as published by the |
@@ -21,6 +21,7 @@ | |||
21 | #include <sound/pcm.h> | 21 | #include <sound/pcm.h> |
22 | #include <sound/initval.h> | 22 | #include <sound/initval.h> |
23 | #include <sound/soc.h> | 23 | #include <sound/soc.h> |
24 | #include <sound/pxa2xx-lib.h> | ||
24 | 25 | ||
25 | #include <mach/hardware.h> | 26 | #include <mach/hardware.h> |
26 | #include <mach/pxa-regs.h> | 27 | #include <mach/pxa-regs.h> |
@@ -30,6 +31,54 @@ | |||
30 | #include "pxa2xx-pcm.h" | 31 | #include "pxa2xx-pcm.h" |
31 | #include "pxa2xx-i2s.h" | 32 | #include "pxa2xx-i2s.h" |
32 | 33 | ||
34 | struct pxa2xx_gpio { | ||
35 | u32 sys; | ||
36 | u32 rx; | ||
37 | u32 tx; | ||
38 | u32 clk; | ||
39 | u32 frm; | ||
40 | }; | ||
41 | |||
42 | /* | ||
43 | * I2S Controller Register and Bit Definitions | ||
44 | */ | ||
45 | #define SACR0 __REG(0x40400000) /* Global Control Register */ | ||
46 | #define SACR1 __REG(0x40400004) /* Serial Audio I 2 S/MSB-Justified Control Register */ | ||
47 | #define SASR0 __REG(0x4040000C) /* Serial Audio I 2 S/MSB-Justified Interface and FIFO Status Register */ | ||
48 | #define SAIMR __REG(0x40400014) /* Serial Audio Interrupt Mask Register */ | ||
49 | #define SAICR __REG(0x40400018) /* Serial Audio Interrupt Clear Register */ | ||
50 | #define SADIV __REG(0x40400060) /* Audio Clock Divider Register. */ | ||
51 | #define SADR __REG(0x40400080) /* Serial Audio Data Register (TX and RX FIFO access Register). */ | ||
52 | |||
53 | #define SACR0_RFTH(x) ((x) << 12) /* Rx FIFO Interrupt or DMA Trigger Threshold */ | ||
54 | #define SACR0_TFTH(x) ((x) << 8) /* Tx FIFO Interrupt or DMA Trigger Threshold */ | ||
55 | #define SACR0_STRF (1 << 5) /* FIFO Select for EFWR Special Function */ | ||
56 | #define SACR0_EFWR (1 << 4) /* Enable EFWR Function */ | ||
57 | #define SACR0_RST (1 << 3) /* FIFO, i2s Register Reset */ | ||
58 | #define SACR0_BCKD (1 << 2) /* Bit Clock Direction */ | ||
59 | #define SACR0_ENB (1 << 0) /* Enable I2S Link */ | ||
60 | #define SACR1_ENLBF (1 << 5) /* Enable Loopback */ | ||
61 | #define SACR1_DRPL (1 << 4) /* Disable Replaying Function */ | ||
62 | #define SACR1_DREC (1 << 3) /* Disable Recording Function */ | ||
63 | #define SACR1_AMSL (1 << 0) /* Specify Alternate Mode */ | ||
64 | |||
65 | #define SASR0_I2SOFF (1 << 7) /* Controller Status */ | ||
66 | #define SASR0_ROR (1 << 6) /* Rx FIFO Overrun */ | ||
67 | #define SASR0_TUR (1 << 5) /* Tx FIFO Underrun */ | ||
68 | #define SASR0_RFS (1 << 4) /* Rx FIFO Service Request */ | ||
69 | #define SASR0_TFS (1 << 3) /* Tx FIFO Service Request */ | ||
70 | #define SASR0_BSY (1 << 2) /* I2S Busy */ | ||
71 | #define SASR0_RNE (1 << 1) /* Rx FIFO Not Empty */ | ||
72 | #define SASR0_TNF (1 << 0) /* Tx FIFO Not Empty */ | ||
73 | |||
74 | #define SAICR_ROR (1 << 6) /* Clear Rx FIFO Overrun Interrupt */ | ||
75 | #define SAICR_TUR (1 << 5) /* Clear Tx FIFO Underrun Interrupt */ | ||
76 | |||
77 | #define SAIMR_ROR (1 << 6) /* Enable Rx FIFO Overrun Condition Interrupt */ | ||
78 | #define SAIMR_TUR (1 << 5) /* Enable Tx FIFO Underrun Condition Interrupt */ | ||
79 | #define SAIMR_RFS (1 << 4) /* Enable Rx FIFO Service Interrupt */ | ||
80 | #define SAIMR_TFS (1 << 3) /* Enable Tx FIFO Service Interrupt */ | ||
81 | |||
33 | struct pxa_i2s_port { | 82 | struct pxa_i2s_port { |
34 | u32 sadiv; | 83 | u32 sadiv; |
35 | u32 sacr0; | 84 | u32 sacr0; |
@@ -44,7 +93,7 @@ static struct clk *clk_i2s; | |||
44 | static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { | 93 | static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { |
45 | .name = "I2S PCM Stereo out", | 94 | .name = "I2S PCM Stereo out", |
46 | .dev_addr = __PREG(SADR), | 95 | .dev_addr = __PREG(SADR), |
47 | .drcmr = &DRCMRTXSADR, | 96 | .drcmr = &DRCMR(3), |
48 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | | 97 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | |
49 | DCMD_BURST32 | DCMD_WIDTH4, | 98 | DCMD_BURST32 | DCMD_WIDTH4, |
50 | }; | 99 | }; |
@@ -52,7 +101,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { | |||
52 | static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = { | 101 | static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = { |
53 | .name = "I2S PCM Stereo in", | 102 | .name = "I2S PCM Stereo in", |
54 | .dev_addr = __PREG(SADR), | 103 | .dev_addr = __PREG(SADR), |
55 | .drcmr = &DRCMRRXSADR, | 104 | .drcmr = &DRCMR(2), |
56 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | 105 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | |
57 | DCMD_BURST32 | DCMD_WIDTH4, | 106 | DCMD_BURST32 | DCMD_WIDTH4, |
58 | }; | 107 | }; |
@@ -65,11 +114,6 @@ static struct pxa2xx_gpio gpio_bus[] = { | |||
65 | .frm = GPIO31_SYNC_I2S_MD, | 114 | .frm = GPIO31_SYNC_I2S_MD, |
66 | }, | 115 | }, |
67 | { /* I2S SoC Master */ | 116 | { /* I2S SoC Master */ |
68 | #ifdef CONFIG_PXA27x | ||
69 | .sys = GPIO113_I2S_SYSCLK_MD, | ||
70 | #else | ||
71 | .sys = GPIO32_SYSCLK_I2S_MD, | ||
72 | #endif | ||
73 | .rx = GPIO29_SDATA_IN_I2S_MD, | 117 | .rx = GPIO29_SDATA_IN_I2S_MD, |
74 | .tx = GPIO30_SDATA_OUT_I2S_MD, | 118 | .tx = GPIO30_SDATA_OUT_I2S_MD, |
75 | .clk = GPIO28_BITCLK_OUT_I2S_MD, | 119 | .clk = GPIO28_BITCLK_OUT_I2S_MD, |
@@ -343,6 +387,11 @@ static struct platform_driver pxa2xx_i2s_driver = { | |||
343 | 387 | ||
344 | static int __init pxa2xx_i2s_init(void) | 388 | static int __init pxa2xx_i2s_init(void) |
345 | { | 389 | { |
390 | if (cpu_is_pxa27x()) | ||
391 | gpio_bus[1].sys = GPIO113_I2S_SYSCLK_MD; | ||
392 | else | ||
393 | gpio_bus[1].sys = GPIO32_SYSCLK_I2S_MD; | ||
394 | |||
346 | clk_i2s = ERR_PTR(-ENOENT); | 395 | clk_i2s = ERR_PTR(-ENOENT); |
347 | return platform_driver_register(&pxa2xx_i2s_driver); | 396 | return platform_driver_register(&pxa2xx_i2s_driver); |
348 | } | 397 | } |
@@ -356,6 +405,6 @@ module_init(pxa2xx_i2s_init); | |||
356 | module_exit(pxa2xx_i2s_exit); | 405 | module_exit(pxa2xx_i2s_exit); |
357 | 406 | ||
358 | /* Module information */ | 407 | /* Module information */ |
359 | MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); | 408 | MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); |
360 | MODULE_DESCRIPTION("pxa2xx I2S SoC Interface"); | 409 | MODULE_DESCRIPTION("pxa2xx I2S SoC Interface"); |
361 | MODULE_LICENSE("GPL"); | 410 | MODULE_LICENSE("GPL"); |
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 4345f387fe41..afcd892cd2fa 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c | |||
@@ -10,64 +10,14 @@ | |||
10 | * published by the Free Software Foundation. | 10 | * published by the Free Software Foundation. |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/dma-mapping.h> | 13 | #include <linux/dma-mapping.h> |
18 | 14 | ||
19 | #include <sound/core.h> | 15 | #include <sound/core.h> |
20 | #include <sound/pcm.h> | ||
21 | #include <sound/pcm_params.h> | ||
22 | #include <sound/soc.h> | 16 | #include <sound/soc.h> |
23 | 17 | #include <sound/pxa2xx-lib.h> | |
24 | #include <asm/dma.h> | ||
25 | #include <mach/hardware.h> | ||
26 | #include <mach/pxa-regs.h> | ||
27 | #include <mach/audio.h> | ||
28 | 18 | ||
29 | #include "pxa2xx-pcm.h" | 19 | #include "pxa2xx-pcm.h" |
30 | 20 | #include "../../arm/pxa2xx-pcm.h" | |
31 | static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { | ||
32 | .info = SNDRV_PCM_INFO_MMAP | | ||
33 | SNDRV_PCM_INFO_MMAP_VALID | | ||
34 | SNDRV_PCM_INFO_INTERLEAVED | | ||
35 | SNDRV_PCM_INFO_PAUSE | | ||
36 | SNDRV_PCM_INFO_RESUME, | ||
37 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | ||
38 | SNDRV_PCM_FMTBIT_S24_LE | | ||
39 | SNDRV_PCM_FMTBIT_S32_LE, | ||
40 | .period_bytes_min = 32, | ||
41 | .period_bytes_max = 8192 - 32, | ||
42 | .periods_min = 1, | ||
43 | .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc), | ||
44 | .buffer_bytes_max = 128 * 1024, | ||
45 | .fifo_size = 32, | ||
46 | }; | ||
47 | |||
48 | struct pxa2xx_runtime_data { | ||
49 | int dma_ch; | ||
50 | struct pxa2xx_pcm_dma_params *params; | ||
51 | pxa_dma_desc *dma_desc_array; | ||
52 | dma_addr_t dma_desc_array_phys; | ||
53 | }; | ||
54 | |||
55 | static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) | ||
56 | { | ||
57 | struct snd_pcm_substream *substream = dev_id; | ||
58 | struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; | ||
59 | int dcsr; | ||
60 | |||
61 | dcsr = DCSR(dma_ch); | ||
62 | DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN; | ||
63 | |||
64 | if (dcsr & DCSR_ENDINTR) { | ||
65 | snd_pcm_period_elapsed(substream); | ||
66 | } else { | ||
67 | printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", | ||
68 | prtd->params->name, dma_ch, dcsr); | ||
69 | } | ||
70 | } | ||
71 | 21 | ||
72 | static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, | 22 | static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, |
73 | struct snd_pcm_hw_params *params) | 23 | struct snd_pcm_hw_params *params) |
@@ -76,10 +26,6 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, | |||
76 | struct pxa2xx_runtime_data *prtd = runtime->private_data; | 26 | struct pxa2xx_runtime_data *prtd = runtime->private_data; |
77 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 27 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
78 | struct pxa2xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data; | 28 | struct pxa2xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data; |
79 | size_t totsize = params_buffer_bytes(params); | ||
80 | size_t period = params_period_bytes(params); | ||
81 | pxa_dma_desc *dma_desc; | ||
82 | dma_addr_t dma_buff_phys, next_desc_phys; | ||
83 | int ret; | 29 | int ret; |
84 | 30 | ||
85 | /* return if this is a bufferless transfer e.g. | 31 | /* return if this is a bufferless transfer e.g. |
@@ -106,42 +52,16 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, | |||
106 | prtd->dma_ch = ret; | 52 | prtd->dma_ch = ret; |
107 | } | 53 | } |
108 | 54 | ||
109 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | 55 | return __pxa2xx_pcm_hw_params(substream, params); |
110 | runtime->dma_bytes = totsize; | ||
111 | |||
112 | dma_desc = prtd->dma_desc_array; | ||
113 | next_desc_phys = prtd->dma_desc_array_phys; | ||
114 | dma_buff_phys = runtime->dma_addr; | ||
115 | do { | ||
116 | next_desc_phys += sizeof(pxa_dma_desc); | ||
117 | dma_desc->ddadr = next_desc_phys; | ||
118 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
119 | dma_desc->dsadr = dma_buff_phys; | ||
120 | dma_desc->dtadr = prtd->params->dev_addr; | ||
121 | } else { | ||
122 | dma_desc->dsadr = prtd->params->dev_addr; | ||
123 | dma_desc->dtadr = dma_buff_phys; | ||
124 | } | ||
125 | if (period > totsize) | ||
126 | period = totsize; | ||
127 | dma_desc->dcmd = prtd->params->dcmd | period | DCMD_ENDIRQEN; | ||
128 | dma_desc++; | ||
129 | dma_buff_phys += period; | ||
130 | } while (totsize -= period); | ||
131 | dma_desc[-1].ddadr = prtd->dma_desc_array_phys; | ||
132 | |||
133 | return 0; | ||
134 | } | 56 | } |
135 | 57 | ||
136 | static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) | 58 | static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) |
137 | { | 59 | { |
138 | struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; | 60 | struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; |
139 | 61 | ||
140 | if (prtd && prtd->params) | 62 | __pxa2xx_pcm_hw_free(substream); |
141 | *prtd->params->drcmr = 0; | ||
142 | 63 | ||
143 | if (prtd->dma_ch) { | 64 | if (prtd->dma_ch) { |
144 | snd_pcm_set_runtime_buffer(substream, NULL); | ||
145 | pxa_free_dma(prtd->dma_ch); | 65 | pxa_free_dma(prtd->dma_ch); |
146 | prtd->dma_ch = 0; | 66 | prtd->dma_ch = 0; |
147 | } | 67 | } |
@@ -149,188 +69,21 @@ static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) | |||
149 | return 0; | 69 | return 0; |
150 | } | 70 | } |
151 | 71 | ||
152 | static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) | ||
153 | { | ||
154 | struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; | ||
155 | |||
156 | DCSR(prtd->dma_ch) &= ~DCSR_RUN; | ||
157 | DCSR(prtd->dma_ch) = 0; | ||
158 | DCMD(prtd->dma_ch) = 0; | ||
159 | *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD; | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
165 | { | ||
166 | struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; | ||
167 | int ret = 0; | ||
168 | |||
169 | switch (cmd) { | ||
170 | case SNDRV_PCM_TRIGGER_START: | ||
171 | DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; | ||
172 | DCSR(prtd->dma_ch) = DCSR_RUN; | ||
173 | break; | ||
174 | |||
175 | case SNDRV_PCM_TRIGGER_STOP: | ||
176 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
177 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
178 | DCSR(prtd->dma_ch) &= ~DCSR_RUN; | ||
179 | break; | ||
180 | |||
181 | case SNDRV_PCM_TRIGGER_RESUME: | ||
182 | DCSR(prtd->dma_ch) |= DCSR_RUN; | ||
183 | break; | ||
184 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
185 | DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; | ||
186 | DCSR(prtd->dma_ch) |= DCSR_RUN; | ||
187 | break; | ||
188 | |||
189 | default: | ||
190 | ret = -EINVAL; | ||
191 | } | ||
192 | |||
193 | return ret; | ||
194 | } | ||
195 | |||
196 | static snd_pcm_uframes_t | ||
197 | pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) | ||
198 | { | ||
199 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
200 | struct pxa2xx_runtime_data *prtd = runtime->private_data; | ||
201 | |||
202 | dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? | ||
203 | DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch); | ||
204 | snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr); | ||
205 | |||
206 | if (x == runtime->buffer_size) | ||
207 | x = 0; | ||
208 | return x; | ||
209 | } | ||
210 | |||
211 | static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) | ||
212 | { | ||
213 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
214 | struct pxa2xx_runtime_data *prtd; | ||
215 | int ret; | ||
216 | |||
217 | snd_soc_set_runtime_hwparams(substream, &pxa2xx_pcm_hardware); | ||
218 | |||
219 | /* | ||
220 | * For mysterious reasons (and despite what the manual says) | ||
221 | * playback samples are lost if the DMA count is not a multiple | ||
222 | * of the DMA burst size. Let's add a rule to enforce that. | ||
223 | */ | ||
224 | ret = snd_pcm_hw_constraint_step(runtime, 0, | ||
225 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); | ||
226 | if (ret) | ||
227 | goto out; | ||
228 | |||
229 | ret = snd_pcm_hw_constraint_step(runtime, 0, | ||
230 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); | ||
231 | if (ret) | ||
232 | goto out; | ||
233 | |||
234 | ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); | ||
235 | if (ret < 0) | ||
236 | goto out; | ||
237 | |||
238 | prtd = kzalloc(sizeof(struct pxa2xx_runtime_data), GFP_KERNEL); | ||
239 | if (prtd == NULL) { | ||
240 | ret = -ENOMEM; | ||
241 | goto out; | ||
242 | } | ||
243 | |||
244 | prtd->dma_desc_array = | ||
245 | dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE, | ||
246 | &prtd->dma_desc_array_phys, GFP_KERNEL); | ||
247 | if (!prtd->dma_desc_array) { | ||
248 | ret = -ENOMEM; | ||
249 | goto err1; | ||
250 | } | ||
251 | |||
252 | runtime->private_data = prtd; | ||
253 | return 0; | ||
254 | |||
255 | err1: | ||
256 | kfree(prtd); | ||
257 | out: | ||
258 | return ret; | ||
259 | } | ||
260 | |||
261 | static int pxa2xx_pcm_close(struct snd_pcm_substream *substream) | ||
262 | { | ||
263 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
264 | struct pxa2xx_runtime_data *prtd = runtime->private_data; | ||
265 | |||
266 | dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE, | ||
267 | prtd->dma_desc_array, prtd->dma_desc_array_phys); | ||
268 | kfree(prtd); | ||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, | ||
273 | struct vm_area_struct *vma) | ||
274 | { | ||
275 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
276 | return dma_mmap_writecombine(substream->pcm->card->dev, vma, | ||
277 | runtime->dma_area, | ||
278 | runtime->dma_addr, | ||
279 | runtime->dma_bytes); | ||
280 | } | ||
281 | |||
282 | struct snd_pcm_ops pxa2xx_pcm_ops = { | 72 | struct snd_pcm_ops pxa2xx_pcm_ops = { |
283 | .open = pxa2xx_pcm_open, | 73 | .open = __pxa2xx_pcm_open, |
284 | .close = pxa2xx_pcm_close, | 74 | .close = __pxa2xx_pcm_close, |
285 | .ioctl = snd_pcm_lib_ioctl, | 75 | .ioctl = snd_pcm_lib_ioctl, |
286 | .hw_params = pxa2xx_pcm_hw_params, | 76 | .hw_params = pxa2xx_pcm_hw_params, |
287 | .hw_free = pxa2xx_pcm_hw_free, | 77 | .hw_free = pxa2xx_pcm_hw_free, |
288 | .prepare = pxa2xx_pcm_prepare, | 78 | .prepare = __pxa2xx_pcm_prepare, |
289 | .trigger = pxa2xx_pcm_trigger, | 79 | .trigger = pxa2xx_pcm_trigger, |
290 | .pointer = pxa2xx_pcm_pointer, | 80 | .pointer = pxa2xx_pcm_pointer, |
291 | .mmap = pxa2xx_pcm_mmap, | 81 | .mmap = pxa2xx_pcm_mmap, |
292 | }; | 82 | }; |
293 | 83 | ||
294 | static int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | ||
295 | { | ||
296 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | ||
297 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
298 | size_t size = pxa2xx_pcm_hardware.buffer_bytes_max; | ||
299 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
300 | buf->dev.dev = pcm->card->dev; | ||
301 | buf->private_data = NULL; | ||
302 | buf->area = dma_alloc_writecombine(pcm->card->dev, size, | ||
303 | &buf->addr, GFP_KERNEL); | ||
304 | if (!buf->area) | ||
305 | return -ENOMEM; | ||
306 | buf->bytes = size; | ||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | static void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm) | ||
311 | { | ||
312 | struct snd_pcm_substream *substream; | ||
313 | struct snd_dma_buffer *buf; | ||
314 | int stream; | ||
315 | |||
316 | for (stream = 0; stream < 2; stream++) { | ||
317 | substream = pcm->streams[stream].substream; | ||
318 | if (!substream) | ||
319 | continue; | ||
320 | |||
321 | buf = &substream->dma_buffer; | ||
322 | if (!buf->area) | ||
323 | continue; | ||
324 | |||
325 | dma_free_writecombine(pcm->card->dev, buf->bytes, | ||
326 | buf->area, buf->addr); | ||
327 | buf->area = NULL; | ||
328 | } | ||
329 | } | ||
330 | |||
331 | static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK; | 84 | static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK; |
332 | 85 | ||
333 | int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, | 86 | static int pxa2xx_soc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, |
334 | struct snd_pcm *pcm) | 87 | struct snd_pcm *pcm) |
335 | { | 88 | { |
336 | int ret = 0; | 89 | int ret = 0; |
@@ -360,7 +113,7 @@ int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, | |||
360 | struct snd_soc_platform pxa2xx_soc_platform = { | 113 | struct snd_soc_platform pxa2xx_soc_platform = { |
361 | .name = "pxa2xx-audio", | 114 | .name = "pxa2xx-audio", |
362 | .pcm_ops = &pxa2xx_pcm_ops, | 115 | .pcm_ops = &pxa2xx_pcm_ops, |
363 | .pcm_new = pxa2xx_pcm_new, | 116 | .pcm_new = pxa2xx_soc_pcm_new, |
364 | .pcm_free = pxa2xx_pcm_free_dma_buffers, | 117 | .pcm_free = pxa2xx_pcm_free_dma_buffers, |
365 | }; | 118 | }; |
366 | EXPORT_SYMBOL_GPL(pxa2xx_soc_platform); | 119 | EXPORT_SYMBOL_GPL(pxa2xx_soc_platform); |
diff --git a/sound/soc/pxa/pxa2xx-pcm.h b/sound/soc/pxa/pxa2xx-pcm.h index 54c9c755e508..60c3b20aeeb4 100644 --- a/sound/soc/pxa/pxa2xx-pcm.h +++ b/sound/soc/pxa/pxa2xx-pcm.h | |||
@@ -13,21 +13,6 @@ | |||
13 | #ifndef _PXA2XX_PCM_H | 13 | #ifndef _PXA2XX_PCM_H |
14 | #define _PXA2XX_PCM_H | 14 | #define _PXA2XX_PCM_H |
15 | 15 | ||
16 | struct pxa2xx_pcm_dma_params { | ||
17 | char *name; /* stream identifier */ | ||
18 | u32 dcmd; /* DMA descriptor dcmd field */ | ||
19 | volatile u32 *drcmr; /* the DMA request channel to use */ | ||
20 | u32 dev_addr; /* device physical address for DMA */ | ||
21 | }; | ||
22 | |||
23 | struct pxa2xx_gpio { | ||
24 | u32 sys; | ||
25 | u32 rx; | ||
26 | u32 tx; | ||
27 | u32 clk; | ||
28 | u32 frm; | ||
29 | }; | ||
30 | |||
31 | /* platform data */ | 16 | /* platform data */ |
32 | extern struct snd_soc_platform pxa2xx_soc_platform; | 17 | extern struct snd_soc_platform pxa2xx_soc_platform; |
33 | 18 | ||
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 37cb768fc933..d307b6757e95 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c | |||
@@ -4,7 +4,7 @@ | |||
4 | * Copyright 2005 Wolfson Microelectronics PLC. | 4 | * Copyright 2005 Wolfson Microelectronics PLC. |
5 | * Copyright 2005 Openedhand Ltd. | 5 | * Copyright 2005 Openedhand Ltd. |
6 | * | 6 | * |
7 | * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> | 7 | * Authors: Liam Girdwood <lrg@slimlogic.co.uk> |
8 | * Richard Purdie <richard@openedhand.com> | 8 | * Richard Purdie <richard@openedhand.com> |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify it | 10 | * This program is free software; you can redistribute it and/or modify it |
@@ -19,16 +19,15 @@ | |||
19 | #include <linux/timer.h> | 19 | #include <linux/timer.h> |
20 | #include <linux/interrupt.h> | 20 | #include <linux/interrupt.h> |
21 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
22 | #include <linux/gpio.h> | ||
22 | #include <sound/core.h> | 23 | #include <sound/core.h> |
23 | #include <sound/pcm.h> | 24 | #include <sound/pcm.h> |
24 | #include <sound/soc.h> | 25 | #include <sound/soc.h> |
25 | #include <sound/soc-dapm.h> | 26 | #include <sound/soc-dapm.h> |
26 | 27 | ||
27 | #include <asm/mach-types.h> | 28 | #include <asm/mach-types.h> |
28 | #include <asm/hardware/scoop.h> | ||
29 | #include <mach/pxa-regs.h> | 29 | #include <mach/pxa-regs.h> |
30 | #include <mach/hardware.h> | 30 | #include <mach/hardware.h> |
31 | #include <mach/akita.h> | ||
32 | #include <mach/spitz.h> | 31 | #include <mach/spitz.h> |
33 | #include "../codecs/wm8750.h" | 32 | #include "../codecs/wm8750.h" |
34 | #include "pxa2xx-pcm.h" | 33 | #include "pxa2xx-pcm.h" |
@@ -63,8 +62,8 @@ static void spitz_ext_control(struct snd_soc_codec *codec) | |||
63 | snd_soc_dapm_disable_pin(codec, "Mic Jack"); | 62 | snd_soc_dapm_disable_pin(codec, "Mic Jack"); |
64 | snd_soc_dapm_disable_pin(codec, "Line Jack"); | 63 | snd_soc_dapm_disable_pin(codec, "Line Jack"); |
65 | snd_soc_dapm_enable_pin(codec, "Headphone Jack"); | 64 | snd_soc_dapm_enable_pin(codec, "Headphone Jack"); |
66 | set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | 65 | gpio_set_value(SPITZ_GPIO_MUTE_L, 1); |
67 | set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | 66 | gpio_set_value(SPITZ_GPIO_MUTE_R, 1); |
68 | break; | 67 | break; |
69 | case SPITZ_MIC: | 68 | case SPITZ_MIC: |
70 | /* enable mic jack and bias, mute hp */ | 69 | /* enable mic jack and bias, mute hp */ |
@@ -72,8 +71,8 @@ static void spitz_ext_control(struct snd_soc_codec *codec) | |||
72 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); | 71 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); |
73 | snd_soc_dapm_disable_pin(codec, "Line Jack"); | 72 | snd_soc_dapm_disable_pin(codec, "Line Jack"); |
74 | snd_soc_dapm_enable_pin(codec, "Mic Jack"); | 73 | snd_soc_dapm_enable_pin(codec, "Mic Jack"); |
75 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | 74 | gpio_set_value(SPITZ_GPIO_MUTE_L, 0); |
76 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | 75 | gpio_set_value(SPITZ_GPIO_MUTE_R, 0); |
77 | break; | 76 | break; |
78 | case SPITZ_LINE: | 77 | case SPITZ_LINE: |
79 | /* enable line jack, disable mic bias and mute hp */ | 78 | /* enable line jack, disable mic bias and mute hp */ |
@@ -81,8 +80,8 @@ static void spitz_ext_control(struct snd_soc_codec *codec) | |||
81 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); | 80 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); |
82 | snd_soc_dapm_disable_pin(codec, "Mic Jack"); | 81 | snd_soc_dapm_disable_pin(codec, "Mic Jack"); |
83 | snd_soc_dapm_enable_pin(codec, "Line Jack"); | 82 | snd_soc_dapm_enable_pin(codec, "Line Jack"); |
84 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | 83 | gpio_set_value(SPITZ_GPIO_MUTE_L, 0); |
85 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | 84 | gpio_set_value(SPITZ_GPIO_MUTE_R, 0); |
86 | break; | 85 | break; |
87 | case SPITZ_HEADSET: | 86 | case SPITZ_HEADSET: |
88 | /* enable and unmute headset jack enable mic bias, mute L hp */ | 87 | /* enable and unmute headset jack enable mic bias, mute L hp */ |
@@ -90,8 +89,8 @@ static void spitz_ext_control(struct snd_soc_codec *codec) | |||
90 | snd_soc_dapm_enable_pin(codec, "Mic Jack"); | 89 | snd_soc_dapm_enable_pin(codec, "Mic Jack"); |
91 | snd_soc_dapm_disable_pin(codec, "Line Jack"); | 90 | snd_soc_dapm_disable_pin(codec, "Line Jack"); |
92 | snd_soc_dapm_enable_pin(codec, "Headset Jack"); | 91 | snd_soc_dapm_enable_pin(codec, "Headset Jack"); |
93 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | 92 | gpio_set_value(SPITZ_GPIO_MUTE_L, 0); |
94 | set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | 93 | gpio_set_value(SPITZ_GPIO_MUTE_R, 1); |
95 | break; | 94 | break; |
96 | case SPITZ_HP_OFF: | 95 | case SPITZ_HP_OFF: |
97 | 96 | ||
@@ -100,8 +99,8 @@ static void spitz_ext_control(struct snd_soc_codec *codec) | |||
100 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); | 99 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); |
101 | snd_soc_dapm_disable_pin(codec, "Mic Jack"); | 100 | snd_soc_dapm_disable_pin(codec, "Mic Jack"); |
102 | snd_soc_dapm_disable_pin(codec, "Line Jack"); | 101 | snd_soc_dapm_disable_pin(codec, "Line Jack"); |
103 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); | 102 | gpio_set_value(SPITZ_GPIO_MUTE_L, 0); |
104 | reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); | 103 | gpio_set_value(SPITZ_GPIO_MUTE_R, 0); |
105 | break; | 104 | break; |
106 | } | 105 | } |
107 | snd_soc_dapm_sync(codec); | 106 | snd_soc_dapm_sync(codec); |
@@ -215,23 +214,14 @@ static int spitz_set_spk(struct snd_kcontrol *kcontrol, | |||
215 | static int spitz_mic_bias(struct snd_soc_dapm_widget *w, | 214 | static int spitz_mic_bias(struct snd_soc_dapm_widget *w, |
216 | struct snd_kcontrol *k, int event) | 215 | struct snd_kcontrol *k, int event) |
217 | { | 216 | { |
218 | if (machine_is_borzoi() || machine_is_spitz()) { | 217 | if (machine_is_borzoi() || machine_is_spitz()) |
219 | if (SND_SOC_DAPM_EVENT_ON(event)) | 218 | gpio_set_value(SPITZ_GPIO_MIC_BIAS, |
220 | set_scoop_gpio(&spitzscoop2_device.dev, | 219 | SND_SOC_DAPM_EVENT_ON(event)); |
221 | SPITZ_SCP2_MIC_BIAS); | 220 | |
222 | else | 221 | if (machine_is_akita()) |
223 | reset_scoop_gpio(&spitzscoop2_device.dev, | 222 | gpio_set_value(AKITA_GPIO_MIC_BIAS, |
224 | SPITZ_SCP2_MIC_BIAS); | 223 | SND_SOC_DAPM_EVENT_ON(event)); |
225 | } | ||
226 | 224 | ||
227 | if (machine_is_akita()) { | ||
228 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
229 | akita_set_ioexp(&akitaioexp_device.dev, | ||
230 | AKITA_IOEXP_MIC_BIAS); | ||
231 | else | ||
232 | akita_reset_ioexp(&akitaioexp_device.dev, | ||
233 | AKITA_IOEXP_MIC_BIAS); | ||
234 | } | ||
235 | return 0; | 225 | return 0; |
236 | } | 226 | } |
237 | 227 | ||
@@ -291,13 +281,13 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec) | |||
291 | int i, err; | 281 | int i, err; |
292 | 282 | ||
293 | /* NC codec pins */ | 283 | /* NC codec pins */ |
294 | snd_soc_dapm_disable_pin(codec, "RINPUT1"); | 284 | snd_soc_dapm_nc_pin(codec, "RINPUT1"); |
295 | snd_soc_dapm_disable_pin(codec, "LINPUT2"); | 285 | snd_soc_dapm_nc_pin(codec, "LINPUT2"); |
296 | snd_soc_dapm_disable_pin(codec, "RINPUT2"); | 286 | snd_soc_dapm_nc_pin(codec, "RINPUT2"); |
297 | snd_soc_dapm_disable_pin(codec, "LINPUT3"); | 287 | snd_soc_dapm_nc_pin(codec, "LINPUT3"); |
298 | snd_soc_dapm_disable_pin(codec, "RINPUT3"); | 288 | snd_soc_dapm_nc_pin(codec, "RINPUT3"); |
299 | snd_soc_dapm_disable_pin(codec, "OUT3"); | 289 | snd_soc_dapm_nc_pin(codec, "OUT3"); |
300 | snd_soc_dapm_disable_pin(codec, "MONO1"); | 290 | snd_soc_dapm_nc_pin(codec, "MONO1"); |
301 | 291 | ||
302 | /* Add spitz specific controls */ | 292 | /* Add spitz specific controls */ |
303 | for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) { | 293 | for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) { |
@@ -337,6 +327,7 @@ static struct snd_soc_machine snd_soc_machine_spitz = { | |||
337 | 327 | ||
338 | /* spitz audio private data */ | 328 | /* spitz audio private data */ |
339 | static struct wm8750_setup_data spitz_wm8750_setup = { | 329 | static struct wm8750_setup_data spitz_wm8750_setup = { |
330 | .i2c_bus = 0, | ||
340 | .i2c_address = 0x1b, | 331 | .i2c_address = 0x1b, |
341 | }; | 332 | }; |
342 | 333 | ||
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index 2baaa750f123..afefe41b8c46 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c | |||
@@ -4,7 +4,7 @@ | |||
4 | * Copyright 2005 Wolfson Microelectronics PLC. | 4 | * Copyright 2005 Wolfson Microelectronics PLC. |
5 | * Copyright 2005 Openedhand Ltd. | 5 | * Copyright 2005 Openedhand Ltd. |
6 | * | 6 | * |
7 | * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> | 7 | * Authors: Liam Girdwood <lrg@slimlogic.co.uk> |
8 | * Richard Purdie <richard@openedhand.com> | 8 | * Richard Purdie <richard@openedhand.com> |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify it | 10 | * This program is free software; you can redistribute it and/or modify it |
@@ -190,8 +190,8 @@ static int tosa_ac97_init(struct snd_soc_codec *codec) | |||
190 | { | 190 | { |
191 | int i, err; | 191 | int i, err; |
192 | 192 | ||
193 | snd_soc_dapm_disable_pin(codec, "OUT3"); | 193 | snd_soc_dapm_nc_pin(codec, "OUT3"); |
194 | snd_soc_dapm_disable_pin(codec, "MONOOUT"); | 194 | snd_soc_dapm_nc_pin(codec, "MONOOUT"); |
195 | 195 | ||
196 | /* add tosa specific controls */ | 196 | /* add tosa specific controls */ |
197 | for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) { | 197 | for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) { |
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index 8089f8ee05c0..87ddfefcc2fb 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <sound/soc-dapm.h> | 24 | #include <sound/soc-dapm.h> |
25 | #include <sound/tlv.h> | 25 | #include <sound/tlv.h> |
26 | 26 | ||
27 | #include <asm/mach-types.h> | ||
27 | #include <asm/hardware/scoop.h> | 28 | #include <asm/hardware/scoop.h> |
28 | #include <mach/regs-clock.h> | 29 | #include <mach/regs-clock.h> |
29 | #include <mach/regs-gpio.h> | 30 | #include <mach/regs-gpio.h> |
@@ -510,21 +511,20 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec) | |||
510 | DBG("Entered %s\n", __func__); | 511 | DBG("Entered %s\n", __func__); |
511 | 512 | ||
512 | /* set up NC codec pins */ | 513 | /* set up NC codec pins */ |
513 | snd_soc_dapm_disable_pin(codec, "LOUT2"); | 514 | snd_soc_dapm_nc_pin(codec, "LOUT2"); |
514 | snd_soc_dapm_disable_pin(codec, "ROUT2"); | 515 | snd_soc_dapm_nc_pin(codec, "ROUT2"); |
515 | snd_soc_dapm_disable_pin(codec, "OUT3"); | 516 | snd_soc_dapm_nc_pin(codec, "OUT3"); |
516 | snd_soc_dapm_disable_pin(codec, "OUT4"); | 517 | snd_soc_dapm_nc_pin(codec, "OUT4"); |
517 | snd_soc_dapm_disable_pin(codec, "LINE1"); | 518 | snd_soc_dapm_nc_pin(codec, "LINE1"); |
518 | snd_soc_dapm_disable_pin(codec, "LINE2"); | 519 | snd_soc_dapm_nc_pin(codec, "LINE2"); |
519 | |||
520 | |||
521 | /* set endpoints to default mode */ | ||
522 | set_scenario_endpoints(codec, NEO_AUDIO_OFF); | ||
523 | 520 | ||
524 | /* Add neo1973 specific widgets */ | 521 | /* Add neo1973 specific widgets */ |
525 | snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets, | 522 | snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets, |
526 | ARRAY_SIZE(wm8753_dapm_widgets)); | 523 | ARRAY_SIZE(wm8753_dapm_widgets)); |
527 | 524 | ||
525 | /* set endpoints to default mode */ | ||
526 | set_scenario_endpoints(codec, NEO_AUDIO_OFF); | ||
527 | |||
528 | /* add neo1973 specific controls */ | 528 | /* add neo1973 specific controls */ |
529 | for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) { | 529 | for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) { |
530 | err = snd_ctl_add(codec->card, | 530 | err = snd_ctl_add(codec->card, |
@@ -586,6 +586,7 @@ static struct snd_soc_machine neo1973 = { | |||
586 | }; | 586 | }; |
587 | 587 | ||
588 | static struct wm8753_setup_data neo1973_wm8753_setup = { | 588 | static struct wm8753_setup_data neo1973_wm8753_setup = { |
589 | .i2c_bus = 0, | ||
589 | .i2c_address = 0x1a, | 590 | .i2c_address = 0x1a, |
590 | }; | 591 | }; |
591 | 592 | ||
@@ -596,54 +597,24 @@ static struct snd_soc_device neo1973_snd_devdata = { | |||
596 | .codec_data = &neo1973_wm8753_setup, | 597 | .codec_data = &neo1973_wm8753_setup, |
597 | }; | 598 | }; |
598 | 599 | ||
599 | static struct i2c_client client_template; | 600 | static int lm4857_i2c_probe(struct i2c_client *client, |
600 | 601 | const struct i2c_device_id *id) | |
601 | static const unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END }; | ||
602 | |||
603 | /* Magic definition of all other variables and things */ | ||
604 | I2C_CLIENT_INSMOD; | ||
605 | |||
606 | static int lm4857_amp_probe(struct i2c_adapter *adap, int addr, int kind) | ||
607 | { | 602 | { |
608 | int ret; | ||
609 | |||
610 | DBG("Entered %s\n", __func__); | 603 | DBG("Entered %s\n", __func__); |
611 | 604 | ||
612 | client_template.adapter = adap; | 605 | i2c = client; |
613 | client_template.addr = addr; | ||
614 | |||
615 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | ||
616 | if (i2c == NULL) | ||
617 | return -ENOMEM; | ||
618 | |||
619 | ret = i2c_attach_client(i2c); | ||
620 | if (ret < 0) { | ||
621 | printk(KERN_ERR "LM4857 failed to attach at addr %x\n", addr); | ||
622 | goto exit_err; | ||
623 | } | ||
624 | 606 | ||
625 | lm4857_write_regs(); | 607 | lm4857_write_regs(); |
626 | return ret; | ||
627 | |||
628 | exit_err: | ||
629 | kfree(i2c); | ||
630 | return ret; | ||
631 | } | ||
632 | |||
633 | static int lm4857_i2c_detach(struct i2c_client *client) | ||
634 | { | ||
635 | DBG("Entered %s\n", __func__); | ||
636 | |||
637 | i2c_detach_client(client); | ||
638 | kfree(client); | ||
639 | return 0; | 608 | return 0; |
640 | } | 609 | } |
641 | 610 | ||
642 | static int lm4857_i2c_attach(struct i2c_adapter *adap) | 611 | static int lm4857_i2c_remove(struct i2c_client *client) |
643 | { | 612 | { |
644 | DBG("Entered %s\n", __func__); | 613 | DBG("Entered %s\n", __func__); |
645 | 614 | ||
646 | return i2c_probe(adap, &addr_data, lm4857_amp_probe); | 615 | i2c = NULL; |
616 | |||
617 | return 0; | ||
647 | } | 618 | } |
648 | 619 | ||
649 | static u8 lm4857_state; | 620 | static u8 lm4857_state; |
@@ -681,24 +652,22 @@ static void lm4857_shutdown(struct i2c_client *dev) | |||
681 | lm4857_write_regs(); | 652 | lm4857_write_regs(); |
682 | } | 653 | } |
683 | 654 | ||
684 | /* corgi i2c codec control layer */ | 655 | static const struct i2c_device_id lm4857_i2c_id[] = { |
656 | { "neo1973_lm4857", 0 }, | ||
657 | { } | ||
658 | }; | ||
659 | |||
685 | static struct i2c_driver lm4857_i2c_driver = { | 660 | static struct i2c_driver lm4857_i2c_driver = { |
686 | .driver = { | 661 | .driver = { |
687 | .name = "LM4857 I2C Amp", | 662 | .name = "LM4857 I2C Amp", |
688 | .owner = THIS_MODULE, | 663 | .owner = THIS_MODULE, |
689 | }, | 664 | }, |
690 | .id = I2C_DRIVERID_LM4857, | ||
691 | .suspend = lm4857_suspend, | 665 | .suspend = lm4857_suspend, |
692 | .resume = lm4857_resume, | 666 | .resume = lm4857_resume, |
693 | .shutdown = lm4857_shutdown, | 667 | .shutdown = lm4857_shutdown, |
694 | .attach_adapter = lm4857_i2c_attach, | 668 | .probe = lm4857_i2c_probe, |
695 | .detach_client = lm4857_i2c_detach, | 669 | .remove = lm4857_i2c_remove, |
696 | .command = NULL, | 670 | .id_table = lm4857_i2c_id, |
697 | }; | ||
698 | |||
699 | static struct i2c_client client_template = { | ||
700 | .name = "LM4857", | ||
701 | .driver = &lm4857_i2c_driver, | ||
702 | }; | 671 | }; |
703 | 672 | ||
704 | static struct platform_device *neo1973_snd_device; | 673 | static struct platform_device *neo1973_snd_device; |
@@ -709,6 +678,12 @@ static int __init neo1973_init(void) | |||
709 | 678 | ||
710 | DBG("Entered %s\n", __func__); | 679 | DBG("Entered %s\n", __func__); |
711 | 680 | ||
681 | if (!machine_is_neo1973_gta01()) { | ||
682 | printk(KERN_INFO | ||
683 | "Only GTA01 hardware supported by ASoC driver\n"); | ||
684 | return -ENODEV; | ||
685 | } | ||
686 | |||
712 | neo1973_snd_device = platform_device_alloc("soc-audio", -1); | 687 | neo1973_snd_device = platform_device_alloc("soc-audio", -1); |
713 | if (!neo1973_snd_device) | 688 | if (!neo1973_snd_device) |
714 | return -ENOMEM; | 689 | return -ENOMEM; |
@@ -717,12 +692,15 @@ static int __init neo1973_init(void) | |||
717 | neo1973_snd_devdata.dev = &neo1973_snd_device->dev; | 692 | neo1973_snd_devdata.dev = &neo1973_snd_device->dev; |
718 | ret = platform_device_add(neo1973_snd_device); | 693 | ret = platform_device_add(neo1973_snd_device); |
719 | 694 | ||
720 | if (ret) | 695 | if (ret) { |
721 | platform_device_put(neo1973_snd_device); | 696 | platform_device_put(neo1973_snd_device); |
697 | return ret; | ||
698 | } | ||
722 | 699 | ||
723 | ret = i2c_add_driver(&lm4857_i2c_driver); | 700 | ret = i2c_add_driver(&lm4857_i2c_driver); |
701 | |||
724 | if (ret != 0) | 702 | if (ret != 0) |
725 | printk(KERN_ERR "can't add i2c driver"); | 703 | platform_device_unregister(neo1973_snd_device); |
726 | 704 | ||
727 | return ret; | 705 | return ret; |
728 | } | 706 | } |
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 83f1190293a8..462e635dfc74 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c | |||
@@ -4,8 +4,7 @@ | |||
4 | * Copyright 2005 Wolfson Microelectronics PLC. | 4 | * Copyright 2005 Wolfson Microelectronics PLC. |
5 | * Copyright 2005 Openedhand Ltd. | 5 | * Copyright 2005 Openedhand Ltd. |
6 | * | 6 | * |
7 | * Author: Liam Girdwood | 7 | * Author: Liam Girdwood <lrg@slimlogic.co.uk> |
8 | * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com | ||
9 | * with code, comments and ideas from :- | 8 | * with code, comments and ideas from :- |
10 | * Richard Purdie <richard@openedhand.com> | 9 | * Richard Purdie <richard@openedhand.com> |
11 | * | 10 | * |
@@ -340,6 +339,12 @@ static int soc_codec_close(struct snd_pcm_substream *substream) | |||
340 | } | 339 | } |
341 | codec->active--; | 340 | codec->active--; |
342 | 341 | ||
342 | /* Muting the DAC suppresses artifacts caused during digital | ||
343 | * shutdown, for example from stopping clocks. | ||
344 | */ | ||
345 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
346 | snd_soc_dai_digital_mute(codec_dai, 1); | ||
347 | |||
343 | if (cpu_dai->ops.shutdown) | 348 | if (cpu_dai->ops.shutdown) |
344 | cpu_dai->ops.shutdown(substream); | 349 | cpu_dai->ops.shutdown(substream); |
345 | 350 | ||
@@ -970,9 +975,29 @@ static ssize_t codec_reg_show(struct device *dev, | |||
970 | step = codec->reg_cache_step; | 975 | step = codec->reg_cache_step; |
971 | 976 | ||
972 | count += sprintf(buf, "%s registers\n", codec->name); | 977 | count += sprintf(buf, "%s registers\n", codec->name); |
973 | for (i = 0; i < codec->reg_cache_size; i += step) | 978 | for (i = 0; i < codec->reg_cache_size; i += step) { |
974 | count += sprintf(buf + count, "%2x: %4x\n", i, | 979 | count += sprintf(buf + count, "%2x: ", i); |
975 | codec->read(codec, i)); | 980 | if (count >= PAGE_SIZE - 1) |
981 | break; | ||
982 | |||
983 | if (codec->display_register) | ||
984 | count += codec->display_register(codec, buf + count, | ||
985 | PAGE_SIZE - count, i); | ||
986 | else | ||
987 | count += snprintf(buf + count, PAGE_SIZE - count, | ||
988 | "%4x", codec->read(codec, i)); | ||
989 | |||
990 | if (count >= PAGE_SIZE - 1) | ||
991 | break; | ||
992 | |||
993 | count += snprintf(buf + count, PAGE_SIZE - count, "\n"); | ||
994 | if (count >= PAGE_SIZE - 1) | ||
995 | break; | ||
996 | } | ||
997 | |||
998 | /* Truncate count; min() would cause a warning */ | ||
999 | if (count >= PAGE_SIZE) | ||
1000 | count = PAGE_SIZE - 1; | ||
976 | 1001 | ||
977 | return count; | 1002 | return count; |
978 | } | 1003 | } |
@@ -1296,10 +1321,10 @@ int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, | |||
1296 | 1321 | ||
1297 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | 1322 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; |
1298 | uinfo->count = e->shift_l == e->shift_r ? 1 : 2; | 1323 | uinfo->count = e->shift_l == e->shift_r ? 1 : 2; |
1299 | uinfo->value.enumerated.items = e->mask; | 1324 | uinfo->value.enumerated.items = e->max; |
1300 | 1325 | ||
1301 | if (uinfo->value.enumerated.item > e->mask - 1) | 1326 | if (uinfo->value.enumerated.item > e->max - 1) |
1302 | uinfo->value.enumerated.item = e->mask - 1; | 1327 | uinfo->value.enumerated.item = e->max - 1; |
1303 | strcpy(uinfo->value.enumerated.name, | 1328 | strcpy(uinfo->value.enumerated.name, |
1304 | e->texts[uinfo->value.enumerated.item]); | 1329 | e->texts[uinfo->value.enumerated.item]); |
1305 | return 0; | 1330 | return 0; |
@@ -1322,7 +1347,7 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, | |||
1322 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | 1347 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
1323 | unsigned short val, bitmask; | 1348 | unsigned short val, bitmask; |
1324 | 1349 | ||
1325 | for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) | 1350 | for (bitmask = 1; bitmask < e->max; bitmask <<= 1) |
1326 | ; | 1351 | ; |
1327 | val = snd_soc_read(codec, e->reg); | 1352 | val = snd_soc_read(codec, e->reg); |
1328 | ucontrol->value.enumerated.item[0] | 1353 | ucontrol->value.enumerated.item[0] |
@@ -1352,14 +1377,14 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, | |||
1352 | unsigned short val; | 1377 | unsigned short val; |
1353 | unsigned short mask, bitmask; | 1378 | unsigned short mask, bitmask; |
1354 | 1379 | ||
1355 | for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) | 1380 | for (bitmask = 1; bitmask < e->max; bitmask <<= 1) |
1356 | ; | 1381 | ; |
1357 | if (ucontrol->value.enumerated.item[0] > e->mask - 1) | 1382 | if (ucontrol->value.enumerated.item[0] > e->max - 1) |
1358 | return -EINVAL; | 1383 | return -EINVAL; |
1359 | val = ucontrol->value.enumerated.item[0] << e->shift_l; | 1384 | val = ucontrol->value.enumerated.item[0] << e->shift_l; |
1360 | mask = (bitmask - 1) << e->shift_l; | 1385 | mask = (bitmask - 1) << e->shift_l; |
1361 | if (e->shift_l != e->shift_r) { | 1386 | if (e->shift_l != e->shift_r) { |
1362 | if (ucontrol->value.enumerated.item[1] > e->mask - 1) | 1387 | if (ucontrol->value.enumerated.item[1] > e->max - 1) |
1363 | return -EINVAL; | 1388 | return -EINVAL; |
1364 | val |= ucontrol->value.enumerated.item[1] << e->shift_r; | 1389 | val |= ucontrol->value.enumerated.item[1] << e->shift_r; |
1365 | mask |= (bitmask - 1) << e->shift_r; | 1390 | mask |= (bitmask - 1) << e->shift_r; |
@@ -1386,10 +1411,10 @@ int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, | |||
1386 | 1411 | ||
1387 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | 1412 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; |
1388 | uinfo->count = 1; | 1413 | uinfo->count = 1; |
1389 | uinfo->value.enumerated.items = e->mask; | 1414 | uinfo->value.enumerated.items = e->max; |
1390 | 1415 | ||
1391 | if (uinfo->value.enumerated.item > e->mask - 1) | 1416 | if (uinfo->value.enumerated.item > e->max - 1) |
1392 | uinfo->value.enumerated.item = e->mask - 1; | 1417 | uinfo->value.enumerated.item = e->max - 1; |
1393 | strcpy(uinfo->value.enumerated.name, | 1418 | strcpy(uinfo->value.enumerated.name, |
1394 | e->texts[uinfo->value.enumerated.item]); | 1419 | e->texts[uinfo->value.enumerated.item]); |
1395 | return 0; | 1420 | return 0; |
@@ -1434,9 +1459,11 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext); | |||
1434 | int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, | 1459 | int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, |
1435 | struct snd_ctl_elem_info *uinfo) | 1460 | struct snd_ctl_elem_info *uinfo) |
1436 | { | 1461 | { |
1437 | int max = (kcontrol->private_value >> 16) & 0xff; | 1462 | struct soc_mixer_control *mc = |
1438 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1463 | (struct soc_mixer_control *)kcontrol->private_value; |
1439 | int rshift = (kcontrol->private_value >> 12) & 0x0f; | 1464 | int max = mc->max; |
1465 | unsigned int shift = mc->min; | ||
1466 | unsigned int rshift = mc->rshift; | ||
1440 | 1467 | ||
1441 | if (max == 1) | 1468 | if (max == 1) |
1442 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | 1469 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; |
@@ -1462,13 +1489,15 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw); | |||
1462 | int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, | 1489 | int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, |
1463 | struct snd_ctl_elem_value *ucontrol) | 1490 | struct snd_ctl_elem_value *ucontrol) |
1464 | { | 1491 | { |
1492 | struct soc_mixer_control *mc = | ||
1493 | (struct soc_mixer_control *)kcontrol->private_value; | ||
1465 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 1494 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
1466 | int reg = kcontrol->private_value & 0xff; | 1495 | unsigned int reg = mc->reg; |
1467 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1496 | unsigned int shift = mc->shift; |
1468 | int rshift = (kcontrol->private_value >> 12) & 0x0f; | 1497 | unsigned int rshift = mc->rshift; |
1469 | int max = (kcontrol->private_value >> 16) & 0xff; | 1498 | int max = mc->max; |
1470 | int mask = (1 << fls(max)) - 1; | 1499 | unsigned int mask = (1 << fls(max)) - 1; |
1471 | int invert = (kcontrol->private_value >> 24) & 0x01; | 1500 | unsigned int invert = mc->invert; |
1472 | 1501 | ||
1473 | ucontrol->value.integer.value[0] = | 1502 | ucontrol->value.integer.value[0] = |
1474 | (snd_soc_read(codec, reg) >> shift) & mask; | 1503 | (snd_soc_read(codec, reg) >> shift) & mask; |
@@ -1499,13 +1528,15 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw); | |||
1499 | int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, | 1528 | int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, |
1500 | struct snd_ctl_elem_value *ucontrol) | 1529 | struct snd_ctl_elem_value *ucontrol) |
1501 | { | 1530 | { |
1531 | struct soc_mixer_control *mc = | ||
1532 | (struct soc_mixer_control *)kcontrol->private_value; | ||
1502 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 1533 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
1503 | int reg = kcontrol->private_value & 0xff; | 1534 | unsigned int reg = mc->reg; |
1504 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1535 | unsigned int shift = mc->shift; |
1505 | int rshift = (kcontrol->private_value >> 12) & 0x0f; | 1536 | unsigned int rshift = mc->rshift; |
1506 | int max = (kcontrol->private_value >> 16) & 0xff; | 1537 | int max = mc->max; |
1507 | int mask = (1 << fls(max)) - 1; | 1538 | unsigned int mask = (1 << fls(max)) - 1; |
1508 | int invert = (kcontrol->private_value >> 24) & 0x01; | 1539 | unsigned int invert = mc->invert; |
1509 | unsigned short val, val2, val_mask; | 1540 | unsigned short val, val2, val_mask; |
1510 | 1541 | ||
1511 | val = (ucontrol->value.integer.value[0] & mask); | 1542 | val = (ucontrol->value.integer.value[0] & mask); |
@@ -1537,7 +1568,9 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw); | |||
1537 | int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol, | 1568 | int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol, |
1538 | struct snd_ctl_elem_info *uinfo) | 1569 | struct snd_ctl_elem_info *uinfo) |
1539 | { | 1570 | { |
1540 | int max = (kcontrol->private_value >> 12) & 0xff; | 1571 | struct soc_mixer_control *mc = |
1572 | (struct soc_mixer_control *)kcontrol->private_value; | ||
1573 | int max = mc->max; | ||
1541 | 1574 | ||
1542 | if (max == 1) | 1575 | if (max == 1) |
1543 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | 1576 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; |
@@ -1563,13 +1596,15 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r); | |||
1563 | int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol, | 1596 | int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol, |
1564 | struct snd_ctl_elem_value *ucontrol) | 1597 | struct snd_ctl_elem_value *ucontrol) |
1565 | { | 1598 | { |
1599 | struct soc_mixer_control *mc = | ||
1600 | (struct soc_mixer_control *)kcontrol->private_value; | ||
1566 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 1601 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
1567 | int reg = kcontrol->private_value & 0xff; | 1602 | unsigned int reg = mc->reg; |
1568 | int reg2 = (kcontrol->private_value >> 24) & 0xff; | 1603 | unsigned int reg2 = mc->rreg; |
1569 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1604 | unsigned int shift = mc->shift; |
1570 | int max = (kcontrol->private_value >> 12) & 0xff; | 1605 | int max = mc->max; |
1571 | int mask = (1<<fls(max))-1; | 1606 | unsigned int mask = (1<<fls(max))-1; |
1572 | int invert = (kcontrol->private_value >> 20) & 0x01; | 1607 | unsigned int invert = mc->invert; |
1573 | 1608 | ||
1574 | ucontrol->value.integer.value[0] = | 1609 | ucontrol->value.integer.value[0] = |
1575 | (snd_soc_read(codec, reg) >> shift) & mask; | 1610 | (snd_soc_read(codec, reg) >> shift) & mask; |
@@ -1598,13 +1633,15 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r); | |||
1598 | int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, | 1633 | int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, |
1599 | struct snd_ctl_elem_value *ucontrol) | 1634 | struct snd_ctl_elem_value *ucontrol) |
1600 | { | 1635 | { |
1636 | struct soc_mixer_control *mc = | ||
1637 | (struct soc_mixer_control *)kcontrol->private_value; | ||
1601 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 1638 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
1602 | int reg = kcontrol->private_value & 0xff; | 1639 | unsigned int reg = mc->reg; |
1603 | int reg2 = (kcontrol->private_value >> 24) & 0xff; | 1640 | unsigned int reg2 = mc->rreg; |
1604 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1641 | unsigned int shift = mc->shift; |
1605 | int max = (kcontrol->private_value >> 12) & 0xff; | 1642 | int max = mc->max; |
1606 | int mask = (1 << fls(max)) - 1; | 1643 | unsigned int mask = (1 << fls(max)) - 1; |
1607 | int invert = (kcontrol->private_value >> 20) & 0x01; | 1644 | unsigned int invert = mc->invert; |
1608 | int err; | 1645 | int err; |
1609 | unsigned short val, val2, val_mask; | 1646 | unsigned short val, val2, val_mask; |
1610 | 1647 | ||
@@ -1641,8 +1678,10 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r); | |||
1641 | int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol, | 1678 | int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol, |
1642 | struct snd_ctl_elem_info *uinfo) | 1679 | struct snd_ctl_elem_info *uinfo) |
1643 | { | 1680 | { |
1644 | int max = (signed char)((kcontrol->private_value >> 16) & 0xff); | 1681 | struct soc_mixer_control *mc = |
1645 | int min = (signed char)((kcontrol->private_value >> 24) & 0xff); | 1682 | (struct soc_mixer_control *)kcontrol->private_value; |
1683 | int max = mc->max; | ||
1684 | int min = mc->min; | ||
1646 | 1685 | ||
1647 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 1686 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
1648 | uinfo->count = 2; | 1687 | uinfo->count = 2; |
@@ -1664,9 +1703,11 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8); | |||
1664 | int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol, | 1703 | int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol, |
1665 | struct snd_ctl_elem_value *ucontrol) | 1704 | struct snd_ctl_elem_value *ucontrol) |
1666 | { | 1705 | { |
1706 | struct soc_mixer_control *mc = | ||
1707 | (struct soc_mixer_control *)kcontrol->private_value; | ||
1667 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 1708 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
1668 | int reg = kcontrol->private_value & 0xff; | 1709 | unsigned int reg = mc->reg; |
1669 | int min = (signed char)((kcontrol->private_value >> 24) & 0xff); | 1710 | int min = mc->min; |
1670 | int val = snd_soc_read(codec, reg); | 1711 | int val = snd_soc_read(codec, reg); |
1671 | 1712 | ||
1672 | ucontrol->value.integer.value[0] = | 1713 | ucontrol->value.integer.value[0] = |
@@ -1689,9 +1730,11 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8); | |||
1689 | int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, | 1730 | int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, |
1690 | struct snd_ctl_elem_value *ucontrol) | 1731 | struct snd_ctl_elem_value *ucontrol) |
1691 | { | 1732 | { |
1733 | struct soc_mixer_control *mc = | ||
1734 | (struct soc_mixer_control *)kcontrol->private_value; | ||
1692 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 1735 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
1693 | int reg = kcontrol->private_value & 0xff; | 1736 | unsigned int reg = mc->reg; |
1694 | int min = (signed char)((kcontrol->private_value >> 24) & 0xff); | 1737 | int min = mc->min; |
1695 | unsigned short val; | 1738 | unsigned short val; |
1696 | 1739 | ||
1697 | val = (ucontrol->value.integer.value[0]+min) & 0xff; | 1740 | val = (ucontrol->value.integer.value[0]+min) & 0xff; |
@@ -1842,7 +1885,7 @@ module_init(snd_soc_init); | |||
1842 | module_exit(snd_soc_exit); | 1885 | module_exit(snd_soc_exit); |
1843 | 1886 | ||
1844 | /* Module information */ | 1887 | /* Module information */ |
1845 | MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); | 1888 | MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); |
1846 | MODULE_DESCRIPTION("ALSA SoC Core"); | 1889 | MODULE_DESCRIPTION("ALSA SoC Core"); |
1847 | MODULE_LICENSE("GPL"); | 1890 | MODULE_LICENSE("GPL"); |
1848 | MODULE_ALIAS("platform:soc-audio"); | 1891 | MODULE_ALIAS("platform:soc-audio"); |
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f9d100bc8479..efbd0b37810a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -2,8 +2,7 @@ | |||
2 | * soc-dapm.c -- ALSA SoC Dynamic Audio Power Management | 2 | * soc-dapm.c -- ALSA SoC Dynamic Audio Power Management |
3 | * | 3 | * |
4 | * Copyright 2005 Wolfson Microelectronics PLC. | 4 | * Copyright 2005 Wolfson Microelectronics PLC. |
5 | * Author: Liam Girdwood | 5 | * Author: Liam Girdwood <lrg@slimlogic.co.uk> |
6 | * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com | ||
7 | * | 6 | * |
8 | * This program is free software; you can redistribute it and/or modify it | 7 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the | 8 | * under the terms of the GNU General Public License as published by the |
@@ -38,6 +37,7 @@ | |||
38 | #include <linux/bitops.h> | 37 | #include <linux/bitops.h> |
39 | #include <linux/platform_device.h> | 38 | #include <linux/platform_device.h> |
40 | #include <linux/jiffies.h> | 39 | #include <linux/jiffies.h> |
40 | #include <linux/debugfs.h> | ||
41 | #include <sound/core.h> | 41 | #include <sound/core.h> |
42 | #include <sound/pcm.h> | 42 | #include <sound/pcm.h> |
43 | #include <sound/pcm_params.h> | 43 | #include <sound/pcm_params.h> |
@@ -67,7 +67,9 @@ static int dapm_status = 1; | |||
67 | module_param(dapm_status, int, 0); | 67 | module_param(dapm_status, int, 0); |
68 | MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries"); | 68 | MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries"); |
69 | 69 | ||
70 | static unsigned int pop_time; | 70 | static struct dentry *asoc_debugfs; |
71 | |||
72 | static u32 pop_time; | ||
71 | 73 | ||
72 | static void pop_wait(void) | 74 | static void pop_wait(void) |
73 | { | 75 | { |
@@ -104,10 +106,13 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, | |||
104 | case snd_soc_dapm_switch: | 106 | case snd_soc_dapm_switch: |
105 | case snd_soc_dapm_mixer: { | 107 | case snd_soc_dapm_mixer: { |
106 | int val; | 108 | int val; |
107 | int reg = w->kcontrols[i].private_value & 0xff; | 109 | struct soc_mixer_control *mc = (struct soc_mixer_control *) |
108 | int shift = (w->kcontrols[i].private_value >> 8) & 0x0f; | 110 | w->kcontrols[i].private_value; |
109 | int mask = (w->kcontrols[i].private_value >> 16) & 0xff; | 111 | unsigned int reg = mc->reg; |
110 | int invert = (w->kcontrols[i].private_value >> 24) & 0x01; | 112 | unsigned int shift = mc->shift; |
113 | int max = mc->max; | ||
114 | unsigned int mask = (1 << fls(max)) - 1; | ||
115 | unsigned int invert = mc->invert; | ||
111 | 116 | ||
112 | val = snd_soc_read(w->codec, reg); | 117 | val = snd_soc_read(w->codec, reg); |
113 | val = (val >> shift) & mask; | 118 | val = (val >> shift) & mask; |
@@ -122,13 +127,13 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, | |||
122 | struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value; | 127 | struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value; |
123 | int val, item, bitmask; | 128 | int val, item, bitmask; |
124 | 129 | ||
125 | for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) | 130 | for (bitmask = 1; bitmask < e->max; bitmask <<= 1) |
126 | ; | 131 | ; |
127 | val = snd_soc_read(w->codec, e->reg); | 132 | val = snd_soc_read(w->codec, e->reg); |
128 | item = (val >> e->shift_l) & (bitmask - 1); | 133 | item = (val >> e->shift_l) & (bitmask - 1); |
129 | 134 | ||
130 | p->connect = 0; | 135 | p->connect = 0; |
131 | for (i = 0; i < e->mask; i++) { | 136 | for (i = 0; i < e->max; i++) { |
132 | if (!(strcmp(p->name, e->texts[i])) && item == i) | 137 | if (!(strcmp(p->name, e->texts[i])) && item == i) |
133 | p->connect = 1; | 138 | p->connect = 1; |
134 | } | 139 | } |
@@ -165,7 +170,7 @@ static int dapm_connect_mux(struct snd_soc_codec *codec, | |||
165 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | 170 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
166 | int i; | 171 | int i; |
167 | 172 | ||
168 | for (i = 0; i < e->mask; i++) { | 173 | for (i = 0; i < e->max; i++) { |
169 | if (!(strcmp(control_name, e->texts[i]))) { | 174 | if (!(strcmp(control_name, e->texts[i]))) { |
170 | list_add(&path->list, &codec->dapm_paths); | 175 | list_add(&path->list, &codec->dapm_paths); |
171 | list_add(&path->list_sink, &dest->sources); | 176 | list_add(&path->list_sink, &dest->sources); |
@@ -247,16 +252,19 @@ static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power) | |||
247 | return 0; | 252 | return 0; |
248 | 253 | ||
249 | if (widget->num_kcontrols && k) { | 254 | if (widget->num_kcontrols && k) { |
250 | int reg = k->private_value & 0xff; | 255 | struct soc_mixer_control *mc = |
251 | int shift = (k->private_value >> 8) & 0x0f; | 256 | (struct soc_mixer_control *)k->private_value; |
252 | int mask = (k->private_value >> 16) & 0xff; | 257 | unsigned int reg = mc->reg; |
253 | int invert = (k->private_value >> 24) & 0x01; | 258 | unsigned int shift = mc->shift; |
259 | int max = mc->max; | ||
260 | unsigned int mask = (1 << fls(max)) - 1; | ||
261 | unsigned int invert = mc->invert; | ||
254 | 262 | ||
255 | if (power) { | 263 | if (power) { |
256 | int i; | 264 | int i; |
257 | /* power up has happended, increase volume to last level */ | 265 | /* power up has happended, increase volume to last level */ |
258 | if (invert) { | 266 | if (invert) { |
259 | for (i = mask; i > widget->saved_value; i--) | 267 | for (i = max; i > widget->saved_value; i--) |
260 | snd_soc_update_bits(widget->codec, reg, mask, i); | 268 | snd_soc_update_bits(widget->codec, reg, mask, i); |
261 | } else { | 269 | } else { |
262 | for (i = 0; i < widget->saved_value; i++) | 270 | for (i = 0; i < widget->saved_value; i++) |
@@ -684,7 +692,7 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action) | |||
684 | /* test and update the power status of a mux widget */ | 692 | /* test and update the power status of a mux widget */ |
685 | static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, | 693 | static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, |
686 | struct snd_kcontrol *kcontrol, int mask, | 694 | struct snd_kcontrol *kcontrol, int mask, |
687 | int val, struct soc_enum* e) | 695 | int mux, int val, struct soc_enum *e) |
688 | { | 696 | { |
689 | struct snd_soc_dapm_path *path; | 697 | struct snd_soc_dapm_path *path; |
690 | int found = 0; | 698 | int found = 0; |
@@ -700,12 +708,12 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, | |||
700 | if (path->kcontrol != kcontrol) | 708 | if (path->kcontrol != kcontrol) |
701 | continue; | 709 | continue; |
702 | 710 | ||
703 | if (!path->name || ! e->texts[val]) | 711 | if (!path->name || !e->texts[mux]) |
704 | continue; | 712 | continue; |
705 | 713 | ||
706 | found = 1; | 714 | found = 1; |
707 | /* we now need to match the string in the enum to the path */ | 715 | /* we now need to match the string in the enum to the path */ |
708 | if (!(strcmp(path->name, e->texts[val]))) | 716 | if (!(strcmp(path->name, e->texts[mux]))) |
709 | path->connect = 1; /* new connection */ | 717 | path->connect = 1; /* new connection */ |
710 | else | 718 | else |
711 | path->connect = 0; /* old connection must be powered down */ | 719 | path->connect = 0; /* old connection must be powered down */ |
@@ -811,51 +819,35 @@ static ssize_t dapm_widget_show(struct device *dev, | |||
811 | 819 | ||
812 | static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL); | 820 | static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL); |
813 | 821 | ||
814 | /* pop/click delay times */ | ||
815 | static ssize_t dapm_pop_time_show(struct device *dev, | ||
816 | struct device_attribute *attr, char *buf) | ||
817 | { | ||
818 | return sprintf(buf, "%d\n", pop_time); | ||
819 | } | ||
820 | |||
821 | static ssize_t dapm_pop_time_store(struct device *dev, | ||
822 | struct device_attribute *attr, | ||
823 | const char *buf, size_t count) | ||
824 | |||
825 | { | ||
826 | unsigned long val; | ||
827 | |||
828 | if (strict_strtoul(buf, 10, &val) >= 0) | ||
829 | pop_time = val; | ||
830 | else | ||
831 | printk(KERN_ERR "Unable to parse pop_time setting\n"); | ||
832 | |||
833 | return count; | ||
834 | } | ||
835 | |||
836 | static DEVICE_ATTR(dapm_pop_time, 0744, dapm_pop_time_show, | ||
837 | dapm_pop_time_store); | ||
838 | |||
839 | int snd_soc_dapm_sys_add(struct device *dev) | 822 | int snd_soc_dapm_sys_add(struct device *dev) |
840 | { | 823 | { |
841 | int ret = 0; | 824 | int ret = 0; |
842 | 825 | ||
843 | if (dapm_status) { | 826 | if (!dapm_status) |
844 | ret = device_create_file(dev, &dev_attr_dapm_widget); | 827 | return 0; |
845 | 828 | ||
846 | if (ret == 0) | 829 | ret = device_create_file(dev, &dev_attr_dapm_widget); |
847 | ret = device_create_file(dev, &dev_attr_dapm_pop_time); | 830 | if (ret != 0) |
848 | } | 831 | return ret; |
849 | 832 | ||
850 | return ret; | 833 | asoc_debugfs = debugfs_create_dir("asoc", NULL); |
834 | if (!IS_ERR(asoc_debugfs)) | ||
835 | debugfs_create_u32("dapm_pop_time", 0744, asoc_debugfs, | ||
836 | &pop_time); | ||
837 | else | ||
838 | asoc_debugfs = NULL; | ||
839 | |||
840 | return 0; | ||
851 | } | 841 | } |
852 | 842 | ||
853 | static void snd_soc_dapm_sys_remove(struct device *dev) | 843 | static void snd_soc_dapm_sys_remove(struct device *dev) |
854 | { | 844 | { |
855 | if (dapm_status) { | 845 | if (dapm_status) { |
856 | device_remove_file(dev, &dev_attr_dapm_pop_time); | ||
857 | device_remove_file(dev, &dev_attr_dapm_widget); | 846 | device_remove_file(dev, &dev_attr_dapm_widget); |
858 | } | 847 | } |
848 | |||
849 | if (asoc_debugfs) | ||
850 | debugfs_remove_recursive(asoc_debugfs); | ||
859 | } | 851 | } |
860 | 852 | ||
861 | /* free all dapm widgets and resources */ | 853 | /* free all dapm widgets and resources */ |
@@ -1133,12 +1125,14 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, | |||
1133 | struct snd_ctl_elem_value *ucontrol) | 1125 | struct snd_ctl_elem_value *ucontrol) |
1134 | { | 1126 | { |
1135 | struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); | 1127 | struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); |
1136 | int reg = kcontrol->private_value & 0xff; | 1128 | struct soc_mixer_control *mc = |
1137 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1129 | (struct soc_mixer_control *)kcontrol->private_value; |
1138 | int rshift = (kcontrol->private_value >> 12) & 0x0f; | 1130 | unsigned int reg = mc->reg; |
1139 | int max = (kcontrol->private_value >> 16) & 0xff; | 1131 | unsigned int shift = mc->shift; |
1140 | int invert = (kcontrol->private_value >> 24) & 0x01; | 1132 | unsigned int rshift = mc->rshift; |
1141 | int mask = (1 << fls(max)) - 1; | 1133 | int max = mc->max; |
1134 | unsigned int invert = mc->invert; | ||
1135 | unsigned int mask = (1 << fls(max)) - 1; | ||
1142 | 1136 | ||
1143 | /* return the saved value if we are powered down */ | 1137 | /* return the saved value if we are powered down */ |
1144 | if (widget->id == snd_soc_dapm_pga && !widget->power) { | 1138 | if (widget->id == snd_soc_dapm_pga && !widget->power) { |
@@ -1176,12 +1170,14 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, | |||
1176 | struct snd_ctl_elem_value *ucontrol) | 1170 | struct snd_ctl_elem_value *ucontrol) |
1177 | { | 1171 | { |
1178 | struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); | 1172 | struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); |
1179 | int reg = kcontrol->private_value & 0xff; | 1173 | struct soc_mixer_control *mc = |
1180 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1174 | (struct soc_mixer_control *)kcontrol->private_value; |
1181 | int rshift = (kcontrol->private_value >> 12) & 0x0f; | 1175 | unsigned int reg = mc->reg; |
1182 | int max = (kcontrol->private_value >> 16) & 0xff; | 1176 | unsigned int shift = mc->shift; |
1183 | int mask = (1 << fls(max)) - 1; | 1177 | unsigned int rshift = mc->rshift; |
1184 | int invert = (kcontrol->private_value >> 24) & 0x01; | 1178 | int max = mc->max; |
1179 | unsigned int mask = (1 << fls(max)) - 1; | ||
1180 | unsigned int invert = mc->invert; | ||
1185 | unsigned short val, val2, val_mask; | 1181 | unsigned short val, val2, val_mask; |
1186 | int ret; | 1182 | int ret; |
1187 | 1183 | ||
@@ -1248,7 +1244,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, | |||
1248 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | 1244 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
1249 | unsigned short val, bitmask; | 1245 | unsigned short val, bitmask; |
1250 | 1246 | ||
1251 | for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) | 1247 | for (bitmask = 1; bitmask < e->max; bitmask <<= 1) |
1252 | ; | 1248 | ; |
1253 | val = snd_soc_read(widget->codec, e->reg); | 1249 | val = snd_soc_read(widget->codec, e->reg); |
1254 | ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1); | 1250 | ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1); |
@@ -1278,15 +1274,15 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, | |||
1278 | unsigned short mask, bitmask; | 1274 | unsigned short mask, bitmask; |
1279 | int ret = 0; | 1275 | int ret = 0; |
1280 | 1276 | ||
1281 | for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) | 1277 | for (bitmask = 1; bitmask < e->max; bitmask <<= 1) |
1282 | ; | 1278 | ; |
1283 | if (ucontrol->value.enumerated.item[0] > e->mask - 1) | 1279 | if (ucontrol->value.enumerated.item[0] > e->max - 1) |
1284 | return -EINVAL; | 1280 | return -EINVAL; |
1285 | mux = ucontrol->value.enumerated.item[0]; | 1281 | mux = ucontrol->value.enumerated.item[0]; |
1286 | val = mux << e->shift_l; | 1282 | val = mux << e->shift_l; |
1287 | mask = (bitmask - 1) << e->shift_l; | 1283 | mask = (bitmask - 1) << e->shift_l; |
1288 | if (e->shift_l != e->shift_r) { | 1284 | if (e->shift_l != e->shift_r) { |
1289 | if (ucontrol->value.enumerated.item[1] > e->mask - 1) | 1285 | if (ucontrol->value.enumerated.item[1] > e->max - 1) |
1290 | return -EINVAL; | 1286 | return -EINVAL; |
1291 | val |= ucontrol->value.enumerated.item[1] << e->shift_r; | 1287 | val |= ucontrol->value.enumerated.item[1] << e->shift_r; |
1292 | mask |= (bitmask - 1) << e->shift_r; | 1288 | mask |= (bitmask - 1) << e->shift_r; |
@@ -1294,7 +1290,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, | |||
1294 | 1290 | ||
1295 | mutex_lock(&widget->codec->mutex); | 1291 | mutex_lock(&widget->codec->mutex); |
1296 | widget->value = val; | 1292 | widget->value = val; |
1297 | dapm_mux_update_power(widget, kcontrol, mask, mux, e); | 1293 | dapm_mux_update_power(widget, kcontrol, mask, mux, val, e); |
1298 | if (widget->event) { | 1294 | if (widget->event) { |
1299 | if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { | 1295 | if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { |
1300 | ret = widget->event(widget, | 1296 | ret = widget->event(widget, |
@@ -1487,6 +1483,26 @@ int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin) | |||
1487 | EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); | 1483 | EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); |
1488 | 1484 | ||
1489 | /** | 1485 | /** |
1486 | * snd_soc_dapm_nc_pin - permanently disable pin. | ||
1487 | * @codec: SoC codec | ||
1488 | * @pin: pin name | ||
1489 | * | ||
1490 | * Marks the specified pin as being not connected, disabling it along | ||
1491 | * any parent or child widgets. At present this is identical to | ||
1492 | * snd_soc_dapm_disable_pin() but in future it will be extended to do | ||
1493 | * additional things such as disabling controls which only affect | ||
1494 | * paths through the pin. | ||
1495 | * | ||
1496 | * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to | ||
1497 | * do any widget power switching. | ||
1498 | */ | ||
1499 | int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, char *pin) | ||
1500 | { | ||
1501 | return snd_soc_dapm_set_pin(codec, pin, 0); | ||
1502 | } | ||
1503 | EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin); | ||
1504 | |||
1505 | /** | ||
1490 | * snd_soc_dapm_get_pin_status - get audio pin status | 1506 | * snd_soc_dapm_get_pin_status - get audio pin status |
1491 | * @codec: audio codec | 1507 | * @codec: audio codec |
1492 | * @pin: audio signal pin endpoint (or start point) | 1508 | * @pin: audio signal pin endpoint (or start point) |
@@ -1524,6 +1540,6 @@ void snd_soc_dapm_free(struct snd_soc_device *socdev) | |||
1524 | EXPORT_SYMBOL_GPL(snd_soc_dapm_free); | 1540 | EXPORT_SYMBOL_GPL(snd_soc_dapm_free); |
1525 | 1541 | ||
1526 | /* Module information */ | 1542 | /* Module information */ |
1527 | MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); | 1543 | MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); |
1528 | MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC"); | 1544 | MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC"); |
1529 | MODULE_LICENSE("GPL"); | 1545 | MODULE_LICENSE("GPL"); |