diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-24 16:37:37 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-24 16:37:37 -0400 |
commit | dbf7b5915b39bfff548e4c6a3a753fc291a60e25 (patch) | |
tree | 55c457a22aa869d2ab558317877138369ae5f9bb /sound/soc/codecs/arizona.c | |
parent | d14b7a419a664cd7c1c585c9e7fffee9e9051d53 (diff) | |
parent | c1b623d9e4117d18d244e9b7fb30d2c27aeaf074 (diff) |
Merge tag 'sound-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound update from Takashi Iwai:
"This is a fairly quiet release in all sound area. Only a little bit
of changes in the core side while most of changes are seen in the
drivers.
HD-audio:
- A few new codec additions for Nvidia, Realtek and VIA
- Intel Haswell audio support
- Support for "phantom" jacks for consistent jack reporting
- Major clean-ups in HDMI/DP driver codes
- A workaround for inverted digital-mic pins with Realtek codecs
- Removal of beep_mode=2 option
ASoC:
- Added the ability to add and remove DAPM paths dynamically, mostly
for reparenting on clock changes
- New machine drivers for Marvell Brownstone, ST-Ericsson Ux500
reference platform and ttc-dkp
- New CPU drivers for Blackfin BF6xx SPORTs in I2S mode, Marvell MMP,
Synopsis Designware I2S controllers, and SPEAr DMA and S/PDIF
- New CODEC drivers for Dialog DA732x, ST STA529, ST-Ericsson AB8500,
TI Isabelle and Wolfson Microelectronics WM5102 and WM5110
- DAPM fixes for the recent locking changes
- Fix for _PRE and _POST widgets (which have been broken for a few
releases now)
- A couple of minor driver updates
Misc
- Conversion to new dev_pm_ops in platform and PCI drivers
- LTC support and some fixes in PCXHR driver
- A few fixes and PM support for ISA OPti9xx and WSS cards
- Some TLV code cleanup
- Move driver-specific headers from include/sound to local dirs"
* tag 'sound-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (212 commits)
ASoC: dapm: Fix _PRE and _POST events for DAPM performance improvements
ALSA: hda - add dock support for Thinkpad X230 Tablet
ALSA: hda - Turn on PIN_OUT from hdmi playback prepare.
ASoC imx-audmux: add MX31_AUDMUX_PORT7_SSI_PINS_7 define
ASoC: littlemill: Add userspace control of the WM1250 I/O
ASoC: wm8994: Update micdet for irqdomain conversion
ALSA: hda - make sure alc268 does not OOPS on codec parse
ALSA: hda - Add support for Realtek ALC282
ALSA: hda - Fix index number conflicts of phantom jacks
ALSA: opti9xx: Fix section mismatch by PM support
ALSA: snd-opti9xx: Implement suspend/resume
ALSA: hda - Add new GPU codec ID to snd-hda
ALSA: hda - Fix driver type of Haswell controller to AZX_DRIVER_SCH
ALSA: hda - add Haswell HDMI codec id
ALSA: hda - Add DeviceID for Haswell HDA
ALSA: wss_lib: Fix resume on Yamaha OPL3-SAx
ALSA: wss_lib: fix suspend/resume
ALSA: es1938: replace TLV_DB_RANGE_HEAD with DECLARE_TLV_DB_RANGE
ALSA: tlv: add DECLARE_TLV_DB_RANGE()
ALSA: tlv: add DECLARE_TLV_CONTAINER()
...
Diffstat (limited to 'sound/soc/codecs/arizona.c')
-rw-r--r-- | sound/soc/codecs/arizona.c | 937 |
1 files changed, 937 insertions, 0 deletions
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c new file mode 100644 index 000000000000..5c9cacaf2d52 --- /dev/null +++ b/sound/soc/codecs/arizona.c | |||
@@ -0,0 +1,937 @@ | |||
1 | /* | ||
2 | * arizona.c - Wolfson Arizona class device shared support | ||
3 | * | ||
4 | * Copyright 2012 Wolfson Microelectronics plc | ||
5 | * | ||
6 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/gcd.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/pm_runtime.h> | ||
16 | #include <sound/pcm.h> | ||
17 | #include <sound/pcm_params.h> | ||
18 | #include <sound/tlv.h> | ||
19 | |||
20 | #include <linux/mfd/arizona/core.h> | ||
21 | #include <linux/mfd/arizona/registers.h> | ||
22 | |||
23 | #include "arizona.h" | ||
24 | |||
25 | #define ARIZONA_AIF_BCLK_CTRL 0x00 | ||
26 | #define ARIZONA_AIF_TX_PIN_CTRL 0x01 | ||
27 | #define ARIZONA_AIF_RX_PIN_CTRL 0x02 | ||
28 | #define ARIZONA_AIF_RATE_CTRL 0x03 | ||
29 | #define ARIZONA_AIF_FORMAT 0x04 | ||
30 | #define ARIZONA_AIF_TX_BCLK_RATE 0x05 | ||
31 | #define ARIZONA_AIF_RX_BCLK_RATE 0x06 | ||
32 | #define ARIZONA_AIF_FRAME_CTRL_1 0x07 | ||
33 | #define ARIZONA_AIF_FRAME_CTRL_2 0x08 | ||
34 | #define ARIZONA_AIF_FRAME_CTRL_3 0x09 | ||
35 | #define ARIZONA_AIF_FRAME_CTRL_4 0x0A | ||
36 | #define ARIZONA_AIF_FRAME_CTRL_5 0x0B | ||
37 | #define ARIZONA_AIF_FRAME_CTRL_6 0x0C | ||
38 | #define ARIZONA_AIF_FRAME_CTRL_7 0x0D | ||
39 | #define ARIZONA_AIF_FRAME_CTRL_8 0x0E | ||
40 | #define ARIZONA_AIF_FRAME_CTRL_9 0x0F | ||
41 | #define ARIZONA_AIF_FRAME_CTRL_10 0x10 | ||
42 | #define ARIZONA_AIF_FRAME_CTRL_11 0x11 | ||
43 | #define ARIZONA_AIF_FRAME_CTRL_12 0x12 | ||
44 | #define ARIZONA_AIF_FRAME_CTRL_13 0x13 | ||
45 | #define ARIZONA_AIF_FRAME_CTRL_14 0x14 | ||
46 | #define ARIZONA_AIF_FRAME_CTRL_15 0x15 | ||
47 | #define ARIZONA_AIF_FRAME_CTRL_16 0x16 | ||
48 | #define ARIZONA_AIF_FRAME_CTRL_17 0x17 | ||
49 | #define ARIZONA_AIF_FRAME_CTRL_18 0x18 | ||
50 | #define ARIZONA_AIF_TX_ENABLES 0x19 | ||
51 | #define ARIZONA_AIF_RX_ENABLES 0x1A | ||
52 | #define ARIZONA_AIF_FORCE_WRITE 0x1B | ||
53 | |||
54 | #define arizona_fll_err(_fll, fmt, ...) \ | ||
55 | dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) | ||
56 | #define arizona_fll_warn(_fll, fmt, ...) \ | ||
57 | dev_warn(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) | ||
58 | #define arizona_fll_dbg(_fll, fmt, ...) \ | ||
59 | dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) | ||
60 | |||
61 | #define arizona_aif_err(_dai, fmt, ...) \ | ||
62 | dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) | ||
63 | #define arizona_aif_warn(_dai, fmt, ...) \ | ||
64 | dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) | ||
65 | #define arizona_aif_dbg(_dai, fmt, ...) \ | ||
66 | dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) | ||
67 | |||
68 | const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { | ||
69 | "None", | ||
70 | "Tone Generator 1", | ||
71 | "Tone Generator 2", | ||
72 | "Haptics", | ||
73 | "AEC", | ||
74 | "Mic Mute Mixer", | ||
75 | "Noise Generator", | ||
76 | "IN1L", | ||
77 | "IN1R", | ||
78 | "IN2L", | ||
79 | "IN2R", | ||
80 | "IN3L", | ||
81 | "IN3R", | ||
82 | "IN4L", | ||
83 | "IN4R", | ||
84 | "AIF1RX1", | ||
85 | "AIF1RX2", | ||
86 | "AIF1RX3", | ||
87 | "AIF1RX4", | ||
88 | "AIF1RX5", | ||
89 | "AIF1RX6", | ||
90 | "AIF1RX7", | ||
91 | "AIF1RX8", | ||
92 | "AIF2RX1", | ||
93 | "AIF2RX2", | ||
94 | "AIF3RX1", | ||
95 | "AIF3RX2", | ||
96 | "SLIMRX1", | ||
97 | "SLIMRX2", | ||
98 | "SLIMRX3", | ||
99 | "SLIMRX4", | ||
100 | "SLIMRX5", | ||
101 | "SLIMRX6", | ||
102 | "SLIMRX7", | ||
103 | "SLIMRX8", | ||
104 | "EQ1", | ||
105 | "EQ2", | ||
106 | "EQ3", | ||
107 | "EQ4", | ||
108 | "DRC1L", | ||
109 | "DRC1R", | ||
110 | "DRC2L", | ||
111 | "DRC2R", | ||
112 | "LHPF1", | ||
113 | "LHPF2", | ||
114 | "LHPF3", | ||
115 | "LHPF4", | ||
116 | "DSP1.1", | ||
117 | "DSP1.2", | ||
118 | "DSP1.3", | ||
119 | "DSP1.4", | ||
120 | "DSP1.5", | ||
121 | "DSP1.6", | ||
122 | "ASRC1L", | ||
123 | "ASRC1R", | ||
124 | "ASRC2L", | ||
125 | "ASRC2R", | ||
126 | }; | ||
127 | EXPORT_SYMBOL_GPL(arizona_mixer_texts); | ||
128 | |||
129 | int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { | ||
130 | 0x00, /* None */ | ||
131 | 0x04, /* Tone */ | ||
132 | 0x05, | ||
133 | 0x06, /* Haptics */ | ||
134 | 0x08, /* AEC */ | ||
135 | 0x0c, /* Noise mixer */ | ||
136 | 0x0d, /* Comfort noise */ | ||
137 | 0x10, /* IN1L */ | ||
138 | 0x11, | ||
139 | 0x12, | ||
140 | 0x13, | ||
141 | 0x14, | ||
142 | 0x15, | ||
143 | 0x16, | ||
144 | 0x17, | ||
145 | 0x20, /* AIF1RX1 */ | ||
146 | 0x21, | ||
147 | 0x22, | ||
148 | 0x23, | ||
149 | 0x24, | ||
150 | 0x25, | ||
151 | 0x26, | ||
152 | 0x27, | ||
153 | 0x28, /* AIF2RX1 */ | ||
154 | 0x29, | ||
155 | 0x30, /* AIF3RX1 */ | ||
156 | 0x31, | ||
157 | 0x38, /* SLIMRX1 */ | ||
158 | 0x39, | ||
159 | 0x3a, | ||
160 | 0x3b, | ||
161 | 0x3c, | ||
162 | 0x3d, | ||
163 | 0x3e, | ||
164 | 0x3f, | ||
165 | 0x50, /* EQ1 */ | ||
166 | 0x51, | ||
167 | 0x52, | ||
168 | 0x53, | ||
169 | 0x58, /* DRC1L */ | ||
170 | 0x59, | ||
171 | 0x5a, | ||
172 | 0x5b, | ||
173 | 0x60, /* LHPF1 */ | ||
174 | 0x61, | ||
175 | 0x62, | ||
176 | 0x63, | ||
177 | 0x68, /* DSP1.1 */ | ||
178 | 0x69, | ||
179 | 0x6a, | ||
180 | 0x6b, | ||
181 | 0x6c, | ||
182 | 0x6d, | ||
183 | 0x90, /* ASRC1L */ | ||
184 | 0x91, | ||
185 | 0x92, | ||
186 | 0x93, | ||
187 | }; | ||
188 | EXPORT_SYMBOL_GPL(arizona_mixer_values); | ||
189 | |||
190 | const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0); | ||
191 | EXPORT_SYMBOL_GPL(arizona_mixer_tlv); | ||
192 | |||
193 | static const char *arizona_lhpf_mode_text[] = { | ||
194 | "Low-pass", "High-pass" | ||
195 | }; | ||
196 | |||
197 | const struct soc_enum arizona_lhpf1_mode = | ||
198 | SOC_ENUM_SINGLE(ARIZONA_HPLPF1_1, ARIZONA_LHPF1_MODE_SHIFT, 2, | ||
199 | arizona_lhpf_mode_text); | ||
200 | EXPORT_SYMBOL_GPL(arizona_lhpf1_mode); | ||
201 | |||
202 | const struct soc_enum arizona_lhpf2_mode = | ||
203 | SOC_ENUM_SINGLE(ARIZONA_HPLPF2_1, ARIZONA_LHPF2_MODE_SHIFT, 2, | ||
204 | arizona_lhpf_mode_text); | ||
205 | EXPORT_SYMBOL_GPL(arizona_lhpf2_mode); | ||
206 | |||
207 | const struct soc_enum arizona_lhpf3_mode = | ||
208 | SOC_ENUM_SINGLE(ARIZONA_HPLPF3_1, ARIZONA_LHPF3_MODE_SHIFT, 2, | ||
209 | arizona_lhpf_mode_text); | ||
210 | EXPORT_SYMBOL_GPL(arizona_lhpf3_mode); | ||
211 | |||
212 | const struct soc_enum arizona_lhpf4_mode = | ||
213 | SOC_ENUM_SINGLE(ARIZONA_HPLPF4_1, ARIZONA_LHPF4_MODE_SHIFT, 2, | ||
214 | arizona_lhpf_mode_text); | ||
215 | EXPORT_SYMBOL_GPL(arizona_lhpf4_mode); | ||
216 | |||
217 | int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, | ||
218 | int event) | ||
219 | { | ||
220 | return 0; | ||
221 | } | ||
222 | EXPORT_SYMBOL_GPL(arizona_in_ev); | ||
223 | |||
224 | int arizona_out_ev(struct snd_soc_dapm_widget *w, | ||
225 | struct snd_kcontrol *kcontrol, | ||
226 | int event) | ||
227 | { | ||
228 | return 0; | ||
229 | } | ||
230 | EXPORT_SYMBOL_GPL(arizona_out_ev); | ||
231 | |||
232 | int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, | ||
233 | int source, unsigned int freq, int dir) | ||
234 | { | ||
235 | struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
236 | struct arizona *arizona = priv->arizona; | ||
237 | char *name; | ||
238 | unsigned int reg; | ||
239 | unsigned int mask = ARIZONA_SYSCLK_FREQ_MASK | ARIZONA_SYSCLK_SRC_MASK; | ||
240 | unsigned int val = source << ARIZONA_SYSCLK_SRC_SHIFT; | ||
241 | unsigned int *clk; | ||
242 | |||
243 | switch (clk_id) { | ||
244 | case ARIZONA_CLK_SYSCLK: | ||
245 | name = "SYSCLK"; | ||
246 | reg = ARIZONA_SYSTEM_CLOCK_1; | ||
247 | clk = &priv->sysclk; | ||
248 | mask |= ARIZONA_SYSCLK_FRAC; | ||
249 | break; | ||
250 | case ARIZONA_CLK_ASYNCCLK: | ||
251 | name = "ASYNCCLK"; | ||
252 | reg = ARIZONA_ASYNC_CLOCK_1; | ||
253 | clk = &priv->asyncclk; | ||
254 | break; | ||
255 | default: | ||
256 | return -EINVAL; | ||
257 | } | ||
258 | |||
259 | switch (freq) { | ||
260 | case 5644800: | ||
261 | case 6144000: | ||
262 | break; | ||
263 | case 11289600: | ||
264 | case 12288000: | ||
265 | val |= 1 << ARIZONA_SYSCLK_FREQ_SHIFT; | ||
266 | break; | ||
267 | case 22579200: | ||
268 | case 24576000: | ||
269 | val |= 2 << ARIZONA_SYSCLK_FREQ_SHIFT; | ||
270 | break; | ||
271 | case 45158400: | ||
272 | case 49152000: | ||
273 | val |= 3 << ARIZONA_SYSCLK_FREQ_SHIFT; | ||
274 | break; | ||
275 | default: | ||
276 | return -EINVAL; | ||
277 | } | ||
278 | |||
279 | *clk = freq; | ||
280 | |||
281 | if (freq % 6144000) | ||
282 | val |= ARIZONA_SYSCLK_FRAC; | ||
283 | |||
284 | dev_dbg(arizona->dev, "%s set to %uHz", name, freq); | ||
285 | |||
286 | return regmap_update_bits(arizona->regmap, reg, mask, val); | ||
287 | } | ||
288 | EXPORT_SYMBOL_GPL(arizona_set_sysclk); | ||
289 | |||
290 | static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | ||
291 | { | ||
292 | struct snd_soc_codec *codec = dai->codec; | ||
293 | int lrclk, bclk, mode, base; | ||
294 | |||
295 | base = dai->driver->base; | ||
296 | |||
297 | lrclk = 0; | ||
298 | bclk = 0; | ||
299 | |||
300 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
301 | case SND_SOC_DAIFMT_DSP_A: | ||
302 | mode = 0; | ||
303 | break; | ||
304 | case SND_SOC_DAIFMT_DSP_B: | ||
305 | mode = 1; | ||
306 | break; | ||
307 | case SND_SOC_DAIFMT_I2S: | ||
308 | mode = 2; | ||
309 | break; | ||
310 | case SND_SOC_DAIFMT_LEFT_J: | ||
311 | mode = 3; | ||
312 | break; | ||
313 | default: | ||
314 | arizona_aif_err(dai, "Unsupported DAI format %d\n", | ||
315 | fmt & SND_SOC_DAIFMT_FORMAT_MASK); | ||
316 | return -EINVAL; | ||
317 | } | ||
318 | |||
319 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
320 | case SND_SOC_DAIFMT_CBS_CFS: | ||
321 | break; | ||
322 | case SND_SOC_DAIFMT_CBS_CFM: | ||
323 | lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR; | ||
324 | break; | ||
325 | case SND_SOC_DAIFMT_CBM_CFS: | ||
326 | bclk |= ARIZONA_AIF1_BCLK_MSTR; | ||
327 | break; | ||
328 | case SND_SOC_DAIFMT_CBM_CFM: | ||
329 | bclk |= ARIZONA_AIF1_BCLK_MSTR; | ||
330 | lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR; | ||
331 | break; | ||
332 | default: | ||
333 | arizona_aif_err(dai, "Unsupported master mode %d\n", | ||
334 | fmt & SND_SOC_DAIFMT_MASTER_MASK); | ||
335 | return -EINVAL; | ||
336 | } | ||
337 | |||
338 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
339 | case SND_SOC_DAIFMT_NB_NF: | ||
340 | break; | ||
341 | case SND_SOC_DAIFMT_IB_IF: | ||
342 | bclk |= ARIZONA_AIF1_BCLK_INV; | ||
343 | lrclk |= ARIZONA_AIF1TX_LRCLK_INV; | ||
344 | break; | ||
345 | case SND_SOC_DAIFMT_IB_NF: | ||
346 | bclk |= ARIZONA_AIF1_BCLK_INV; | ||
347 | break; | ||
348 | case SND_SOC_DAIFMT_NB_IF: | ||
349 | lrclk |= ARIZONA_AIF1TX_LRCLK_INV; | ||
350 | break; | ||
351 | default: | ||
352 | return -EINVAL; | ||
353 | } | ||
354 | |||
355 | snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, | ||
356 | ARIZONA_AIF1_BCLK_INV | ARIZONA_AIF1_BCLK_MSTR, | ||
357 | bclk); | ||
358 | snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_PIN_CTRL, | ||
359 | ARIZONA_AIF1TX_LRCLK_INV | | ||
360 | ARIZONA_AIF1TX_LRCLK_MSTR, lrclk); | ||
361 | snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_PIN_CTRL, | ||
362 | ARIZONA_AIF1RX_LRCLK_INV | | ||
363 | ARIZONA_AIF1RX_LRCLK_MSTR, lrclk); | ||
364 | snd_soc_update_bits(codec, base + ARIZONA_AIF_FORMAT, | ||
365 | ARIZONA_AIF1_FMT_MASK, mode); | ||
366 | |||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | static const int arizona_48k_bclk_rates[] = { | ||
371 | -1, | ||
372 | 48000, | ||
373 | 64000, | ||
374 | 96000, | ||
375 | 128000, | ||
376 | 192000, | ||
377 | 256000, | ||
378 | 384000, | ||
379 | 512000, | ||
380 | 768000, | ||
381 | 1024000, | ||
382 | 1536000, | ||
383 | 2048000, | ||
384 | 3072000, | ||
385 | 4096000, | ||
386 | 6144000, | ||
387 | 8192000, | ||
388 | 12288000, | ||
389 | 24576000, | ||
390 | }; | ||
391 | |||
392 | static const unsigned int arizona_48k_rates[] = { | ||
393 | 12000, | ||
394 | 24000, | ||
395 | 48000, | ||
396 | 96000, | ||
397 | 192000, | ||
398 | 384000, | ||
399 | 768000, | ||
400 | 4000, | ||
401 | 8000, | ||
402 | 16000, | ||
403 | 32000, | ||
404 | 64000, | ||
405 | 128000, | ||
406 | 256000, | ||
407 | 512000, | ||
408 | }; | ||
409 | |||
410 | static const struct snd_pcm_hw_constraint_list arizona_48k_constraint = { | ||
411 | .count = ARRAY_SIZE(arizona_48k_rates), | ||
412 | .list = arizona_48k_rates, | ||
413 | }; | ||
414 | |||
415 | static const int arizona_44k1_bclk_rates[] = { | ||
416 | -1, | ||
417 | 44100, | ||
418 | 58800, | ||
419 | 88200, | ||
420 | 117600, | ||
421 | 177640, | ||
422 | 235200, | ||
423 | 352800, | ||
424 | 470400, | ||
425 | 705600, | ||
426 | 940800, | ||
427 | 1411200, | ||
428 | 1881600, | ||
429 | 2882400, | ||
430 | 3763200, | ||
431 | 5644800, | ||
432 | 7526400, | ||
433 | 11289600, | ||
434 | 22579200, | ||
435 | }; | ||
436 | |||
437 | static const unsigned int arizona_44k1_rates[] = { | ||
438 | 11025, | ||
439 | 22050, | ||
440 | 44100, | ||
441 | 88200, | ||
442 | 176400, | ||
443 | 352800, | ||
444 | 705600, | ||
445 | }; | ||
446 | |||
447 | static const struct snd_pcm_hw_constraint_list arizona_44k1_constraint = { | ||
448 | .count = ARRAY_SIZE(arizona_44k1_rates), | ||
449 | .list = arizona_44k1_rates, | ||
450 | }; | ||
451 | |||
452 | static int arizona_sr_vals[] = { | ||
453 | 0, | ||
454 | 12000, | ||
455 | 24000, | ||
456 | 48000, | ||
457 | 96000, | ||
458 | 192000, | ||
459 | 384000, | ||
460 | 768000, | ||
461 | 0, | ||
462 | 11025, | ||
463 | 22050, | ||
464 | 44100, | ||
465 | 88200, | ||
466 | 176400, | ||
467 | 352800, | ||
468 | 705600, | ||
469 | 4000, | ||
470 | 8000, | ||
471 | 16000, | ||
472 | 32000, | ||
473 | 64000, | ||
474 | 128000, | ||
475 | 256000, | ||
476 | 512000, | ||
477 | }; | ||
478 | |||
479 | static int arizona_startup(struct snd_pcm_substream *substream, | ||
480 | struct snd_soc_dai *dai) | ||
481 | { | ||
482 | struct snd_soc_codec *codec = dai->codec; | ||
483 | struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
484 | struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; | ||
485 | const struct snd_pcm_hw_constraint_list *constraint; | ||
486 | unsigned int base_rate; | ||
487 | |||
488 | switch (dai_priv->clk) { | ||
489 | case ARIZONA_CLK_SYSCLK: | ||
490 | base_rate = priv->sysclk; | ||
491 | break; | ||
492 | case ARIZONA_CLK_ASYNCCLK: | ||
493 | base_rate = priv->asyncclk; | ||
494 | break; | ||
495 | default: | ||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | if (base_rate % 8000) | ||
500 | constraint = &arizona_44k1_constraint; | ||
501 | else | ||
502 | constraint = &arizona_48k_constraint; | ||
503 | |||
504 | return snd_pcm_hw_constraint_list(substream->runtime, 0, | ||
505 | SNDRV_PCM_HW_PARAM_RATE, | ||
506 | constraint); | ||
507 | } | ||
508 | |||
509 | static int arizona_hw_params(struct snd_pcm_substream *substream, | ||
510 | struct snd_pcm_hw_params *params, | ||
511 | struct snd_soc_dai *dai) | ||
512 | { | ||
513 | struct snd_soc_codec *codec = dai->codec; | ||
514 | struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
515 | struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; | ||
516 | int base = dai->driver->base; | ||
517 | const int *rates; | ||
518 | int i; | ||
519 | int bclk, lrclk, wl, frame, sr_val; | ||
520 | |||
521 | if (params_rate(params) % 8000) | ||
522 | rates = &arizona_44k1_bclk_rates[0]; | ||
523 | else | ||
524 | rates = &arizona_48k_bclk_rates[0]; | ||
525 | |||
526 | for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) { | ||
527 | if (rates[i] >= snd_soc_params_to_bclk(params) && | ||
528 | rates[i] % params_rate(params) == 0) { | ||
529 | bclk = i; | ||
530 | break; | ||
531 | } | ||
532 | } | ||
533 | if (i == ARRAY_SIZE(arizona_44k1_bclk_rates)) { | ||
534 | arizona_aif_err(dai, "Unsupported sample rate %dHz\n", | ||
535 | params_rate(params)); | ||
536 | return -EINVAL; | ||
537 | } | ||
538 | |||
539 | for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) | ||
540 | if (arizona_sr_vals[i] == params_rate(params)) | ||
541 | break; | ||
542 | if (i == ARRAY_SIZE(arizona_sr_vals)) { | ||
543 | arizona_aif_err(dai, "Unsupported sample rate %dHz\n", | ||
544 | params_rate(params)); | ||
545 | return -EINVAL; | ||
546 | } | ||
547 | sr_val = i; | ||
548 | |||
549 | lrclk = snd_soc_params_to_bclk(params) / params_rate(params); | ||
550 | |||
551 | arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", | ||
552 | rates[bclk], rates[bclk] / lrclk); | ||
553 | |||
554 | wl = snd_pcm_format_width(params_format(params)); | ||
555 | frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl; | ||
556 | |||
557 | /* | ||
558 | * We will need to be more flexible than this in future, | ||
559 | * currently we use a single sample rate for SYSCLK. | ||
560 | */ | ||
561 | switch (dai_priv->clk) { | ||
562 | case ARIZONA_CLK_SYSCLK: | ||
563 | snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, | ||
564 | ARIZONA_SAMPLE_RATE_1_MASK, sr_val); | ||
565 | snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, | ||
566 | ARIZONA_AIF1_RATE_MASK, 0); | ||
567 | break; | ||
568 | case ARIZONA_CLK_ASYNCCLK: | ||
569 | snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1, | ||
570 | ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val); | ||
571 | snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, | ||
572 | ARIZONA_AIF1_RATE_MASK, 8); | ||
573 | break; | ||
574 | default: | ||
575 | arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); | ||
576 | return -EINVAL; | ||
577 | } | ||
578 | |||
579 | snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, | ||
580 | ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); | ||
581 | snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_BCLK_RATE, | ||
582 | ARIZONA_AIF1TX_BCPF_MASK, lrclk); | ||
583 | snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_BCLK_RATE, | ||
584 | ARIZONA_AIF1RX_BCPF_MASK, lrclk); | ||
585 | snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_1, | ||
586 | ARIZONA_AIF1TX_WL_MASK | | ||
587 | ARIZONA_AIF1TX_SLOT_LEN_MASK, frame); | ||
588 | snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_2, | ||
589 | ARIZONA_AIF1RX_WL_MASK | | ||
590 | ARIZONA_AIF1RX_SLOT_LEN_MASK, frame); | ||
591 | |||
592 | return 0; | ||
593 | } | ||
594 | |||
595 | static const char *arizona_dai_clk_str(int clk_id) | ||
596 | { | ||
597 | switch (clk_id) { | ||
598 | case ARIZONA_CLK_SYSCLK: | ||
599 | return "SYSCLK"; | ||
600 | case ARIZONA_CLK_ASYNCCLK: | ||
601 | return "ASYNCCLK"; | ||
602 | default: | ||
603 | return "Unknown clock"; | ||
604 | } | ||
605 | } | ||
606 | |||
607 | static int arizona_dai_set_sysclk(struct snd_soc_dai *dai, | ||
608 | int clk_id, unsigned int freq, int dir) | ||
609 | { | ||
610 | struct snd_soc_codec *codec = dai->codec; | ||
611 | struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
612 | struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; | ||
613 | struct snd_soc_dapm_route routes[2]; | ||
614 | |||
615 | switch (clk_id) { | ||
616 | case ARIZONA_CLK_SYSCLK: | ||
617 | case ARIZONA_CLK_ASYNCCLK: | ||
618 | break; | ||
619 | default: | ||
620 | return -EINVAL; | ||
621 | } | ||
622 | |||
623 | if (clk_id == dai_priv->clk) | ||
624 | return 0; | ||
625 | |||
626 | if (dai->active) { | ||
627 | dev_err(codec->dev, "Can't change clock on active DAI %d\n", | ||
628 | dai->id); | ||
629 | return -EBUSY; | ||
630 | } | ||
631 | |||
632 | memset(&routes, 0, sizeof(routes)); | ||
633 | routes[0].sink = dai->driver->capture.stream_name; | ||
634 | routes[1].sink = dai->driver->playback.stream_name; | ||
635 | |||
636 | routes[0].source = arizona_dai_clk_str(dai_priv->clk); | ||
637 | routes[1].source = arizona_dai_clk_str(dai_priv->clk); | ||
638 | snd_soc_dapm_del_routes(&codec->dapm, routes, ARRAY_SIZE(routes)); | ||
639 | |||
640 | routes[0].source = arizona_dai_clk_str(clk_id); | ||
641 | routes[1].source = arizona_dai_clk_str(clk_id); | ||
642 | snd_soc_dapm_add_routes(&codec->dapm, routes, ARRAY_SIZE(routes)); | ||
643 | |||
644 | return snd_soc_dapm_sync(&codec->dapm); | ||
645 | } | ||
646 | |||
647 | const struct snd_soc_dai_ops arizona_dai_ops = { | ||
648 | .startup = arizona_startup, | ||
649 | .set_fmt = arizona_set_fmt, | ||
650 | .hw_params = arizona_hw_params, | ||
651 | .set_sysclk = arizona_dai_set_sysclk, | ||
652 | }; | ||
653 | EXPORT_SYMBOL_GPL(arizona_dai_ops); | ||
654 | |||
655 | int arizona_init_dai(struct arizona_priv *priv, int id) | ||
656 | { | ||
657 | struct arizona_dai_priv *dai_priv = &priv->dai[id]; | ||
658 | |||
659 | dai_priv->clk = ARIZONA_CLK_SYSCLK; | ||
660 | |||
661 | return 0; | ||
662 | } | ||
663 | EXPORT_SYMBOL_GPL(arizona_init_dai); | ||
664 | |||
665 | static irqreturn_t arizona_fll_lock(int irq, void *data) | ||
666 | { | ||
667 | struct arizona_fll *fll = data; | ||
668 | |||
669 | arizona_fll_dbg(fll, "Locked\n"); | ||
670 | |||
671 | complete(&fll->lock); | ||
672 | |||
673 | return IRQ_HANDLED; | ||
674 | } | ||
675 | |||
676 | static irqreturn_t arizona_fll_clock_ok(int irq, void *data) | ||
677 | { | ||
678 | struct arizona_fll *fll = data; | ||
679 | |||
680 | arizona_fll_dbg(fll, "clock OK\n"); | ||
681 | |||
682 | complete(&fll->ok); | ||
683 | |||
684 | return IRQ_HANDLED; | ||
685 | } | ||
686 | |||
687 | static struct { | ||
688 | unsigned int min; | ||
689 | unsigned int max; | ||
690 | u16 fratio; | ||
691 | int ratio; | ||
692 | } fll_fratios[] = { | ||
693 | { 0, 64000, 4, 16 }, | ||
694 | { 64000, 128000, 3, 8 }, | ||
695 | { 128000, 256000, 2, 4 }, | ||
696 | { 256000, 1000000, 1, 2 }, | ||
697 | { 1000000, 13500000, 0, 1 }, | ||
698 | }; | ||
699 | |||
700 | struct arizona_fll_cfg { | ||
701 | int n; | ||
702 | int theta; | ||
703 | int lambda; | ||
704 | int refdiv; | ||
705 | int outdiv; | ||
706 | int fratio; | ||
707 | }; | ||
708 | |||
709 | static int arizona_calc_fll(struct arizona_fll *fll, | ||
710 | struct arizona_fll_cfg *cfg, | ||
711 | unsigned int Fref, | ||
712 | unsigned int Fout) | ||
713 | { | ||
714 | unsigned int target, div, gcd_fll; | ||
715 | int i, ratio; | ||
716 | |||
717 | arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, Fout); | ||
718 | |||
719 | /* Fref must be <=13.5MHz */ | ||
720 | div = 1; | ||
721 | cfg->refdiv = 0; | ||
722 | while ((Fref / div) > 13500000) { | ||
723 | div *= 2; | ||
724 | cfg->refdiv++; | ||
725 | |||
726 | if (div > 8) { | ||
727 | arizona_fll_err(fll, | ||
728 | "Can't scale %dMHz in to <=13.5MHz\n", | ||
729 | Fref); | ||
730 | return -EINVAL; | ||
731 | } | ||
732 | } | ||
733 | |||
734 | /* Apply the division for our remaining calculations */ | ||
735 | Fref /= div; | ||
736 | |||
737 | /* Fvco should be over the targt; don't check the upper bound */ | ||
738 | div = 1; | ||
739 | while (Fout * div < 90000000 * fll->vco_mult) { | ||
740 | div++; | ||
741 | if (div > 7) { | ||
742 | arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n", | ||
743 | Fout); | ||
744 | return -EINVAL; | ||
745 | } | ||
746 | } | ||
747 | target = Fout * div / fll->vco_mult; | ||
748 | cfg->outdiv = div; | ||
749 | |||
750 | arizona_fll_dbg(fll, "Fvco=%dHz\n", target); | ||
751 | |||
752 | /* Find an appropraite FLL_FRATIO and factor it out of the target */ | ||
753 | for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { | ||
754 | if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { | ||
755 | cfg->fratio = fll_fratios[i].fratio; | ||
756 | ratio = fll_fratios[i].ratio; | ||
757 | break; | ||
758 | } | ||
759 | } | ||
760 | if (i == ARRAY_SIZE(fll_fratios)) { | ||
761 | arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n", | ||
762 | Fref); | ||
763 | return -EINVAL; | ||
764 | } | ||
765 | |||
766 | cfg->n = target / (ratio * Fref); | ||
767 | |||
768 | if (target % Fref) { | ||
769 | gcd_fll = gcd(target, ratio * Fref); | ||
770 | arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll); | ||
771 | |||
772 | cfg->theta = (target - (cfg->n * ratio * Fref)) | ||
773 | / gcd_fll; | ||
774 | cfg->lambda = (ratio * Fref) / gcd_fll; | ||
775 | } else { | ||
776 | cfg->theta = 0; | ||
777 | cfg->lambda = 0; | ||
778 | } | ||
779 | |||
780 | arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", | ||
781 | cfg->n, cfg->theta, cfg->lambda); | ||
782 | arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", | ||
783 | cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv); | ||
784 | |||
785 | return 0; | ||
786 | |||
787 | } | ||
788 | |||
789 | static void arizona_apply_fll(struct arizona *arizona, unsigned int base, | ||
790 | struct arizona_fll_cfg *cfg, int source) | ||
791 | { | ||
792 | regmap_update_bits(arizona->regmap, base + 3, | ||
793 | ARIZONA_FLL1_THETA_MASK, cfg->theta); | ||
794 | regmap_update_bits(arizona->regmap, base + 4, | ||
795 | ARIZONA_FLL1_LAMBDA_MASK, cfg->lambda); | ||
796 | regmap_update_bits(arizona->regmap, base + 5, | ||
797 | ARIZONA_FLL1_FRATIO_MASK, | ||
798 | cfg->fratio << ARIZONA_FLL1_FRATIO_SHIFT); | ||
799 | regmap_update_bits(arizona->regmap, base + 6, | ||
800 | ARIZONA_FLL1_CLK_REF_DIV_MASK | | ||
801 | ARIZONA_FLL1_CLK_REF_SRC_MASK, | ||
802 | cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT | | ||
803 | source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT); | ||
804 | |||
805 | regmap_update_bits(arizona->regmap, base + 2, | ||
806 | ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK, | ||
807 | ARIZONA_FLL1_CTRL_UPD | cfg->n); | ||
808 | } | ||
809 | |||
810 | int arizona_set_fll(struct arizona_fll *fll, int source, | ||
811 | unsigned int Fref, unsigned int Fout) | ||
812 | { | ||
813 | struct arizona *arizona = fll->arizona; | ||
814 | struct arizona_fll_cfg cfg, sync; | ||
815 | unsigned int reg, val; | ||
816 | int syncsrc; | ||
817 | bool ena; | ||
818 | int ret; | ||
819 | |||
820 | ret = regmap_read(arizona->regmap, fll->base + 1, ®); | ||
821 | if (ret != 0) { | ||
822 | arizona_fll_err(fll, "Failed to read current state: %d\n", | ||
823 | ret); | ||
824 | return ret; | ||
825 | } | ||
826 | ena = reg & ARIZONA_FLL1_ENA; | ||
827 | |||
828 | if (Fout) { | ||
829 | /* Do we have a 32kHz reference? */ | ||
830 | regmap_read(arizona->regmap, ARIZONA_CLOCK_32K_1, &val); | ||
831 | switch (val & ARIZONA_CLK_32K_SRC_MASK) { | ||
832 | case ARIZONA_CLK_SRC_MCLK1: | ||
833 | case ARIZONA_CLK_SRC_MCLK2: | ||
834 | syncsrc = val & ARIZONA_CLK_32K_SRC_MASK; | ||
835 | break; | ||
836 | default: | ||
837 | syncsrc = -1; | ||
838 | } | ||
839 | |||
840 | if (source == syncsrc) | ||
841 | syncsrc = -1; | ||
842 | |||
843 | if (syncsrc >= 0) { | ||
844 | ret = arizona_calc_fll(fll, &sync, Fref, Fout); | ||
845 | if (ret != 0) | ||
846 | return ret; | ||
847 | |||
848 | ret = arizona_calc_fll(fll, &cfg, 32768, Fout); | ||
849 | if (ret != 0) | ||
850 | return ret; | ||
851 | } else { | ||
852 | ret = arizona_calc_fll(fll, &cfg, Fref, Fout); | ||
853 | if (ret != 0) | ||
854 | return ret; | ||
855 | } | ||
856 | } else { | ||
857 | regmap_update_bits(arizona->regmap, fll->base + 1, | ||
858 | ARIZONA_FLL1_ENA, 0); | ||
859 | regmap_update_bits(arizona->regmap, fll->base + 0x11, | ||
860 | ARIZONA_FLL1_SYNC_ENA, 0); | ||
861 | |||
862 | if (ena) | ||
863 | pm_runtime_put_autosuspend(arizona->dev); | ||
864 | |||
865 | return 0; | ||
866 | } | ||
867 | |||
868 | regmap_update_bits(arizona->regmap, fll->base + 5, | ||
869 | ARIZONA_FLL1_OUTDIV_MASK, | ||
870 | cfg.outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); | ||
871 | |||
872 | if (syncsrc >= 0) { | ||
873 | arizona_apply_fll(arizona, fll->base, &cfg, syncsrc); | ||
874 | arizona_apply_fll(arizona, fll->base + 0x10, &sync, source); | ||
875 | } else { | ||
876 | arizona_apply_fll(arizona, fll->base, &cfg, source); | ||
877 | } | ||
878 | |||
879 | if (!ena) | ||
880 | pm_runtime_get(arizona->dev); | ||
881 | |||
882 | /* Clear any pending completions */ | ||
883 | try_wait_for_completion(&fll->ok); | ||
884 | |||
885 | regmap_update_bits(arizona->regmap, fll->base + 1, | ||
886 | ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA); | ||
887 | if (syncsrc >= 0) | ||
888 | regmap_update_bits(arizona->regmap, fll->base + 0x11, | ||
889 | ARIZONA_FLL1_SYNC_ENA, | ||
890 | ARIZONA_FLL1_SYNC_ENA); | ||
891 | |||
892 | ret = wait_for_completion_timeout(&fll->ok, | ||
893 | msecs_to_jiffies(25)); | ||
894 | if (ret == 0) | ||
895 | arizona_fll_warn(fll, "Timed out waiting for lock\n"); | ||
896 | |||
897 | return 0; | ||
898 | } | ||
899 | EXPORT_SYMBOL_GPL(arizona_set_fll); | ||
900 | |||
901 | int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, | ||
902 | int ok_irq, struct arizona_fll *fll) | ||
903 | { | ||
904 | int ret; | ||
905 | |||
906 | init_completion(&fll->lock); | ||
907 | init_completion(&fll->ok); | ||
908 | |||
909 | fll->id = id; | ||
910 | fll->base = base; | ||
911 | fll->arizona = arizona; | ||
912 | |||
913 | snprintf(fll->lock_name, sizeof(fll->lock_name), "FLL%d lock", id); | ||
914 | snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name), | ||
915 | "FLL%d clock OK", id); | ||
916 | |||
917 | ret = arizona_request_irq(arizona, lock_irq, fll->lock_name, | ||
918 | arizona_fll_lock, fll); | ||
919 | if (ret != 0) { | ||
920 | dev_err(arizona->dev, "Failed to get FLL%d lock IRQ: %d\n", | ||
921 | id, ret); | ||
922 | } | ||
923 | |||
924 | ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name, | ||
925 | arizona_fll_clock_ok, fll); | ||
926 | if (ret != 0) { | ||
927 | dev_err(arizona->dev, "Failed to get FLL%d clock OK IRQ: %d\n", | ||
928 | id, ret); | ||
929 | } | ||
930 | |||
931 | return 0; | ||
932 | } | ||
933 | EXPORT_SYMBOL_GPL(arizona_init_fll); | ||
934 | |||
935 | MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); | ||
936 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | ||
937 | MODULE_LICENSE("GPL"); | ||