Commit 9745e824a81e54ef009500e120e35b68e387b941
1 parent
1593d7dd8c
Exists in
master
and in
7 other branches
ASoC: Support non-crystal master clocks for WM8731
Instead of unconditionally enabling the crystal oscillator on the WM8731 only enable it when explicitly selected via set_sysclk(), allowing machine drivers to specify that they drive a clock into MCLK alone. This avoids any conflicts between the oscillator and the external MCLK source and saves power for systems which do not need the oscillator. This should also deliver a small power saving on systems using the crystal since the oscillator will only be enabled when the ADC or DAC is active. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Showing 6 changed files with 36 additions and 7 deletions Inline Diff
sound/soc/atmel/sam9g20_wm8731.c
1 | /* | 1 | /* |
2 | * sam9g20_wm8731 -- SoC audio for AT91SAM9G20-based | 2 | * sam9g20_wm8731 -- SoC audio for AT91SAM9G20-based |
3 | * ATMEL AT91SAM9G20ek board. | 3 | * ATMEL AT91SAM9G20ek board. |
4 | * | 4 | * |
5 | * Copyright (C) 2005 SAN People | 5 | * Copyright (C) 2005 SAN People |
6 | * Copyright (C) 2008 Atmel | 6 | * Copyright (C) 2008 Atmel |
7 | * | 7 | * |
8 | * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com> | 8 | * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com> |
9 | * | 9 | * |
10 | * Based on ati_b1_wm8731.c by: | 10 | * Based on ati_b1_wm8731.c by: |
11 | * Frank Mandarino <fmandarino@endrelia.com> | 11 | * Frank Mandarino <fmandarino@endrelia.com> |
12 | * Copyright 2006 Endrelia Technologies Inc. | 12 | * Copyright 2006 Endrelia Technologies Inc. |
13 | * Based on corgi.c by: | 13 | * Based on corgi.c by: |
14 | * Copyright 2005 Wolfson Microelectronics PLC. | 14 | * Copyright 2005 Wolfson Microelectronics PLC. |
15 | * Copyright 2005 Openedhand Ltd. | 15 | * Copyright 2005 Openedhand Ltd. |
16 | * | 16 | * |
17 | * This program is free software; you can redistribute it and/or modify | 17 | * This program is free software; you can redistribute it and/or modify |
18 | * it under the terms of the GNU General Public License as published by | 18 | * it under the terms of the GNU General Public License as published by |
19 | * the Free Software Foundation; either version 2 of the License, or | 19 | * the Free Software Foundation; either version 2 of the License, or |
20 | * (at your option) any later version. | 20 | * (at your option) any later version. |
21 | * | 21 | * |
22 | * This program is distributed in the hope that it will be useful, | 22 | * This program is distributed in the hope that it will be useful, |
23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
25 | * GNU General Public License for more details. | 25 | * GNU General Public License for more details. |
26 | * | 26 | * |
27 | * You should have received a copy of the GNU General Public License | 27 | * You should have received a copy of the GNU General Public License |
28 | * along with this program; if not, write to the Free Software | 28 | * along with this program; if not, write to the Free Software |
29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
30 | */ | 30 | */ |
31 | 31 | ||
32 | #include <linux/module.h> | 32 | #include <linux/module.h> |
33 | #include <linux/moduleparam.h> | 33 | #include <linux/moduleparam.h> |
34 | #include <linux/kernel.h> | 34 | #include <linux/kernel.h> |
35 | #include <linux/clk.h> | 35 | #include <linux/clk.h> |
36 | #include <linux/timer.h> | 36 | #include <linux/timer.h> |
37 | #include <linux/interrupt.h> | 37 | #include <linux/interrupt.h> |
38 | #include <linux/platform_device.h> | 38 | #include <linux/platform_device.h> |
39 | #include <linux/i2c.h> | 39 | #include <linux/i2c.h> |
40 | 40 | ||
41 | #include <linux/atmel-ssc.h> | 41 | #include <linux/atmel-ssc.h> |
42 | 42 | ||
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> |
46 | #include <sound/soc.h> | 46 | #include <sound/soc.h> |
47 | #include <sound/soc-dapm.h> | 47 | #include <sound/soc-dapm.h> |
48 | 48 | ||
49 | #include <asm/mach-types.h> | 49 | #include <asm/mach-types.h> |
50 | #include <mach/hardware.h> | 50 | #include <mach/hardware.h> |
51 | #include <mach/gpio.h> | 51 | #include <mach/gpio.h> |
52 | 52 | ||
53 | #include "../codecs/wm8731.h" | 53 | #include "../codecs/wm8731.h" |
54 | #include "atmel-pcm.h" | 54 | #include "atmel-pcm.h" |
55 | #include "atmel_ssc_dai.h" | 55 | #include "atmel_ssc_dai.h" |
56 | 56 | ||
57 | #define MCLK_RATE 12000000 | 57 | #define MCLK_RATE 12000000 |
58 | 58 | ||
59 | /* | 59 | /* |
60 | * As shipped the board does not have inputs. However, it is relatively | 60 | * As shipped the board does not have inputs. However, it is relatively |
61 | * straightforward to modify the board to hook them up so support is left | 61 | * straightforward to modify the board to hook them up so support is left |
62 | * in the driver. | 62 | * in the driver. |
63 | */ | 63 | */ |
64 | #undef ENABLE_MIC_INPUT | 64 | #undef ENABLE_MIC_INPUT |
65 | 65 | ||
66 | static struct clk *mclk; | 66 | static struct clk *mclk; |
67 | 67 | ||
68 | static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream, | 68 | static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream, |
69 | struct snd_pcm_hw_params *params) | 69 | struct snd_pcm_hw_params *params) |
70 | { | 70 | { |
71 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 71 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
72 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | 72 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
73 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | 73 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
74 | int ret; | 74 | int ret; |
75 | 75 | ||
76 | /* set codec DAI configuration */ | 76 | /* set codec DAI configuration */ |
77 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | 77 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | |
78 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | 78 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); |
79 | if (ret < 0) | 79 | if (ret < 0) |
80 | return ret; | 80 | return ret; |
81 | 81 | ||
82 | /* set cpu DAI configuration */ | 82 | /* set cpu DAI configuration */ |
83 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | 83 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | |
84 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | 84 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); |
85 | if (ret < 0) | 85 | if (ret < 0) |
86 | return ret; | 86 | return ret; |
87 | 87 | ||
88 | return 0; | 88 | return 0; |
89 | } | 89 | } |
90 | 90 | ||
91 | static struct snd_soc_ops at91sam9g20ek_ops = { | 91 | static struct snd_soc_ops at91sam9g20ek_ops = { |
92 | .hw_params = at91sam9g20ek_hw_params, | 92 | .hw_params = at91sam9g20ek_hw_params, |
93 | }; | 93 | }; |
94 | 94 | ||
95 | static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card, | 95 | static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card, |
96 | enum snd_soc_bias_level level) | 96 | enum snd_soc_bias_level level) |
97 | { | 97 | { |
98 | static int mclk_on; | 98 | static int mclk_on; |
99 | int ret = 0; | 99 | int ret = 0; |
100 | 100 | ||
101 | switch (level) { | 101 | switch (level) { |
102 | case SND_SOC_BIAS_ON: | 102 | case SND_SOC_BIAS_ON: |
103 | case SND_SOC_BIAS_PREPARE: | 103 | case SND_SOC_BIAS_PREPARE: |
104 | if (!mclk_on) | 104 | if (!mclk_on) |
105 | ret = clk_enable(mclk); | 105 | ret = clk_enable(mclk); |
106 | if (ret == 0) | 106 | if (ret == 0) |
107 | mclk_on = 1; | 107 | mclk_on = 1; |
108 | break; | 108 | break; |
109 | 109 | ||
110 | case SND_SOC_BIAS_OFF: | 110 | case SND_SOC_BIAS_OFF: |
111 | case SND_SOC_BIAS_STANDBY: | 111 | case SND_SOC_BIAS_STANDBY: |
112 | if (mclk_on) | 112 | if (mclk_on) |
113 | clk_disable(mclk); | 113 | clk_disable(mclk); |
114 | mclk_on = 0; | 114 | mclk_on = 0; |
115 | break; | 115 | break; |
116 | } | 116 | } |
117 | 117 | ||
118 | return ret; | 118 | return ret; |
119 | } | 119 | } |
120 | 120 | ||
121 | static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = { | 121 | static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = { |
122 | SND_SOC_DAPM_MIC("Int Mic", NULL), | 122 | SND_SOC_DAPM_MIC("Int Mic", NULL), |
123 | SND_SOC_DAPM_SPK("Ext Spk", NULL), | 123 | SND_SOC_DAPM_SPK("Ext Spk", NULL), |
124 | }; | 124 | }; |
125 | 125 | ||
126 | static const struct snd_soc_dapm_route intercon[] = { | 126 | static const struct snd_soc_dapm_route intercon[] = { |
127 | 127 | ||
128 | /* speaker connected to LHPOUT */ | 128 | /* speaker connected to LHPOUT */ |
129 | {"Ext Spk", NULL, "LHPOUT"}, | 129 | {"Ext Spk", NULL, "LHPOUT"}, |
130 | 130 | ||
131 | /* mic is connected to Mic Jack, with WM8731 Mic Bias */ | 131 | /* mic is connected to Mic Jack, with WM8731 Mic Bias */ |
132 | {"MICIN", NULL, "Mic Bias"}, | 132 | {"MICIN", NULL, "Mic Bias"}, |
133 | {"Mic Bias", NULL, "Int Mic"}, | 133 | {"Mic Bias", NULL, "Int Mic"}, |
134 | }; | 134 | }; |
135 | 135 | ||
136 | /* | 136 | /* |
137 | * Logic for a wm8731 as connected on a at91sam9g20ek board. | 137 | * Logic for a wm8731 as connected on a at91sam9g20ek board. |
138 | */ | 138 | */ |
139 | static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) | 139 | static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) |
140 | { | 140 | { |
141 | struct snd_soc_codec *codec = rtd->codec; | 141 | struct snd_soc_codec *codec = rtd->codec; |
142 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | 142 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
143 | int ret; | 143 | int ret; |
144 | 144 | ||
145 | printk(KERN_DEBUG | 145 | printk(KERN_DEBUG |
146 | "at91sam9g20ek_wm8731 " | 146 | "at91sam9g20ek_wm8731 " |
147 | ": at91sam9g20ek_wm8731_init() called\n"); | 147 | ": at91sam9g20ek_wm8731_init() called\n"); |
148 | 148 | ||
149 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, | 149 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, |
150 | MCLK_RATE, SND_SOC_CLOCK_IN); | 150 | MCLK_RATE, SND_SOC_CLOCK_IN); |
151 | if (ret < 0) { | 151 | if (ret < 0) { |
152 | printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret); | 152 | printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret); |
153 | return ret; | 153 | return ret; |
154 | } | 154 | } |
155 | 155 | ||
156 | /* Add specific widgets */ | 156 | /* Add specific widgets */ |
157 | snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets, | 157 | snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets, |
158 | ARRAY_SIZE(at91sam9g20ek_dapm_widgets)); | 158 | ARRAY_SIZE(at91sam9g20ek_dapm_widgets)); |
159 | /* Set up specific audio path interconnects */ | 159 | /* Set up specific audio path interconnects */ |
160 | snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); | 160 | snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); |
161 | 161 | ||
162 | /* not connected */ | 162 | /* not connected */ |
163 | snd_soc_dapm_nc_pin(codec, "RLINEIN"); | 163 | snd_soc_dapm_nc_pin(codec, "RLINEIN"); |
164 | snd_soc_dapm_nc_pin(codec, "LLINEIN"); | 164 | snd_soc_dapm_nc_pin(codec, "LLINEIN"); |
165 | 165 | ||
166 | #ifdef ENABLE_MIC_INPUT | 166 | #ifdef ENABLE_MIC_INPUT |
167 | snd_soc_dapm_enable_pin(codec, "Int Mic"); | 167 | snd_soc_dapm_enable_pin(codec, "Int Mic"); |
168 | #else | 168 | #else |
169 | snd_soc_dapm_nc_pin(codec, "Int Mic"); | 169 | snd_soc_dapm_nc_pin(codec, "Int Mic"); |
170 | #endif | 170 | #endif |
171 | 171 | ||
172 | /* always connected */ | 172 | /* always connected */ |
173 | snd_soc_dapm_enable_pin(codec, "Ext Spk"); | 173 | snd_soc_dapm_enable_pin(codec, "Ext Spk"); |
174 | 174 | ||
175 | snd_soc_dapm_sync(codec); | 175 | snd_soc_dapm_sync(codec); |
176 | 176 | ||
177 | return 0; | 177 | return 0; |
178 | } | 178 | } |
179 | 179 | ||
180 | static struct snd_soc_dai_link at91sam9g20ek_dai = { | 180 | static struct snd_soc_dai_link at91sam9g20ek_dai = { |
181 | .name = "WM8731", | 181 | .name = "WM8731", |
182 | .stream_name = "WM8731 PCM", | 182 | .stream_name = "WM8731 PCM", |
183 | .cpu_dai_name = "atmel-ssc-dai.0", | 183 | .cpu_dai_name = "atmel-ssc-dai.0", |
184 | .codec_dai_name = "wm8731-hifi", | 184 | .codec_dai_name = "wm8731-hifi", |
185 | .init = at91sam9g20ek_wm8731_init, | 185 | .init = at91sam9g20ek_wm8731_init, |
186 | .platform_name = "atmel_pcm-audio", | 186 | .platform_name = "atmel_pcm-audio", |
187 | .codec_name = "wm8731-codec.0-001a", | 187 | .codec_name = "wm8731-codec.0-001a", |
188 | .ops = &at91sam9g20ek_ops, | 188 | .ops = &at91sam9g20ek_ops, |
189 | }; | 189 | }; |
190 | 190 | ||
191 | static struct snd_soc_card snd_soc_at91sam9g20ek = { | 191 | static struct snd_soc_card snd_soc_at91sam9g20ek = { |
192 | .name = "AT91SAMG20-EK", | 192 | .name = "AT91SAMG20-EK", |
193 | .dai_link = &at91sam9g20ek_dai, | 193 | .dai_link = &at91sam9g20ek_dai, |
194 | .num_links = 1, | 194 | .num_links = 1, |
195 | .set_bias_level = at91sam9g20ek_set_bias_level, | 195 | .set_bias_level = at91sam9g20ek_set_bias_level, |
196 | }; | 196 | }; |
197 | 197 | ||
198 | static struct platform_device *at91sam9g20ek_snd_device; | 198 | static struct platform_device *at91sam9g20ek_snd_device; |
199 | 199 | ||
200 | static int __init at91sam9g20ek_init(void) | 200 | static int __init at91sam9g20ek_init(void) |
201 | { | 201 | { |
202 | struct clk *pllb; | 202 | struct clk *pllb; |
203 | int ret; | 203 | int ret; |
204 | 204 | ||
205 | if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc())) | 205 | if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc())) |
206 | return -ENODEV; | 206 | return -ENODEV; |
207 | 207 | ||
208 | /* | 208 | /* |
209 | * Codec MCLK is supplied by PCK0 - set it up. | 209 | * Codec MCLK is supplied by PCK0 - set it up. |
210 | */ | 210 | */ |
211 | mclk = clk_get(NULL, "pck0"); | 211 | mclk = clk_get(NULL, "pck0"); |
212 | if (IS_ERR(mclk)) { | 212 | if (IS_ERR(mclk)) { |
213 | printk(KERN_ERR "ASoC: Failed to get MCLK\n"); | 213 | printk(KERN_ERR "ASoC: Failed to get MCLK\n"); |
214 | ret = PTR_ERR(mclk); | 214 | ret = PTR_ERR(mclk); |
215 | goto err; | 215 | goto err; |
216 | } | 216 | } |
217 | 217 | ||
218 | pllb = clk_get(NULL, "pllb"); | 218 | pllb = clk_get(NULL, "pllb"); |
219 | if (IS_ERR(mclk)) { | 219 | if (IS_ERR(mclk)) { |
220 | printk(KERN_ERR "ASoC: Failed to get PLLB\n"); | 220 | printk(KERN_ERR "ASoC: Failed to get PLLB\n"); |
221 | ret = PTR_ERR(mclk); | 221 | ret = PTR_ERR(mclk); |
222 | goto err_mclk; | 222 | goto err_mclk; |
223 | } | 223 | } |
224 | ret = clk_set_parent(mclk, pllb); | 224 | ret = clk_set_parent(mclk, pllb); |
225 | clk_put(pllb); | 225 | clk_put(pllb); |
226 | if (ret != 0) { | 226 | if (ret != 0) { |
227 | printk(KERN_ERR "ASoC: Failed to set MCLK parent\n"); | 227 | printk(KERN_ERR "ASoC: Failed to set MCLK parent\n"); |
228 | goto err_mclk; | 228 | goto err_mclk; |
229 | } | 229 | } |
230 | 230 | ||
231 | clk_set_rate(mclk, MCLK_RATE); | 231 | clk_set_rate(mclk, MCLK_RATE); |
232 | 232 | ||
233 | at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1); | 233 | at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1); |
234 | if (!at91sam9g20ek_snd_device) { | 234 | if (!at91sam9g20ek_snd_device) { |
235 | printk(KERN_ERR "ASoC: Platform device allocation failed\n"); | 235 | printk(KERN_ERR "ASoC: Platform device allocation failed\n"); |
236 | ret = -ENOMEM; | 236 | ret = -ENOMEM; |
237 | } | 237 | } |
238 | 238 | ||
239 | platform_set_drvdata(at91sam9g20ek_snd_device, | 239 | platform_set_drvdata(at91sam9g20ek_snd_device, |
240 | &snd_soc_at91sam9g20ek); | 240 | &snd_soc_at91sam9g20ek); |
241 | 241 | ||
242 | ret = platform_device_add(at91sam9g20ek_snd_device); | 242 | ret = platform_device_add(at91sam9g20ek_snd_device); |
243 | if (ret) { | 243 | if (ret) { |
244 | printk(KERN_ERR "ASoC: Platform device allocation failed\n"); | 244 | printk(KERN_ERR "ASoC: Platform device allocation failed\n"); |
245 | platform_device_put(at91sam9g20ek_snd_device); | 245 | platform_device_put(at91sam9g20ek_snd_device); |
246 | } | 246 | } |
247 | 247 | ||
248 | return ret; | 248 | return ret; |
249 | 249 | ||
250 | err_mclk: | 250 | err_mclk: |
251 | clk_put(mclk); | 251 | clk_put(mclk); |
252 | mclk = NULL; | 252 | mclk = NULL; |
253 | err: | 253 | err: |
254 | return ret; | 254 | return ret; |
255 | } | 255 | } |
256 | 256 | ||
257 | static void __exit at91sam9g20ek_exit(void) | 257 | static void __exit at91sam9g20ek_exit(void) |
258 | { | 258 | { |
259 | platform_device_unregister(at91sam9g20ek_snd_device); | 259 | platform_device_unregister(at91sam9g20ek_snd_device); |
260 | at91sam9g20ek_snd_device = NULL; | 260 | at91sam9g20ek_snd_device = NULL; |
261 | clk_put(mclk); | 261 | clk_put(mclk); |
262 | mclk = NULL; | 262 | mclk = NULL; |
263 | } | 263 | } |
264 | 264 | ||
265 | module_init(at91sam9g20ek_init); | 265 | module_init(at91sam9g20ek_init); |
266 | module_exit(at91sam9g20ek_exit); | 266 | module_exit(at91sam9g20ek_exit); |
267 | 267 | ||
268 | /* Module information */ | 268 | /* Module information */ |
269 | MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>"); | 269 | MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>"); |
270 | MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731"); | 270 | MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731"); |
271 | MODULE_LICENSE("GPL"); | 271 | MODULE_LICENSE("GPL"); |
272 | 272 |
sound/soc/au1x/db1200.c
1 | /* | 1 | /* |
2 | * DB1200 ASoC audio fabric support code. | 2 | * DB1200 ASoC audio fabric support code. |
3 | * | 3 | * |
4 | * (c) 2008-9 Manuel Lauss <manuel.lauss@gmail.com> | 4 | * (c) 2008-9 Manuel Lauss <manuel.lauss@gmail.com> |
5 | * | 5 | * |
6 | */ | 6 | */ |
7 | 7 | ||
8 | #include <linux/module.h> | 8 | #include <linux/module.h> |
9 | #include <linux/moduleparam.h> | 9 | #include <linux/moduleparam.h> |
10 | #include <linux/timer.h> | 10 | #include <linux/timer.h> |
11 | #include <linux/interrupt.h> | 11 | #include <linux/interrupt.h> |
12 | #include <linux/platform_device.h> | 12 | #include <linux/platform_device.h> |
13 | #include <sound/core.h> | 13 | #include <sound/core.h> |
14 | #include <sound/pcm.h> | 14 | #include <sound/pcm.h> |
15 | #include <sound/soc.h> | 15 | #include <sound/soc.h> |
16 | #include <sound/soc-dapm.h> | 16 | #include <sound/soc-dapm.h> |
17 | #include <asm/mach-au1x00/au1000.h> | 17 | #include <asm/mach-au1x00/au1000.h> |
18 | #include <asm/mach-au1x00/au1xxx_psc.h> | 18 | #include <asm/mach-au1x00/au1xxx_psc.h> |
19 | #include <asm/mach-au1x00/au1xxx_dbdma.h> | 19 | #include <asm/mach-au1x00/au1xxx_dbdma.h> |
20 | #include <asm/mach-db1x00/bcsr.h> | 20 | #include <asm/mach-db1x00/bcsr.h> |
21 | 21 | ||
22 | #include "../codecs/wm8731.h" | 22 | #include "../codecs/wm8731.h" |
23 | #include "psc.h" | 23 | #include "psc.h" |
24 | 24 | ||
25 | /*------------------------- AC97 PART ---------------------------*/ | 25 | /*------------------------- AC97 PART ---------------------------*/ |
26 | 26 | ||
27 | static struct snd_soc_dai_link db1200_ac97_dai = { | 27 | static struct snd_soc_dai_link db1200_ac97_dai = { |
28 | .name = "AC97", | 28 | .name = "AC97", |
29 | .stream_name = "AC97 HiFi", | 29 | .stream_name = "AC97 HiFi", |
30 | .cpu_dai_name = "au1xpsc-ac97", | 30 | .cpu_dai_name = "au1xpsc-ac97", |
31 | .codec_dai_name = "ac97-hifi", | 31 | .codec_dai_name = "ac97-hifi", |
32 | .platform_name = "au1xpsc-pcm-audio", | 32 | .platform_name = "au1xpsc-pcm-audio", |
33 | .codec_name = "ac97-codec", | 33 | .codec_name = "ac97-codec", |
34 | }; | 34 | }; |
35 | 35 | ||
36 | static struct snd_soc_card db1200_ac97_machine = { | 36 | static struct snd_soc_card db1200_ac97_machine = { |
37 | .name = "DB1200_AC97", | 37 | .name = "DB1200_AC97", |
38 | .dai_link = &db1200_ac97_dai, | 38 | .dai_link = &db1200_ac97_dai, |
39 | .num_links = 1, | 39 | .num_links = 1, |
40 | }; | 40 | }; |
41 | 41 | ||
42 | /*------------------------- I2S PART ---------------------------*/ | 42 | /*------------------------- I2S PART ---------------------------*/ |
43 | 43 | ||
44 | static int db1200_i2s_startup(struct snd_pcm_substream *substream) | 44 | static int db1200_i2s_startup(struct snd_pcm_substream *substream) |
45 | { | 45 | { |
46 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 46 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
47 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | 47 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
48 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | 48 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
49 | int ret; | 49 | int ret; |
50 | 50 | ||
51 | /* WM8731 has its own 12MHz crystal */ | 51 | /* WM8731 has its own 12MHz crystal */ |
52 | snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, | 52 | snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, |
53 | 12000000, SND_SOC_CLOCK_IN); | 53 | 12000000, SND_SOC_CLOCK_IN); |
54 | 54 | ||
55 | /* codec is bitclock and lrclk master */ | 55 | /* codec is bitclock and lrclk master */ |
56 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_LEFT_J | | 56 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_LEFT_J | |
57 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | 57 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); |
58 | if (ret < 0) | 58 | if (ret < 0) |
59 | goto out; | 59 | goto out; |
60 | 60 | ||
61 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_LEFT_J | | 61 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_LEFT_J | |
62 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | 62 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); |
63 | if (ret < 0) | 63 | if (ret < 0) |
64 | goto out; | 64 | goto out; |
65 | 65 | ||
66 | ret = 0; | 66 | ret = 0; |
67 | out: | 67 | out: |
68 | return ret; | 68 | return ret; |
69 | } | 69 | } |
70 | 70 | ||
71 | static struct snd_soc_ops db1200_i2s_wm8731_ops = { | 71 | static struct snd_soc_ops db1200_i2s_wm8731_ops = { |
72 | .startup = db1200_i2s_startup, | 72 | .startup = db1200_i2s_startup, |
73 | }; | 73 | }; |
74 | 74 | ||
75 | static struct snd_soc_dai_link db1200_i2s_dai = { | 75 | static struct snd_soc_dai_link db1200_i2s_dai = { |
76 | .name = "WM8731", | 76 | .name = "WM8731", |
77 | .stream_name = "WM8731 PCM", | 77 | .stream_name = "WM8731 PCM", |
78 | .cpu_dai_name = "au1xpsc", | 78 | .cpu_dai_name = "au1xpsc", |
79 | .codec_dai_name = "wm8731-hifi" | 79 | .codec_dai_name = "wm8731-hifi" |
80 | .platform_name = "au1xpsc-pcm-audio", | 80 | .platform_name = "au1xpsc-pcm-audio", |
81 | .codec_name = "wm8731-codec.0-001a", | 81 | .codec_name = "wm8731-codec.0-001a", |
82 | .ops = &db1200_i2s_wm8731_ops, | 82 | .ops = &db1200_i2s_wm8731_ops, |
83 | }; | 83 | }; |
84 | 84 | ||
85 | static struct snd_soc_card db1200_i2s_machine = { | 85 | static struct snd_soc_card db1200_i2s_machine = { |
86 | .name = "DB1200_I2S", | 86 | .name = "DB1200_I2S", |
87 | .dai_link = &db1200_i2s_dai, | 87 | .dai_link = &db1200_i2s_dai, |
88 | .num_links = 1, | 88 | .num_links = 1, |
89 | }; | 89 | }; |
90 | 90 | ||
91 | /*------------------------- COMMON PART ---------------------------*/ | 91 | /*------------------------- COMMON PART ---------------------------*/ |
92 | 92 | ||
93 | static struct platform_device *db1200_asoc_dev; | 93 | static struct platform_device *db1200_asoc_dev; |
94 | 94 | ||
95 | static int __init db1200_audio_load(void) | 95 | static int __init db1200_audio_load(void) |
96 | { | 96 | { |
97 | int ret; | 97 | int ret; |
98 | 98 | ||
99 | ret = -ENOMEM; | 99 | ret = -ENOMEM; |
100 | db1200_asoc_dev = platform_device_alloc("soc-audio", -1); | 100 | db1200_asoc_dev = platform_device_alloc("soc-audio", -1); |
101 | if (!db1200_asoc_dev) | 101 | if (!db1200_asoc_dev) |
102 | goto out; | 102 | goto out; |
103 | 103 | ||
104 | /* DB1200 board setup set PSC1MUX to preferred audio device */ | 104 | /* DB1200 board setup set PSC1MUX to preferred audio device */ |
105 | if (bcsr_read(BCSR_RESETS) & BCSR_RESETS_PSC1MUX) | 105 | if (bcsr_read(BCSR_RESETS) & BCSR_RESETS_PSC1MUX) |
106 | platform_set_drvdata(db1200_asoc_dev, &db1200_i2s_machine); | 106 | platform_set_drvdata(db1200_asoc_dev, &db1200_i2s_machine); |
107 | else | 107 | else |
108 | platform_set_drvdata(db1200_asoc_dev, &db1200_ac97_machine); | 108 | platform_set_drvdata(db1200_asoc_dev, &db1200_ac97_machine); |
109 | 109 | ||
110 | ret = platform_device_add(db1200_asoc_dev); | 110 | ret = platform_device_add(db1200_asoc_dev); |
111 | 111 | ||
112 | if (ret) { | 112 | if (ret) { |
113 | platform_device_put(db1200_asoc_dev); | 113 | platform_device_put(db1200_asoc_dev); |
114 | db1200_asoc_dev = NULL; | 114 | db1200_asoc_dev = NULL; |
115 | } | 115 | } |
116 | out: | 116 | out: |
117 | return ret; | 117 | return ret; |
118 | } | 118 | } |
119 | 119 | ||
120 | static void __exit db1200_audio_unload(void) | 120 | static void __exit db1200_audio_unload(void) |
121 | { | 121 | { |
122 | platform_device_unregister(db1200_asoc_dev); | 122 | platform_device_unregister(db1200_asoc_dev); |
123 | } | 123 | } |
124 | 124 | ||
125 | module_init(db1200_audio_load); | 125 | module_init(db1200_audio_load); |
126 | module_exit(db1200_audio_unload); | 126 | module_exit(db1200_audio_unload); |
127 | 127 | ||
128 | MODULE_LICENSE("GPL"); | 128 | MODULE_LICENSE("GPL"); |
129 | MODULE_DESCRIPTION("DB1200 ASoC audio support"); | 129 | MODULE_DESCRIPTION("DB1200 ASoC audio support"); |
130 | MODULE_AUTHOR("Manuel Lauss"); | 130 | MODULE_AUTHOR("Manuel Lauss"); |
131 | 131 |
sound/soc/codecs/wm8731.c
1 | /* | 1 | /* |
2 | * wm8731.c -- WM8731 ALSA SoC Audio driver | 2 | * wm8731.c -- WM8731 ALSA SoC Audio driver |
3 | * | 3 | * |
4 | * Copyright 2005 Openedhand Ltd. | 4 | * Copyright 2005 Openedhand Ltd. |
5 | * | 5 | * |
6 | * Author: Richard Purdie <richard@openedhand.com> | 6 | * Author: Richard Purdie <richard@openedhand.com> |
7 | * | 7 | * |
8 | * Based on wm8753.c by Liam Girdwood | 8 | * Based on wm8753.c by Liam Girdwood |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2 as | 11 | * it under the terms of the GNU General Public License version 2 as |
12 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/moduleparam.h> | 16 | #include <linux/moduleparam.h> |
17 | #include <linux/init.h> | 17 | #include <linux/init.h> |
18 | #include <linux/delay.h> | 18 | #include <linux/delay.h> |
19 | #include <linux/pm.h> | 19 | #include <linux/pm.h> |
20 | #include <linux/i2c.h> | 20 | #include <linux/i2c.h> |
21 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
23 | #include <linux/regulator/consumer.h> | 23 | #include <linux/regulator/consumer.h> |
24 | #include <linux/spi/spi.h> | 24 | #include <linux/spi/spi.h> |
25 | #include <sound/core.h> | 25 | #include <sound/core.h> |
26 | #include <sound/pcm.h> | 26 | #include <sound/pcm.h> |
27 | #include <sound/pcm_params.h> | 27 | #include <sound/pcm_params.h> |
28 | #include <sound/soc.h> | 28 | #include <sound/soc.h> |
29 | #include <sound/soc-dapm.h> | 29 | #include <sound/soc-dapm.h> |
30 | #include <sound/initval.h> | 30 | #include <sound/initval.h> |
31 | #include <sound/tlv.h> | 31 | #include <sound/tlv.h> |
32 | 32 | ||
33 | #include "wm8731.h" | 33 | #include "wm8731.h" |
34 | 34 | ||
35 | #define WM8731_NUM_SUPPLIES 4 | 35 | #define WM8731_NUM_SUPPLIES 4 |
36 | static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { | 36 | static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { |
37 | "AVDD", | 37 | "AVDD", |
38 | "HPVDD", | 38 | "HPVDD", |
39 | "DCVDD", | 39 | "DCVDD", |
40 | "DBVDD", | 40 | "DBVDD", |
41 | }; | 41 | }; |
42 | 42 | ||
43 | /* codec private data */ | 43 | /* codec private data */ |
44 | struct wm8731_priv { | 44 | struct wm8731_priv { |
45 | enum snd_soc_control_type control_type; | 45 | enum snd_soc_control_type control_type; |
46 | struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; | 46 | struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; |
47 | u16 reg_cache[WM8731_CACHEREGNUM]; | 47 | u16 reg_cache[WM8731_CACHEREGNUM]; |
48 | unsigned int sysclk; | 48 | unsigned int sysclk; |
49 | int sysclk_type; | ||
49 | }; | 50 | }; |
50 | 51 | ||
51 | 52 | ||
52 | /* | 53 | /* |
53 | * wm8731 register cache | 54 | * wm8731 register cache |
54 | * We can't read the WM8731 register space when we are | 55 | * We can't read the WM8731 register space when we are |
55 | * using 2 wire for device control, so we cache them instead. | 56 | * using 2 wire for device control, so we cache them instead. |
56 | * There is no point in caching the reset register | 57 | * There is no point in caching the reset register |
57 | */ | 58 | */ |
58 | static const u16 wm8731_reg[WM8731_CACHEREGNUM] = { | 59 | static const u16 wm8731_reg[WM8731_CACHEREGNUM] = { |
59 | 0x0097, 0x0097, 0x0079, 0x0079, | 60 | 0x0097, 0x0097, 0x0079, 0x0079, |
60 | 0x000a, 0x0008, 0x009f, 0x000a, | 61 | 0x000a, 0x0008, 0x009f, 0x000a, |
61 | 0x0000, 0x0000 | 62 | 0x0000, 0x0000 |
62 | }; | 63 | }; |
63 | 64 | ||
64 | #define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0) | 65 | #define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0) |
65 | 66 | ||
66 | static const char *wm8731_input_select[] = {"Line In", "Mic"}; | 67 | static const char *wm8731_input_select[] = {"Line In", "Mic"}; |
67 | static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; | 68 | static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; |
68 | 69 | ||
69 | static const struct soc_enum wm8731_enum[] = { | 70 | static const struct soc_enum wm8731_enum[] = { |
70 | SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select), | 71 | SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select), |
71 | SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph), | 72 | SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph), |
72 | }; | 73 | }; |
73 | 74 | ||
74 | static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); | 75 | static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); |
75 | static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0); | 76 | static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0); |
76 | static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); | 77 | static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); |
77 | 78 | ||
78 | static const struct snd_kcontrol_new wm8731_snd_controls[] = { | 79 | static const struct snd_kcontrol_new wm8731_snd_controls[] = { |
79 | 80 | ||
80 | SOC_DOUBLE_R_TLV("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V, | 81 | SOC_DOUBLE_R_TLV("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V, |
81 | 0, 127, 0, out_tlv), | 82 | 0, 127, 0, out_tlv), |
82 | SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V, | 83 | SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V, |
83 | 7, 1, 0), | 84 | 7, 1, 0), |
84 | 85 | ||
85 | SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0, | 86 | SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0, |
86 | in_tlv), | 87 | in_tlv), |
87 | SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1), | 88 | SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1), |
88 | 89 | ||
89 | SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0), | 90 | SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0), |
90 | SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1), | 91 | SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1), |
91 | 92 | ||
92 | SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1, | 93 | SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1, |
93 | sidetone_tlv), | 94 | sidetone_tlv), |
94 | 95 | ||
95 | SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1), | 96 | SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1), |
96 | SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0), | 97 | SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0), |
97 | 98 | ||
98 | SOC_ENUM("Playback De-emphasis", wm8731_enum[1]), | 99 | SOC_ENUM("Playback De-emphasis", wm8731_enum[1]), |
99 | }; | 100 | }; |
100 | 101 | ||
101 | /* Output Mixer */ | 102 | /* Output Mixer */ |
102 | static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = { | 103 | static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = { |
103 | SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0), | 104 | SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0), |
104 | SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0), | 105 | SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0), |
105 | SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0), | 106 | SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0), |
106 | }; | 107 | }; |
107 | 108 | ||
108 | /* Input mux */ | 109 | /* Input mux */ |
109 | static const struct snd_kcontrol_new wm8731_input_mux_controls = | 110 | static const struct snd_kcontrol_new wm8731_input_mux_controls = |
110 | SOC_DAPM_ENUM("Input Select", wm8731_enum[0]); | 111 | SOC_DAPM_ENUM("Input Select", wm8731_enum[0]); |
111 | 112 | ||
112 | static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { | 113 | static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { |
114 | SND_SOC_DAPM_SUPPLY("OSC", WM8731_PWR, 5, 1, NULL, 0), | ||
113 | SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, | 115 | SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, |
114 | &wm8731_output_mixer_controls[0], | 116 | &wm8731_output_mixer_controls[0], |
115 | ARRAY_SIZE(wm8731_output_mixer_controls)), | 117 | ARRAY_SIZE(wm8731_output_mixer_controls)), |
116 | SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1), | 118 | SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1), |
117 | SND_SOC_DAPM_OUTPUT("LOUT"), | 119 | SND_SOC_DAPM_OUTPUT("LOUT"), |
118 | SND_SOC_DAPM_OUTPUT("LHPOUT"), | 120 | SND_SOC_DAPM_OUTPUT("LHPOUT"), |
119 | SND_SOC_DAPM_OUTPUT("ROUT"), | 121 | SND_SOC_DAPM_OUTPUT("ROUT"), |
120 | SND_SOC_DAPM_OUTPUT("RHPOUT"), | 122 | SND_SOC_DAPM_OUTPUT("RHPOUT"), |
121 | SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1), | 123 | SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1), |
122 | SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls), | 124 | SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls), |
123 | SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0), | 125 | SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0), |
124 | SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1), | 126 | SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1), |
125 | SND_SOC_DAPM_INPUT("MICIN"), | 127 | SND_SOC_DAPM_INPUT("MICIN"), |
126 | SND_SOC_DAPM_INPUT("RLINEIN"), | 128 | SND_SOC_DAPM_INPUT("RLINEIN"), |
127 | SND_SOC_DAPM_INPUT("LLINEIN"), | 129 | SND_SOC_DAPM_INPUT("LLINEIN"), |
128 | }; | 130 | }; |
129 | 131 | ||
132 | static int wm8731_check_osc(struct snd_soc_dapm_widget *source, | ||
133 | struct snd_soc_dapm_widget *sink) | ||
134 | { | ||
135 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(source->codec); | ||
136 | |||
137 | return wm8731->sysclk_type == WM8731_SYSCLK_MCLK; | ||
138 | } | ||
139 | |||
130 | static const struct snd_soc_dapm_route intercon[] = { | 140 | static const struct snd_soc_dapm_route intercon[] = { |
141 | {"DAC", NULL, "OSC", wm8731_check_osc}, | ||
142 | {"ADC", NULL, "OSC", wm8731_check_osc}, | ||
143 | |||
131 | /* output mixer */ | 144 | /* output mixer */ |
132 | {"Output Mixer", "Line Bypass Switch", "Line Input"}, | 145 | {"Output Mixer", "Line Bypass Switch", "Line Input"}, |
133 | {"Output Mixer", "HiFi Playback Switch", "DAC"}, | 146 | {"Output Mixer", "HiFi Playback Switch", "DAC"}, |
134 | {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, | 147 | {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, |
135 | 148 | ||
136 | /* outputs */ | 149 | /* outputs */ |
137 | {"RHPOUT", NULL, "Output Mixer"}, | 150 | {"RHPOUT", NULL, "Output Mixer"}, |
138 | {"ROUT", NULL, "Output Mixer"}, | 151 | {"ROUT", NULL, "Output Mixer"}, |
139 | {"LHPOUT", NULL, "Output Mixer"}, | 152 | {"LHPOUT", NULL, "Output Mixer"}, |
140 | {"LOUT", NULL, "Output Mixer"}, | 153 | {"LOUT", NULL, "Output Mixer"}, |
141 | 154 | ||
142 | /* input mux */ | 155 | /* input mux */ |
143 | {"Input Mux", "Line In", "Line Input"}, | 156 | {"Input Mux", "Line In", "Line Input"}, |
144 | {"Input Mux", "Mic", "Mic Bias"}, | 157 | {"Input Mux", "Mic", "Mic Bias"}, |
145 | {"ADC", NULL, "Input Mux"}, | 158 | {"ADC", NULL, "Input Mux"}, |
146 | 159 | ||
147 | /* inputs */ | 160 | /* inputs */ |
148 | {"Line Input", NULL, "LLINEIN"}, | 161 | {"Line Input", NULL, "LLINEIN"}, |
149 | {"Line Input", NULL, "RLINEIN"}, | 162 | {"Line Input", NULL, "RLINEIN"}, |
150 | {"Mic Bias", NULL, "MICIN"}, | 163 | {"Mic Bias", NULL, "MICIN"}, |
151 | }; | 164 | }; |
152 | 165 | ||
153 | static int wm8731_add_widgets(struct snd_soc_codec *codec) | 166 | static int wm8731_add_widgets(struct snd_soc_codec *codec) |
154 | { | 167 | { |
155 | snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets, | 168 | snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets, |
156 | ARRAY_SIZE(wm8731_dapm_widgets)); | 169 | ARRAY_SIZE(wm8731_dapm_widgets)); |
157 | 170 | ||
158 | snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); | 171 | snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); |
159 | 172 | ||
160 | return 0; | 173 | return 0; |
161 | } | 174 | } |
162 | 175 | ||
163 | struct _coeff_div { | 176 | struct _coeff_div { |
164 | u32 mclk; | 177 | u32 mclk; |
165 | u32 rate; | 178 | u32 rate; |
166 | u16 fs; | 179 | u16 fs; |
167 | u8 sr:4; | 180 | u8 sr:4; |
168 | u8 bosr:1; | 181 | u8 bosr:1; |
169 | u8 usb:1; | 182 | u8 usb:1; |
170 | }; | 183 | }; |
171 | 184 | ||
172 | /* codec mclk clock divider coefficients */ | 185 | /* codec mclk clock divider coefficients */ |
173 | static const struct _coeff_div coeff_div[] = { | 186 | static const struct _coeff_div coeff_div[] = { |
174 | /* 48k */ | 187 | /* 48k */ |
175 | {12288000, 48000, 256, 0x0, 0x0, 0x0}, | 188 | {12288000, 48000, 256, 0x0, 0x0, 0x0}, |
176 | {18432000, 48000, 384, 0x0, 0x1, 0x0}, | 189 | {18432000, 48000, 384, 0x0, 0x1, 0x0}, |
177 | {12000000, 48000, 250, 0x0, 0x0, 0x1}, | 190 | {12000000, 48000, 250, 0x0, 0x0, 0x1}, |
178 | 191 | ||
179 | /* 32k */ | 192 | /* 32k */ |
180 | {12288000, 32000, 384, 0x6, 0x0, 0x0}, | 193 | {12288000, 32000, 384, 0x6, 0x0, 0x0}, |
181 | {18432000, 32000, 576, 0x6, 0x1, 0x0}, | 194 | {18432000, 32000, 576, 0x6, 0x1, 0x0}, |
182 | {12000000, 32000, 375, 0x6, 0x0, 0x1}, | 195 | {12000000, 32000, 375, 0x6, 0x0, 0x1}, |
183 | 196 | ||
184 | /* 8k */ | 197 | /* 8k */ |
185 | {12288000, 8000, 1536, 0x3, 0x0, 0x0}, | 198 | {12288000, 8000, 1536, 0x3, 0x0, 0x0}, |
186 | {18432000, 8000, 2304, 0x3, 0x1, 0x0}, | 199 | {18432000, 8000, 2304, 0x3, 0x1, 0x0}, |
187 | {11289600, 8000, 1408, 0xb, 0x0, 0x0}, | 200 | {11289600, 8000, 1408, 0xb, 0x0, 0x0}, |
188 | {16934400, 8000, 2112, 0xb, 0x1, 0x0}, | 201 | {16934400, 8000, 2112, 0xb, 0x1, 0x0}, |
189 | {12000000, 8000, 1500, 0x3, 0x0, 0x1}, | 202 | {12000000, 8000, 1500, 0x3, 0x0, 0x1}, |
190 | 203 | ||
191 | /* 96k */ | 204 | /* 96k */ |
192 | {12288000, 96000, 128, 0x7, 0x0, 0x0}, | 205 | {12288000, 96000, 128, 0x7, 0x0, 0x0}, |
193 | {18432000, 96000, 192, 0x7, 0x1, 0x0}, | 206 | {18432000, 96000, 192, 0x7, 0x1, 0x0}, |
194 | {12000000, 96000, 125, 0x7, 0x0, 0x1}, | 207 | {12000000, 96000, 125, 0x7, 0x0, 0x1}, |
195 | 208 | ||
196 | /* 44.1k */ | 209 | /* 44.1k */ |
197 | {11289600, 44100, 256, 0x8, 0x0, 0x0}, | 210 | {11289600, 44100, 256, 0x8, 0x0, 0x0}, |
198 | {16934400, 44100, 384, 0x8, 0x1, 0x0}, | 211 | {16934400, 44100, 384, 0x8, 0x1, 0x0}, |
199 | {12000000, 44100, 272, 0x8, 0x1, 0x1}, | 212 | {12000000, 44100, 272, 0x8, 0x1, 0x1}, |
200 | 213 | ||
201 | /* 88.2k */ | 214 | /* 88.2k */ |
202 | {11289600, 88200, 128, 0xf, 0x0, 0x0}, | 215 | {11289600, 88200, 128, 0xf, 0x0, 0x0}, |
203 | {16934400, 88200, 192, 0xf, 0x1, 0x0}, | 216 | {16934400, 88200, 192, 0xf, 0x1, 0x0}, |
204 | {12000000, 88200, 136, 0xf, 0x1, 0x1}, | 217 | {12000000, 88200, 136, 0xf, 0x1, 0x1}, |
205 | }; | 218 | }; |
206 | 219 | ||
207 | static inline int get_coeff(int mclk, int rate) | 220 | static inline int get_coeff(int mclk, int rate) |
208 | { | 221 | { |
209 | int i; | 222 | int i; |
210 | 223 | ||
211 | for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { | 224 | for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { |
212 | if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) | 225 | if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) |
213 | return i; | 226 | return i; |
214 | } | 227 | } |
215 | return 0; | 228 | return 0; |
216 | } | 229 | } |
217 | 230 | ||
218 | static int wm8731_hw_params(struct snd_pcm_substream *substream, | 231 | static int wm8731_hw_params(struct snd_pcm_substream *substream, |
219 | struct snd_pcm_hw_params *params, | 232 | struct snd_pcm_hw_params *params, |
220 | struct snd_soc_dai *dai) | 233 | struct snd_soc_dai *dai) |
221 | { | 234 | { |
222 | struct snd_soc_codec *codec = dai->codec; | 235 | struct snd_soc_codec *codec = dai->codec; |
223 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); | 236 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); |
224 | u16 iface = snd_soc_read(codec, WM8731_IFACE) & 0xfff3; | 237 | u16 iface = snd_soc_read(codec, WM8731_IFACE) & 0xfff3; |
225 | int i = get_coeff(wm8731->sysclk, params_rate(params)); | 238 | int i = get_coeff(wm8731->sysclk, params_rate(params)); |
226 | u16 srate = (coeff_div[i].sr << 2) | | 239 | u16 srate = (coeff_div[i].sr << 2) | |
227 | (coeff_div[i].bosr << 1) | coeff_div[i].usb; | 240 | (coeff_div[i].bosr << 1) | coeff_div[i].usb; |
228 | 241 | ||
229 | snd_soc_write(codec, WM8731_SRATE, srate); | 242 | snd_soc_write(codec, WM8731_SRATE, srate); |
230 | 243 | ||
231 | /* bit size */ | 244 | /* bit size */ |
232 | switch (params_format(params)) { | 245 | switch (params_format(params)) { |
233 | case SNDRV_PCM_FORMAT_S16_LE: | 246 | case SNDRV_PCM_FORMAT_S16_LE: |
234 | break; | 247 | break; |
235 | case SNDRV_PCM_FORMAT_S20_3LE: | 248 | case SNDRV_PCM_FORMAT_S20_3LE: |
236 | iface |= 0x0004; | 249 | iface |= 0x0004; |
237 | break; | 250 | break; |
238 | case SNDRV_PCM_FORMAT_S24_LE: | 251 | case SNDRV_PCM_FORMAT_S24_LE: |
239 | iface |= 0x0008; | 252 | iface |= 0x0008; |
240 | break; | 253 | break; |
241 | } | 254 | } |
242 | 255 | ||
243 | snd_soc_write(codec, WM8731_IFACE, iface); | 256 | snd_soc_write(codec, WM8731_IFACE, iface); |
244 | return 0; | 257 | return 0; |
245 | } | 258 | } |
246 | 259 | ||
247 | static int wm8731_pcm_prepare(struct snd_pcm_substream *substream, | 260 | static int wm8731_pcm_prepare(struct snd_pcm_substream *substream, |
248 | struct snd_soc_dai *dai) | 261 | struct snd_soc_dai *dai) |
249 | { | 262 | { |
250 | struct snd_soc_codec *codec = dai->codec; | 263 | struct snd_soc_codec *codec = dai->codec; |
251 | 264 | ||
252 | /* set active */ | 265 | /* set active */ |
253 | snd_soc_write(codec, WM8731_ACTIVE, 0x0001); | 266 | snd_soc_write(codec, WM8731_ACTIVE, 0x0001); |
254 | 267 | ||
255 | return 0; | 268 | return 0; |
256 | } | 269 | } |
257 | 270 | ||
258 | static void wm8731_shutdown(struct snd_pcm_substream *substream, | 271 | static void wm8731_shutdown(struct snd_pcm_substream *substream, |
259 | struct snd_soc_dai *dai) | 272 | struct snd_soc_dai *dai) |
260 | { | 273 | { |
261 | struct snd_soc_codec *codec = dai->codec; | 274 | struct snd_soc_codec *codec = dai->codec; |
262 | 275 | ||
263 | /* deactivate */ | 276 | /* deactivate */ |
264 | if (!codec->active) { | 277 | if (!codec->active) { |
265 | udelay(50); | 278 | udelay(50); |
266 | snd_soc_write(codec, WM8731_ACTIVE, 0x0); | 279 | snd_soc_write(codec, WM8731_ACTIVE, 0x0); |
267 | } | 280 | } |
268 | } | 281 | } |
269 | 282 | ||
270 | static int wm8731_mute(struct snd_soc_dai *dai, int mute) | 283 | static int wm8731_mute(struct snd_soc_dai *dai, int mute) |
271 | { | 284 | { |
272 | struct snd_soc_codec *codec = dai->codec; | 285 | struct snd_soc_codec *codec = dai->codec; |
273 | u16 mute_reg = snd_soc_read(codec, WM8731_APDIGI) & 0xfff7; | 286 | u16 mute_reg = snd_soc_read(codec, WM8731_APDIGI) & 0xfff7; |
274 | 287 | ||
275 | if (mute) | 288 | if (mute) |
276 | snd_soc_write(codec, WM8731_APDIGI, mute_reg | 0x8); | 289 | snd_soc_write(codec, WM8731_APDIGI, mute_reg | 0x8); |
277 | else | 290 | else |
278 | snd_soc_write(codec, WM8731_APDIGI, mute_reg); | 291 | snd_soc_write(codec, WM8731_APDIGI, mute_reg); |
279 | return 0; | 292 | return 0; |
280 | } | 293 | } |
281 | 294 | ||
282 | static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, | 295 | static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, |
283 | int clk_id, unsigned int freq, int dir) | 296 | int clk_id, unsigned int freq, int dir) |
284 | { | 297 | { |
285 | struct snd_soc_codec *codec = codec_dai->codec; | 298 | struct snd_soc_codec *codec = codec_dai->codec; |
286 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); | 299 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); |
287 | 300 | ||
301 | switch (clk_id) { | ||
302 | case WM8731_SYSCLK_XTAL: | ||
303 | case WM8731_SYSCLK_MCLK: | ||
304 | wm8731->sysclk_type = clk_id; | ||
305 | break; | ||
306 | default: | ||
307 | return -EINVAL; | ||
308 | } | ||
309 | |||
288 | switch (freq) { | 310 | switch (freq) { |
289 | case 11289600: | 311 | case 11289600: |
290 | case 12000000: | 312 | case 12000000: |
291 | case 12288000: | 313 | case 12288000: |
292 | case 16934400: | 314 | case 16934400: |
293 | case 18432000: | 315 | case 18432000: |
294 | wm8731->sysclk = freq; | 316 | wm8731->sysclk = freq; |
295 | return 0; | 317 | break; |
318 | default: | ||
319 | return -EINVAL; | ||
296 | } | 320 | } |
297 | return -EINVAL; | 321 | |
322 | snd_soc_dapm_sync(codec); | ||
323 | |||
324 | return 0; | ||
298 | } | 325 | } |
299 | 326 | ||
300 | 327 | ||
301 | static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai, | 328 | static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai, |
302 | unsigned int fmt) | 329 | unsigned int fmt) |
303 | { | 330 | { |
304 | struct snd_soc_codec *codec = codec_dai->codec; | 331 | struct snd_soc_codec *codec = codec_dai->codec; |
305 | u16 iface = 0; | 332 | u16 iface = 0; |
306 | 333 | ||
307 | /* set master/slave audio interface */ | 334 | /* set master/slave audio interface */ |
308 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | 335 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
309 | case SND_SOC_DAIFMT_CBM_CFM: | 336 | case SND_SOC_DAIFMT_CBM_CFM: |
310 | iface |= 0x0040; | 337 | iface |= 0x0040; |
311 | break; | 338 | break; |
312 | case SND_SOC_DAIFMT_CBS_CFS: | 339 | case SND_SOC_DAIFMT_CBS_CFS: |
313 | break; | 340 | break; |
314 | default: | 341 | default: |
315 | return -EINVAL; | 342 | return -EINVAL; |
316 | } | 343 | } |
317 | 344 | ||
318 | /* interface format */ | 345 | /* interface format */ |
319 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | 346 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
320 | case SND_SOC_DAIFMT_I2S: | 347 | case SND_SOC_DAIFMT_I2S: |
321 | iface |= 0x0002; | 348 | iface |= 0x0002; |
322 | break; | 349 | break; |
323 | case SND_SOC_DAIFMT_RIGHT_J: | 350 | case SND_SOC_DAIFMT_RIGHT_J: |
324 | break; | 351 | break; |
325 | case SND_SOC_DAIFMT_LEFT_J: | 352 | case SND_SOC_DAIFMT_LEFT_J: |
326 | iface |= 0x0001; | 353 | iface |= 0x0001; |
327 | break; | 354 | break; |
328 | case SND_SOC_DAIFMT_DSP_A: | 355 | case SND_SOC_DAIFMT_DSP_A: |
329 | iface |= 0x0003; | 356 | iface |= 0x0003; |
330 | break; | 357 | break; |
331 | case SND_SOC_DAIFMT_DSP_B: | 358 | case SND_SOC_DAIFMT_DSP_B: |
332 | iface |= 0x0013; | 359 | iface |= 0x0013; |
333 | break; | 360 | break; |
334 | default: | 361 | default: |
335 | return -EINVAL; | 362 | return -EINVAL; |
336 | } | 363 | } |
337 | 364 | ||
338 | /* clock inversion */ | 365 | /* clock inversion */ |
339 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | 366 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
340 | case SND_SOC_DAIFMT_NB_NF: | 367 | case SND_SOC_DAIFMT_NB_NF: |
341 | break; | 368 | break; |
342 | case SND_SOC_DAIFMT_IB_IF: | 369 | case SND_SOC_DAIFMT_IB_IF: |
343 | iface |= 0x0090; | 370 | iface |= 0x0090; |
344 | break; | 371 | break; |
345 | case SND_SOC_DAIFMT_IB_NF: | 372 | case SND_SOC_DAIFMT_IB_NF: |
346 | iface |= 0x0080; | 373 | iface |= 0x0080; |
347 | break; | 374 | break; |
348 | case SND_SOC_DAIFMT_NB_IF: | 375 | case SND_SOC_DAIFMT_NB_IF: |
349 | iface |= 0x0010; | 376 | iface |= 0x0010; |
350 | break; | 377 | break; |
351 | default: | 378 | default: |
352 | return -EINVAL; | 379 | return -EINVAL; |
353 | } | 380 | } |
354 | 381 | ||
355 | /* set iface */ | 382 | /* set iface */ |
356 | snd_soc_write(codec, WM8731_IFACE, iface); | 383 | snd_soc_write(codec, WM8731_IFACE, iface); |
357 | return 0; | 384 | return 0; |
358 | } | 385 | } |
359 | 386 | ||
360 | static int wm8731_set_bias_level(struct snd_soc_codec *codec, | 387 | static int wm8731_set_bias_level(struct snd_soc_codec *codec, |
361 | enum snd_soc_bias_level level) | 388 | enum snd_soc_bias_level level) |
362 | { | 389 | { |
363 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); | 390 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); |
364 | int i, ret; | 391 | int i, ret; |
365 | u8 data[2]; | 392 | u8 data[2]; |
366 | u16 *cache = codec->reg_cache; | 393 | u16 *cache = codec->reg_cache; |
367 | u16 reg; | 394 | u16 reg; |
368 | 395 | ||
369 | switch (level) { | 396 | switch (level) { |
370 | case SND_SOC_BIAS_ON: | 397 | case SND_SOC_BIAS_ON: |
371 | break; | 398 | break; |
372 | case SND_SOC_BIAS_PREPARE: | 399 | case SND_SOC_BIAS_PREPARE: |
373 | break; | 400 | break; |
374 | case SND_SOC_BIAS_STANDBY: | 401 | case SND_SOC_BIAS_STANDBY: |
375 | if (codec->bias_level == SND_SOC_BIAS_OFF) { | 402 | if (codec->bias_level == SND_SOC_BIAS_OFF) { |
376 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), | 403 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), |
377 | wm8731->supplies); | 404 | wm8731->supplies); |
378 | if (ret != 0) | 405 | if (ret != 0) |
379 | return ret; | 406 | return ret; |
380 | 407 | ||
381 | /* Sync reg_cache with the hardware */ | 408 | /* Sync reg_cache with the hardware */ |
382 | for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) { | 409 | for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) { |
383 | if (cache[i] == wm8731_reg[i]) | 410 | if (cache[i] == wm8731_reg[i]) |
384 | continue; | 411 | continue; |
385 | 412 | ||
386 | data[0] = (i << 1) | ((cache[i] >> 8) | 413 | data[0] = (i << 1) | ((cache[i] >> 8) |
387 | & 0x0001); | 414 | & 0x0001); |
388 | data[1] = cache[i] & 0x00ff; | 415 | data[1] = cache[i] & 0x00ff; |
389 | codec->hw_write(codec->control_data, data, 2); | 416 | codec->hw_write(codec->control_data, data, 2); |
390 | } | 417 | } |
391 | } | 418 | } |
392 | 419 | ||
393 | /* Clear PWROFF, gate CLKOUT, everything else as-is */ | 420 | /* Clear PWROFF, gate CLKOUT, everything else as-is */ |
394 | reg = snd_soc_read(codec, WM8731_PWR) & 0xff7f; | 421 | reg = snd_soc_read(codec, WM8731_PWR) & 0xff7f; |
395 | snd_soc_write(codec, WM8731_PWR, reg | 0x0040); | 422 | snd_soc_write(codec, WM8731_PWR, reg | 0x0040); |
396 | break; | 423 | break; |
397 | case SND_SOC_BIAS_OFF: | 424 | case SND_SOC_BIAS_OFF: |
398 | snd_soc_write(codec, WM8731_ACTIVE, 0x0); | 425 | snd_soc_write(codec, WM8731_ACTIVE, 0x0); |
399 | snd_soc_write(codec, WM8731_PWR, 0xffff); | 426 | snd_soc_write(codec, WM8731_PWR, 0xffff); |
400 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), | 427 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), |
401 | wm8731->supplies); | 428 | wm8731->supplies); |
402 | break; | 429 | break; |
403 | } | 430 | } |
404 | codec->bias_level = level; | 431 | codec->bias_level = level; |
405 | return 0; | 432 | return 0; |
406 | } | 433 | } |
407 | 434 | ||
408 | #define WM8731_RATES SNDRV_PCM_RATE_8000_96000 | 435 | #define WM8731_RATES SNDRV_PCM_RATE_8000_96000 |
409 | 436 | ||
410 | #define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ | 437 | #define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ |
411 | SNDRV_PCM_FMTBIT_S24_LE) | 438 | SNDRV_PCM_FMTBIT_S24_LE) |
412 | 439 | ||
413 | static struct snd_soc_dai_ops wm8731_dai_ops = { | 440 | static struct snd_soc_dai_ops wm8731_dai_ops = { |
414 | .prepare = wm8731_pcm_prepare, | 441 | .prepare = wm8731_pcm_prepare, |
415 | .hw_params = wm8731_hw_params, | 442 | .hw_params = wm8731_hw_params, |
416 | .shutdown = wm8731_shutdown, | 443 | .shutdown = wm8731_shutdown, |
417 | .digital_mute = wm8731_mute, | 444 | .digital_mute = wm8731_mute, |
418 | .set_sysclk = wm8731_set_dai_sysclk, | 445 | .set_sysclk = wm8731_set_dai_sysclk, |
419 | .set_fmt = wm8731_set_dai_fmt, | 446 | .set_fmt = wm8731_set_dai_fmt, |
420 | }; | 447 | }; |
421 | 448 | ||
422 | static struct snd_soc_dai_driver wm8731_dai = { | 449 | static struct snd_soc_dai_driver wm8731_dai = { |
423 | .name = "wm8731-hifi", | 450 | .name = "wm8731-hifi", |
424 | .playback = { | 451 | .playback = { |
425 | .stream_name = "Playback", | 452 | .stream_name = "Playback", |
426 | .channels_min = 1, | 453 | .channels_min = 1, |
427 | .channels_max = 2, | 454 | .channels_max = 2, |
428 | .rates = WM8731_RATES, | 455 | .rates = WM8731_RATES, |
429 | .formats = WM8731_FORMATS,}, | 456 | .formats = WM8731_FORMATS,}, |
430 | .capture = { | 457 | .capture = { |
431 | .stream_name = "Capture", | 458 | .stream_name = "Capture", |
432 | .channels_min = 1, | 459 | .channels_min = 1, |
433 | .channels_max = 2, | 460 | .channels_max = 2, |
434 | .rates = WM8731_RATES, | 461 | .rates = WM8731_RATES, |
435 | .formats = WM8731_FORMATS,}, | 462 | .formats = WM8731_FORMATS,}, |
436 | .ops = &wm8731_dai_ops, | 463 | .ops = &wm8731_dai_ops, |
437 | .symmetric_rates = 1, | 464 | .symmetric_rates = 1, |
438 | }; | 465 | }; |
439 | 466 | ||
440 | #ifdef CONFIG_PM | 467 | #ifdef CONFIG_PM |
441 | static int wm8731_suspend(struct snd_soc_codec *codec, pm_message_t state) | 468 | static int wm8731_suspend(struct snd_soc_codec *codec, pm_message_t state) |
442 | { | 469 | { |
443 | wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); | 470 | wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); |
444 | 471 | ||
445 | return 0; | 472 | return 0; |
446 | } | 473 | } |
447 | 474 | ||
448 | static int wm8731_resume(struct snd_soc_codec *codec) | 475 | static int wm8731_resume(struct snd_soc_codec *codec) |
449 | { | 476 | { |
450 | wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 477 | wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
451 | 478 | ||
452 | return 0; | 479 | return 0; |
453 | } | 480 | } |
454 | #else | 481 | #else |
455 | #define wm8731_suspend NULL | 482 | #define wm8731_suspend NULL |
456 | #define wm8731_resume NULL | 483 | #define wm8731_resume NULL |
457 | #endif | 484 | #endif |
458 | 485 | ||
459 | static int wm8731_probe(struct snd_soc_codec *codec) | 486 | static int wm8731_probe(struct snd_soc_codec *codec) |
460 | { | 487 | { |
461 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); | 488 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); |
462 | int ret = 0, i; | 489 | int ret = 0, i; |
463 | 490 | ||
464 | codec->bias_level = SND_SOC_BIAS_OFF, | 491 | codec->bias_level = SND_SOC_BIAS_OFF, |
465 | 492 | ||
466 | ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8731->control_type); | 493 | ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8731->control_type); |
467 | if (ret < 0) { | 494 | if (ret < 0) { |
468 | dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); | 495 | dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); |
469 | return ret; | 496 | return ret; |
470 | } | 497 | } |
471 | 498 | ||
472 | for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++) | 499 | for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++) |
473 | wm8731->supplies[i].supply = wm8731_supply_names[i]; | 500 | wm8731->supplies[i].supply = wm8731_supply_names[i]; |
474 | 501 | ||
475 | ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies), | 502 | ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies), |
476 | wm8731->supplies); | 503 | wm8731->supplies); |
477 | if (ret != 0) { | 504 | if (ret != 0) { |
478 | dev_err(codec->dev, "Failed to request supplies: %d\n", ret); | 505 | dev_err(codec->dev, "Failed to request supplies: %d\n", ret); |
479 | return ret; | 506 | return ret; |
480 | } | 507 | } |
481 | 508 | ||
482 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), | 509 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), |
483 | wm8731->supplies); | 510 | wm8731->supplies); |
484 | if (ret != 0) { | 511 | if (ret != 0) { |
485 | dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); | 512 | dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); |
486 | goto err_regulator_get; | 513 | goto err_regulator_get; |
487 | } | 514 | } |
488 | 515 | ||
489 | ret = wm8731_reset(codec); | 516 | ret = wm8731_reset(codec); |
490 | if (ret < 0) { | 517 | if (ret < 0) { |
491 | dev_err(codec->dev, "Failed to issue reset: %d\n", ret); | 518 | dev_err(codec->dev, "Failed to issue reset: %d\n", ret); |
492 | goto err_regulator_enable; | 519 | goto err_regulator_enable; |
493 | } | 520 | } |
494 | 521 | ||
495 | wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 522 | wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
496 | 523 | ||
497 | /* Latch the update bits */ | 524 | /* Latch the update bits */ |
498 | snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0); | 525 | snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0); |
499 | snd_soc_update_bits(codec, WM8731_ROUT1V, 0x100, 0); | 526 | snd_soc_update_bits(codec, WM8731_ROUT1V, 0x100, 0); |
500 | snd_soc_update_bits(codec, WM8731_LINVOL, 0x100, 0); | 527 | snd_soc_update_bits(codec, WM8731_LINVOL, 0x100, 0); |
501 | snd_soc_update_bits(codec, WM8731_RINVOL, 0x100, 0); | 528 | snd_soc_update_bits(codec, WM8731_RINVOL, 0x100, 0); |
502 | 529 | ||
503 | /* Disable bypass path by default */ | 530 | /* Disable bypass path by default */ |
504 | snd_soc_update_bits(codec, WM8731_APANA, 0x4, 0); | 531 | snd_soc_update_bits(codec, WM8731_APANA, 0x4, 0); |
505 | 532 | ||
506 | snd_soc_add_controls(codec, wm8731_snd_controls, | 533 | snd_soc_add_controls(codec, wm8731_snd_controls, |
507 | ARRAY_SIZE(wm8731_snd_controls)); | 534 | ARRAY_SIZE(wm8731_snd_controls)); |
508 | wm8731_add_widgets(codec); | 535 | wm8731_add_widgets(codec); |
509 | 536 | ||
510 | /* Regulators will have been enabled by bias management */ | 537 | /* Regulators will have been enabled by bias management */ |
511 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); | 538 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); |
512 | 539 | ||
513 | return 0; | 540 | return 0; |
514 | 541 | ||
515 | err_regulator_enable: | 542 | err_regulator_enable: |
516 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); | 543 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); |
517 | err_regulator_get: | 544 | err_regulator_get: |
518 | regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); | 545 | regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); |
519 | 546 | ||
520 | kfree(wm8731); | 547 | kfree(wm8731); |
521 | return ret; | 548 | return ret; |
522 | } | 549 | } |
523 | 550 | ||
524 | /* power down chip */ | 551 | /* power down chip */ |
525 | static int wm8731_remove(struct snd_soc_codec *codec) | 552 | static int wm8731_remove(struct snd_soc_codec *codec) |
526 | { | 553 | { |
527 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); | 554 | struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); |
528 | 555 | ||
529 | wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); | 556 | wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); |
530 | 557 | ||
531 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); | 558 | regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); |
532 | regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); | 559 | regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); |
533 | 560 | ||
534 | return 0; | 561 | return 0; |
535 | } | 562 | } |
536 | 563 | ||
537 | static struct snd_soc_codec_driver soc_codec_dev_wm8731 = { | 564 | static struct snd_soc_codec_driver soc_codec_dev_wm8731 = { |
538 | .probe = wm8731_probe, | 565 | .probe = wm8731_probe, |
539 | .remove = wm8731_remove, | 566 | .remove = wm8731_remove, |
540 | .suspend = wm8731_suspend, | 567 | .suspend = wm8731_suspend, |
541 | .resume = wm8731_resume, | 568 | .resume = wm8731_resume, |
542 | .set_bias_level = wm8731_set_bias_level, | 569 | .set_bias_level = wm8731_set_bias_level, |
543 | .reg_cache_size = sizeof(wm8731_reg), | 570 | .reg_cache_size = sizeof(wm8731_reg), |
544 | .reg_word_size = sizeof(u16), | 571 | .reg_word_size = sizeof(u16), |
545 | .reg_cache_default = wm8731_reg, | 572 | .reg_cache_default = wm8731_reg, |
546 | }; | 573 | }; |
547 | 574 | ||
548 | #if defined(CONFIG_SPI_MASTER) | 575 | #if defined(CONFIG_SPI_MASTER) |
549 | static int __devinit wm8731_spi_probe(struct spi_device *spi) | 576 | static int __devinit wm8731_spi_probe(struct spi_device *spi) |
550 | { | 577 | { |
551 | struct wm8731_priv *wm8731; | 578 | struct wm8731_priv *wm8731; |
552 | int ret; | 579 | int ret; |
553 | 580 | ||
554 | wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL); | 581 | wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL); |
555 | if (wm8731 == NULL) | 582 | if (wm8731 == NULL) |
556 | return -ENOMEM; | 583 | return -ENOMEM; |
557 | 584 | ||
558 | wm8731->control_type = SND_SOC_SPI; | 585 | wm8731->control_type = SND_SOC_SPI; |
559 | spi_set_drvdata(spi, wm8731); | 586 | spi_set_drvdata(spi, wm8731); |
560 | 587 | ||
561 | ret = snd_soc_register_codec(&spi->dev, | 588 | ret = snd_soc_register_codec(&spi->dev, |
562 | &soc_codec_dev_wm8731, &wm8731_dai, 1); | 589 | &soc_codec_dev_wm8731, &wm8731_dai, 1); |
563 | if (ret < 0) | 590 | if (ret < 0) |
564 | kfree(wm8731); | 591 | kfree(wm8731); |
565 | return ret; | 592 | return ret; |
566 | } | 593 | } |
567 | 594 | ||
568 | static int __devexit wm8731_spi_remove(struct spi_device *spi) | 595 | static int __devexit wm8731_spi_remove(struct spi_device *spi) |
569 | { | 596 | { |
570 | snd_soc_unregister_codec(&spi->dev); | 597 | snd_soc_unregister_codec(&spi->dev); |
571 | kfree(spi_get_drvdata(spi)); | 598 | kfree(spi_get_drvdata(spi)); |
572 | return 0; | 599 | return 0; |
573 | } | 600 | } |
574 | 601 | ||
575 | static struct spi_driver wm8731_spi_driver = { | 602 | static struct spi_driver wm8731_spi_driver = { |
576 | .driver = { | 603 | .driver = { |
577 | .name = "wm8731-codec", | 604 | .name = "wm8731-codec", |
578 | .bus = &spi_bus_type, | 605 | .bus = &spi_bus_type, |
579 | .owner = THIS_MODULE, | 606 | .owner = THIS_MODULE, |
580 | }, | 607 | }, |
581 | .probe = wm8731_spi_probe, | 608 | .probe = wm8731_spi_probe, |
582 | .remove = __devexit_p(wm8731_spi_remove), | 609 | .remove = __devexit_p(wm8731_spi_remove), |
583 | }; | 610 | }; |
584 | #endif /* CONFIG_SPI_MASTER */ | 611 | #endif /* CONFIG_SPI_MASTER */ |
585 | 612 | ||
586 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 613 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
587 | static __devinit int wm8731_i2c_probe(struct i2c_client *i2c, | 614 | static __devinit int wm8731_i2c_probe(struct i2c_client *i2c, |
588 | const struct i2c_device_id *id) | 615 | const struct i2c_device_id *id) |
589 | { | 616 | { |
590 | struct wm8731_priv *wm8731; | 617 | struct wm8731_priv *wm8731; |
591 | int ret; | 618 | int ret; |
592 | 619 | ||
593 | wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL); | 620 | wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL); |
594 | if (wm8731 == NULL) | 621 | if (wm8731 == NULL) |
595 | return -ENOMEM; | 622 | return -ENOMEM; |
596 | 623 | ||
597 | i2c_set_clientdata(i2c, wm8731); | 624 | i2c_set_clientdata(i2c, wm8731); |
598 | wm8731->control_type = SND_SOC_I2C; | 625 | wm8731->control_type = SND_SOC_I2C; |
599 | 626 | ||
600 | ret = snd_soc_register_codec(&i2c->dev, | 627 | ret = snd_soc_register_codec(&i2c->dev, |
601 | &soc_codec_dev_wm8731, &wm8731_dai, 1); | 628 | &soc_codec_dev_wm8731, &wm8731_dai, 1); |
602 | if (ret < 0) | 629 | if (ret < 0) |
603 | kfree(wm8731); | 630 | kfree(wm8731); |
604 | return ret; | 631 | return ret; |
605 | } | 632 | } |
606 | 633 | ||
607 | static __devexit int wm8731_i2c_remove(struct i2c_client *client) | 634 | static __devexit int wm8731_i2c_remove(struct i2c_client *client) |
608 | { | 635 | { |
609 | snd_soc_unregister_codec(&client->dev); | 636 | snd_soc_unregister_codec(&client->dev); |
610 | kfree(i2c_get_clientdata(client)); | 637 | kfree(i2c_get_clientdata(client)); |
611 | return 0; | 638 | return 0; |
612 | } | 639 | } |
613 | 640 | ||
614 | static const struct i2c_device_id wm8731_i2c_id[] = { | 641 | static const struct i2c_device_id wm8731_i2c_id[] = { |
615 | { "wm8731", 0 }, | 642 | { "wm8731", 0 }, |
616 | { } | 643 | { } |
617 | }; | 644 | }; |
618 | MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id); | 645 | MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id); |
619 | 646 | ||
620 | static struct i2c_driver wm8731_i2c_driver = { | 647 | static struct i2c_driver wm8731_i2c_driver = { |
621 | .driver = { | 648 | .driver = { |
622 | .name = "wm8731-codec", | 649 | .name = "wm8731-codec", |
623 | .owner = THIS_MODULE, | 650 | .owner = THIS_MODULE, |
624 | }, | 651 | }, |
625 | .probe = wm8731_i2c_probe, | 652 | .probe = wm8731_i2c_probe, |
626 | .remove = __devexit_p(wm8731_i2c_remove), | 653 | .remove = __devexit_p(wm8731_i2c_remove), |
627 | .id_table = wm8731_i2c_id, | 654 | .id_table = wm8731_i2c_id, |
628 | }; | 655 | }; |
629 | #endif | 656 | #endif |
630 | 657 | ||
631 | static int __init wm8731_modinit(void) | 658 | static int __init wm8731_modinit(void) |
632 | { | 659 | { |
633 | int ret = 0; | 660 | int ret = 0; |
634 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 661 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
635 | ret = i2c_add_driver(&wm8731_i2c_driver); | 662 | ret = i2c_add_driver(&wm8731_i2c_driver); |
636 | if (ret != 0) { | 663 | if (ret != 0) { |
637 | printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n", | 664 | printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n", |
638 | ret); | 665 | ret); |
639 | } | 666 | } |
640 | #endif | 667 | #endif |
641 | #if defined(CONFIG_SPI_MASTER) | 668 | #if defined(CONFIG_SPI_MASTER) |
642 | ret = spi_register_driver(&wm8731_spi_driver); | 669 | ret = spi_register_driver(&wm8731_spi_driver); |
643 | if (ret != 0) { | 670 | if (ret != 0) { |
644 | printk(KERN_ERR "Failed to register WM8731 SPI driver: %d\n", | 671 | printk(KERN_ERR "Failed to register WM8731 SPI driver: %d\n", |
645 | ret); | 672 | ret); |
646 | } | 673 | } |
647 | #endif | 674 | #endif |
648 | return ret; | 675 | return ret; |
649 | } | 676 | } |
650 | module_init(wm8731_modinit); | 677 | module_init(wm8731_modinit); |
651 | 678 | ||
652 | static void __exit wm8731_exit(void) | 679 | static void __exit wm8731_exit(void) |
653 | { | 680 | { |
654 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 681 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
655 | i2c_del_driver(&wm8731_i2c_driver); | 682 | i2c_del_driver(&wm8731_i2c_driver); |
656 | #endif | 683 | #endif |
657 | #if defined(CONFIG_SPI_MASTER) | 684 | #if defined(CONFIG_SPI_MASTER) |
658 | spi_unregister_driver(&wm8731_spi_driver); | 685 | spi_unregister_driver(&wm8731_spi_driver); |
659 | #endif | 686 | #endif |
660 | } | 687 | } |
661 | module_exit(wm8731_exit); | 688 | module_exit(wm8731_exit); |
662 | 689 | ||
663 | MODULE_DESCRIPTION("ASoC WM8731 driver"); | 690 | MODULE_DESCRIPTION("ASoC WM8731 driver"); |
664 | MODULE_AUTHOR("Richard Purdie"); | 691 | MODULE_AUTHOR("Richard Purdie"); |
665 | MODULE_LICENSE("GPL"); | 692 | MODULE_LICENSE("GPL"); |
666 | 693 |
sound/soc/codecs/wm8731.h
1 | /* | 1 | /* |
2 | * wm8731.h -- WM8731 Soc Audio driver | 2 | * wm8731.h -- WM8731 Soc Audio driver |
3 | * | 3 | * |
4 | * Copyright 2005 Openedhand Ltd. | 4 | * Copyright 2005 Openedhand Ltd. |
5 | * | 5 | * |
6 | * Author: Richard Purdie <richard@openedhand.com> | 6 | * Author: Richard Purdie <richard@openedhand.com> |
7 | * | 7 | * |
8 | * Based on wm8753.h | 8 | * Based on wm8753.h |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2 as | 11 | * it under the terms of the GNU General Public License version 2 as |
12 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #ifndef _WM8731_H | 15 | #ifndef _WM8731_H |
16 | #define _WM8731_H | 16 | #define _WM8731_H |
17 | 17 | ||
18 | /* WM8731 register space */ | 18 | /* WM8731 register space */ |
19 | 19 | ||
20 | #define WM8731_LINVOL 0x00 | 20 | #define WM8731_LINVOL 0x00 |
21 | #define WM8731_RINVOL 0x01 | 21 | #define WM8731_RINVOL 0x01 |
22 | #define WM8731_LOUT1V 0x02 | 22 | #define WM8731_LOUT1V 0x02 |
23 | #define WM8731_ROUT1V 0x03 | 23 | #define WM8731_ROUT1V 0x03 |
24 | #define WM8731_APANA 0x04 | 24 | #define WM8731_APANA 0x04 |
25 | #define WM8731_APDIGI 0x05 | 25 | #define WM8731_APDIGI 0x05 |
26 | #define WM8731_PWR 0x06 | 26 | #define WM8731_PWR 0x06 |
27 | #define WM8731_IFACE 0x07 | 27 | #define WM8731_IFACE 0x07 |
28 | #define WM8731_SRATE 0x08 | 28 | #define WM8731_SRATE 0x08 |
29 | #define WM8731_ACTIVE 0x09 | 29 | #define WM8731_ACTIVE 0x09 |
30 | #define WM8731_RESET 0x0f | 30 | #define WM8731_RESET 0x0f |
31 | 31 | ||
32 | #define WM8731_CACHEREGNUM 10 | 32 | #define WM8731_CACHEREGNUM 10 |
33 | 33 | ||
34 | #define WM8731_SYSCLK 0 | 34 | #define WM8731_SYSCLK_XTAL 1 |
35 | #define WM8731_SYSCLK_MCLK 2 | ||
36 | |||
35 | #define WM8731_DAI 0 | 37 | #define WM8731_DAI 0 |
36 | 38 | ||
37 | #endif | 39 | #endif |
38 | 40 |
sound/soc/pxa/corgi.c
1 | /* | 1 | /* |
2 | * corgi.c -- SoC audio for Corgi | 2 | * corgi.c -- SoC audio for Corgi |
3 | * | 3 | * |
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 <lrg@slimlogic.co.uk> | 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 |
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 |
12 | * Free Software Foundation; either version 2 of the License, or (at your | 12 | * Free Software Foundation; either version 2 of the License, or (at your |
13 | * option) any later version. | 13 | * option) any later version. |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/moduleparam.h> | 17 | #include <linux/moduleparam.h> |
18 | #include <linux/timer.h> | 18 | #include <linux/timer.h> |
19 | #include <linux/i2c.h> | 19 | #include <linux/i2c.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 <linux/gpio.h> |
23 | #include <sound/core.h> | 23 | #include <sound/core.h> |
24 | #include <sound/pcm.h> | 24 | #include <sound/pcm.h> |
25 | #include <sound/soc.h> | 25 | #include <sound/soc.h> |
26 | #include <sound/soc-dapm.h> | 26 | #include <sound/soc-dapm.h> |
27 | 27 | ||
28 | #include <asm/mach-types.h> | 28 | #include <asm/mach-types.h> |
29 | #include <mach/corgi.h> | 29 | #include <mach/corgi.h> |
30 | #include <mach/audio.h> | 30 | #include <mach/audio.h> |
31 | 31 | ||
32 | #include "../codecs/wm8731.h" | 32 | #include "../codecs/wm8731.h" |
33 | #include "pxa2xx-i2s.h" | 33 | #include "pxa2xx-i2s.h" |
34 | 34 | ||
35 | #define CORGI_HP 0 | 35 | #define CORGI_HP 0 |
36 | #define CORGI_MIC 1 | 36 | #define CORGI_MIC 1 |
37 | #define CORGI_LINE 2 | 37 | #define CORGI_LINE 2 |
38 | #define CORGI_HEADSET 3 | 38 | #define CORGI_HEADSET 3 |
39 | #define CORGI_HP_OFF 4 | 39 | #define CORGI_HP_OFF 4 |
40 | #define CORGI_SPK_ON 0 | 40 | #define CORGI_SPK_ON 0 |
41 | #define CORGI_SPK_OFF 1 | 41 | #define CORGI_SPK_OFF 1 |
42 | 42 | ||
43 | /* audio clock in Hz - rounded from 12.235MHz */ | 43 | /* audio clock in Hz - rounded from 12.235MHz */ |
44 | #define CORGI_AUDIO_CLOCK 12288000 | 44 | #define CORGI_AUDIO_CLOCK 12288000 |
45 | 45 | ||
46 | static int corgi_jack_func; | 46 | static int corgi_jack_func; |
47 | static int corgi_spk_func; | 47 | static int corgi_spk_func; |
48 | 48 | ||
49 | static void corgi_ext_control(struct snd_soc_codec *codec) | 49 | static void corgi_ext_control(struct snd_soc_codec *codec) |
50 | { | 50 | { |
51 | /* set up jack connection */ | 51 | /* set up jack connection */ |
52 | switch (corgi_jack_func) { | 52 | switch (corgi_jack_func) { |
53 | case CORGI_HP: | 53 | case CORGI_HP: |
54 | /* set = unmute headphone */ | 54 | /* set = unmute headphone */ |
55 | gpio_set_value(CORGI_GPIO_MUTE_L, 1); | 55 | gpio_set_value(CORGI_GPIO_MUTE_L, 1); |
56 | gpio_set_value(CORGI_GPIO_MUTE_R, 1); | 56 | gpio_set_value(CORGI_GPIO_MUTE_R, 1); |
57 | snd_soc_dapm_disable_pin(codec, "Mic Jack"); | 57 | snd_soc_dapm_disable_pin(codec, "Mic Jack"); |
58 | snd_soc_dapm_disable_pin(codec, "Line Jack"); | 58 | snd_soc_dapm_disable_pin(codec, "Line Jack"); |
59 | snd_soc_dapm_enable_pin(codec, "Headphone Jack"); | 59 | snd_soc_dapm_enable_pin(codec, "Headphone Jack"); |
60 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); | 60 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); |
61 | break; | 61 | break; |
62 | case CORGI_MIC: | 62 | case CORGI_MIC: |
63 | /* reset = mute headphone */ | 63 | /* reset = mute headphone */ |
64 | gpio_set_value(CORGI_GPIO_MUTE_L, 0); | 64 | gpio_set_value(CORGI_GPIO_MUTE_L, 0); |
65 | gpio_set_value(CORGI_GPIO_MUTE_R, 0); | 65 | gpio_set_value(CORGI_GPIO_MUTE_R, 0); |
66 | snd_soc_dapm_enable_pin(codec, "Mic Jack"); | 66 | snd_soc_dapm_enable_pin(codec, "Mic Jack"); |
67 | snd_soc_dapm_disable_pin(codec, "Line Jack"); | 67 | snd_soc_dapm_disable_pin(codec, "Line Jack"); |
68 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); | 68 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); |
69 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); | 69 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); |
70 | break; | 70 | break; |
71 | case CORGI_LINE: | 71 | case CORGI_LINE: |
72 | gpio_set_value(CORGI_GPIO_MUTE_L, 0); | 72 | gpio_set_value(CORGI_GPIO_MUTE_L, 0); |
73 | gpio_set_value(CORGI_GPIO_MUTE_R, 0); | 73 | gpio_set_value(CORGI_GPIO_MUTE_R, 0); |
74 | snd_soc_dapm_disable_pin(codec, "Mic Jack"); | 74 | snd_soc_dapm_disable_pin(codec, "Mic Jack"); |
75 | snd_soc_dapm_enable_pin(codec, "Line Jack"); | 75 | snd_soc_dapm_enable_pin(codec, "Line Jack"); |
76 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); | 76 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); |
77 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); | 77 | snd_soc_dapm_disable_pin(codec, "Headset Jack"); |
78 | break; | 78 | break; |
79 | case CORGI_HEADSET: | 79 | case CORGI_HEADSET: |
80 | gpio_set_value(CORGI_GPIO_MUTE_L, 0); | 80 | gpio_set_value(CORGI_GPIO_MUTE_L, 0); |
81 | gpio_set_value(CORGI_GPIO_MUTE_R, 1); | 81 | gpio_set_value(CORGI_GPIO_MUTE_R, 1); |
82 | snd_soc_dapm_enable_pin(codec, "Mic Jack"); | 82 | snd_soc_dapm_enable_pin(codec, "Mic Jack"); |
83 | snd_soc_dapm_disable_pin(codec, "Line Jack"); | 83 | snd_soc_dapm_disable_pin(codec, "Line Jack"); |
84 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); | 84 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); |
85 | snd_soc_dapm_enable_pin(codec, "Headset Jack"); | 85 | snd_soc_dapm_enable_pin(codec, "Headset Jack"); |
86 | break; | 86 | break; |
87 | } | 87 | } |
88 | 88 | ||
89 | if (corgi_spk_func == CORGI_SPK_ON) | 89 | if (corgi_spk_func == CORGI_SPK_ON) |
90 | snd_soc_dapm_enable_pin(codec, "Ext Spk"); | 90 | snd_soc_dapm_enable_pin(codec, "Ext Spk"); |
91 | else | 91 | else |
92 | snd_soc_dapm_disable_pin(codec, "Ext Spk"); | 92 | snd_soc_dapm_disable_pin(codec, "Ext Spk"); |
93 | 93 | ||
94 | /* signal a DAPM event */ | 94 | /* signal a DAPM event */ |
95 | snd_soc_dapm_sync(codec); | 95 | snd_soc_dapm_sync(codec); |
96 | } | 96 | } |
97 | 97 | ||
98 | static int corgi_startup(struct snd_pcm_substream *substream) | 98 | static int corgi_startup(struct snd_pcm_substream *substream) |
99 | { | 99 | { |
100 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 100 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
101 | struct snd_soc_codec *codec = rtd->codec; | 101 | struct snd_soc_codec *codec = rtd->codec; |
102 | 102 | ||
103 | /* check the jack status at stream startup */ | 103 | /* check the jack status at stream startup */ |
104 | corgi_ext_control(codec); | 104 | corgi_ext_control(codec); |
105 | return 0; | 105 | return 0; |
106 | } | 106 | } |
107 | 107 | ||
108 | /* we need to unmute the HP at shutdown as the mute burns power on corgi */ | 108 | /* we need to unmute the HP at shutdown as the mute burns power on corgi */ |
109 | static void corgi_shutdown(struct snd_pcm_substream *substream) | 109 | static void corgi_shutdown(struct snd_pcm_substream *substream) |
110 | { | 110 | { |
111 | /* set = unmute headphone */ | 111 | /* set = unmute headphone */ |
112 | gpio_set_value(CORGI_GPIO_MUTE_L, 1); | 112 | gpio_set_value(CORGI_GPIO_MUTE_L, 1); |
113 | gpio_set_value(CORGI_GPIO_MUTE_R, 1); | 113 | gpio_set_value(CORGI_GPIO_MUTE_R, 1); |
114 | } | 114 | } |
115 | 115 | ||
116 | static int corgi_hw_params(struct snd_pcm_substream *substream, | 116 | static int corgi_hw_params(struct snd_pcm_substream *substream, |
117 | struct snd_pcm_hw_params *params) | 117 | struct snd_pcm_hw_params *params) |
118 | { | 118 | { |
119 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 119 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
120 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | 120 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
121 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | 121 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
122 | unsigned int clk = 0; | 122 | unsigned int clk = 0; |
123 | int ret = 0; | 123 | int ret = 0; |
124 | 124 | ||
125 | switch (params_rate(params)) { | 125 | switch (params_rate(params)) { |
126 | case 8000: | 126 | case 8000: |
127 | case 16000: | 127 | case 16000: |
128 | case 48000: | 128 | case 48000: |
129 | case 96000: | 129 | case 96000: |
130 | clk = 12288000; | 130 | clk = 12288000; |
131 | break; | 131 | break; |
132 | case 11025: | 132 | case 11025: |
133 | case 22050: | 133 | case 22050: |
134 | case 44100: | 134 | case 44100: |
135 | clk = 11289600; | 135 | clk = 11289600; |
136 | break; | 136 | break; |
137 | } | 137 | } |
138 | 138 | ||
139 | /* set codec DAI configuration */ | 139 | /* set codec DAI configuration */ |
140 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | 140 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | |
141 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | 141 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); |
142 | if (ret < 0) | 142 | if (ret < 0) |
143 | return ret; | 143 | return ret; |
144 | 144 | ||
145 | /* set cpu DAI configuration */ | 145 | /* set cpu DAI configuration */ |
146 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | 146 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | |
147 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | 147 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); |
148 | if (ret < 0) | 148 | if (ret < 0) |
149 | return ret; | 149 | return ret; |
150 | 150 | ||
151 | /* set the codec system clock for DAC and ADC */ | 151 | /* set the codec system clock for DAC and ADC */ |
152 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, clk, | 152 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, clk, |
153 | SND_SOC_CLOCK_IN); | 153 | SND_SOC_CLOCK_IN); |
154 | if (ret < 0) | 154 | if (ret < 0) |
155 | return ret; | 155 | return ret; |
156 | 156 | ||
157 | /* set the I2S system clock as input (unused) */ | 157 | /* set the I2S system clock as input (unused) */ |
158 | ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, | 158 | ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, |
159 | SND_SOC_CLOCK_IN); | 159 | SND_SOC_CLOCK_IN); |
160 | if (ret < 0) | 160 | if (ret < 0) |
161 | return ret; | 161 | return ret; |
162 | 162 | ||
163 | return 0; | 163 | return 0; |
164 | } | 164 | } |
165 | 165 | ||
166 | static struct snd_soc_ops corgi_ops = { | 166 | static struct snd_soc_ops corgi_ops = { |
167 | .startup = corgi_startup, | 167 | .startup = corgi_startup, |
168 | .hw_params = corgi_hw_params, | 168 | .hw_params = corgi_hw_params, |
169 | .shutdown = corgi_shutdown, | 169 | .shutdown = corgi_shutdown, |
170 | }; | 170 | }; |
171 | 171 | ||
172 | static int corgi_get_jack(struct snd_kcontrol *kcontrol, | 172 | static int corgi_get_jack(struct snd_kcontrol *kcontrol, |
173 | struct snd_ctl_elem_value *ucontrol) | 173 | struct snd_ctl_elem_value *ucontrol) |
174 | { | 174 | { |
175 | ucontrol->value.integer.value[0] = corgi_jack_func; | 175 | ucontrol->value.integer.value[0] = corgi_jack_func; |
176 | return 0; | 176 | return 0; |
177 | } | 177 | } |
178 | 178 | ||
179 | static int corgi_set_jack(struct snd_kcontrol *kcontrol, | 179 | static int corgi_set_jack(struct snd_kcontrol *kcontrol, |
180 | struct snd_ctl_elem_value *ucontrol) | 180 | struct snd_ctl_elem_value *ucontrol) |
181 | { | 181 | { |
182 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 182 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
183 | 183 | ||
184 | if (corgi_jack_func == ucontrol->value.integer.value[0]) | 184 | if (corgi_jack_func == ucontrol->value.integer.value[0]) |
185 | return 0; | 185 | return 0; |
186 | 186 | ||
187 | corgi_jack_func = ucontrol->value.integer.value[0]; | 187 | corgi_jack_func = ucontrol->value.integer.value[0]; |
188 | corgi_ext_control(codec); | 188 | corgi_ext_control(codec); |
189 | return 1; | 189 | return 1; |
190 | } | 190 | } |
191 | 191 | ||
192 | static int corgi_get_spk(struct snd_kcontrol *kcontrol, | 192 | static int corgi_get_spk(struct snd_kcontrol *kcontrol, |
193 | struct snd_ctl_elem_value *ucontrol) | 193 | struct snd_ctl_elem_value *ucontrol) |
194 | { | 194 | { |
195 | ucontrol->value.integer.value[0] = corgi_spk_func; | 195 | ucontrol->value.integer.value[0] = corgi_spk_func; |
196 | return 0; | 196 | return 0; |
197 | } | 197 | } |
198 | 198 | ||
199 | static int corgi_set_spk(struct snd_kcontrol *kcontrol, | 199 | static int corgi_set_spk(struct snd_kcontrol *kcontrol, |
200 | struct snd_ctl_elem_value *ucontrol) | 200 | struct snd_ctl_elem_value *ucontrol) |
201 | { | 201 | { |
202 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 202 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
203 | 203 | ||
204 | if (corgi_spk_func == ucontrol->value.integer.value[0]) | 204 | if (corgi_spk_func == ucontrol->value.integer.value[0]) |
205 | return 0; | 205 | return 0; |
206 | 206 | ||
207 | corgi_spk_func = ucontrol->value.integer.value[0]; | 207 | corgi_spk_func = ucontrol->value.integer.value[0]; |
208 | corgi_ext_control(codec); | 208 | corgi_ext_control(codec); |
209 | return 1; | 209 | return 1; |
210 | } | 210 | } |
211 | 211 | ||
212 | static int corgi_amp_event(struct snd_soc_dapm_widget *w, | 212 | static int corgi_amp_event(struct snd_soc_dapm_widget *w, |
213 | struct snd_kcontrol *k, int event) | 213 | struct snd_kcontrol *k, int event) |
214 | { | 214 | { |
215 | gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event)); | 215 | gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event)); |
216 | return 0; | 216 | return 0; |
217 | } | 217 | } |
218 | 218 | ||
219 | static int corgi_mic_event(struct snd_soc_dapm_widget *w, | 219 | static int corgi_mic_event(struct snd_soc_dapm_widget *w, |
220 | struct snd_kcontrol *k, int event) | 220 | struct snd_kcontrol *k, int event) |
221 | { | 221 | { |
222 | gpio_set_value(CORGI_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event)); | 222 | gpio_set_value(CORGI_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event)); |
223 | return 0; | 223 | return 0; |
224 | } | 224 | } |
225 | 225 | ||
226 | /* corgi machine dapm widgets */ | 226 | /* corgi machine dapm widgets */ |
227 | static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { | 227 | static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { |
228 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | 228 | SND_SOC_DAPM_HP("Headphone Jack", NULL), |
229 | SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event), | 229 | SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event), |
230 | SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event), | 230 | SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event), |
231 | SND_SOC_DAPM_LINE("Line Jack", NULL), | 231 | SND_SOC_DAPM_LINE("Line Jack", NULL), |
232 | SND_SOC_DAPM_HP("Headset Jack", NULL), | 232 | SND_SOC_DAPM_HP("Headset Jack", NULL), |
233 | }; | 233 | }; |
234 | 234 | ||
235 | /* Corgi machine audio map (connections to the codec pins) */ | 235 | /* Corgi machine audio map (connections to the codec pins) */ |
236 | static const struct snd_soc_dapm_route audio_map[] = { | 236 | static const struct snd_soc_dapm_route audio_map[] = { |
237 | 237 | ||
238 | /* headset Jack - in = micin, out = LHPOUT*/ | 238 | /* headset Jack - in = micin, out = LHPOUT*/ |
239 | {"Headset Jack", NULL, "LHPOUT"}, | 239 | {"Headset Jack", NULL, "LHPOUT"}, |
240 | 240 | ||
241 | /* headphone connected to LHPOUT1, RHPOUT1 */ | 241 | /* headphone connected to LHPOUT1, RHPOUT1 */ |
242 | {"Headphone Jack", NULL, "LHPOUT"}, | 242 | {"Headphone Jack", NULL, "LHPOUT"}, |
243 | {"Headphone Jack", NULL, "RHPOUT"}, | 243 | {"Headphone Jack", NULL, "RHPOUT"}, |
244 | 244 | ||
245 | /* speaker connected to LOUT, ROUT */ | 245 | /* speaker connected to LOUT, ROUT */ |
246 | {"Ext Spk", NULL, "ROUT"}, | 246 | {"Ext Spk", NULL, "ROUT"}, |
247 | {"Ext Spk", NULL, "LOUT"}, | 247 | {"Ext Spk", NULL, "LOUT"}, |
248 | 248 | ||
249 | /* mic is connected to MICIN (via right channel of headphone jack) */ | 249 | /* mic is connected to MICIN (via right channel of headphone jack) */ |
250 | {"MICIN", NULL, "Mic Jack"}, | 250 | {"MICIN", NULL, "Mic Jack"}, |
251 | 251 | ||
252 | /* Same as the above but no mic bias for line signals */ | 252 | /* Same as the above but no mic bias for line signals */ |
253 | {"MICIN", NULL, "Line Jack"}, | 253 | {"MICIN", NULL, "Line Jack"}, |
254 | }; | 254 | }; |
255 | 255 | ||
256 | static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", | 256 | static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", |
257 | "Off"}; | 257 | "Off"}; |
258 | static const char *spk_function[] = {"On", "Off"}; | 258 | static const char *spk_function[] = {"On", "Off"}; |
259 | static const struct soc_enum corgi_enum[] = { | 259 | static const struct soc_enum corgi_enum[] = { |
260 | SOC_ENUM_SINGLE_EXT(5, jack_function), | 260 | SOC_ENUM_SINGLE_EXT(5, jack_function), |
261 | SOC_ENUM_SINGLE_EXT(2, spk_function), | 261 | SOC_ENUM_SINGLE_EXT(2, spk_function), |
262 | }; | 262 | }; |
263 | 263 | ||
264 | static const struct snd_kcontrol_new wm8731_corgi_controls[] = { | 264 | static const struct snd_kcontrol_new wm8731_corgi_controls[] = { |
265 | SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack, | 265 | SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack, |
266 | corgi_set_jack), | 266 | corgi_set_jack), |
267 | SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk, | 267 | SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk, |
268 | corgi_set_spk), | 268 | corgi_set_spk), |
269 | }; | 269 | }; |
270 | 270 | ||
271 | /* | 271 | /* |
272 | * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device | 272 | * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device |
273 | */ | 273 | */ |
274 | static int corgi_wm8731_init(struct snd_soc_pcm_runtime *rtd) | 274 | static int corgi_wm8731_init(struct snd_soc_pcm_runtime *rtd) |
275 | { | 275 | { |
276 | struct snd_soc_codec *codec = rtd->codec; | 276 | struct snd_soc_codec *codec = rtd->codec; |
277 | int err; | 277 | int err; |
278 | 278 | ||
279 | snd_soc_dapm_nc_pin(codec, "LLINEIN"); | 279 | snd_soc_dapm_nc_pin(codec, "LLINEIN"); |
280 | snd_soc_dapm_nc_pin(codec, "RLINEIN"); | 280 | snd_soc_dapm_nc_pin(codec, "RLINEIN"); |
281 | 281 | ||
282 | /* Add corgi specific controls */ | 282 | /* Add corgi specific controls */ |
283 | err = snd_soc_add_controls(codec, wm8731_corgi_controls, | 283 | err = snd_soc_add_controls(codec, wm8731_corgi_controls, |
284 | ARRAY_SIZE(wm8731_corgi_controls)); | 284 | ARRAY_SIZE(wm8731_corgi_controls)); |
285 | if (err < 0) | 285 | if (err < 0) |
286 | return err; | 286 | return err; |
287 | 287 | ||
288 | /* Add corgi specific widgets */ | 288 | /* Add corgi specific widgets */ |
289 | snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets, | 289 | snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets, |
290 | ARRAY_SIZE(wm8731_dapm_widgets)); | 290 | ARRAY_SIZE(wm8731_dapm_widgets)); |
291 | 291 | ||
292 | /* Set up corgi specific audio path audio_map */ | 292 | /* Set up corgi specific audio path audio_map */ |
293 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | 293 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); |
294 | 294 | ||
295 | snd_soc_dapm_sync(codec); | 295 | snd_soc_dapm_sync(codec); |
296 | return 0; | 296 | return 0; |
297 | } | 297 | } |
298 | 298 | ||
299 | /* corgi digital audio interface glue - connects codec <--> CPU */ | 299 | /* corgi digital audio interface glue - connects codec <--> CPU */ |
300 | static struct snd_soc_dai_link corgi_dai = { | 300 | static struct snd_soc_dai_link corgi_dai = { |
301 | .name = "WM8731", | 301 | .name = "WM8731", |
302 | .stream_name = "WM8731", | 302 | .stream_name = "WM8731", |
303 | .cpu_dai_name = "pxa-is2-dai", | 303 | .cpu_dai_name = "pxa-is2-dai", |
304 | .codec_dai_name = "wm8731-hifi", | 304 | .codec_dai_name = "wm8731-hifi", |
305 | .platform_name = "pxa-pcm-audio", | 305 | .platform_name = "pxa-pcm-audio", |
306 | .codec_name = "wm8731-codec-0.001a", | 306 | .codec_name = "wm8731-codec-0.001a", |
307 | .init = corgi_wm8731_init, | 307 | .init = corgi_wm8731_init, |
308 | .ops = &corgi_ops, | 308 | .ops = &corgi_ops, |
309 | }; | 309 | }; |
310 | 310 | ||
311 | /* corgi audio machine driver */ | 311 | /* corgi audio machine driver */ |
312 | static struct snd_soc_card snd_soc_corgi = { | 312 | static struct snd_soc_card snd_soc_corgi = { |
313 | .name = "Corgi", | 313 | .name = "Corgi", |
314 | .dai_link = &corgi_dai, | 314 | .dai_link = &corgi_dai, |
315 | .num_links = 1, | 315 | .num_links = 1, |
316 | }; | 316 | }; |
317 | 317 | ||
318 | static struct platform_device *corgi_snd_device; | 318 | static struct platform_device *corgi_snd_device; |
319 | 319 | ||
320 | static int __init corgi_init(void) | 320 | static int __init corgi_init(void) |
321 | { | 321 | { |
322 | int ret; | 322 | int ret; |
323 | 323 | ||
324 | if (!(machine_is_corgi() || machine_is_shepherd() || | 324 | if (!(machine_is_corgi() || machine_is_shepherd() || |
325 | machine_is_husky())) | 325 | machine_is_husky())) |
326 | return -ENODEV; | 326 | return -ENODEV; |
327 | 327 | ||
328 | corgi_snd_device = platform_device_alloc("soc-audio", -1); | 328 | corgi_snd_device = platform_device_alloc("soc-audio", -1); |
329 | if (!corgi_snd_device) | 329 | if (!corgi_snd_device) |
330 | return -ENOMEM; | 330 | return -ENOMEM; |
331 | 331 | ||
332 | platform_set_drvdata(corgi_snd_device, &snd_soc_corgi); | 332 | platform_set_drvdata(corgi_snd_device, &snd_soc_corgi); |
333 | ret = platform_device_add(corgi_snd_device); | 333 | ret = platform_device_add(corgi_snd_device); |
334 | 334 | ||
335 | if (ret) | 335 | if (ret) |
336 | platform_device_put(corgi_snd_device); | 336 | platform_device_put(corgi_snd_device); |
337 | 337 | ||
338 | return ret; | 338 | return ret; |
339 | } | 339 | } |
340 | 340 | ||
341 | static void __exit corgi_exit(void) | 341 | static void __exit corgi_exit(void) |
342 | { | 342 | { |
343 | platform_device_unregister(corgi_snd_device); | 343 | platform_device_unregister(corgi_snd_device); |
344 | } | 344 | } |
345 | 345 | ||
346 | module_init(corgi_init); | 346 | module_init(corgi_init); |
347 | module_exit(corgi_exit); | 347 | module_exit(corgi_exit); |
348 | 348 | ||
349 | /* Module information */ | 349 | /* Module information */ |
350 | MODULE_AUTHOR("Richard Purdie"); | 350 | MODULE_AUTHOR("Richard Purdie"); |
351 | MODULE_DESCRIPTION("ALSA SoC Corgi"); | 351 | MODULE_DESCRIPTION("ALSA SoC Corgi"); |
352 | MODULE_LICENSE("GPL"); | 352 | MODULE_LICENSE("GPL"); |
353 | 353 |
sound/soc/pxa/poodle.c
1 | /* | 1 | /* |
2 | * poodle.c -- SoC audio for Poodle | 2 | * poodle.c -- SoC audio for Poodle |
3 | * | 3 | * |
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 <lrg@slimlogic.co.uk> | 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 |
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 |
12 | * Free Software Foundation; either version 2 of the License, or (at your | 12 | * Free Software Foundation; either version 2 of the License, or (at your |
13 | * option) any later version. | 13 | * option) any later version. |
14 | * | 14 | * |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/moduleparam.h> | 18 | #include <linux/moduleparam.h> |
19 | #include <linux/timer.h> | 19 | #include <linux/timer.h> |
20 | #include <linux/i2c.h> | 20 | #include <linux/i2c.h> |
21 | #include <linux/interrupt.h> | 21 | #include <linux/interrupt.h> |
22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
23 | #include <sound/core.h> | 23 | #include <sound/core.h> |
24 | #include <sound/pcm.h> | 24 | #include <sound/pcm.h> |
25 | #include <sound/soc.h> | 25 | #include <sound/soc.h> |
26 | #include <sound/soc-dapm.h> | 26 | #include <sound/soc-dapm.h> |
27 | 27 | ||
28 | #include <asm/mach-types.h> | 28 | #include <asm/mach-types.h> |
29 | #include <asm/hardware/locomo.h> | 29 | #include <asm/hardware/locomo.h> |
30 | #include <mach/poodle.h> | 30 | #include <mach/poodle.h> |
31 | #include <mach/audio.h> | 31 | #include <mach/audio.h> |
32 | 32 | ||
33 | #include "../codecs/wm8731.h" | 33 | #include "../codecs/wm8731.h" |
34 | #include "pxa2xx-i2s.h" | 34 | #include "pxa2xx-i2s.h" |
35 | 35 | ||
36 | #define POODLE_HP 1 | 36 | #define POODLE_HP 1 |
37 | #define POODLE_HP_OFF 0 | 37 | #define POODLE_HP_OFF 0 |
38 | #define POODLE_SPK_ON 1 | 38 | #define POODLE_SPK_ON 1 |
39 | #define POODLE_SPK_OFF 0 | 39 | #define POODLE_SPK_OFF 0 |
40 | 40 | ||
41 | /* audio clock in Hz - rounded from 12.235MHz */ | 41 | /* audio clock in Hz - rounded from 12.235MHz */ |
42 | #define POODLE_AUDIO_CLOCK 12288000 | 42 | #define POODLE_AUDIO_CLOCK 12288000 |
43 | 43 | ||
44 | static int poodle_jack_func; | 44 | static int poodle_jack_func; |
45 | static int poodle_spk_func; | 45 | static int poodle_spk_func; |
46 | 46 | ||
47 | static void poodle_ext_control(struct snd_soc_codec *codec) | 47 | static void poodle_ext_control(struct snd_soc_codec *codec) |
48 | { | 48 | { |
49 | /* set up jack connection */ | 49 | /* set up jack connection */ |
50 | if (poodle_jack_func == POODLE_HP) { | 50 | if (poodle_jack_func == POODLE_HP) { |
51 | /* set = unmute headphone */ | 51 | /* set = unmute headphone */ |
52 | locomo_gpio_write(&poodle_locomo_device.dev, | 52 | locomo_gpio_write(&poodle_locomo_device.dev, |
53 | POODLE_LOCOMO_GPIO_MUTE_L, 1); | 53 | POODLE_LOCOMO_GPIO_MUTE_L, 1); |
54 | locomo_gpio_write(&poodle_locomo_device.dev, | 54 | locomo_gpio_write(&poodle_locomo_device.dev, |
55 | POODLE_LOCOMO_GPIO_MUTE_R, 1); | 55 | POODLE_LOCOMO_GPIO_MUTE_R, 1); |
56 | snd_soc_dapm_enable_pin(codec, "Headphone Jack"); | 56 | snd_soc_dapm_enable_pin(codec, "Headphone Jack"); |
57 | } else { | 57 | } else { |
58 | locomo_gpio_write(&poodle_locomo_device.dev, | 58 | locomo_gpio_write(&poodle_locomo_device.dev, |
59 | POODLE_LOCOMO_GPIO_MUTE_L, 0); | 59 | POODLE_LOCOMO_GPIO_MUTE_L, 0); |
60 | locomo_gpio_write(&poodle_locomo_device.dev, | 60 | locomo_gpio_write(&poodle_locomo_device.dev, |
61 | POODLE_LOCOMO_GPIO_MUTE_R, 0); | 61 | POODLE_LOCOMO_GPIO_MUTE_R, 0); |
62 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); | 62 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); |
63 | } | 63 | } |
64 | 64 | ||
65 | /* set the enpoints to their new connetion states */ | 65 | /* set the enpoints to their new connetion states */ |
66 | if (poodle_spk_func == POODLE_SPK_ON) | 66 | if (poodle_spk_func == POODLE_SPK_ON) |
67 | snd_soc_dapm_enable_pin(codec, "Ext Spk"); | 67 | snd_soc_dapm_enable_pin(codec, "Ext Spk"); |
68 | else | 68 | else |
69 | snd_soc_dapm_disable_pin(codec, "Ext Spk"); | 69 | snd_soc_dapm_disable_pin(codec, "Ext Spk"); |
70 | 70 | ||
71 | /* signal a DAPM event */ | 71 | /* signal a DAPM event */ |
72 | snd_soc_dapm_sync(codec); | 72 | snd_soc_dapm_sync(codec); |
73 | } | 73 | } |
74 | 74 | ||
75 | static int poodle_startup(struct snd_pcm_substream *substream) | 75 | static int poodle_startup(struct snd_pcm_substream *substream) |
76 | { | 76 | { |
77 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 77 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
78 | struct snd_soc_codec *codec = rtd->codec; | 78 | struct snd_soc_codec *codec = rtd->codec; |
79 | 79 | ||
80 | /* check the jack status at stream startup */ | 80 | /* check the jack status at stream startup */ |
81 | poodle_ext_control(codec); | 81 | poodle_ext_control(codec); |
82 | return 0; | 82 | return 0; |
83 | } | 83 | } |
84 | 84 | ||
85 | /* we need to unmute the HP at shutdown as the mute burns power on poodle */ | 85 | /* we need to unmute the HP at shutdown as the mute burns power on poodle */ |
86 | static void poodle_shutdown(struct snd_pcm_substream *substream) | 86 | static void poodle_shutdown(struct snd_pcm_substream *substream) |
87 | { | 87 | { |
88 | /* set = unmute headphone */ | 88 | /* set = unmute headphone */ |
89 | locomo_gpio_write(&poodle_locomo_device.dev, | 89 | locomo_gpio_write(&poodle_locomo_device.dev, |
90 | POODLE_LOCOMO_GPIO_MUTE_L, 1); | 90 | POODLE_LOCOMO_GPIO_MUTE_L, 1); |
91 | locomo_gpio_write(&poodle_locomo_device.dev, | 91 | locomo_gpio_write(&poodle_locomo_device.dev, |
92 | POODLE_LOCOMO_GPIO_MUTE_R, 1); | 92 | POODLE_LOCOMO_GPIO_MUTE_R, 1); |
93 | } | 93 | } |
94 | 94 | ||
95 | static int poodle_hw_params(struct snd_pcm_substream *substream, | 95 | static int poodle_hw_params(struct snd_pcm_substream *substream, |
96 | struct snd_pcm_hw_params *params) | 96 | struct snd_pcm_hw_params *params) |
97 | { | 97 | { |
98 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 98 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
99 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | 99 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
100 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | 100 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
101 | unsigned int clk = 0; | 101 | unsigned int clk = 0; |
102 | int ret = 0; | 102 | int ret = 0; |
103 | 103 | ||
104 | switch (params_rate(params)) { | 104 | switch (params_rate(params)) { |
105 | case 8000: | 105 | case 8000: |
106 | case 16000: | 106 | case 16000: |
107 | case 48000: | 107 | case 48000: |
108 | case 96000: | 108 | case 96000: |
109 | clk = 12288000; | 109 | clk = 12288000; |
110 | break; | 110 | break; |
111 | case 11025: | 111 | case 11025: |
112 | case 22050: | 112 | case 22050: |
113 | case 44100: | 113 | case 44100: |
114 | clk = 11289600; | 114 | clk = 11289600; |
115 | break; | 115 | break; |
116 | } | 116 | } |
117 | 117 | ||
118 | /* set codec DAI configuration */ | 118 | /* set codec DAI configuration */ |
119 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | 119 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | |
120 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | 120 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); |
121 | if (ret < 0) | 121 | if (ret < 0) |
122 | return ret; | 122 | return ret; |
123 | 123 | ||
124 | /* set cpu DAI configuration */ | 124 | /* set cpu DAI configuration */ |
125 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | 125 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | |
126 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | 126 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); |
127 | if (ret < 0) | 127 | if (ret < 0) |
128 | return ret; | 128 | return ret; |
129 | 129 | ||
130 | /* set the codec system clock for DAC and ADC */ | 130 | /* set the codec system clock for DAC and ADC */ |
131 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, clk, | 131 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, clk, |
132 | SND_SOC_CLOCK_IN); | 132 | SND_SOC_CLOCK_IN); |
133 | if (ret < 0) | 133 | if (ret < 0) |
134 | return ret; | 134 | return ret; |
135 | 135 | ||
136 | /* set the I2S system clock as input (unused) */ | 136 | /* set the I2S system clock as input (unused) */ |
137 | ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, | 137 | ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, |
138 | SND_SOC_CLOCK_IN); | 138 | SND_SOC_CLOCK_IN); |
139 | if (ret < 0) | 139 | if (ret < 0) |
140 | return ret; | 140 | return ret; |
141 | 141 | ||
142 | return 0; | 142 | return 0; |
143 | } | 143 | } |
144 | 144 | ||
145 | static struct snd_soc_ops poodle_ops = { | 145 | static struct snd_soc_ops poodle_ops = { |
146 | .startup = poodle_startup, | 146 | .startup = poodle_startup, |
147 | .hw_params = poodle_hw_params, | 147 | .hw_params = poodle_hw_params, |
148 | .shutdown = poodle_shutdown, | 148 | .shutdown = poodle_shutdown, |
149 | }; | 149 | }; |
150 | 150 | ||
151 | static int poodle_get_jack(struct snd_kcontrol *kcontrol, | 151 | static int poodle_get_jack(struct snd_kcontrol *kcontrol, |
152 | struct snd_ctl_elem_value *ucontrol) | 152 | struct snd_ctl_elem_value *ucontrol) |
153 | { | 153 | { |
154 | ucontrol->value.integer.value[0] = poodle_jack_func; | 154 | ucontrol->value.integer.value[0] = poodle_jack_func; |
155 | return 0; | 155 | return 0; |
156 | } | 156 | } |
157 | 157 | ||
158 | static int poodle_set_jack(struct snd_kcontrol *kcontrol, | 158 | static int poodle_set_jack(struct snd_kcontrol *kcontrol, |
159 | struct snd_ctl_elem_value *ucontrol) | 159 | struct snd_ctl_elem_value *ucontrol) |
160 | { | 160 | { |
161 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 161 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
162 | 162 | ||
163 | if (poodle_jack_func == ucontrol->value.integer.value[0]) | 163 | if (poodle_jack_func == ucontrol->value.integer.value[0]) |
164 | return 0; | 164 | return 0; |
165 | 165 | ||
166 | poodle_jack_func = ucontrol->value.integer.value[0]; | 166 | poodle_jack_func = ucontrol->value.integer.value[0]; |
167 | poodle_ext_control(codec); | 167 | poodle_ext_control(codec); |
168 | return 1; | 168 | return 1; |
169 | } | 169 | } |
170 | 170 | ||
171 | static int poodle_get_spk(struct snd_kcontrol *kcontrol, | 171 | static int poodle_get_spk(struct snd_kcontrol *kcontrol, |
172 | struct snd_ctl_elem_value *ucontrol) | 172 | struct snd_ctl_elem_value *ucontrol) |
173 | { | 173 | { |
174 | ucontrol->value.integer.value[0] = poodle_spk_func; | 174 | ucontrol->value.integer.value[0] = poodle_spk_func; |
175 | return 0; | 175 | return 0; |
176 | } | 176 | } |
177 | 177 | ||
178 | static int poodle_set_spk(struct snd_kcontrol *kcontrol, | 178 | static int poodle_set_spk(struct snd_kcontrol *kcontrol, |
179 | struct snd_ctl_elem_value *ucontrol) | 179 | struct snd_ctl_elem_value *ucontrol) |
180 | { | 180 | { |
181 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 181 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
182 | 182 | ||
183 | if (poodle_spk_func == ucontrol->value.integer.value[0]) | 183 | if (poodle_spk_func == ucontrol->value.integer.value[0]) |
184 | return 0; | 184 | return 0; |
185 | 185 | ||
186 | poodle_spk_func = ucontrol->value.integer.value[0]; | 186 | poodle_spk_func = ucontrol->value.integer.value[0]; |
187 | poodle_ext_control(codec); | 187 | poodle_ext_control(codec); |
188 | return 1; | 188 | return 1; |
189 | } | 189 | } |
190 | 190 | ||
191 | static int poodle_amp_event(struct snd_soc_dapm_widget *w, | 191 | static int poodle_amp_event(struct snd_soc_dapm_widget *w, |
192 | struct snd_kcontrol *k, int event) | 192 | struct snd_kcontrol *k, int event) |
193 | { | 193 | { |
194 | if (SND_SOC_DAPM_EVENT_ON(event)) | 194 | if (SND_SOC_DAPM_EVENT_ON(event)) |
195 | locomo_gpio_write(&poodle_locomo_device.dev, | 195 | locomo_gpio_write(&poodle_locomo_device.dev, |
196 | POODLE_LOCOMO_GPIO_AMP_ON, 0); | 196 | POODLE_LOCOMO_GPIO_AMP_ON, 0); |
197 | else | 197 | else |
198 | locomo_gpio_write(&poodle_locomo_device.dev, | 198 | locomo_gpio_write(&poodle_locomo_device.dev, |
199 | POODLE_LOCOMO_GPIO_AMP_ON, 1); | 199 | POODLE_LOCOMO_GPIO_AMP_ON, 1); |
200 | 200 | ||
201 | return 0; | 201 | return 0; |
202 | } | 202 | } |
203 | 203 | ||
204 | /* poodle machine dapm widgets */ | 204 | /* poodle machine dapm widgets */ |
205 | static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { | 205 | static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { |
206 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | 206 | SND_SOC_DAPM_HP("Headphone Jack", NULL), |
207 | SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event), | 207 | SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event), |
208 | }; | 208 | }; |
209 | 209 | ||
210 | /* Corgi machine connections to the codec pins */ | 210 | /* Corgi machine connections to the codec pins */ |
211 | static const struct snd_soc_dapm_route audio_map[] = { | 211 | static const struct snd_soc_dapm_route audio_map[] = { |
212 | 212 | ||
213 | /* headphone connected to LHPOUT1, RHPOUT1 */ | 213 | /* headphone connected to LHPOUT1, RHPOUT1 */ |
214 | {"Headphone Jack", NULL, "LHPOUT"}, | 214 | {"Headphone Jack", NULL, "LHPOUT"}, |
215 | {"Headphone Jack", NULL, "RHPOUT"}, | 215 | {"Headphone Jack", NULL, "RHPOUT"}, |
216 | 216 | ||
217 | /* speaker connected to LOUT, ROUT */ | 217 | /* speaker connected to LOUT, ROUT */ |
218 | {"Ext Spk", NULL, "ROUT"}, | 218 | {"Ext Spk", NULL, "ROUT"}, |
219 | {"Ext Spk", NULL, "LOUT"}, | 219 | {"Ext Spk", NULL, "LOUT"}, |
220 | }; | 220 | }; |
221 | 221 | ||
222 | static const char *jack_function[] = {"Off", "Headphone"}; | 222 | static const char *jack_function[] = {"Off", "Headphone"}; |
223 | static const char *spk_function[] = {"Off", "On"}; | 223 | static const char *spk_function[] = {"Off", "On"}; |
224 | static const struct soc_enum poodle_enum[] = { | 224 | static const struct soc_enum poodle_enum[] = { |
225 | SOC_ENUM_SINGLE_EXT(2, jack_function), | 225 | SOC_ENUM_SINGLE_EXT(2, jack_function), |
226 | SOC_ENUM_SINGLE_EXT(2, spk_function), | 226 | SOC_ENUM_SINGLE_EXT(2, spk_function), |
227 | }; | 227 | }; |
228 | 228 | ||
229 | static const struct snd_kcontrol_new wm8731_poodle_controls[] = { | 229 | static const struct snd_kcontrol_new wm8731_poodle_controls[] = { |
230 | SOC_ENUM_EXT("Jack Function", poodle_enum[0], poodle_get_jack, | 230 | SOC_ENUM_EXT("Jack Function", poodle_enum[0], poodle_get_jack, |
231 | poodle_set_jack), | 231 | poodle_set_jack), |
232 | SOC_ENUM_EXT("Speaker Function", poodle_enum[1], poodle_get_spk, | 232 | SOC_ENUM_EXT("Speaker Function", poodle_enum[1], poodle_get_spk, |
233 | poodle_set_spk), | 233 | poodle_set_spk), |
234 | }; | 234 | }; |
235 | 235 | ||
236 | /* | 236 | /* |
237 | * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device | 237 | * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device |
238 | */ | 238 | */ |
239 | static int poodle_wm8731_init(struct snd_soc_pcm_runtime *rtd) | 239 | static int poodle_wm8731_init(struct snd_soc_pcm_runtime *rtd) |
240 | { | 240 | { |
241 | struct snd_soc_codec *codec = rtd->codec; | 241 | struct snd_soc_codec *codec = rtd->codec; |
242 | int err; | 242 | int err; |
243 | 243 | ||
244 | snd_soc_dapm_nc_pin(codec, "LLINEIN"); | 244 | snd_soc_dapm_nc_pin(codec, "LLINEIN"); |
245 | snd_soc_dapm_nc_pin(codec, "RLINEIN"); | 245 | snd_soc_dapm_nc_pin(codec, "RLINEIN"); |
246 | snd_soc_dapm_enable_pin(codec, "MICIN"); | 246 | snd_soc_dapm_enable_pin(codec, "MICIN"); |
247 | 247 | ||
248 | /* Add poodle specific controls */ | 248 | /* Add poodle specific controls */ |
249 | err = snd_soc_add_controls(codec, wm8731_poodle_controls, | 249 | err = snd_soc_add_controls(codec, wm8731_poodle_controls, |
250 | ARRAY_SIZE(wm8731_poodle_controls)); | 250 | ARRAY_SIZE(wm8731_poodle_controls)); |
251 | if (err < 0) | 251 | if (err < 0) |
252 | return err; | 252 | return err; |
253 | 253 | ||
254 | /* Add poodle specific widgets */ | 254 | /* Add poodle specific widgets */ |
255 | snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets, | 255 | snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets, |
256 | ARRAY_SIZE(wm8731_dapm_widgets)); | 256 | ARRAY_SIZE(wm8731_dapm_widgets)); |
257 | 257 | ||
258 | /* Set up poodle specific audio path audio_map */ | 258 | /* Set up poodle specific audio path audio_map */ |
259 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | 259 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); |
260 | 260 | ||
261 | snd_soc_dapm_sync(codec); | 261 | snd_soc_dapm_sync(codec); |
262 | return 0; | 262 | return 0; |
263 | } | 263 | } |
264 | 264 | ||
265 | /* poodle digital audio interface glue - connects codec <--> CPU */ | 265 | /* poodle digital audio interface glue - connects codec <--> CPU */ |
266 | static struct snd_soc_dai_link poodle_dai = { | 266 | static struct snd_soc_dai_link poodle_dai = { |
267 | .name = "WM8731", | 267 | .name = "WM8731", |
268 | .stream_name = "WM8731", | 268 | .stream_name = "WM8731", |
269 | .cpu_dai_name = "pxa-i2s", | 269 | .cpu_dai_name = "pxa-i2s", |
270 | .codec_dai_name = "wm8731-hifi", | 270 | .codec_dai_name = "wm8731-hifi", |
271 | .platform_name = "pxa-pcm-audio", | 271 | .platform_name = "pxa-pcm-audio", |
272 | .codec_name = "wm8731-codec.0-001a", | 272 | .codec_name = "wm8731-codec.0-001a", |
273 | .init = poodle_wm8731_init, | 273 | .init = poodle_wm8731_init, |
274 | .ops = &poodle_ops, | 274 | .ops = &poodle_ops, |
275 | }; | 275 | }; |
276 | 276 | ||
277 | /* poodle audio machine driver */ | 277 | /* poodle audio machine driver */ |
278 | static struct snd_soc_card snd_soc_poodle = { | 278 | static struct snd_soc_card snd_soc_poodle = { |
279 | .name = "Poodle", | 279 | .name = "Poodle", |
280 | .dai_link = &poodle_dai, | 280 | .dai_link = &poodle_dai, |
281 | .num_links = 1, | 281 | .num_links = 1, |
282 | .owner = THIS_MODULE, | 282 | .owner = THIS_MODULE, |
283 | }; | 283 | }; |
284 | 284 | ||
285 | static struct platform_device *poodle_snd_device; | 285 | static struct platform_device *poodle_snd_device; |
286 | 286 | ||
287 | static int __init poodle_init(void) | 287 | static int __init poodle_init(void) |
288 | { | 288 | { |
289 | int ret; | 289 | int ret; |
290 | 290 | ||
291 | if (!machine_is_poodle()) | 291 | if (!machine_is_poodle()) |
292 | return -ENODEV; | 292 | return -ENODEV; |
293 | 293 | ||
294 | locomo_gpio_set_dir(&poodle_locomo_device.dev, | 294 | locomo_gpio_set_dir(&poodle_locomo_device.dev, |
295 | POODLE_LOCOMO_GPIO_AMP_ON, 0); | 295 | POODLE_LOCOMO_GPIO_AMP_ON, 0); |
296 | /* should we mute HP at startup - burning power ?*/ | 296 | /* should we mute HP at startup - burning power ?*/ |
297 | locomo_gpio_set_dir(&poodle_locomo_device.dev, | 297 | locomo_gpio_set_dir(&poodle_locomo_device.dev, |
298 | POODLE_LOCOMO_GPIO_MUTE_L, 0); | 298 | POODLE_LOCOMO_GPIO_MUTE_L, 0); |
299 | locomo_gpio_set_dir(&poodle_locomo_device.dev, | 299 | locomo_gpio_set_dir(&poodle_locomo_device.dev, |
300 | POODLE_LOCOMO_GPIO_MUTE_R, 0); | 300 | POODLE_LOCOMO_GPIO_MUTE_R, 0); |
301 | 301 | ||
302 | poodle_snd_device = platform_device_alloc("soc-audio", -1); | 302 | poodle_snd_device = platform_device_alloc("soc-audio", -1); |
303 | if (!poodle_snd_device) | 303 | if (!poodle_snd_device) |
304 | return -ENOMEM; | 304 | return -ENOMEM; |
305 | 305 | ||
306 | platform_set_drvdata(poodle_snd_device, &snd_soc_poodle); | 306 | platform_set_drvdata(poodle_snd_device, &snd_soc_poodle); |
307 | ret = platform_device_add(poodle_snd_device); | 307 | ret = platform_device_add(poodle_snd_device); |
308 | 308 | ||
309 | if (ret) | 309 | if (ret) |
310 | platform_device_put(poodle_snd_device); | 310 | platform_device_put(poodle_snd_device); |
311 | 311 | ||
312 | return ret; | 312 | return ret; |
313 | } | 313 | } |
314 | 314 | ||
315 | static void __exit poodle_exit(void) | 315 | static void __exit poodle_exit(void) |
316 | { | 316 | { |
317 | platform_device_unregister(poodle_snd_device); | 317 | platform_device_unregister(poodle_snd_device); |
318 | } | 318 | } |
319 | 319 | ||
320 | module_init(poodle_init); | 320 | module_init(poodle_init); |
321 | module_exit(poodle_exit); | 321 | module_exit(poodle_exit); |
322 | 322 | ||
323 | /* Module information */ | 323 | /* Module information */ |
324 | MODULE_AUTHOR("Richard Purdie"); | 324 | MODULE_AUTHOR("Richard Purdie"); |
325 | MODULE_DESCRIPTION("ALSA SoC Poodle"); | 325 | MODULE_DESCRIPTION("ALSA SoC Poodle"); |
326 | MODULE_LICENSE("GPL"); | 326 | MODULE_LICENSE("GPL"); |
327 | 327 |