diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-06-18 16:08:44 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-06-23 07:11:46 -0400 |
commit | 07ed873e4c975a26c327a6bd306693678ef63351 (patch) | |
tree | ece6644481ef3c7982930f01c4874ab8660775dc /sound/soc/codecs/arizona.c | |
parent | 9dfdd5abcf2b350d4fdb207c0dff3194e2fd73db (diff) |
ASoC: Add shared code for Wolfson Arizona class devices
The Wolfson Arizona series of audio hub CODECs can share a large amount
of their driver code as the result of a common register map. This patch
adds some of this core support, providing a basis for the initial WM5102
audio driver.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs/arizona.c')
-rw-r--r-- | sound/soc/codecs/arizona.c | 781 |
1 files changed, 781 insertions, 0 deletions
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c new file mode 100644 index 000000000000..3b5730b90686 --- /dev/null +++ b/sound/soc/codecs/arizona.c | |||
@@ -0,0 +1,781 @@ | |||
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 | "AIF1RX1", | ||
83 | "AIF1RX2", | ||
84 | "AIF1RX3", | ||
85 | "AIF1RX4", | ||
86 | "AIF1RX5", | ||
87 | "AIF1RX6", | ||
88 | "AIF1RX7", | ||
89 | "AIF1RX8", | ||
90 | "AIF2RX1", | ||
91 | "AIF2RX2", | ||
92 | "AIF3RX1", | ||
93 | "AIF3RX2", | ||
94 | "SLIMRX1", | ||
95 | "SLIMRX2", | ||
96 | "SLIMRX3", | ||
97 | "SLIMRX4", | ||
98 | "SLIMRX5", | ||
99 | "SLIMRX6", | ||
100 | "SLIMRX7", | ||
101 | "SLIMRX8", | ||
102 | "EQ1", | ||
103 | "EQ2", | ||
104 | "EQ3", | ||
105 | "EQ4", | ||
106 | "DRC1L", | ||
107 | "DRC1R", | ||
108 | "DRC2L", | ||
109 | "DRC2R", | ||
110 | "LHPF1", | ||
111 | "LHPF2", | ||
112 | "LHPF3", | ||
113 | "LHPF4", | ||
114 | "DSP1.1", | ||
115 | "DSP1.2", | ||
116 | "DSP1.3", | ||
117 | "DSP1.4", | ||
118 | "DSP1.5", | ||
119 | "DSP1.6", | ||
120 | "ASRC1L", | ||
121 | "ASRC1R", | ||
122 | "ASRC2L", | ||
123 | "ASRC2R", | ||
124 | }; | ||
125 | EXPORT_SYMBOL_GPL(arizona_mixer_texts); | ||
126 | |||
127 | int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { | ||
128 | 0x00, /* None */ | ||
129 | 0x04, /* Tone */ | ||
130 | 0x05, | ||
131 | 0x06, /* Haptics */ | ||
132 | 0x08, /* AEC */ | ||
133 | 0x0c, /* Noise mixer */ | ||
134 | 0x0d, /* Comfort noise */ | ||
135 | 0x10, /* IN1L */ | ||
136 | 0x11, | ||
137 | 0x12, | ||
138 | 0x13, | ||
139 | 0x14, | ||
140 | 0x15, | ||
141 | 0x20, /* AIF1RX1 */ | ||
142 | 0x21, | ||
143 | 0x22, | ||
144 | 0x23, | ||
145 | 0x24, | ||
146 | 0x25, | ||
147 | 0x26, | ||
148 | 0x27, | ||
149 | 0x28, /* AIF2RX1 */ | ||
150 | 0x29, | ||
151 | 0x30, /* AIF3RX1 */ | ||
152 | 0x31, | ||
153 | 0x38, /* SLIMRX1 */ | ||
154 | 0x39, | ||
155 | 0x3a, | ||
156 | 0x3b, | ||
157 | 0x3c, | ||
158 | 0x3d, | ||
159 | 0x3e, | ||
160 | 0x3f, | ||
161 | 0x50, /* EQ1 */ | ||
162 | 0x51, | ||
163 | 0x52, | ||
164 | 0x53, | ||
165 | 0x58, /* DRC1L */ | ||
166 | 0x59, | ||
167 | 0x5a, | ||
168 | 0x5b, | ||
169 | 0x60, /* LHPF1 */ | ||
170 | 0x61, | ||
171 | 0x62, | ||
172 | 0x63, | ||
173 | 0x68, /* DSP1.1 */ | ||
174 | 0x69, | ||
175 | 0x6a, | ||
176 | 0x6b, | ||
177 | 0x6c, | ||
178 | 0x6d, | ||
179 | 0x90, /* ASRC1L */ | ||
180 | 0x91, | ||
181 | 0x92, | ||
182 | 0x93, | ||
183 | }; | ||
184 | EXPORT_SYMBOL_GPL(arizona_mixer_values); | ||
185 | |||
186 | const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0); | ||
187 | EXPORT_SYMBOL_GPL(arizona_mixer_tlv); | ||
188 | |||
189 | static const char *arizona_lhpf_mode_text[] = { | ||
190 | "Low-pass", "High-pass" | ||
191 | }; | ||
192 | |||
193 | const struct soc_enum arizona_lhpf1_mode = | ||
194 | SOC_ENUM_SINGLE(ARIZONA_HPLPF1_1, ARIZONA_LHPF1_MODE_SHIFT, 2, | ||
195 | arizona_lhpf_mode_text); | ||
196 | EXPORT_SYMBOL_GPL(arizona_lhpf1_mode); | ||
197 | |||
198 | const struct soc_enum arizona_lhpf2_mode = | ||
199 | SOC_ENUM_SINGLE(ARIZONA_HPLPF2_1, ARIZONA_LHPF2_MODE_SHIFT, 2, | ||
200 | arizona_lhpf_mode_text); | ||
201 | EXPORT_SYMBOL_GPL(arizona_lhpf2_mode); | ||
202 | |||
203 | const struct soc_enum arizona_lhpf3_mode = | ||
204 | SOC_ENUM_SINGLE(ARIZONA_HPLPF3_1, ARIZONA_LHPF3_MODE_SHIFT, 2, | ||
205 | arizona_lhpf_mode_text); | ||
206 | EXPORT_SYMBOL_GPL(arizona_lhpf3_mode); | ||
207 | |||
208 | const struct soc_enum arizona_lhpf4_mode = | ||
209 | SOC_ENUM_SINGLE(ARIZONA_HPLPF4_1, ARIZONA_LHPF4_MODE_SHIFT, 2, | ||
210 | arizona_lhpf_mode_text); | ||
211 | EXPORT_SYMBOL_GPL(arizona_lhpf4_mode); | ||
212 | |||
213 | int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, | ||
214 | int event) | ||
215 | { | ||
216 | return 0; | ||
217 | } | ||
218 | EXPORT_SYMBOL_GPL(arizona_in_ev); | ||
219 | |||
220 | int arizona_out_ev(struct snd_soc_dapm_widget *w, | ||
221 | struct snd_kcontrol *kcontrol, | ||
222 | int event) | ||
223 | { | ||
224 | return 0; | ||
225 | } | ||
226 | EXPORT_SYMBOL_GPL(arizona_out_ev); | ||
227 | |||
228 | int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, | ||
229 | int source, unsigned int freq, int dir) | ||
230 | { | ||
231 | struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
232 | struct arizona *arizona = priv->arizona; | ||
233 | char *name; | ||
234 | unsigned int reg; | ||
235 | unsigned int mask = ARIZONA_SYSCLK_FREQ_MASK | ARIZONA_SYSCLK_SRC_MASK; | ||
236 | unsigned int val = source << ARIZONA_SYSCLK_SRC_SHIFT; | ||
237 | unsigned int *clk; | ||
238 | |||
239 | switch (clk_id) { | ||
240 | case ARIZONA_CLK_SYSCLK: | ||
241 | name = "SYSCLK"; | ||
242 | reg = ARIZONA_SYSTEM_CLOCK_1; | ||
243 | clk = &priv->sysclk; | ||
244 | mask |= ARIZONA_SYSCLK_FRAC; | ||
245 | break; | ||
246 | case ARIZONA_CLK_ASYNCCLK: | ||
247 | name = "ASYNCCLK"; | ||
248 | reg = ARIZONA_ASYNC_CLOCK_1; | ||
249 | clk = &priv->asyncclk; | ||
250 | break; | ||
251 | default: | ||
252 | return -EINVAL; | ||
253 | } | ||
254 | |||
255 | switch (freq) { | ||
256 | case 5644800: | ||
257 | case 6144000: | ||
258 | break; | ||
259 | case 11289600: | ||
260 | case 12288000: | ||
261 | val |= 1 << ARIZONA_SYSCLK_FREQ_SHIFT; | ||
262 | break; | ||
263 | case 22579200: | ||
264 | case 24576000: | ||
265 | val |= 2 << ARIZONA_SYSCLK_FREQ_SHIFT; | ||
266 | break; | ||
267 | case 45158400: | ||
268 | case 49152000: | ||
269 | val |= 3 << ARIZONA_SYSCLK_FREQ_SHIFT; | ||
270 | break; | ||
271 | default: | ||
272 | return -EINVAL; | ||
273 | } | ||
274 | |||
275 | *clk = freq; | ||
276 | |||
277 | if (freq % 6144000) | ||
278 | val |= ARIZONA_SYSCLK_FRAC; | ||
279 | |||
280 | dev_dbg(arizona->dev, "%s set to %uHz", name, freq); | ||
281 | |||
282 | return regmap_update_bits(arizona->regmap, reg, mask, val); | ||
283 | } | ||
284 | EXPORT_SYMBOL_GPL(arizona_set_sysclk); | ||
285 | |||
286 | static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | ||
287 | { | ||
288 | struct snd_soc_codec *codec = dai->codec; | ||
289 | int lrclk, bclk, mode, base; | ||
290 | |||
291 | base = dai->driver->base; | ||
292 | |||
293 | lrclk = 0; | ||
294 | bclk = 0; | ||
295 | |||
296 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
297 | case SND_SOC_DAIFMT_DSP_A: | ||
298 | mode = 0; | ||
299 | break; | ||
300 | case SND_SOC_DAIFMT_DSP_B: | ||
301 | mode = 1; | ||
302 | break; | ||
303 | case SND_SOC_DAIFMT_I2S: | ||
304 | mode = 2; | ||
305 | break; | ||
306 | case SND_SOC_DAIFMT_LEFT_J: | ||
307 | mode = 3; | ||
308 | break; | ||
309 | default: | ||
310 | arizona_aif_err(dai, "Unsupported DAI format %d\n", | ||
311 | fmt & SND_SOC_DAIFMT_FORMAT_MASK); | ||
312 | return -EINVAL; | ||
313 | } | ||
314 | |||
315 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
316 | case SND_SOC_DAIFMT_CBS_CFS: | ||
317 | break; | ||
318 | case SND_SOC_DAIFMT_CBS_CFM: | ||
319 | lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR; | ||
320 | break; | ||
321 | case SND_SOC_DAIFMT_CBM_CFS: | ||
322 | bclk |= ARIZONA_AIF1_BCLK_MSTR; | ||
323 | break; | ||
324 | case SND_SOC_DAIFMT_CBM_CFM: | ||
325 | bclk |= ARIZONA_AIF1_BCLK_MSTR; | ||
326 | lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR; | ||
327 | break; | ||
328 | default: | ||
329 | arizona_aif_err(dai, "Unsupported master mode %d\n", | ||
330 | fmt & SND_SOC_DAIFMT_MASTER_MASK); | ||
331 | return -EINVAL; | ||
332 | } | ||
333 | |||
334 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
335 | case SND_SOC_DAIFMT_NB_NF: | ||
336 | break; | ||
337 | case SND_SOC_DAIFMT_IB_IF: | ||
338 | bclk |= ARIZONA_AIF1_BCLK_INV; | ||
339 | lrclk |= ARIZONA_AIF1TX_LRCLK_INV; | ||
340 | break; | ||
341 | case SND_SOC_DAIFMT_IB_NF: | ||
342 | bclk |= ARIZONA_AIF1_BCLK_INV; | ||
343 | break; | ||
344 | case SND_SOC_DAIFMT_NB_IF: | ||
345 | lrclk |= ARIZONA_AIF1TX_LRCLK_INV; | ||
346 | break; | ||
347 | default: | ||
348 | return -EINVAL; | ||
349 | } | ||
350 | |||
351 | snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, | ||
352 | ARIZONA_AIF1_BCLK_INV | ARIZONA_AIF1_BCLK_MSTR, | ||
353 | bclk); | ||
354 | snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_PIN_CTRL, | ||
355 | ARIZONA_AIF1TX_LRCLK_INV | | ||
356 | ARIZONA_AIF1TX_LRCLK_MSTR, lrclk); | ||
357 | snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_PIN_CTRL, | ||
358 | ARIZONA_AIF1RX_LRCLK_INV | | ||
359 | ARIZONA_AIF1RX_LRCLK_MSTR, lrclk); | ||
360 | snd_soc_update_bits(codec, base + ARIZONA_AIF_FORMAT, | ||
361 | ARIZONA_AIF1_FMT_MASK, mode); | ||
362 | |||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | static const int arizona_48k_rates[] = { | ||
367 | -1, | ||
368 | 48000, | ||
369 | 64000, | ||
370 | 96000, | ||
371 | 128000, | ||
372 | 192000, | ||
373 | 256000, | ||
374 | 384000, | ||
375 | 512000, | ||
376 | 768000, | ||
377 | 1024000, | ||
378 | 1536000, | ||
379 | 2048000, | ||
380 | 3072000, | ||
381 | 4096000, | ||
382 | 6144000, | ||
383 | 8192000, | ||
384 | 12288000, | ||
385 | 24576000, | ||
386 | }; | ||
387 | |||
388 | static const int arizona_44k1_rates[] = { | ||
389 | -1, | ||
390 | 44100, | ||
391 | 58800, | ||
392 | 88200, | ||
393 | 117600, | ||
394 | 177640, | ||
395 | 235200, | ||
396 | 352800, | ||
397 | 470400, | ||
398 | 705600, | ||
399 | 940800, | ||
400 | 1411200, | ||
401 | 1881600, | ||
402 | 2882400, | ||
403 | 3763200, | ||
404 | 5644800, | ||
405 | 7526400, | ||
406 | 11289600, | ||
407 | 22579200, | ||
408 | }; | ||
409 | |||
410 | static int arizona_sr_vals[] = { | ||
411 | 0, | ||
412 | 12000, | ||
413 | 24000, | ||
414 | 48000, | ||
415 | 96000, | ||
416 | 192000, | ||
417 | 384000, | ||
418 | 768000, | ||
419 | 0, | ||
420 | 11025, | ||
421 | 22050, | ||
422 | 44100, | ||
423 | 88200, | ||
424 | 176400, | ||
425 | 352800, | ||
426 | 705600, | ||
427 | 4000, | ||
428 | 8000, | ||
429 | 16000, | ||
430 | 32000, | ||
431 | 64000, | ||
432 | 128000, | ||
433 | 256000, | ||
434 | 512000, | ||
435 | }; | ||
436 | |||
437 | static int arizona_hw_params(struct snd_pcm_substream *substream, | ||
438 | struct snd_pcm_hw_params *params, | ||
439 | struct snd_soc_dai *dai) | ||
440 | { | ||
441 | struct snd_soc_codec *codec = dai->codec; | ||
442 | int base = dai->driver->base; | ||
443 | const int *rates; | ||
444 | int i; | ||
445 | int bclk, lrclk, wl, frame, sr_val; | ||
446 | |||
447 | if (params_rate(params) % 8000) | ||
448 | rates = &arizona_44k1_rates[0]; | ||
449 | else | ||
450 | rates = &arizona_48k_rates[0]; | ||
451 | |||
452 | for (i = 0; i < ARRAY_SIZE(arizona_44k1_rates); i++) { | ||
453 | if (rates[i] == snd_soc_params_to_bclk(params)) { | ||
454 | bclk = i; | ||
455 | break; | ||
456 | } | ||
457 | } | ||
458 | if (i == ARRAY_SIZE(arizona_44k1_rates)) { | ||
459 | arizona_aif_err(dai, "Unsupported sample rate %dHz\n", | ||
460 | params_rate(params)); | ||
461 | return -EINVAL; | ||
462 | } | ||
463 | |||
464 | /* | ||
465 | * We will need to be more flexible than this in future, | ||
466 | * currently we use a single sample rate for the chip. | ||
467 | */ | ||
468 | for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) | ||
469 | if (arizona_sr_vals[i] == params_rate(params)) | ||
470 | break; | ||
471 | if (i == ARRAY_SIZE(arizona_sr_vals)) { | ||
472 | arizona_aif_err(dai, "Unsupported sample rate %dHz\n", | ||
473 | params_rate(params)); | ||
474 | return -EINVAL; | ||
475 | } | ||
476 | sr_val = i; | ||
477 | |||
478 | lrclk = snd_soc_params_to_bclk(params) / params_rate(params); | ||
479 | |||
480 | arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", | ||
481 | rates[bclk], rates[bclk] / lrclk); | ||
482 | |||
483 | wl = snd_pcm_format_width(params_format(params)); | ||
484 | frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl; | ||
485 | |||
486 | snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, | ||
487 | ARIZONA_SAMPLE_RATE_1_MASK, sr_val); | ||
488 | snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, | ||
489 | ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); | ||
490 | snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_BCLK_RATE, | ||
491 | ARIZONA_AIF1TX_BCPF_MASK, lrclk); | ||
492 | snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_BCLK_RATE, | ||
493 | ARIZONA_AIF1RX_BCPF_MASK, lrclk); | ||
494 | snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_1, | ||
495 | ARIZONA_AIF1TX_WL_MASK | | ||
496 | ARIZONA_AIF1TX_SLOT_LEN_MASK, frame); | ||
497 | snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_2, | ||
498 | ARIZONA_AIF1RX_WL_MASK | | ||
499 | ARIZONA_AIF1RX_SLOT_LEN_MASK, frame); | ||
500 | |||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | const struct snd_soc_dai_ops arizona_dai_ops = { | ||
505 | .set_fmt = arizona_set_fmt, | ||
506 | .hw_params = arizona_hw_params, | ||
507 | }; | ||
508 | |||
509 | static irqreturn_t arizona_fll_lock(int irq, void *data) | ||
510 | { | ||
511 | struct arizona_fll *fll = data; | ||
512 | |||
513 | arizona_fll_dbg(fll, "Locked\n"); | ||
514 | |||
515 | complete(&fll->lock); | ||
516 | |||
517 | return IRQ_HANDLED; | ||
518 | } | ||
519 | |||
520 | static irqreturn_t arizona_fll_clock_ok(int irq, void *data) | ||
521 | { | ||
522 | struct arizona_fll *fll = data; | ||
523 | |||
524 | arizona_fll_dbg(fll, "clock OK\n"); | ||
525 | |||
526 | complete(&fll->ok); | ||
527 | |||
528 | return IRQ_HANDLED; | ||
529 | } | ||
530 | |||
531 | static struct { | ||
532 | unsigned int min; | ||
533 | unsigned int max; | ||
534 | u16 fratio; | ||
535 | int ratio; | ||
536 | } fll_fratios[] = { | ||
537 | { 0, 64000, 4, 16 }, | ||
538 | { 64000, 128000, 3, 8 }, | ||
539 | { 128000, 256000, 2, 4 }, | ||
540 | { 256000, 1000000, 1, 2 }, | ||
541 | { 1000000, 13500000, 0, 1 }, | ||
542 | }; | ||
543 | |||
544 | struct arizona_fll_cfg { | ||
545 | int n; | ||
546 | int theta; | ||
547 | int lambda; | ||
548 | int refdiv; | ||
549 | int outdiv; | ||
550 | int fratio; | ||
551 | }; | ||
552 | |||
553 | static int arizona_calc_fll(struct arizona_fll *fll, | ||
554 | struct arizona_fll_cfg *cfg, | ||
555 | unsigned int Fref, | ||
556 | unsigned int Fout) | ||
557 | { | ||
558 | unsigned int target, div, gcd_fll; | ||
559 | int i, ratio; | ||
560 | |||
561 | arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, Fout); | ||
562 | |||
563 | /* Fref must be <=13.5MHz */ | ||
564 | div = 1; | ||
565 | cfg->refdiv = 0; | ||
566 | while ((Fref / div) > 13500000) { | ||
567 | div *= 2; | ||
568 | cfg->refdiv++; | ||
569 | |||
570 | if (div > 8) { | ||
571 | arizona_fll_err(fll, | ||
572 | "Can't scale %dMHz in to <=13.5MHz\n", | ||
573 | Fref); | ||
574 | return -EINVAL; | ||
575 | } | ||
576 | } | ||
577 | |||
578 | /* Apply the division for our remaining calculations */ | ||
579 | Fref /= div; | ||
580 | |||
581 | /* Fvco should be 90-100MHz; don't check the upper bound */ | ||
582 | div = 1; | ||
583 | while (Fout * div < 90000000) { | ||
584 | div++; | ||
585 | if (div > 7) { | ||
586 | arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n", | ||
587 | Fout); | ||
588 | return -EINVAL; | ||
589 | } | ||
590 | } | ||
591 | target = Fout * div; | ||
592 | cfg->outdiv = div; | ||
593 | |||
594 | arizona_fll_dbg(fll, "Fvco=%dHz\n", target); | ||
595 | |||
596 | /* Find an appropraite FLL_FRATIO and factor it out of the target */ | ||
597 | for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { | ||
598 | if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { | ||
599 | cfg->fratio = fll_fratios[i].fratio; | ||
600 | ratio = fll_fratios[i].ratio; | ||
601 | break; | ||
602 | } | ||
603 | } | ||
604 | if (i == ARRAY_SIZE(fll_fratios)) { | ||
605 | arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n", | ||
606 | Fref); | ||
607 | return -EINVAL; | ||
608 | } | ||
609 | |||
610 | cfg->n = target / (ratio * Fref); | ||
611 | |||
612 | if (target % Fref) { | ||
613 | gcd_fll = gcd(target, ratio * Fref); | ||
614 | arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll); | ||
615 | |||
616 | cfg->theta = (target - (cfg->n * ratio * Fref)) | ||
617 | / gcd_fll; | ||
618 | cfg->lambda = (ratio * Fref) / gcd_fll; | ||
619 | } else { | ||
620 | cfg->theta = 0; | ||
621 | cfg->lambda = 0; | ||
622 | } | ||
623 | |||
624 | arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", | ||
625 | cfg->n, cfg->theta, cfg->lambda); | ||
626 | arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", | ||
627 | cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv); | ||
628 | |||
629 | return 0; | ||
630 | |||
631 | } | ||
632 | |||
633 | static void arizona_apply_fll(struct arizona *arizona, unsigned int base, | ||
634 | struct arizona_fll_cfg *cfg, int source) | ||
635 | { | ||
636 | regmap_update_bits(arizona->regmap, base + 3, | ||
637 | ARIZONA_FLL1_THETA_MASK, cfg->theta); | ||
638 | regmap_update_bits(arizona->regmap, base + 4, | ||
639 | ARIZONA_FLL1_LAMBDA_MASK, cfg->lambda); | ||
640 | regmap_update_bits(arizona->regmap, base + 5, | ||
641 | ARIZONA_FLL1_FRATIO_MASK, | ||
642 | cfg->fratio << ARIZONA_FLL1_FRATIO_SHIFT); | ||
643 | regmap_update_bits(arizona->regmap, base + 6, | ||
644 | ARIZONA_FLL1_CLK_REF_DIV_MASK | | ||
645 | ARIZONA_FLL1_CLK_REF_SRC_MASK, | ||
646 | cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT | | ||
647 | source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT); | ||
648 | |||
649 | regmap_update_bits(arizona->regmap, base + 2, | ||
650 | ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK, | ||
651 | ARIZONA_FLL1_CTRL_UPD | cfg->n); | ||
652 | } | ||
653 | |||
654 | int arizona_set_fll(struct arizona_fll *fll, int source, | ||
655 | unsigned int Fref, unsigned int Fout) | ||
656 | { | ||
657 | struct arizona *arizona = fll->arizona; | ||
658 | struct arizona_fll_cfg cfg, sync; | ||
659 | unsigned int reg, val; | ||
660 | int syncsrc; | ||
661 | bool ena; | ||
662 | int ret; | ||
663 | |||
664 | ret = regmap_read(arizona->regmap, fll->base + 1, ®); | ||
665 | if (ret != 0) { | ||
666 | arizona_fll_err(fll, "Failed to read current state: %d\n", | ||
667 | ret); | ||
668 | return ret; | ||
669 | } | ||
670 | ena = reg & ARIZONA_FLL1_ENA; | ||
671 | |||
672 | if (Fout) { | ||
673 | /* Do we have a 32kHz reference? */ | ||
674 | regmap_read(arizona->regmap, ARIZONA_CLOCK_32K_1, &val); | ||
675 | switch (val & ARIZONA_CLK_32K_SRC_MASK) { | ||
676 | case ARIZONA_CLK_SRC_MCLK1: | ||
677 | case ARIZONA_CLK_SRC_MCLK2: | ||
678 | syncsrc = val & ARIZONA_CLK_32K_SRC_MASK; | ||
679 | break; | ||
680 | default: | ||
681 | syncsrc = -1; | ||
682 | } | ||
683 | |||
684 | if (source == syncsrc) | ||
685 | syncsrc = -1; | ||
686 | |||
687 | if (syncsrc >= 0) { | ||
688 | ret = arizona_calc_fll(fll, &sync, Fref, Fout); | ||
689 | if (ret != 0) | ||
690 | return ret; | ||
691 | |||
692 | ret = arizona_calc_fll(fll, &cfg, 32768, Fout); | ||
693 | if (ret != 0) | ||
694 | return ret; | ||
695 | } else { | ||
696 | ret = arizona_calc_fll(fll, &cfg, Fref, Fout); | ||
697 | if (ret != 0) | ||
698 | return ret; | ||
699 | } | ||
700 | } else { | ||
701 | regmap_update_bits(arizona->regmap, fll->base + 1, | ||
702 | ARIZONA_FLL1_ENA, 0); | ||
703 | regmap_update_bits(arizona->regmap, fll->base + 0x11, | ||
704 | ARIZONA_FLL1_SYNC_ENA, 0); | ||
705 | |||
706 | if (ena) | ||
707 | pm_runtime_put_autosuspend(arizona->dev); | ||
708 | |||
709 | return 0; | ||
710 | } | ||
711 | |||
712 | regmap_update_bits(arizona->regmap, fll->base + 5, | ||
713 | ARIZONA_FLL1_OUTDIV_MASK, | ||
714 | cfg.outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); | ||
715 | |||
716 | if (syncsrc >= 0) { | ||
717 | arizona_apply_fll(arizona, fll->base, &cfg, syncsrc); | ||
718 | arizona_apply_fll(arizona, fll->base + 0x10, &sync, source); | ||
719 | } else { | ||
720 | arizona_apply_fll(arizona, fll->base, &cfg, source); | ||
721 | } | ||
722 | |||
723 | if (!ena) | ||
724 | pm_runtime_get(arizona->dev); | ||
725 | |||
726 | /* Clear any pending completions */ | ||
727 | try_wait_for_completion(&fll->ok); | ||
728 | |||
729 | regmap_update_bits(arizona->regmap, fll->base + 1, | ||
730 | ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA); | ||
731 | if (syncsrc >= 0) | ||
732 | regmap_update_bits(arizona->regmap, fll->base + 0x11, | ||
733 | ARIZONA_FLL1_SYNC_ENA, | ||
734 | ARIZONA_FLL1_SYNC_ENA); | ||
735 | |||
736 | ret = wait_for_completion_timeout(&fll->ok, | ||
737 | msecs_to_jiffies(25)); | ||
738 | if (ret == 0) | ||
739 | arizona_fll_warn(fll, "Timed out waiting for lock\n"); | ||
740 | |||
741 | return 0; | ||
742 | } | ||
743 | EXPORT_SYMBOL_GPL(arizona_set_fll); | ||
744 | |||
745 | int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, | ||
746 | int ok_irq, struct arizona_fll *fll) | ||
747 | { | ||
748 | int ret; | ||
749 | |||
750 | init_completion(&fll->lock); | ||
751 | init_completion(&fll->ok); | ||
752 | |||
753 | fll->id = id; | ||
754 | fll->base = base; | ||
755 | fll->arizona = arizona; | ||
756 | |||
757 | snprintf(fll->lock_name, sizeof(fll->lock_name), "FLL%d lock", id); | ||
758 | snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name), | ||
759 | "FLL%d clock OK", id); | ||
760 | |||
761 | ret = arizona_request_irq(arizona, lock_irq, fll->lock_name, | ||
762 | arizona_fll_lock, fll); | ||
763 | if (ret != 0) { | ||
764 | dev_err(arizona->dev, "Failed to get FLL%d lock IRQ: %d\n", | ||
765 | id, ret); | ||
766 | } | ||
767 | |||
768 | ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name, | ||
769 | arizona_fll_clock_ok, fll); | ||
770 | if (ret != 0) { | ||
771 | dev_err(arizona->dev, "Failed to get FLL%d clock OK IRQ: %d\n", | ||
772 | id, ret); | ||
773 | } | ||
774 | |||
775 | return 0; | ||
776 | } | ||
777 | EXPORT_SYMBOL_GPL(arizona_init_fll); | ||
778 | |||
779 | MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); | ||
780 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | ||
781 | MODULE_LICENSE("GPL"); | ||