blob: 6d98cf41f727a7456d9ea2c17b9717be447e6bde [file] [log] [blame]
Mark Brown07ed8732012-06-18 21:08:44 +01001/*
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
68const 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",
Mark Brownc9c56fd2012-07-09 19:09:01 +010082 "IN4L",
83 "IN4R",
Mark Brown07ed8732012-06-18 21:08:44 +010084 "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",
Mark Brownc922cc42012-09-26 16:43:44 +0100122 "DSP2.1",
123 "DSP2.2",
124 "DSP2.3",
125 "DSP2.4",
126 "DSP2.5",
127 "DSP2.6",
128 "DSP3.1",
129 "DSP3.2",
130 "DSP3.3",
131 "DSP3.4",
132 "DSP3.5",
133 "DSP3.6",
134 "DSP4.1",
135 "DSP4.2",
136 "DSP4.3",
137 "DSP4.4",
138 "DSP4.5",
139 "DSP4.6",
Mark Brown07ed8732012-06-18 21:08:44 +0100140 "ASRC1L",
141 "ASRC1R",
142 "ASRC2L",
143 "ASRC2R",
Mark Brown91660bd2012-12-05 20:35:24 +0900144 "ISRC1INT1",
145 "ISRC1INT2",
146 "ISRC1INT3",
147 "ISRC1INT4",
148 "ISRC1DEC1",
149 "ISRC1DEC2",
150 "ISRC1DEC3",
151 "ISRC1DEC4",
152 "ISRC2INT1",
153 "ISRC2INT2",
154 "ISRC2INT3",
155 "ISRC2INT4",
156 "ISRC2DEC1",
157 "ISRC2DEC2",
158 "ISRC2DEC3",
159 "ISRC2DEC4",
160 "ISRC3INT1",
161 "ISRC3INT2",
162 "ISRC3INT3",
163 "ISRC3INT4",
164 "ISRC3DEC1",
165 "ISRC3DEC2",
166 "ISRC3DEC3",
167 "ISRC3DEC4",
Mark Brown07ed8732012-06-18 21:08:44 +0100168};
169EXPORT_SYMBOL_GPL(arizona_mixer_texts);
170
171int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
172 0x00, /* None */
173 0x04, /* Tone */
174 0x05,
175 0x06, /* Haptics */
176 0x08, /* AEC */
177 0x0c, /* Noise mixer */
178 0x0d, /* Comfort noise */
179 0x10, /* IN1L */
180 0x11,
181 0x12,
182 0x13,
183 0x14,
184 0x15,
Mark Brownc9c56fd2012-07-09 19:09:01 +0100185 0x16,
186 0x17,
Mark Brown07ed8732012-06-18 21:08:44 +0100187 0x20, /* AIF1RX1 */
188 0x21,
189 0x22,
190 0x23,
191 0x24,
192 0x25,
193 0x26,
194 0x27,
195 0x28, /* AIF2RX1 */
196 0x29,
197 0x30, /* AIF3RX1 */
198 0x31,
199 0x38, /* SLIMRX1 */
200 0x39,
201 0x3a,
202 0x3b,
203 0x3c,
204 0x3d,
205 0x3e,
206 0x3f,
207 0x50, /* EQ1 */
208 0x51,
209 0x52,
210 0x53,
211 0x58, /* DRC1L */
212 0x59,
213 0x5a,
214 0x5b,
215 0x60, /* LHPF1 */
216 0x61,
217 0x62,
218 0x63,
219 0x68, /* DSP1.1 */
220 0x69,
221 0x6a,
222 0x6b,
223 0x6c,
224 0x6d,
Mark Brownc922cc42012-09-26 16:43:44 +0100225 0x70, /* DSP2.1 */
226 0x71,
227 0x72,
228 0x73,
229 0x74,
230 0x75,
231 0x78, /* DSP3.1 */
232 0x79,
233 0x7a,
234 0x7b,
235 0x7c,
236 0x7d,
237 0x80, /* DSP4.1 */
238 0x81,
239 0x82,
240 0x83,
241 0x84,
242 0x85,
Mark Brown07ed8732012-06-18 21:08:44 +0100243 0x90, /* ASRC1L */
244 0x91,
245 0x92,
246 0x93,
Mark Brown91660bd2012-12-05 20:35:24 +0900247 0xa0, /* ISRC1INT1 */
248 0xa1,
249 0xa2,
250 0xa3,
251 0xa4, /* ISRC1DEC1 */
252 0xa5,
253 0xa6,
254 0xa7,
255 0xa8, /* ISRC2DEC1 */
256 0xa9,
257 0xaa,
258 0xab,
259 0xac, /* ISRC2INT1 */
260 0xad,
261 0xae,
262 0xaf,
263 0xb0, /* ISRC3DEC1 */
264 0xb1,
265 0xb2,
266 0xb3,
267 0xb4, /* ISRC3INT1 */
268 0xb5,
269 0xb6,
270 0xb7,
Mark Brown07ed8732012-06-18 21:08:44 +0100271};
272EXPORT_SYMBOL_GPL(arizona_mixer_values);
273
274const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0);
275EXPORT_SYMBOL_GPL(arizona_mixer_tlv);
276
Mark Browne853a002012-12-09 12:25:52 +0900277static const char *arizona_vol_ramp_text[] = {
278 "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
279 "15ms/6dB", "30ms/6dB",
280};
281
282const struct soc_enum arizona_in_vd_ramp =
283 SOC_ENUM_SINGLE(ARIZONA_INPUT_VOLUME_RAMP,
284 ARIZONA_IN_VD_RAMP_SHIFT, 7, arizona_vol_ramp_text);
285EXPORT_SYMBOL_GPL(arizona_in_vd_ramp);
286
287const struct soc_enum arizona_in_vi_ramp =
288 SOC_ENUM_SINGLE(ARIZONA_INPUT_VOLUME_RAMP,
289 ARIZONA_IN_VI_RAMP_SHIFT, 7, arizona_vol_ramp_text);
290EXPORT_SYMBOL_GPL(arizona_in_vi_ramp);
291
292const struct soc_enum arizona_out_vd_ramp =
293 SOC_ENUM_SINGLE(ARIZONA_OUTPUT_VOLUME_RAMP,
294 ARIZONA_OUT_VD_RAMP_SHIFT, 7, arizona_vol_ramp_text);
295EXPORT_SYMBOL_GPL(arizona_out_vd_ramp);
296
297const struct soc_enum arizona_out_vi_ramp =
298 SOC_ENUM_SINGLE(ARIZONA_OUTPUT_VOLUME_RAMP,
299 ARIZONA_OUT_VI_RAMP_SHIFT, 7, arizona_vol_ramp_text);
300EXPORT_SYMBOL_GPL(arizona_out_vi_ramp);
301
Mark Brown07ed8732012-06-18 21:08:44 +0100302static const char *arizona_lhpf_mode_text[] = {
303 "Low-pass", "High-pass"
304};
305
306const struct soc_enum arizona_lhpf1_mode =
307 SOC_ENUM_SINGLE(ARIZONA_HPLPF1_1, ARIZONA_LHPF1_MODE_SHIFT, 2,
308 arizona_lhpf_mode_text);
309EXPORT_SYMBOL_GPL(arizona_lhpf1_mode);
310
311const struct soc_enum arizona_lhpf2_mode =
312 SOC_ENUM_SINGLE(ARIZONA_HPLPF2_1, ARIZONA_LHPF2_MODE_SHIFT, 2,
313 arizona_lhpf_mode_text);
314EXPORT_SYMBOL_GPL(arizona_lhpf2_mode);
315
316const struct soc_enum arizona_lhpf3_mode =
317 SOC_ENUM_SINGLE(ARIZONA_HPLPF3_1, ARIZONA_LHPF3_MODE_SHIFT, 2,
318 arizona_lhpf_mode_text);
319EXPORT_SYMBOL_GPL(arizona_lhpf3_mode);
320
321const struct soc_enum arizona_lhpf4_mode =
322 SOC_ENUM_SINGLE(ARIZONA_HPLPF4_1, ARIZONA_LHPF4_MODE_SHIFT, 2,
323 arizona_lhpf_mode_text);
324EXPORT_SYMBOL_GPL(arizona_lhpf4_mode);
325
Mark Brown845571c2012-12-18 13:47:57 +0000326static const char *arizona_ng_hold_text[] = {
327 "30ms", "120ms", "250ms", "500ms",
328};
329
330const struct soc_enum arizona_ng_hold =
331 SOC_ENUM_SINGLE(ARIZONA_NOISE_GATE_CONTROL, ARIZONA_NGATE_HOLD_SHIFT,
332 4, arizona_ng_hold_text);
333EXPORT_SYMBOL_GPL(arizona_ng_hold);
334
Mark Brown07ed8732012-06-18 21:08:44 +0100335int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
336 int event)
337{
338 return 0;
339}
340EXPORT_SYMBOL_GPL(arizona_in_ev);
341
342int arizona_out_ev(struct snd_soc_dapm_widget *w,
343 struct snd_kcontrol *kcontrol,
344 int event)
345{
346 return 0;
347}
348EXPORT_SYMBOL_GPL(arizona_out_ev);
349
Mark Browncbd840d2012-08-08 17:52:44 +0100350static unsigned int arizona_sysclk_48k_rates[] = {
351 6144000,
352 12288000,
Dimitris Papastamos96e1f182012-11-15 11:41:30 +0000353 24576000,
Mark Browncbd840d2012-08-08 17:52:44 +0100354 49152000,
Mark Brownaeaeee12012-09-26 17:50:02 +0100355 73728000,
356 98304000,
357 147456000,
Mark Browncbd840d2012-08-08 17:52:44 +0100358};
359
360static unsigned int arizona_sysclk_44k1_rates[] = {
361 5644800,
362 11289600,
Dimitris Papastamos96e1f182012-11-15 11:41:30 +0000363 22579200,
Mark Browncbd840d2012-08-08 17:52:44 +0100364 45158400,
Mark Brownaeaeee12012-09-26 17:50:02 +0100365 67737600,
366 90316800,
367 135475200,
Mark Browncbd840d2012-08-08 17:52:44 +0100368};
369
370static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
371 unsigned int freq)
372{
373 struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
374 unsigned int reg;
375 unsigned int *rates;
376 int ref, div, refclk;
377
378 switch (clk) {
379 case ARIZONA_CLK_OPCLK:
380 reg = ARIZONA_OUTPUT_SYSTEM_CLOCK;
381 refclk = priv->sysclk;
382 break;
383 case ARIZONA_CLK_ASYNC_OPCLK:
384 reg = ARIZONA_OUTPUT_ASYNC_CLOCK;
385 refclk = priv->asyncclk;
386 break;
387 default:
388 return -EINVAL;
389 }
390
391 if (refclk % 8000)
392 rates = arizona_sysclk_44k1_rates;
393 else
394 rates = arizona_sysclk_48k_rates;
395
396 for (ref = 0; ref < ARRAY_SIZE(arizona_sysclk_48k_rates) &&
397 rates[ref] <= refclk; ref++) {
398 div = 1;
399 while (rates[ref] / div >= freq && div < 32) {
400 if (rates[ref] / div == freq) {
401 dev_dbg(codec->dev, "Configured %dHz OPCLK\n",
402 freq);
403 snd_soc_update_bits(codec, reg,
404 ARIZONA_OPCLK_DIV_MASK |
405 ARIZONA_OPCLK_SEL_MASK,
406 (div <<
407 ARIZONA_OPCLK_DIV_SHIFT) |
408 ref);
409 return 0;
410 }
411 div++;
412 }
413 }
414
415 dev_err(codec->dev, "Unable to generate %dHz OPCLK\n", freq);
416 return -EINVAL;
417}
418
Mark Brown07ed8732012-06-18 21:08:44 +0100419int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
420 int source, unsigned int freq, int dir)
421{
422 struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
423 struct arizona *arizona = priv->arizona;
424 char *name;
425 unsigned int reg;
426 unsigned int mask = ARIZONA_SYSCLK_FREQ_MASK | ARIZONA_SYSCLK_SRC_MASK;
427 unsigned int val = source << ARIZONA_SYSCLK_SRC_SHIFT;
428 unsigned int *clk;
429
430 switch (clk_id) {
431 case ARIZONA_CLK_SYSCLK:
432 name = "SYSCLK";
433 reg = ARIZONA_SYSTEM_CLOCK_1;
434 clk = &priv->sysclk;
435 mask |= ARIZONA_SYSCLK_FRAC;
436 break;
437 case ARIZONA_CLK_ASYNCCLK:
438 name = "ASYNCCLK";
439 reg = ARIZONA_ASYNC_CLOCK_1;
440 clk = &priv->asyncclk;
441 break;
Mark Browncbd840d2012-08-08 17:52:44 +0100442 case ARIZONA_CLK_OPCLK:
443 case ARIZONA_CLK_ASYNC_OPCLK:
444 return arizona_set_opclk(codec, clk_id, freq);
Mark Brown07ed8732012-06-18 21:08:44 +0100445 default:
446 return -EINVAL;
447 }
448
449 switch (freq) {
450 case 5644800:
451 case 6144000:
452 break;
453 case 11289600:
454 case 12288000:
455 val |= 1 << ARIZONA_SYSCLK_FREQ_SHIFT;
456 break;
457 case 22579200:
458 case 24576000:
459 val |= 2 << ARIZONA_SYSCLK_FREQ_SHIFT;
460 break;
461 case 45158400:
462 case 49152000:
463 val |= 3 << ARIZONA_SYSCLK_FREQ_SHIFT;
464 break;
Mark Brown38113362012-11-26 16:01:37 +0000465 case 67737600:
466 case 73728000:
467 val |= 4 << ARIZONA_SYSCLK_FREQ_SHIFT;
468 break;
469 case 90316800:
470 case 98304000:
471 val |= 5 << ARIZONA_SYSCLK_FREQ_SHIFT;
472 break;
473 case 135475200:
474 case 147456000:
475 val |= 6 << ARIZONA_SYSCLK_FREQ_SHIFT;
476 break;
Mark Brown07ed8732012-06-18 21:08:44 +0100477 default:
478 return -EINVAL;
479 }
480
481 *clk = freq;
482
483 if (freq % 6144000)
484 val |= ARIZONA_SYSCLK_FRAC;
485
486 dev_dbg(arizona->dev, "%s set to %uHz", name, freq);
487
488 return regmap_update_bits(arizona->regmap, reg, mask, val);
489}
490EXPORT_SYMBOL_GPL(arizona_set_sysclk);
491
492static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
493{
494 struct snd_soc_codec *codec = dai->codec;
495 int lrclk, bclk, mode, base;
496
497 base = dai->driver->base;
498
499 lrclk = 0;
500 bclk = 0;
501
502 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
503 case SND_SOC_DAIFMT_DSP_A:
504 mode = 0;
505 break;
Mark Brown07ed8732012-06-18 21:08:44 +0100506 case SND_SOC_DAIFMT_I2S:
507 mode = 2;
508 break;
Mark Brown07ed8732012-06-18 21:08:44 +0100509 default:
510 arizona_aif_err(dai, "Unsupported DAI format %d\n",
511 fmt & SND_SOC_DAIFMT_FORMAT_MASK);
512 return -EINVAL;
513 }
514
515 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
516 case SND_SOC_DAIFMT_CBS_CFS:
517 break;
518 case SND_SOC_DAIFMT_CBS_CFM:
519 lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR;
520 break;
521 case SND_SOC_DAIFMT_CBM_CFS:
522 bclk |= ARIZONA_AIF1_BCLK_MSTR;
523 break;
524 case SND_SOC_DAIFMT_CBM_CFM:
525 bclk |= ARIZONA_AIF1_BCLK_MSTR;
526 lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR;
527 break;
528 default:
529 arizona_aif_err(dai, "Unsupported master mode %d\n",
530 fmt & SND_SOC_DAIFMT_MASTER_MASK);
531 return -EINVAL;
532 }
533
534 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
535 case SND_SOC_DAIFMT_NB_NF:
536 break;
537 case SND_SOC_DAIFMT_IB_IF:
538 bclk |= ARIZONA_AIF1_BCLK_INV;
539 lrclk |= ARIZONA_AIF1TX_LRCLK_INV;
540 break;
541 case SND_SOC_DAIFMT_IB_NF:
542 bclk |= ARIZONA_AIF1_BCLK_INV;
543 break;
544 case SND_SOC_DAIFMT_NB_IF:
545 lrclk |= ARIZONA_AIF1TX_LRCLK_INV;
546 break;
547 default:
548 return -EINVAL;
549 }
550
551 snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL,
552 ARIZONA_AIF1_BCLK_INV | ARIZONA_AIF1_BCLK_MSTR,
553 bclk);
554 snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_PIN_CTRL,
555 ARIZONA_AIF1TX_LRCLK_INV |
556 ARIZONA_AIF1TX_LRCLK_MSTR, lrclk);
557 snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_PIN_CTRL,
558 ARIZONA_AIF1RX_LRCLK_INV |
559 ARIZONA_AIF1RX_LRCLK_MSTR, lrclk);
560 snd_soc_update_bits(codec, base + ARIZONA_AIF_FORMAT,
561 ARIZONA_AIF1_FMT_MASK, mode);
562
563 return 0;
564}
565
Mark Brown949e6bc2012-07-04 18:58:04 +0100566static const int arizona_48k_bclk_rates[] = {
Mark Brown07ed8732012-06-18 21:08:44 +0100567 -1,
568 48000,
569 64000,
570 96000,
571 128000,
572 192000,
573 256000,
574 384000,
575 512000,
576 768000,
577 1024000,
578 1536000,
579 2048000,
580 3072000,
581 4096000,
582 6144000,
583 8192000,
584 12288000,
585 24576000,
586};
587
Mark Brown5b2eec32012-07-04 17:32:05 +0100588static const unsigned int arizona_48k_rates[] = {
589 12000,
590 24000,
591 48000,
592 96000,
593 192000,
594 384000,
595 768000,
596 4000,
597 8000,
598 16000,
599 32000,
600 64000,
601 128000,
602 256000,
603 512000,
604};
605
606static const struct snd_pcm_hw_constraint_list arizona_48k_constraint = {
607 .count = ARRAY_SIZE(arizona_48k_rates),
608 .list = arizona_48k_rates,
609};
610
Mark Brown949e6bc2012-07-04 18:58:04 +0100611static const int arizona_44k1_bclk_rates[] = {
Mark Brown07ed8732012-06-18 21:08:44 +0100612 -1,
613 44100,
614 58800,
615 88200,
616 117600,
617 177640,
618 235200,
619 352800,
620 470400,
621 705600,
622 940800,
623 1411200,
624 1881600,
Heather Lomond4758be32012-09-05 05:02:10 -0400625 2822400,
Mark Brown07ed8732012-06-18 21:08:44 +0100626 3763200,
627 5644800,
628 7526400,
629 11289600,
630 22579200,
631};
632
Mark Brown5b2eec32012-07-04 17:32:05 +0100633static const unsigned int arizona_44k1_rates[] = {
634 11025,
635 22050,
636 44100,
637 88200,
638 176400,
639 352800,
640 705600,
641};
642
643static const struct snd_pcm_hw_constraint_list arizona_44k1_constraint = {
644 .count = ARRAY_SIZE(arizona_44k1_rates),
645 .list = arizona_44k1_rates,
646};
647
Mark Brown07ed8732012-06-18 21:08:44 +0100648static int arizona_sr_vals[] = {
649 0,
650 12000,
651 24000,
652 48000,
653 96000,
654 192000,
655 384000,
656 768000,
657 0,
658 11025,
659 22050,
660 44100,
661 88200,
662 176400,
663 352800,
664 705600,
665 4000,
666 8000,
667 16000,
668 32000,
669 64000,
670 128000,
671 256000,
672 512000,
673};
674
Mark Brown5b2eec32012-07-04 17:32:05 +0100675static int arizona_startup(struct snd_pcm_substream *substream,
676 struct snd_soc_dai *dai)
677{
678 struct snd_soc_codec *codec = dai->codec;
679 struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
680 struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
681 const struct snd_pcm_hw_constraint_list *constraint;
682 unsigned int base_rate;
683
684 switch (dai_priv->clk) {
685 case ARIZONA_CLK_SYSCLK:
686 base_rate = priv->sysclk;
687 break;
688 case ARIZONA_CLK_ASYNCCLK:
689 base_rate = priv->asyncclk;
690 break;
691 default:
692 return 0;
693 }
694
695 if (base_rate % 8000)
696 constraint = &arizona_44k1_constraint;
697 else
698 constraint = &arizona_48k_constraint;
699
700 return snd_pcm_hw_constraint_list(substream->runtime, 0,
701 SNDRV_PCM_HW_PARAM_RATE,
702 constraint);
703}
704
Mark Brownb272efc2012-10-10 15:10:08 +0900705static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
706 struct snd_pcm_hw_params *params,
707 struct snd_soc_dai *dai)
Mark Brown07ed8732012-06-18 21:08:44 +0100708{
709 struct snd_soc_codec *codec = dai->codec;
Mark Brownc013b272012-07-04 20:05:57 +0100710 struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
711 struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
Mark Brown07ed8732012-06-18 21:08:44 +0100712 int base = dai->driver->base;
Mark Brownb272efc2012-10-10 15:10:08 +0900713 int i, sr_val;
714
715 /*
716 * We will need to be more flexible than this in future,
717 * currently we use a single sample rate for SYSCLK.
718 */
719 for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++)
720 if (arizona_sr_vals[i] == params_rate(params))
721 break;
722 if (i == ARRAY_SIZE(arizona_sr_vals)) {
723 arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
724 params_rate(params));
725 return -EINVAL;
726 }
727 sr_val = i;
728
729 switch (dai_priv->clk) {
730 case ARIZONA_CLK_SYSCLK:
731 snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1,
732 ARIZONA_SAMPLE_RATE_1_MASK, sr_val);
733 if (base)
734 snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
735 ARIZONA_AIF1_RATE_MASK, 0);
736 break;
737 case ARIZONA_CLK_ASYNCCLK:
738 snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1,
739 ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val);
740 if (base)
741 snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
742 ARIZONA_AIF1_RATE_MASK,
743 8 << ARIZONA_AIF1_RATE_SHIFT);
744 break;
745 default:
746 arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
747 return -EINVAL;
748 }
749
750 return 0;
751}
752
753static int arizona_hw_params(struct snd_pcm_substream *substream,
754 struct snd_pcm_hw_params *params,
755 struct snd_soc_dai *dai)
756{
757 struct snd_soc_codec *codec = dai->codec;
758 int base = dai->driver->base;
Mark Brown07ed8732012-06-18 21:08:44 +0100759 const int *rates;
Mark Brownb272efc2012-10-10 15:10:08 +0900760 int i, ret;
761 int bclk, lrclk, wl, frame;
Mark Brown07ed8732012-06-18 21:08:44 +0100762
763 if (params_rate(params) % 8000)
Mark Brown949e6bc2012-07-04 18:58:04 +0100764 rates = &arizona_44k1_bclk_rates[0];
Mark Brown07ed8732012-06-18 21:08:44 +0100765 else
Mark Brown949e6bc2012-07-04 18:58:04 +0100766 rates = &arizona_48k_bclk_rates[0];
Mark Brown07ed8732012-06-18 21:08:44 +0100767
Mark Brown949e6bc2012-07-04 18:58:04 +0100768 for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) {
Mark Brown50017652012-07-04 19:07:09 +0100769 if (rates[i] >= snd_soc_params_to_bclk(params) &&
770 rates[i] % params_rate(params) == 0) {
Mark Brown07ed8732012-06-18 21:08:44 +0100771 bclk = i;
772 break;
773 }
774 }
Mark Brown949e6bc2012-07-04 18:58:04 +0100775 if (i == ARRAY_SIZE(arizona_44k1_bclk_rates)) {
Mark Brown07ed8732012-06-18 21:08:44 +0100776 arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
777 params_rate(params));
778 return -EINVAL;
779 }
780
Mark Brown07ed8732012-06-18 21:08:44 +0100781 lrclk = snd_soc_params_to_bclk(params) / params_rate(params);
782
783 arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n",
784 rates[bclk], rates[bclk] / lrclk);
785
786 wl = snd_pcm_format_width(params_format(params));
787 frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl;
788
Mark Brownb272efc2012-10-10 15:10:08 +0900789 ret = arizona_hw_params_rate(substream, params, dai);
790 if (ret != 0)
791 return ret;
Mark Brownc013b272012-07-04 20:05:57 +0100792
Mark Brown07ed8732012-06-18 21:08:44 +0100793 snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL,
794 ARIZONA_AIF1_BCLK_FREQ_MASK, bclk);
795 snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_BCLK_RATE,
796 ARIZONA_AIF1TX_BCPF_MASK, lrclk);
797 snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_BCLK_RATE,
798 ARIZONA_AIF1RX_BCPF_MASK, lrclk);
799 snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_1,
800 ARIZONA_AIF1TX_WL_MASK |
801 ARIZONA_AIF1TX_SLOT_LEN_MASK, frame);
802 snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_2,
803 ARIZONA_AIF1RX_WL_MASK |
804 ARIZONA_AIF1RX_SLOT_LEN_MASK, frame);
805
806 return 0;
807}
808
Mark Brown410837a2012-07-05 17:26:59 +0100809static const char *arizona_dai_clk_str(int clk_id)
810{
811 switch (clk_id) {
812 case ARIZONA_CLK_SYSCLK:
813 return "SYSCLK";
814 case ARIZONA_CLK_ASYNCCLK:
815 return "ASYNCCLK";
816 default:
817 return "Unknown clock";
818 }
819}
820
Mark Brown5b2eec32012-07-04 17:32:05 +0100821static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
822 int clk_id, unsigned int freq, int dir)
823{
824 struct snd_soc_codec *codec = dai->codec;
825 struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
826 struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
Mark Brown410837a2012-07-05 17:26:59 +0100827 struct snd_soc_dapm_route routes[2];
Mark Brown5b2eec32012-07-04 17:32:05 +0100828
829 switch (clk_id) {
830 case ARIZONA_CLK_SYSCLK:
831 case ARIZONA_CLK_ASYNCCLK:
832 break;
833 default:
834 return -EINVAL;
835 }
836
Mark Brown410837a2012-07-05 17:26:59 +0100837 if (clk_id == dai_priv->clk)
838 return 0;
839
840 if (dai->active) {
Mark Brown5b2eec32012-07-04 17:32:05 +0100841 dev_err(codec->dev, "Can't change clock on active DAI %d\n",
842 dai->id);
843 return -EBUSY;
844 }
845
Mark Brownc8d35a62012-12-07 12:49:40 +0900846 dev_dbg(codec->dev, "Setting AIF%d to %s\n", dai->id + 1,
847 arizona_dai_clk_str(clk_id));
848
Mark Brown410837a2012-07-05 17:26:59 +0100849 memset(&routes, 0, sizeof(routes));
850 routes[0].sink = dai->driver->capture.stream_name;
851 routes[1].sink = dai->driver->playback.stream_name;
Mark Brown5b2eec32012-07-04 17:32:05 +0100852
Mark Brown410837a2012-07-05 17:26:59 +0100853 routes[0].source = arizona_dai_clk_str(dai_priv->clk);
854 routes[1].source = arizona_dai_clk_str(dai_priv->clk);
855 snd_soc_dapm_del_routes(&codec->dapm, routes, ARRAY_SIZE(routes));
856
857 routes[0].source = arizona_dai_clk_str(clk_id);
858 routes[1].source = arizona_dai_clk_str(clk_id);
859 snd_soc_dapm_add_routes(&codec->dapm, routes, ARRAY_SIZE(routes));
860
Mark Brown0c778e82012-12-06 18:22:25 +0900861 dai_priv->clk = clk_id;
862
Mark Brown410837a2012-07-05 17:26:59 +0100863 return snd_soc_dapm_sync(&codec->dapm);
Mark Brown5b2eec32012-07-04 17:32:05 +0100864}
865
Mark Brown01df2592012-12-12 16:22:08 +0900866static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
867{
868 struct snd_soc_codec *codec = dai->codec;
869 int base = dai->driver->base;
870 unsigned int reg;
871
872 if (tristate)
873 reg = ARIZONA_AIF1_TRI;
874 else
875 reg = 0;
876
877 return snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
878 ARIZONA_AIF1_TRI, reg);
879}
880
Mark Brown07ed8732012-06-18 21:08:44 +0100881const struct snd_soc_dai_ops arizona_dai_ops = {
Mark Brown5b2eec32012-07-04 17:32:05 +0100882 .startup = arizona_startup,
Mark Brown07ed8732012-06-18 21:08:44 +0100883 .set_fmt = arizona_set_fmt,
884 .hw_params = arizona_hw_params,
Mark Brown5b2eec32012-07-04 17:32:05 +0100885 .set_sysclk = arizona_dai_set_sysclk,
Mark Brown01df2592012-12-12 16:22:08 +0900886 .set_tristate = arizona_set_tristate,
Mark Brown07ed8732012-06-18 21:08:44 +0100887};
Mark Browna8379872012-07-09 12:16:41 +0100888EXPORT_SYMBOL_GPL(arizona_dai_ops);
Mark Brown07ed8732012-06-18 21:08:44 +0100889
Mark Brown5b2eec32012-07-04 17:32:05 +0100890int arizona_init_dai(struct arizona_priv *priv, int id)
891{
892 struct arizona_dai_priv *dai_priv = &priv->dai[id];
893
894 dai_priv->clk = ARIZONA_CLK_SYSCLK;
895
896 return 0;
897}
898EXPORT_SYMBOL_GPL(arizona_init_dai);
899
Mark Brown07ed8732012-06-18 21:08:44 +0100900static irqreturn_t arizona_fll_lock(int irq, void *data)
901{
902 struct arizona_fll *fll = data;
903
Mark Brown6b315952012-09-12 18:44:40 +0800904 arizona_fll_dbg(fll, "Lock status changed\n");
Mark Brown07ed8732012-06-18 21:08:44 +0100905
906 complete(&fll->lock);
907
908 return IRQ_HANDLED;
909}
910
911static irqreturn_t arizona_fll_clock_ok(int irq, void *data)
912{
913 struct arizona_fll *fll = data;
914
915 arizona_fll_dbg(fll, "clock OK\n");
916
917 complete(&fll->ok);
918
919 return IRQ_HANDLED;
920}
921
922static struct {
923 unsigned int min;
924 unsigned int max;
925 u16 fratio;
926 int ratio;
927} fll_fratios[] = {
928 { 0, 64000, 4, 16 },
929 { 64000, 128000, 3, 8 },
930 { 128000, 256000, 2, 4 },
931 { 256000, 1000000, 1, 2 },
932 { 1000000, 13500000, 0, 1 },
933};
934
935struct arizona_fll_cfg {
936 int n;
937 int theta;
938 int lambda;
939 int refdiv;
940 int outdiv;
941 int fratio;
942};
943
944static int arizona_calc_fll(struct arizona_fll *fll,
945 struct arizona_fll_cfg *cfg,
946 unsigned int Fref,
947 unsigned int Fout)
948{
949 unsigned int target, div, gcd_fll;
950 int i, ratio;
951
952 arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, Fout);
953
954 /* Fref must be <=13.5MHz */
955 div = 1;
956 cfg->refdiv = 0;
957 while ((Fref / div) > 13500000) {
958 div *= 2;
959 cfg->refdiv++;
960
961 if (div > 8) {
962 arizona_fll_err(fll,
963 "Can't scale %dMHz in to <=13.5MHz\n",
964 Fref);
965 return -EINVAL;
966 }
967 }
968
969 /* Apply the division for our remaining calculations */
970 Fref /= div;
971
Mark Brown2b4d39f2012-07-10 17:03:46 +0100972 /* Fvco should be over the targt; don't check the upper bound */
Mark Brown07ed8732012-06-18 21:08:44 +0100973 div = 1;
Mark Brown2b4d39f2012-07-10 17:03:46 +0100974 while (Fout * div < 90000000 * fll->vco_mult) {
Mark Brown07ed8732012-06-18 21:08:44 +0100975 div++;
976 if (div > 7) {
977 arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n",
978 Fout);
979 return -EINVAL;
980 }
981 }
Mark Brown2b4d39f2012-07-10 17:03:46 +0100982 target = Fout * div / fll->vco_mult;
Mark Brown07ed8732012-06-18 21:08:44 +0100983 cfg->outdiv = div;
984
985 arizona_fll_dbg(fll, "Fvco=%dHz\n", target);
986
987 /* Find an appropraite FLL_FRATIO and factor it out of the target */
988 for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
989 if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
990 cfg->fratio = fll_fratios[i].fratio;
991 ratio = fll_fratios[i].ratio;
992 break;
993 }
994 }
995 if (i == ARRAY_SIZE(fll_fratios)) {
996 arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n",
997 Fref);
998 return -EINVAL;
999 }
1000
1001 cfg->n = target / (ratio * Fref);
1002
1003 if (target % Fref) {
1004 gcd_fll = gcd(target, ratio * Fref);
1005 arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll);
1006
1007 cfg->theta = (target - (cfg->n * ratio * Fref))
1008 / gcd_fll;
1009 cfg->lambda = (ratio * Fref) / gcd_fll;
1010 } else {
1011 cfg->theta = 0;
1012 cfg->lambda = 0;
1013 }
1014
1015 arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n",
1016 cfg->n, cfg->theta, cfg->lambda);
1017 arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n",
1018 cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv);
1019
1020 return 0;
1021
1022}
1023
1024static void arizona_apply_fll(struct arizona *arizona, unsigned int base,
1025 struct arizona_fll_cfg *cfg, int source)
1026{
1027 regmap_update_bits(arizona->regmap, base + 3,
1028 ARIZONA_FLL1_THETA_MASK, cfg->theta);
1029 regmap_update_bits(arizona->regmap, base + 4,
1030 ARIZONA_FLL1_LAMBDA_MASK, cfg->lambda);
1031 regmap_update_bits(arizona->regmap, base + 5,
1032 ARIZONA_FLL1_FRATIO_MASK,
1033 cfg->fratio << ARIZONA_FLL1_FRATIO_SHIFT);
1034 regmap_update_bits(arizona->regmap, base + 6,
1035 ARIZONA_FLL1_CLK_REF_DIV_MASK |
1036 ARIZONA_FLL1_CLK_REF_SRC_MASK,
1037 cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT |
1038 source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT);
1039
1040 regmap_update_bits(arizona->regmap, base + 2,
1041 ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK,
1042 ARIZONA_FLL1_CTRL_UPD | cfg->n);
1043}
1044
1045int arizona_set_fll(struct arizona_fll *fll, int source,
1046 unsigned int Fref, unsigned int Fout)
1047{
1048 struct arizona *arizona = fll->arizona;
1049 struct arizona_fll_cfg cfg, sync;
1050 unsigned int reg, val;
1051 int syncsrc;
1052 bool ena;
1053 int ret;
1054
Mark Brown1cbe4bc2012-11-21 14:12:22 +09001055 if (fll->fref == Fref && fll->fout == Fout)
1056 return 0;
1057
Mark Brown07ed8732012-06-18 21:08:44 +01001058 ret = regmap_read(arizona->regmap, fll->base + 1, &reg);
1059 if (ret != 0) {
1060 arizona_fll_err(fll, "Failed to read current state: %d\n",
1061 ret);
1062 return ret;
1063 }
1064 ena = reg & ARIZONA_FLL1_ENA;
1065
1066 if (Fout) {
1067 /* Do we have a 32kHz reference? */
1068 regmap_read(arizona->regmap, ARIZONA_CLOCK_32K_1, &val);
1069 switch (val & ARIZONA_CLK_32K_SRC_MASK) {
1070 case ARIZONA_CLK_SRC_MCLK1:
1071 case ARIZONA_CLK_SRC_MCLK2:
1072 syncsrc = val & ARIZONA_CLK_32K_SRC_MASK;
1073 break;
1074 default:
1075 syncsrc = -1;
1076 }
1077
1078 if (source == syncsrc)
1079 syncsrc = -1;
1080
1081 if (syncsrc >= 0) {
1082 ret = arizona_calc_fll(fll, &sync, Fref, Fout);
1083 if (ret != 0)
1084 return ret;
1085
1086 ret = arizona_calc_fll(fll, &cfg, 32768, Fout);
1087 if (ret != 0)
1088 return ret;
1089 } else {
1090 ret = arizona_calc_fll(fll, &cfg, Fref, Fout);
1091 if (ret != 0)
1092 return ret;
1093 }
1094 } else {
1095 regmap_update_bits(arizona->regmap, fll->base + 1,
1096 ARIZONA_FLL1_ENA, 0);
1097 regmap_update_bits(arizona->regmap, fll->base + 0x11,
1098 ARIZONA_FLL1_SYNC_ENA, 0);
1099
1100 if (ena)
1101 pm_runtime_put_autosuspend(arizona->dev);
1102
Mark Brown50fcfe42012-11-28 11:50:34 +00001103 fll->fref = Fref;
1104 fll->fout = Fout;
1105
Mark Brown07ed8732012-06-18 21:08:44 +01001106 return 0;
1107 }
1108
1109 regmap_update_bits(arizona->regmap, fll->base + 5,
1110 ARIZONA_FLL1_OUTDIV_MASK,
1111 cfg.outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);
1112
1113 if (syncsrc >= 0) {
1114 arizona_apply_fll(arizona, fll->base, &cfg, syncsrc);
1115 arizona_apply_fll(arizona, fll->base + 0x10, &sync, source);
1116 } else {
1117 arizona_apply_fll(arizona, fll->base, &cfg, source);
1118 }
1119
1120 if (!ena)
1121 pm_runtime_get(arizona->dev);
1122
1123 /* Clear any pending completions */
1124 try_wait_for_completion(&fll->ok);
1125
1126 regmap_update_bits(arizona->regmap, fll->base + 1,
1127 ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
1128 if (syncsrc >= 0)
1129 regmap_update_bits(arizona->regmap, fll->base + 0x11,
1130 ARIZONA_FLL1_SYNC_ENA,
1131 ARIZONA_FLL1_SYNC_ENA);
1132
1133 ret = wait_for_completion_timeout(&fll->ok,
Mark Brown09871a92012-12-06 15:29:34 +09001134 msecs_to_jiffies(250));
Mark Brown07ed8732012-06-18 21:08:44 +01001135 if (ret == 0)
1136 arizona_fll_warn(fll, "Timed out waiting for lock\n");
1137
Mark Brown1cbe4bc2012-11-21 14:12:22 +09001138 fll->fref = Fref;
1139 fll->fout = Fout;
1140
Mark Brown07ed8732012-06-18 21:08:44 +01001141 return 0;
1142}
1143EXPORT_SYMBOL_GPL(arizona_set_fll);
1144
1145int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
1146 int ok_irq, struct arizona_fll *fll)
1147{
1148 int ret;
1149
1150 init_completion(&fll->lock);
1151 init_completion(&fll->ok);
1152
1153 fll->id = id;
1154 fll->base = base;
1155 fll->arizona = arizona;
1156
1157 snprintf(fll->lock_name, sizeof(fll->lock_name), "FLL%d lock", id);
1158 snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name),
1159 "FLL%d clock OK", id);
1160
1161 ret = arizona_request_irq(arizona, lock_irq, fll->lock_name,
1162 arizona_fll_lock, fll);
1163 if (ret != 0) {
1164 dev_err(arizona->dev, "Failed to get FLL%d lock IRQ: %d\n",
1165 id, ret);
1166 }
1167
1168 ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name,
1169 arizona_fll_clock_ok, fll);
1170 if (ret != 0) {
1171 dev_err(arizona->dev, "Failed to get FLL%d clock OK IRQ: %d\n",
1172 id, ret);
1173 }
1174
1175 return 0;
1176}
1177EXPORT_SYMBOL_GPL(arizona_init_fll);
1178
Mark Brownbc9ab6d2013-01-04 19:31:00 +00001179/**
1180 * arizona_set_output_mode - Set the mode of the specified output
1181 *
1182 * @codec: Device to configure
1183 * @output: Output number
1184 * @diff: True to set the output to differential mode
1185 *
1186 * Some systems use external analogue switches to connect more
1187 * analogue devices to the CODEC than are supported by the device. In
1188 * some systems this requires changing the switched output from single
1189 * ended to differential mode dynamically at runtime, an operation
1190 * supported using this function.
1191 *
1192 * Most systems have a single static configuration and should use
1193 * platform data instead.
1194 */
1195int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff)
1196{
1197 unsigned int reg, val;
1198
1199 if (output < 1 || output > 6)
1200 return -EINVAL;
1201
1202 reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8;
1203
1204 if (diff)
1205 val = ARIZONA_OUT1_MONO;
1206 else
1207 val = 0;
1208
1209 return snd_soc_update_bits(codec, reg, ARIZONA_OUT1_MONO, val);
1210}
1211EXPORT_SYMBOL_GPL(arizona_set_output_mode);
1212
Mark Brown07ed8732012-06-18 21:08:44 +01001213MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
1214MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
1215MODULE_LICENSE("GPL");