Blame view

sound/soc/codecs/wm8974.c 19.3 KB
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
1
2
3
  /*
   * wm8974.c  --  WM8974 ALSA Soc Audio driver
   *
8b83a1936   Mark Brown   ASoC: WM8974 cosm...
4
   * Copyright 2006-2009 Wolfson Microelectronics PLC.
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
5
   *
9a185b9ab   Mark Brown   ASoC: Remove refe...
6
   * Author: Liam Girdwood <Liam.Girdwood@wolfsonmicro.com>
0a1bf5533   Mark Brown   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   Mark Brown   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   Tejun Heo   include cleanup: ...
20
  #include <linux/slab.h>
0a1bf5533   Mark Brown   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   Mark Brown   ASoC: Add WM8974 ...
25
  #include <sound/initval.h>
a5f8d2f1b   Mark Brown   ASoC: Add WM8974 ...
26
  #include <sound/tlv.h>
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
27
28
  
  #include "wm8974.h"
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
29
  static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
1a55b3f6e   Mark Brown   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   Mark Brown   ASoC: Add WM8974 ...
45
  };
df1ef7a38   Mark Brown   ASoC: Refresh WM8...
46
  #define WM8974_POWER1_BIASEN  0x08
48c03ce72   Guennadi Liakhovetski   ASoC: wm8974: fix...
47
  #define WM8974_POWER1_BUFIOEN 0x04
df1ef7a38   Mark Brown   ASoC: Refresh WM8...
48

4fcbbb67a   Mark Brown   ASoC: Update WM89...
49
  struct wm8974_priv {
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
50
  	enum snd_soc_control_type control_type;
4fcbbb67a   Mark Brown   ASoC: Update WM89...
51
  };
1e97f50b7   Mark Brown   ASoC: Factor out ...
52
  #define wm8974_reset(c)	snd_soc_write(c, WM8974_RESET, 0)
0a1bf5533   Mark Brown   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   Mark Brown   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   Mark Brown   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   Mark Brown   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   Mark Brown   ASoC: Add WM8974 ...
101
  SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
0a1bf5533   Mark Brown   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   javier Martin   ASoC: Correct a b...
105
  SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0),
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
106

a5f8d2f1b   Mark Brown   ASoC: Add WM8974 ...
107
  SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL,  0, 255, 0, digital_tlv),
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
108
109
110
  
  SOC_ENUM("Equaliser Function", wm8974_enum[3]),
  SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
a5f8d2f1b   Mark Brown   ASoC: Add WM8974 ...
111
  SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1,  0, 24, 1, eq_tlv),
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
112
113
114
  
  SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
  SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
a5f8d2f1b   Mark Brown   ASoC: Add WM8974 ...
115
  SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2,  0, 24, 1, eq_tlv),
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
116
117
118
  
  SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
  SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
a5f8d2f1b   Mark Brown   ASoC: Add WM8974 ...
119
  SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3,  0, 24, 1, eq_tlv),
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
120
121
122
  
  SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
  SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
a5f8d2f1b   Mark Brown   ASoC: Add WM8974 ...
123
  SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4,  0, 24, 1, eq_tlv),
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
124
125
126
  
  SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
  SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
a5f8d2f1b   Mark Brown   ASoC: Add WM8974 ...
127
  SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5,  0, 24, 1, eq_tlv),
0a1bf5533   Mark Brown   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   Mark Brown   ASoC: Add WM8974 ...
152
  SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA,  0, 63, 0, inpga_tlv),
0a1bf5533   Mark Brown   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   Mark Brown   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   Mark Brown   ASoC: Add WM8974 ...
159
160
  
  SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST,  8, 1, 0),
8a123ee2a   Mark Brown   ASoC: WM8974 DAPM...
161
  SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
b2c3e9231   Guennadi Liakhovetski   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   Mark Brown   ASoC: Add WM8974 ...
166
  };
0a1bf5533   Mark Brown   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   Mark Brown   ASoC: Correct inv...
171
  SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 0),
0a1bf5533   Mark Brown   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   Mark Brown   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   Mark Brown   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   Mark Brown   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   Mark Brown   ASoC: WM8974 DAPM...
208
  SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
0a1bf5533   Mark Brown   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   Mark Brown   ASoC: Add WM8974 ...
213

8a123ee2a   Mark Brown   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   Mark Brown   ASoC: Add WM8974 ...
218

48dd231b0   Mark Brown   ASoC: Convert wm8...
219
  SND_SOC_DAPM_SUPPLY("Mic Bias", WM8974_POWER1, 4, 0, NULL, 0),
0a1bf5533   Mark Brown   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   Mark Brown   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   Mark Brown   ASoC: Add WM8974 ...
258
259
  
  	/* Inputs */
8a123ee2a   Mark Brown   ASoC: WM8974 DAPM...
260
  	{"Aux Input", NULL, "AUX"},
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
261
262
263
264
  };
  
  static int wm8974_add_widgets(struct snd_soc_codec *codec)
  {
ce6120cca   Liam Girdwood   ASoC: Decouple DA...
265
  	struct snd_soc_dapm_context *dapm = &codec->dapm;
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
266

ce6120cca   Liam Girdwood   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   Mark Brown   ASoC: Add WM8974 ...
270

0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
271
272
273
274
  	return 0;
  }
  
  struct pll_ {
c36b2fc73   Mark Brown   ASoC: Clean up WM...
275
  	unsigned int pre_div:1;
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
276
277
278
  	unsigned int n:4;
  	unsigned int k;
  };
91d0c3ecb   Mark Brown   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   Mark Brown   ASoC: Clean up WM...
282
283
  static void pll_factors(struct pll_ *pll_div,
  			unsigned int target, unsigned int source)
91d0c3ecb   Mark Brown   ASoC: Refresh WM8...
284
285
286
  {
  	unsigned long long Kpart;
  	unsigned int K, Ndiv, Nmod;
c36b2fc73   Mark Brown   ASoC: Clean up WM...
287
288
  	/* There is a fixed divide by 4 in the output path */
  	target *= 4;
91d0c3ecb   Mark Brown   ASoC: Refresh WM8...
289
290
  	Ndiv = target / source;
  	if (Ndiv < 6) {
c36b2fc73   Mark Brown   ASoC: Clean up WM...
291
292
  		source /= 2;
  		pll_div->pre_div = 1;
91d0c3ecb   Mark Brown   ASoC: Refresh WM8...
293
294
  		Ndiv = target / source;
  	} else
c36b2fc73   Mark Brown   ASoC: Clean up WM...
295
  		pll_div->pre_div = 0;
91d0c3ecb   Mark Brown   ASoC: Refresh WM8...
296
297
298
  
  	if ((Ndiv < 6) || (Ndiv > 12))
  		printk(KERN_WARNING
8b83a1936   Mark Brown   ASoC: WM8974 cosm...
299
300
  			"WM8974 N value %u outwith recommended range!
  ",
91d0c3ecb   Mark Brown   ASoC: Refresh WM8...
301
  			Ndiv);
c36b2fc73   Mark Brown   ASoC: Clean up WM...
302
  	pll_div->n = Ndiv;
91d0c3ecb   Mark Brown   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   Mark Brown   ASoC: Clean up WM...
316
  	pll_div->k = K;
91d0c3ecb   Mark Brown   ASoC: Refresh WM8...
317
  }
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
318

85488037b   Mark Brown   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   Mark Brown   ASoC: Add WM8974 ...
321
322
  {
  	struct snd_soc_codec *codec = codec_dai->codec;
c36b2fc73   Mark Brown   ASoC: Clean up WM...
323
  	struct pll_ pll_div;
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
324
  	u16 reg;
1a55b3f6e   Mark Brown   ASoC: WM8974 chec...
325
  	if (freq_in == 0 || freq_out == 0) {
91d0c3ecb   Mark Brown   ASoC: Refresh WM8...
326
  		/* Clock CODEC directly from MCLK */
1e97f50b7   Mark Brown   ASoC: Factor out ...
327
328
  		reg = snd_soc_read(codec, WM8974_CLOCK);
  		snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
91d0c3ecb   Mark Brown   ASoC: Refresh WM8...
329
330
  
  		/* Turn off PLL */
1e97f50b7   Mark Brown   ASoC: Factor out ...
331
332
  		reg = snd_soc_read(codec, WM8974_POWER1);
  		snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
333
334
  		return 0;
  	}
c36b2fc73   Mark Brown   ASoC: Clean up WM...
335
  	pll_factors(&pll_div, freq_out, freq_in);
91d0c3ecb   Mark Brown   ASoC: Refresh WM8...
336

1e97f50b7   Mark Brown   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   Mark Brown   ASoC: WM8974 chec...
343

91d0c3ecb   Mark Brown   ASoC: Refresh WM8...
344
  	/* Run CODEC from PLL instead of MCLK */
1e97f50b7   Mark Brown   ASoC: Factor out ...
345
346
  	reg = snd_soc_read(codec, WM8974_CLOCK);
  	snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
91d0c3ecb   Mark Brown   ASoC: Refresh WM8...
347
348
  
  	return 0;
0a1bf5533   Mark Brown   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   Mark Brown   ASoC: Factor out ...
362
363
  		reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
  		snd_soc_write(codec, WM8974_GPIO, reg | div);
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
364
365
  		break;
  	case WM8974_MCLKDIV:
1e97f50b7   Mark Brown   ASoC: Factor out ...
366
367
  		reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
  		snd_soc_write(codec, WM8974_CLOCK, reg | div);
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
368
  		break;
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
369
  	case WM8974_BCLKDIV:
1e97f50b7   Mark Brown   ASoC: Factor out ...
370
371
  		reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
  		snd_soc_write(codec, WM8974_CLOCK, reg | div);
0a1bf5533   Mark Brown   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   Mark Brown   ASoC: Factor out ...
385
  	u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
0a1bf5533   Mark Brown   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   Mark Brown   ASoC: Factor out ...
431
432
  	snd_soc_write(codec, WM8974_IFACE, iface);
  	snd_soc_write(codec, WM8974_CLOCK, clk);
0a1bf5533   Mark Brown   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   Mark Brown   ASoC: Factor out ...
441
442
  	u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
  	u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
0a1bf5533   Mark Brown   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   Guennadi Liakhovetski   ASoC: fix params_...
461
  	case 8000:
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
462
463
  		adn |= 0x5 << 1;
  		break;
b3172f222   Guennadi Liakhovetski   ASoC: fix params_...
464
  	case 11025:
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
465
466
  		adn |= 0x4 << 1;
  		break;
b3172f222   Guennadi Liakhovetski   ASoC: fix params_...
467
  	case 16000:
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
468
469
  		adn |= 0x3 << 1;
  		break;
b3172f222   Guennadi Liakhovetski   ASoC: fix params_...
470
  	case 22050:
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
471
472
  		adn |= 0x2 << 1;
  		break;
b3172f222   Guennadi Liakhovetski   ASoC: fix params_...
473
  	case 32000:
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
474
475
  		adn |= 0x1 << 1;
  		break;
b3172f222   Guennadi Liakhovetski   ASoC: fix params_...
476
477
  	case 44100:
  	case 48000:
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
478
479
  		break;
  	}
1e97f50b7   Mark Brown   ASoC: Factor out ...
480
481
  	snd_soc_write(codec, WM8974_IFACE, iface);
  	snd_soc_write(codec, WM8974_ADD, adn);
0a1bf5533   Mark Brown   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   Mark Brown   ASoC: Factor out ...
488
  	u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
489

1a55b3f6e   Mark Brown   ASoC: WM8974 chec...
490
  	if (mute)
1e97f50b7   Mark Brown   ASoC: Factor out ...
491
  		snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
492
  	else
1e97f50b7   Mark Brown   ASoC: Factor out ...
493
  		snd_soc_write(codec, WM8974_DAC, mute_reg);
0a1bf5533   Mark Brown   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   Mark Brown   ASoC: Factor out ...
501
  	u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
df1ef7a38   Mark Brown   ASoC: Refresh WM8...
502

0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
503
504
  	switch (level) {
  	case SND_SOC_BIAS_ON:
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
505
  	case SND_SOC_BIAS_PREPARE:
df1ef7a38   Mark Brown   ASoC: Refresh WM8...
506
  		power1 |= 0x1;  /* VMID 50k */
1e97f50b7   Mark Brown   ASoC: Factor out ...
507
  		snd_soc_write(codec, WM8974_POWER1, power1);
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
508
  		break;
df1ef7a38   Mark Brown   ASoC: Refresh WM8...
509

0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
510
  	case SND_SOC_BIAS_STANDBY:
df1ef7a38   Mark Brown   ASoC: Refresh WM8...
511
  		power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
ce6120cca   Liam Girdwood   ASoC: Decouple DA...
512
  		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
0bad3d845   Axel Lin   ASoC: wm8974: Con...
513
  			snd_soc_cache_sync(codec);
df1ef7a38   Mark Brown   ASoC: Refresh WM8...
514
  			/* Initial cap charge at VMID 5k */
1e97f50b7   Mark Brown   ASoC: Factor out ...
515
  			snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
df1ef7a38   Mark Brown   ASoC: Refresh WM8...
516
517
518
519
  			mdelay(100);
  		}
  
  		power1 |= 0x2;  /* VMID 500k */
1e97f50b7   Mark Brown   ASoC: Factor out ...
520
  		snd_soc_write(codec, WM8974_POWER1, power1);
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
521
  		break;
df1ef7a38   Mark Brown   ASoC: Refresh WM8...
522

0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
523
  	case SND_SOC_BIAS_OFF:
1e97f50b7   Mark Brown   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   Mark Brown   ASoC: Add WM8974 ...
527
528
  		break;
  	}
df1ef7a38   Mark Brown   ASoC: Refresh WM8...
529

ce6120cca   Liam Girdwood   ASoC: Decouple DA...
530
  	codec->dapm.bias_level = level;
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
531
532
  	return 0;
  }
1a55b3f6e   Mark Brown   ASoC: WM8974 chec...
533
  #define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
0a1bf5533   Mark Brown   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   Lars-Peter Clausen   ASoC: Constify sn...
537
  static const struct snd_soc_dai_ops wm8974_ops = {
0a1bf5533   Mark Brown   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   Liam Girdwood   ASoC: multi-compo...
544
545
  static struct snd_soc_dai_driver wm8974_dai = {
  	.name = "wm8974-hifi",
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
546
547
548
  	.playback = {
  		.stream_name = "Playback",
  		.channels_min = 1,
33d81af4d   Mark Brown   ASoC: Declare 2 c...
549
  		.channels_max = 2,   /* Only 1 channel of data */
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
550
551
552
553
554
  		.rates = WM8974_RATES,
  		.formats = WM8974_FORMATS,},
  	.capture = {
  		.stream_name = "Capture",
  		.channels_min = 1,
33d81af4d   Mark Brown   ASoC: Declare 2 c...
555
  		.channels_max = 2,   /* Only 1 channel of data */
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
556
557
558
  		.rates = WM8974_RATES,
  		.formats = WM8974_FORMATS,},
  	.ops = &wm8974_ops,
cb11d39ea   Mark Brown   ASoC: Use symmetr...
559
  	.symmetric_rates = 1,
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
560
  };
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
561

84b315ee8   Lars-Peter Clausen   ASoC: Drop unused...
562
  static int wm8974_suspend(struct snd_soc_codec *codec)
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
563
  {
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
564
565
566
  	wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
  	return 0;
  }
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
567
  static int wm8974_resume(struct snd_soc_codec *codec)
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
568
  {
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
569
  	wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
570
571
  	return 0;
  }
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
572
  static int wm8974_probe(struct snd_soc_codec *codec)
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
573
  {
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
574
  	int ret = 0;
f0fba2ad1   Liam Girdwood   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   Mark Brown   ASoC: Update WM89...
580
  	}
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
581

f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
582
  	ret = wm8974_reset(codec);
1a55b3f6e   Mark Brown   ASoC: WM8974 chec...
583
  	if (ret < 0) {
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
584
585
586
  		dev_err(codec->dev, "Failed to issue reset
  ");
  		return ret;
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
587
  	}
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
588
  	wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
4fcbbb67a   Mark Brown   ASoC: Update WM89...
589
590
  	snd_soc_add_controls(codec, wm8974_snd_controls,
  			     ARRAY_SIZE(wm8974_snd_controls));
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
591
  	wm8974_add_widgets(codec);
4fcbbb67a   Mark Brown   ASoC: Update WM89...
592

0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
593
  	return ret;
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
594
  }
4fcbbb67a   Mark Brown   ASoC: Update WM89...
595
  /* power down chip */
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
596
  static int wm8974_remove(struct snd_soc_codec *codec)
4fcbbb67a   Mark Brown   ASoC: Update WM89...
597
  {
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
598
  	wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
4fcbbb67a   Mark Brown   ASoC: Update WM89...
599
600
  	return 0;
  }
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
601

f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
602
  static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
4fcbbb67a   Mark Brown   ASoC: Update WM89...
603
604
605
606
  	.probe = 	wm8974_probe,
  	.remove = 	wm8974_remove,
  	.suspend = 	wm8974_suspend,
  	.resume =	wm8974_resume,
f0fba2ad1   Liam Girdwood   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   Mark Brown   ASoC: Update WM89...
611
  };
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
612

f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
613
  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
4fcbbb67a   Mark Brown   ASoC: Update WM89...
614
615
  static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
  				      const struct i2c_device_id *id)
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
616
  {
4fcbbb67a   Mark Brown   ASoC: Update WM89...
617
  	struct wm8974_priv *wm8974;
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
618
  	int ret;
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
619

4fcbbb67a   Mark Brown   ASoC: Update WM89...
620
621
  	wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
  	if (wm8974 == NULL)
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
622
  		return -ENOMEM;
4fcbbb67a   Mark Brown   ASoC: Update WM89...
623
  	i2c_set_clientdata(i2c, wm8974);
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
624

f0fba2ad1   Liam Girdwood   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   Mark Brown   ASoC: Update WM89...
630
  }
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
631

4fcbbb67a   Mark Brown   ASoC: Update WM89...
632
633
  static __devexit int wm8974_i2c_remove(struct i2c_client *client)
  {
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
634
635
  	snd_soc_unregister_codec(&client->dev);
  	kfree(i2c_get_clientdata(client));
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
636
637
  	return 0;
  }
4fcbbb67a   Mark Brown   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   Mark Brown   ASoC: Remove unus...
646
  		.name = "wm8974",
4fcbbb67a   Mark Brown   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   Mark Brown   ASoC: Add WM8974 ...
652
  };
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
653
  #endif
0a1bf5533   Mark Brown   ASoC: Add WM8974 ...
654
655
656
  
  static int __init wm8974_modinit(void)
  {
f0fba2ad1   Liam Girdwood   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   Mark Brown   ASoC: Add WM8974 ...
667
668
669
670
671
  }
  module_init(wm8974_modinit);
  
  static void __exit wm8974_exit(void)
  {
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
672
  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
4fcbbb67a   Mark Brown   ASoC: Update WM89...
673
  	i2c_del_driver(&wm8974_i2c_driver);
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
674
  #endif
0a1bf5533   Mark Brown   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");