Blame view
sound/soc/codecs/wm8974.c
19.3 KB
0a1bf5533 ASoC: Add WM8974 ... |
1 2 3 |
/* * wm8974.c -- WM8974 ALSA Soc Audio driver * |
8b83a1936 ASoC: WM8974 cosm... |
4 |
* Copyright 2006-2009 Wolfson Microelectronics PLC. |
0a1bf5533 ASoC: Add WM8974 ... |
5 |
* |
9a185b9ab ASoC: Remove refe... |
6 |
* Author: Liam Girdwood <Liam.Girdwood@wolfsonmicro.com> |
0a1bf5533 ASoC: Add WM8974 ... |
7 8 9 10 11 12 13 14 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/module.h> #include <linux/moduleparam.h> |
0a1bf5533 ASoC: Add WM8974 ... |
15 16 17 18 19 |
#include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> |
5a0e3ad6a include cleanup: ... |
20 |
#include <linux/slab.h> |
0a1bf5533 ASoC: Add WM8974 ... |
21 22 23 24 |
#include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> |
0a1bf5533 ASoC: Add WM8974 ... |
25 |
#include <sound/initval.h> |
a5f8d2f1b ASoC: Add WM8974 ... |
26 |
#include <sound/tlv.h> |
0a1bf5533 ASoC: Add WM8974 ... |
27 28 |
#include "wm8974.h" |
0a1bf5533 ASoC: Add WM8974 ... |
29 |
static const u16 wm8974_reg[WM8974_CACHEREGNUM] = { |
1a55b3f6e ASoC: WM8974 chec... |
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
0x0000, 0x0000, 0x0000, 0x0000, 0x0050, 0x0000, 0x0140, 0x0000, 0x0000, 0x0000, 0x0000, 0x00ff, 0x0000, 0x0000, 0x0100, 0x00ff, 0x0000, 0x0000, 0x012c, 0x002c, 0x002c, 0x002c, 0x002c, 0x0000, 0x0032, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0038, 0x000b, 0x0032, 0x0000, 0x0008, 0x000c, 0x0093, 0x00e9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0010, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0039, 0x0000, 0x0000, |
0a1bf5533 ASoC: Add WM8974 ... |
45 |
}; |
df1ef7a38 ASoC: Refresh WM8... |
46 |
#define WM8974_POWER1_BIASEN 0x08 |
48c03ce72 ASoC: wm8974: fix... |
47 |
#define WM8974_POWER1_BUFIOEN 0x04 |
df1ef7a38 ASoC: Refresh WM8... |
48 |
|
4fcbbb67a ASoC: Update WM89... |
49 |
struct wm8974_priv { |
f0fba2ad1 ASoC: multi-compo... |
50 |
enum snd_soc_control_type control_type; |
4fcbbb67a ASoC: Update WM89... |
51 |
}; |
1e97f50b7 ASoC: Factor out ... |
52 |
#define wm8974_reset(c) snd_soc_write(c, WM8974_RESET, 0) |
0a1bf5533 ASoC: Add WM8974 ... |
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" }; static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" }; static const char *wm8974_eqmode[] = {"Capture", "Playback" }; static const char *wm8974_bw[] = {"Narrow", "Wide" }; static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" }; static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" }; static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" }; static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" }; static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" }; static const char *wm8974_alc[] = {"ALC", "Limiter" }; static const struct soc_enum wm8974_enum[] = { SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */ SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */ SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp), SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode), SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1), SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw), SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2), SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw), SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3), SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw), SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4), SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw), SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5), SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc), }; |
8a123ee2a ASoC: WM8974 DAPM... |
84 85 86 87 |
static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" }; static const struct soc_enum wm8974_auxmode = SOC_ENUM_SINGLE(WM8974_INPUT, 3, 2, wm8974_auxmode_text); |
a5f8d2f1b ASoC: Add WM8974 ... |
88 89 90 91 |
static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1); static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0); static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0); |
0a1bf5533 ASoC: Add WM8974 ... |
92 93 94 95 96 97 98 99 100 |
static const struct snd_kcontrol_new wm8974_snd_controls[] = { SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0), SOC_ENUM("DAC Companding", wm8974_enum[1]), SOC_ENUM("ADC Companding", wm8974_enum[0]), SOC_ENUM("Playback De-emphasis", wm8974_enum[2]), SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0), |
a5f8d2f1b ASoC: Add WM8974 ... |
101 |
SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv), |
0a1bf5533 ASoC: Add WM8974 ... |
102 103 104 |
SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0), SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0), |
25cbf4652 ASoC: Correct a b... |
105 |
SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0), |
0a1bf5533 ASoC: Add WM8974 ... |
106 |
|
a5f8d2f1b ASoC: Add WM8974 ... |
107 |
SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv), |
0a1bf5533 ASoC: Add WM8974 ... |
108 109 110 |
SOC_ENUM("Equaliser Function", wm8974_enum[3]), SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]), |
a5f8d2f1b ASoC: Add WM8974 ... |
111 |
SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv), |
0a1bf5533 ASoC: Add WM8974 ... |
112 113 114 |
SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]), SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]), |
a5f8d2f1b ASoC: Add WM8974 ... |
115 |
SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv), |
0a1bf5533 ASoC: Add WM8974 ... |
116 117 118 |
SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]), SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]), |
a5f8d2f1b ASoC: Add WM8974 ... |
119 |
SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv), |
0a1bf5533 ASoC: Add WM8974 ... |
120 121 122 |
SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]), SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]), |
a5f8d2f1b ASoC: Add WM8974 ... |
123 |
SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv), |
0a1bf5533 ASoC: Add WM8974 ... |
124 125 126 |
SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]), SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]), |
a5f8d2f1b ASoC: Add WM8974 ... |
127 |
SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv), |
0a1bf5533 ASoC: Add WM8974 ... |
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0), SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0), SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0), SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0), SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0), SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0), SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0), SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0), SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0), SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0), SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0), SOC_ENUM("ALC Capture Mode", wm8974_enum[13]), SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0), SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0), SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0), SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0), SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0), |
a5f8d2f1b ASoC: Add WM8974 ... |
152 |
SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv), |
0a1bf5533 ASoC: Add WM8974 ... |
153 154 155 |
SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0), SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1), |
8a123ee2a ASoC: WM8974 DAPM... |
156 157 158 |
SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv), SOC_ENUM("Aux Mode", wm8974_auxmode), |
0a1bf5533 ASoC: Add WM8974 ... |
159 160 |
SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0), |
8a123ee2a ASoC: WM8974 DAPM... |
161 |
SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1), |
b2c3e9231 ASoC: clean up wm... |
162 163 164 165 |
/* DAC / ADC oversampling */ SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0), SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0), |
0a1bf5533 ASoC: Add WM8974 ... |
166 |
}; |
0a1bf5533 ASoC: Add WM8974 ... |
167 168 169 170 |
/* Speaker Output Mixer */ static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = { SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0), SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0), |
759512fba ASoC: Correct inv... |
171 |
SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 0), |
0a1bf5533 ASoC: Add WM8974 ... |
172 173 174 175 176 177 |
}; /* Mono Output Mixer */ static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = { SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0), SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0), |
8a123ee2a ASoC: WM8974 DAPM... |
178 179 180 181 182 183 184 185 186 187 188 189 190 |
SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0), }; /* Boost mixer */ static const struct snd_kcontrol_new wm8974_boost_mixer[] = { SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0), }; /* Input PGA */ static const struct snd_kcontrol_new wm8974_inpga[] = { SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0), SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0), SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0), |
0a1bf5533 ASoC: Add WM8974 ... |
191 192 193 194 195 196 197 198 199 |
}; /* AUX Input boost vol */ static const struct snd_kcontrol_new wm8974_aux_boost_controls = SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0); /* Mic Input boost vol */ static const struct snd_kcontrol_new wm8974_mic_boost_controls = SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0); |
0a1bf5533 ASoC: Add WM8974 ... |
200 201 202 203 204 205 206 207 |
static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = { SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0, &wm8974_speaker_mixer_controls[0], ARRAY_SIZE(wm8974_speaker_mixer_controls)), SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0, &wm8974_mono_mixer_controls[0], ARRAY_SIZE(wm8974_mono_mixer_controls)), SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0), |
8a123ee2a ASoC: WM8974 DAPM... |
208 |
SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0), |
0a1bf5533 ASoC: Add WM8974 ... |
209 210 211 212 |
SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0), SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0), SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0), SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0), |
0a1bf5533 ASoC: Add WM8974 ... |
213 |
|
8a123ee2a ASoC: WM8974 DAPM... |
214 215 216 217 |
SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga, ARRAY_SIZE(wm8974_inpga)), SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)), |
0a1bf5533 ASoC: Add WM8974 ... |
218 |
|
48dd231b0 ASoC: Convert wm8... |
219 |
SND_SOC_DAPM_SUPPLY("Mic Bias", WM8974_POWER1, 4, 0, NULL, 0), |
0a1bf5533 ASoC: Add WM8974 ... |
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
SND_SOC_DAPM_INPUT("MICN"), SND_SOC_DAPM_INPUT("MICP"), SND_SOC_DAPM_INPUT("AUX"), SND_SOC_DAPM_OUTPUT("MONOOUT"), SND_SOC_DAPM_OUTPUT("SPKOUTP"), SND_SOC_DAPM_OUTPUT("SPKOUTN"), }; static const struct snd_soc_dapm_route audio_map[] = { /* Mono output mixer */ {"Mono Mixer", "PCM Playback Switch", "DAC"}, {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"}, /* Speaker output mixer */ {"Speaker Mixer", "PCM Playback Switch", "DAC"}, {"Speaker Mixer", "Aux Playback Switch", "Aux Input"}, {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"}, /* Outputs */ {"Mono Out", NULL, "Mono Mixer"}, {"MONOOUT", NULL, "Mono Out"}, {"SpkN Out", NULL, "Speaker Mixer"}, {"SpkP Out", NULL, "Speaker Mixer"}, {"SPKOUTN", NULL, "SpkN Out"}, {"SPKOUTP", NULL, "SpkP Out"}, /* Boost Mixer */ |
8a123ee2a ASoC: WM8974 DAPM... |
249 250 251 252 253 254 255 256 257 |
{"ADC", NULL, "Boost Mixer"}, {"Boost Mixer", "Aux Switch", "Aux Input"}, {"Boost Mixer", NULL, "Input PGA"}, {"Boost Mixer", NULL, "MICP"}, /* Input PGA */ {"Input PGA", "Aux Switch", "Aux Input"}, {"Input PGA", "MicN Switch", "MICN"}, {"Input PGA", "MicP Switch", "MICP"}, |
0a1bf5533 ASoC: Add WM8974 ... |
258 259 |
/* Inputs */ |
8a123ee2a ASoC: WM8974 DAPM... |
260 |
{"Aux Input", NULL, "AUX"}, |
0a1bf5533 ASoC: Add WM8974 ... |
261 262 263 264 |
}; static int wm8974_add_widgets(struct snd_soc_codec *codec) { |
ce6120cca ASoC: Decouple DA... |
265 |
struct snd_soc_dapm_context *dapm = &codec->dapm; |
0a1bf5533 ASoC: Add WM8974 ... |
266 |
|
ce6120cca ASoC: Decouple DA... |
267 268 269 |
snd_soc_dapm_new_controls(dapm, wm8974_dapm_widgets, ARRAY_SIZE(wm8974_dapm_widgets)); snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); |
0a1bf5533 ASoC: Add WM8974 ... |
270 |
|
0a1bf5533 ASoC: Add WM8974 ... |
271 272 273 274 |
return 0; } struct pll_ { |
c36b2fc73 ASoC: Clean up WM... |
275 |
unsigned int pre_div:1; |
0a1bf5533 ASoC: Add WM8974 ... |
276 277 278 |
unsigned int n:4; unsigned int k; }; |
91d0c3ecb ASoC: Refresh WM8... |
279 280 281 |
/* The size in bits of the pll divide multiplied by 10 * to allow rounding later */ #define FIXED_PLL_SIZE ((1 << 24) * 10) |
c36b2fc73 ASoC: Clean up WM... |
282 283 |
static void pll_factors(struct pll_ *pll_div, unsigned int target, unsigned int source) |
91d0c3ecb ASoC: Refresh WM8... |
284 285 286 |
{ unsigned long long Kpart; unsigned int K, Ndiv, Nmod; |
c36b2fc73 ASoC: Clean up WM... |
287 288 |
/* There is a fixed divide by 4 in the output path */ target *= 4; |
91d0c3ecb ASoC: Refresh WM8... |
289 290 |
Ndiv = target / source; if (Ndiv < 6) { |
c36b2fc73 ASoC: Clean up WM... |
291 292 |
source /= 2; pll_div->pre_div = 1; |
91d0c3ecb ASoC: Refresh WM8... |
293 294 |
Ndiv = target / source; } else |
c36b2fc73 ASoC: Clean up WM... |
295 |
pll_div->pre_div = 0; |
91d0c3ecb ASoC: Refresh WM8... |
296 297 298 |
if ((Ndiv < 6) || (Ndiv > 12)) printk(KERN_WARNING |
8b83a1936 ASoC: WM8974 cosm... |
299 300 |
"WM8974 N value %u outwith recommended range! ", |
91d0c3ecb ASoC: Refresh WM8... |
301 |
Ndiv); |
c36b2fc73 ASoC: Clean up WM... |
302 |
pll_div->n = Ndiv; |
91d0c3ecb ASoC: Refresh WM8... |
303 304 305 306 307 308 309 310 311 312 313 314 315 |
Nmod = target % source; Kpart = FIXED_PLL_SIZE * (long long)Nmod; do_div(Kpart, source); K = Kpart & 0xFFFFFFFF; /* Check if we need to round */ if ((K % 10) >= 5) K += 5; /* Move down to proper range now rounding is done */ K /= 10; |
c36b2fc73 ASoC: Clean up WM... |
316 |
pll_div->k = K; |
91d0c3ecb ASoC: Refresh WM8... |
317 |
} |
0a1bf5533 ASoC: Add WM8974 ... |
318 |
|
85488037b ASoC: Add source ... |
319 320 |
static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) |
0a1bf5533 ASoC: Add WM8974 ... |
321 322 |
{ struct snd_soc_codec *codec = codec_dai->codec; |
c36b2fc73 ASoC: Clean up WM... |
323 |
struct pll_ pll_div; |
0a1bf5533 ASoC: Add WM8974 ... |
324 |
u16 reg; |
1a55b3f6e ASoC: WM8974 chec... |
325 |
if (freq_in == 0 || freq_out == 0) { |
91d0c3ecb ASoC: Refresh WM8... |
326 |
/* Clock CODEC directly from MCLK */ |
1e97f50b7 ASoC: Factor out ... |
327 328 |
reg = snd_soc_read(codec, WM8974_CLOCK); snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff); |
91d0c3ecb ASoC: Refresh WM8... |
329 330 |
/* Turn off PLL */ |
1e97f50b7 ASoC: Factor out ... |
331 332 |
reg = snd_soc_read(codec, WM8974_POWER1); snd_soc_write(codec, WM8974_POWER1, reg & 0x1df); |
0a1bf5533 ASoC: Add WM8974 ... |
333 334 |
return 0; } |
c36b2fc73 ASoC: Clean up WM... |
335 |
pll_factors(&pll_div, freq_out, freq_in); |
91d0c3ecb ASoC: Refresh WM8... |
336 |
|
1e97f50b7 ASoC: Factor out ... |
337 338 339 340 341 342 |
snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n); snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18); snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff); snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff); reg = snd_soc_read(codec, WM8974_POWER1); snd_soc_write(codec, WM8974_POWER1, reg | 0x020); |
1a55b3f6e ASoC: WM8974 chec... |
343 |
|
91d0c3ecb ASoC: Refresh WM8... |
344 |
/* Run CODEC from PLL instead of MCLK */ |
1e97f50b7 ASoC: Factor out ... |
345 346 |
reg = snd_soc_read(codec, WM8974_CLOCK); snd_soc_write(codec, WM8974_CLOCK, reg | 0x100); |
91d0c3ecb ASoC: Refresh WM8... |
347 348 |
return 0; |
0a1bf5533 ASoC: Add WM8974 ... |
349 350 351 352 353 354 355 356 357 358 359 360 361 |
} /* * Configure WM8974 clock dividers. */ static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { struct snd_soc_codec *codec = codec_dai->codec; u16 reg; switch (div_id) { case WM8974_OPCLKDIV: |
1e97f50b7 ASoC: Factor out ... |
362 363 |
reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf; snd_soc_write(codec, WM8974_GPIO, reg | div); |
0a1bf5533 ASoC: Add WM8974 ... |
364 365 |
break; case WM8974_MCLKDIV: |
1e97f50b7 ASoC: Factor out ... |
366 367 |
reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f; snd_soc_write(codec, WM8974_CLOCK, reg | div); |
0a1bf5533 ASoC: Add WM8974 ... |
368 |
break; |
0a1bf5533 ASoC: Add WM8974 ... |
369 |
case WM8974_BCLKDIV: |
1e97f50b7 ASoC: Factor out ... |
370 371 |
reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3; snd_soc_write(codec, WM8974_CLOCK, reg | div); |
0a1bf5533 ASoC: Add WM8974 ... |
372 373 374 375 376 377 378 379 380 381 382 383 384 |
break; default: return -EINVAL; } return 0; } static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; u16 iface = 0; |
1e97f50b7 ASoC: Factor out ... |
385 |
u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe; |
0a1bf5533 ASoC: Add WM8974 ... |
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 |
/* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: clk |= 0x0001; break; case SND_SOC_DAIFMT_CBS_CFS: break; default: return -EINVAL; } /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: iface |= 0x0010; break; case SND_SOC_DAIFMT_RIGHT_J: break; case SND_SOC_DAIFMT_LEFT_J: iface |= 0x0008; break; case SND_SOC_DAIFMT_DSP_A: iface |= 0x00018; break; default: return -EINVAL; } /* clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_IB_IF: iface |= 0x0180; break; case SND_SOC_DAIFMT_IB_NF: iface |= 0x0100; break; case SND_SOC_DAIFMT_NB_IF: iface |= 0x0080; break; default: return -EINVAL; } |
1e97f50b7 ASoC: Factor out ... |
431 432 |
snd_soc_write(codec, WM8974_IFACE, iface); snd_soc_write(codec, WM8974_CLOCK, clk); |
0a1bf5533 ASoC: Add WM8974 ... |
433 434 435 436 437 438 439 440 |
return 0; } static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; |
1e97f50b7 ASoC: Factor out ... |
441 442 |
u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f; u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1; |
0a1bf5533 ASoC: Add WM8974 ... |
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
/* bit size */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: break; case SNDRV_PCM_FORMAT_S20_3LE: iface |= 0x0020; break; case SNDRV_PCM_FORMAT_S24_LE: iface |= 0x0040; break; case SNDRV_PCM_FORMAT_S32_LE: iface |= 0x0060; break; } /* filter coefficient */ switch (params_rate(params)) { |
b3172f222 ASoC: fix params_... |
461 |
case 8000: |
0a1bf5533 ASoC: Add WM8974 ... |
462 463 |
adn |= 0x5 << 1; break; |
b3172f222 ASoC: fix params_... |
464 |
case 11025: |
0a1bf5533 ASoC: Add WM8974 ... |
465 466 |
adn |= 0x4 << 1; break; |
b3172f222 ASoC: fix params_... |
467 |
case 16000: |
0a1bf5533 ASoC: Add WM8974 ... |
468 469 |
adn |= 0x3 << 1; break; |
b3172f222 ASoC: fix params_... |
470 |
case 22050: |
0a1bf5533 ASoC: Add WM8974 ... |
471 472 |
adn |= 0x2 << 1; break; |
b3172f222 ASoC: fix params_... |
473 |
case 32000: |
0a1bf5533 ASoC: Add WM8974 ... |
474 475 |
adn |= 0x1 << 1; break; |
b3172f222 ASoC: fix params_... |
476 477 |
case 44100: case 48000: |
0a1bf5533 ASoC: Add WM8974 ... |
478 479 |
break; } |
1e97f50b7 ASoC: Factor out ... |
480 481 |
snd_soc_write(codec, WM8974_IFACE, iface); snd_soc_write(codec, WM8974_ADD, adn); |
0a1bf5533 ASoC: Add WM8974 ... |
482 483 484 485 486 487 |
return 0; } static int wm8974_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; |
1e97f50b7 ASoC: Factor out ... |
488 |
u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf; |
0a1bf5533 ASoC: Add WM8974 ... |
489 |
|
1a55b3f6e ASoC: WM8974 chec... |
490 |
if (mute) |
1e97f50b7 ASoC: Factor out ... |
491 |
snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40); |
0a1bf5533 ASoC: Add WM8974 ... |
492 |
else |
1e97f50b7 ASoC: Factor out ... |
493 |
snd_soc_write(codec, WM8974_DAC, mute_reg); |
0a1bf5533 ASoC: Add WM8974 ... |
494 495 496 497 498 499 500 |
return 0; } /* liam need to make this lower power with dapm */ static int wm8974_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { |
1e97f50b7 ASoC: Factor out ... |
501 |
u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3; |
df1ef7a38 ASoC: Refresh WM8... |
502 |
|
0a1bf5533 ASoC: Add WM8974 ... |
503 504 |
switch (level) { case SND_SOC_BIAS_ON: |
0a1bf5533 ASoC: Add WM8974 ... |
505 |
case SND_SOC_BIAS_PREPARE: |
df1ef7a38 ASoC: Refresh WM8... |
506 |
power1 |= 0x1; /* VMID 50k */ |
1e97f50b7 ASoC: Factor out ... |
507 |
snd_soc_write(codec, WM8974_POWER1, power1); |
0a1bf5533 ASoC: Add WM8974 ... |
508 |
break; |
df1ef7a38 ASoC: Refresh WM8... |
509 |
|
0a1bf5533 ASoC: Add WM8974 ... |
510 |
case SND_SOC_BIAS_STANDBY: |
df1ef7a38 ASoC: Refresh WM8... |
511 |
power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN; |
ce6120cca ASoC: Decouple DA... |
512 |
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { |
0bad3d845 ASoC: wm8974: Con... |
513 |
snd_soc_cache_sync(codec); |
df1ef7a38 ASoC: Refresh WM8... |
514 |
/* Initial cap charge at VMID 5k */ |
1e97f50b7 ASoC: Factor out ... |
515 |
snd_soc_write(codec, WM8974_POWER1, power1 | 0x3); |
df1ef7a38 ASoC: Refresh WM8... |
516 517 518 519 |
mdelay(100); } power1 |= 0x2; /* VMID 500k */ |
1e97f50b7 ASoC: Factor out ... |
520 |
snd_soc_write(codec, WM8974_POWER1, power1); |
0a1bf5533 ASoC: Add WM8974 ... |
521 |
break; |
df1ef7a38 ASoC: Refresh WM8... |
522 |
|
0a1bf5533 ASoC: Add WM8974 ... |
523 |
case SND_SOC_BIAS_OFF: |
1e97f50b7 ASoC: Factor out ... |
524 525 526 |
snd_soc_write(codec, WM8974_POWER1, 0); snd_soc_write(codec, WM8974_POWER2, 0); snd_soc_write(codec, WM8974_POWER3, 0); |
0a1bf5533 ASoC: Add WM8974 ... |
527 528 |
break; } |
df1ef7a38 ASoC: Refresh WM8... |
529 |
|
ce6120cca ASoC: Decouple DA... |
530 |
codec->dapm.bias_level = level; |
0a1bf5533 ASoC: Add WM8974 ... |
531 532 |
return 0; } |
1a55b3f6e ASoC: WM8974 chec... |
533 |
#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000) |
0a1bf5533 ASoC: Add WM8974 ... |
534 535 536 |
#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE) |
85e7652d8 ASoC: Constify sn... |
537 |
static const struct snd_soc_dai_ops wm8974_ops = { |
0a1bf5533 ASoC: Add WM8974 ... |
538 539 540 541 542 543 |
.hw_params = wm8974_pcm_hw_params, .digital_mute = wm8974_mute, .set_fmt = wm8974_set_dai_fmt, .set_clkdiv = wm8974_set_dai_clkdiv, .set_pll = wm8974_set_dai_pll, }; |
f0fba2ad1 ASoC: multi-compo... |
544 545 |
static struct snd_soc_dai_driver wm8974_dai = { .name = "wm8974-hifi", |
0a1bf5533 ASoC: Add WM8974 ... |
546 547 548 |
.playback = { .stream_name = "Playback", .channels_min = 1, |
33d81af4d ASoC: Declare 2 c... |
549 |
.channels_max = 2, /* Only 1 channel of data */ |
0a1bf5533 ASoC: Add WM8974 ... |
550 551 552 553 554 |
.rates = WM8974_RATES, .formats = WM8974_FORMATS,}, .capture = { .stream_name = "Capture", .channels_min = 1, |
33d81af4d ASoC: Declare 2 c... |
555 |
.channels_max = 2, /* Only 1 channel of data */ |
0a1bf5533 ASoC: Add WM8974 ... |
556 557 558 |
.rates = WM8974_RATES, .formats = WM8974_FORMATS,}, .ops = &wm8974_ops, |
cb11d39ea ASoC: Use symmetr... |
559 |
.symmetric_rates = 1, |
0a1bf5533 ASoC: Add WM8974 ... |
560 |
}; |
0a1bf5533 ASoC: Add WM8974 ... |
561 |
|
84b315ee8 ASoC: Drop unused... |
562 |
static int wm8974_suspend(struct snd_soc_codec *codec) |
0a1bf5533 ASoC: Add WM8974 ... |
563 |
{ |
0a1bf5533 ASoC: Add WM8974 ... |
564 565 566 |
wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } |
f0fba2ad1 ASoC: multi-compo... |
567 |
static int wm8974_resume(struct snd_soc_codec *codec) |
0a1bf5533 ASoC: Add WM8974 ... |
568 |
{ |
0a1bf5533 ASoC: Add WM8974 ... |
569 |
wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
0a1bf5533 ASoC: Add WM8974 ... |
570 571 |
return 0; } |
f0fba2ad1 ASoC: multi-compo... |
572 |
static int wm8974_probe(struct snd_soc_codec *codec) |
0a1bf5533 ASoC: Add WM8974 ... |
573 |
{ |
0a1bf5533 ASoC: Add WM8974 ... |
574 |
int ret = 0; |
f0fba2ad1 ASoC: multi-compo... |
575 576 577 578 579 |
ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d ", ret); return ret; |
4fcbbb67a ASoC: Update WM89... |
580 |
} |
0a1bf5533 ASoC: Add WM8974 ... |
581 |
|
f0fba2ad1 ASoC: multi-compo... |
582 |
ret = wm8974_reset(codec); |
1a55b3f6e ASoC: WM8974 chec... |
583 |
if (ret < 0) { |
f0fba2ad1 ASoC: multi-compo... |
584 585 586 |
dev_err(codec->dev, "Failed to issue reset "); return ret; |
0a1bf5533 ASoC: Add WM8974 ... |
587 |
} |
f0fba2ad1 ASoC: multi-compo... |
588 |
wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
4fcbbb67a ASoC: Update WM89... |
589 590 |
snd_soc_add_controls(codec, wm8974_snd_controls, ARRAY_SIZE(wm8974_snd_controls)); |
0a1bf5533 ASoC: Add WM8974 ... |
591 |
wm8974_add_widgets(codec); |
4fcbbb67a ASoC: Update WM89... |
592 |
|
0a1bf5533 ASoC: Add WM8974 ... |
593 |
return ret; |
0a1bf5533 ASoC: Add WM8974 ... |
594 |
} |
4fcbbb67a ASoC: Update WM89... |
595 |
/* power down chip */ |
f0fba2ad1 ASoC: multi-compo... |
596 |
static int wm8974_remove(struct snd_soc_codec *codec) |
4fcbbb67a ASoC: Update WM89... |
597 |
{ |
f0fba2ad1 ASoC: multi-compo... |
598 |
wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF); |
4fcbbb67a ASoC: Update WM89... |
599 600 |
return 0; } |
0a1bf5533 ASoC: Add WM8974 ... |
601 |
|
f0fba2ad1 ASoC: multi-compo... |
602 |
static struct snd_soc_codec_driver soc_codec_dev_wm8974 = { |
4fcbbb67a ASoC: Update WM89... |
603 604 605 606 |
.probe = wm8974_probe, .remove = wm8974_remove, .suspend = wm8974_suspend, .resume = wm8974_resume, |
f0fba2ad1 ASoC: multi-compo... |
607 608 609 610 |
.set_bias_level = wm8974_set_bias_level, .reg_cache_size = ARRAY_SIZE(wm8974_reg), .reg_word_size = sizeof(u16), .reg_cache_default = wm8974_reg, |
4fcbbb67a ASoC: Update WM89... |
611 |
}; |
0a1bf5533 ASoC: Add WM8974 ... |
612 |
|
f0fba2ad1 ASoC: multi-compo... |
613 |
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
4fcbbb67a ASoC: Update WM89... |
614 615 |
static __devinit int wm8974_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) |
0a1bf5533 ASoC: Add WM8974 ... |
616 |
{ |
4fcbbb67a ASoC: Update WM89... |
617 |
struct wm8974_priv *wm8974; |
f0fba2ad1 ASoC: multi-compo... |
618 |
int ret; |
0a1bf5533 ASoC: Add WM8974 ... |
619 |
|
4fcbbb67a ASoC: Update WM89... |
620 621 |
wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL); if (wm8974 == NULL) |
0a1bf5533 ASoC: Add WM8974 ... |
622 |
return -ENOMEM; |
4fcbbb67a ASoC: Update WM89... |
623 |
i2c_set_clientdata(i2c, wm8974); |
0a1bf5533 ASoC: Add WM8974 ... |
624 |
|
f0fba2ad1 ASoC: multi-compo... |
625 626 627 628 629 |
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8974, &wm8974_dai, 1); if (ret < 0) kfree(wm8974); return ret; |
4fcbbb67a ASoC: Update WM89... |
630 |
} |
0a1bf5533 ASoC: Add WM8974 ... |
631 |
|
4fcbbb67a ASoC: Update WM89... |
632 633 |
static __devexit int wm8974_i2c_remove(struct i2c_client *client) { |
f0fba2ad1 ASoC: multi-compo... |
634 635 |
snd_soc_unregister_codec(&client->dev); kfree(i2c_get_clientdata(client)); |
0a1bf5533 ASoC: Add WM8974 ... |
636 637 |
return 0; } |
4fcbbb67a ASoC: Update WM89... |
638 639 640 641 642 643 644 645 |
static const struct i2c_device_id wm8974_i2c_id[] = { { "wm8974", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id); static struct i2c_driver wm8974_i2c_driver = { .driver = { |
091edccf7 ASoC: Remove unus... |
646 |
.name = "wm8974", |
4fcbbb67a ASoC: Update WM89... |
647 648 649 650 651 |
.owner = THIS_MODULE, }, .probe = wm8974_i2c_probe, .remove = __devexit_p(wm8974_i2c_remove), .id_table = wm8974_i2c_id, |
0a1bf5533 ASoC: Add WM8974 ... |
652 |
}; |
f0fba2ad1 ASoC: multi-compo... |
653 |
#endif |
0a1bf5533 ASoC: Add WM8974 ... |
654 655 656 |
static int __init wm8974_modinit(void) { |
f0fba2ad1 ASoC: multi-compo... |
657 658 659 660 661 662 663 664 665 666 |
int ret = 0; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) ret = i2c_add_driver(&wm8974_i2c_driver); if (ret != 0) { printk(KERN_ERR "Failed to register wm8974 I2C driver: %d ", ret); } #endif return ret; |
0a1bf5533 ASoC: Add WM8974 ... |
667 668 669 670 671 |
} module_init(wm8974_modinit); static void __exit wm8974_exit(void) { |
f0fba2ad1 ASoC: multi-compo... |
672 |
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
4fcbbb67a ASoC: Update WM89... |
673 |
i2c_del_driver(&wm8974_i2c_driver); |
f0fba2ad1 ASoC: multi-compo... |
674 |
#endif |
0a1bf5533 ASoC: Add WM8974 ... |
675 676 677 678 679 680 |
} module_exit(wm8974_exit); MODULE_DESCRIPTION("ASoC WM8974 driver"); MODULE_AUTHOR("Liam Girdwood"); MODULE_LICENSE("GPL"); |