Blame view

sound/soc/codecs/wm8940.c 22.2 KB
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  /*
   * wm8940.c  --  WM8940 ALSA Soc Audio driver
   *
   * Author: Jonathan Cameron <jic23@cam.ac.uk>
   *
   * Based on wm8510.c
   *    Copyright  2006 Wolfson Microelectronics PLC.
   *    Author:  Liam Girdwood <lrg@slimlogic.co.uk>
   *
   * 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.
   *
   * Not currently handled:
   * Notch filter control
   * AUXMode (inverting vs mixer)
   * No means to obtain current gain if alc enabled.
   * No use made of gpio
   * Fast VMID discharge for power down
   * Soft Start
   * DLR and ALR Swaps not enabled
   * Digital Sidetone not supported
   */
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/delay.h>
  #include <linux/pm.h>
  #include <linux/i2c.h>
fbbf7fea8   Mark Brown   ASoC: wm8940: Con...
31
  #include <linux/regmap.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
32
  #include <linux/slab.h>
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
33
34
35
36
  #include <sound/core.h>
  #include <sound/pcm.h>
  #include <sound/pcm_params.h>
  #include <sound/soc.h>
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
37
38
39
40
41
42
43
  #include <sound/initval.h>
  #include <sound/tlv.h>
  
  #include "wm8940.h"
  
  struct wm8940_priv {
  	unsigned int sysclk;
fbbf7fea8   Mark Brown   ASoC: wm8940: Con...
44
  	struct regmap *regmap;
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
45
  };
fbbf7fea8   Mark Brown   ASoC: wm8940: Con...
46
  static bool wm8940_volatile_register(struct device *dev, unsigned int reg)
788b6e8ef   Axel Lin   ASoC: wm8940: Con...
47
48
49
  {
  	switch (reg) {
  	case WM8940_SOFTRESET:
fbbf7fea8   Mark Brown   ASoC: wm8940: Con...
50
  		return true;
788b6e8ef   Axel Lin   ASoC: wm8940: Con...
51
  	default:
fbbf7fea8   Mark Brown   ASoC: wm8940: Con...
52
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
  		return false;
  	}
  }
  
  static bool wm8940_readable_register(struct device *dev, unsigned int reg)
  {
  	switch (reg) {
  	case WM8940_SOFTRESET:
  	case WM8940_POWER1:
  	case WM8940_POWER2:
  	case WM8940_POWER3:
  	case WM8940_IFACE:
  	case WM8940_COMPANDINGCTL:
  	case WM8940_CLOCK:
  	case WM8940_ADDCNTRL:
  	case WM8940_GPIO:
  	case WM8940_CTLINT:
  	case WM8940_DAC:
  	case WM8940_DACVOL:
  	case WM8940_ADC:
  	case WM8940_ADCVOL:
  	case WM8940_NOTCH1:
  	case WM8940_NOTCH2:
  	case WM8940_NOTCH3:
  	case WM8940_NOTCH4:
  	case WM8940_NOTCH5:
  	case WM8940_NOTCH6:
  	case WM8940_NOTCH7:
  	case WM8940_NOTCH8:
  	case WM8940_DACLIM1:
  	case WM8940_DACLIM2:
  	case WM8940_ALC1:
  	case WM8940_ALC2:
  	case WM8940_ALC3:
  	case WM8940_NOISEGATE:
  	case WM8940_PLLN:
  	case WM8940_PLLK1:
  	case WM8940_PLLK2:
  	case WM8940_PLLK3:
  	case WM8940_ALC4:
  	case WM8940_INPUTCTL:
  	case WM8940_PGAGAIN:
  	case WM8940_ADCBOOST:
  	case WM8940_OUTPUTCTL:
  	case WM8940_SPKMIX:
  	case WM8940_SPKVOL:
  	case WM8940_MONOMIX:
  		return true;
  	default:
  		return false;
788b6e8ef   Axel Lin   ASoC: wm8940: Con...
102
103
  	}
  }
fbbf7fea8   Mark Brown   ASoC: wm8940: Con...
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  static const struct reg_default wm8940_reg_defaults[] = {
  	{  0x1, 0x0000 }, /* Power 1 */
  	{  0x2, 0x0000 }, /* Power 2 */
  	{  0x3, 0x0000 }, /* Power 3 */
  	{  0x4, 0x0010 }, /* Interface Control */
  	{  0x5, 0x0000 }, /* Companding Control */
  	{  0x6, 0x0140 }, /* Clock Control */
  	{  0x7, 0x0000 }, /* Additional Controls */
  	{  0x8, 0x0000 }, /* GPIO Control */
  	{  0x9, 0x0002 }, /* Auto Increment Control */
  	{  0xa, 0x0000 }, /* DAC Control */
  	{  0xb, 0x00FF }, /* DAC Volume */
  
  	{  0xe, 0x0100 }, /* ADC Control */
  	{  0xf, 0x00FF }, /* ADC Volume */
  	{ 0x10, 0x0000 }, /* Notch Filter 1 Control 1 */
  	{ 0x11, 0x0000 }, /* Notch Filter 1 Control 2 */
  	{ 0x12, 0x0000 }, /* Notch Filter 2 Control 1 */
  	{ 0x13, 0x0000 }, /* Notch Filter 2 Control 2 */
  	{ 0x14, 0x0000 }, /* Notch Filter 3 Control 1 */
  	{ 0x15, 0x0000 }, /* Notch Filter 3 Control 2 */
  	{ 0x16, 0x0000 }, /* Notch Filter 4 Control 1 */
  	{ 0x17, 0x0000 }, /* Notch Filter 4 Control 2 */
  	{ 0x18, 0x0032 }, /* DAC Limit Control 1 */
  	{ 0x19, 0x0000 }, /* DAC Limit Control 2 */
  
  	{ 0x20, 0x0038 }, /* ALC Control 1 */
  	{ 0x21, 0x000B }, /* ALC Control 2 */
  	{ 0x22, 0x0032 }, /* ALC Control 3 */
  	{ 0x23, 0x0000 }, /* Noise Gate */
  	{ 0x24, 0x0041 }, /* PLLN */
  	{ 0x25, 0x000C }, /* PLLK1 */
  	{ 0x26, 0x0093 }, /* PLLK2 */
  	{ 0x27, 0x00E9 }, /* PLLK3 */
  
  	{ 0x2a, 0x0030 }, /* ALC Control 4 */
  
  	{ 0x2c, 0x0002 }, /* Input Control */
  	{ 0x2d, 0x0050 }, /* PGA Gain */
  
  	{ 0x2f, 0x0002 }, /* ADC Boost Control */
  
  	{ 0x31, 0x0002 }, /* Output Control */
  	{ 0x32, 0x0000 }, /* Speaker Mixer Control */
  
  	{ 0x36, 0x0079 }, /* Speaker Volume */
  
  	{ 0x38, 0x0000 }, /* Mono Mixer Control */
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
152
  };
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
153
  static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" };
47ef34271   Takashi Iwai   ASoC: wm8940: Use...
154
155
156
157
  static SOC_ENUM_SINGLE_DECL(wm8940_adc_companding_enum,
  			    WM8940_COMPANDINGCTL, 1, wm8940_companding);
  static SOC_ENUM_SINGLE_DECL(wm8940_dac_companding_enum,
  			    WM8940_COMPANDINGCTL, 3, wm8940_companding);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
158
159
  
  static const char *wm8940_alc_mode_text[] = {"ALC", "Limiter"};
47ef34271   Takashi Iwai   ASoC: wm8940: Use...
160
161
  static SOC_ENUM_SINGLE_DECL(wm8940_alc_mode_enum,
  			    WM8940_ALC3, 8, wm8940_alc_mode_text);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
162
163
  
  static const char *wm8940_mic_bias_level_text[] = {"0.9", "0.65"};
47ef34271   Takashi Iwai   ASoC: wm8940: Use...
164
165
  static SOC_ENUM_SINGLE_DECL(wm8940_mic_bias_level_enum,
  			    WM8940_INPUTCTL, 8, wm8940_mic_bias_level_text);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
166
167
  
  static const char *wm8940_filter_mode_text[] = {"Audio", "Application"};
47ef34271   Takashi Iwai   ASoC: wm8940: Use...
168
169
  static SOC_ENUM_SINGLE_DECL(wm8940_filter_mode_enum,
  			    WM8940_ADC, 7, wm8940_filter_mode_text);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
170

6be01cfb8   Mark Brown   ASoC: Staticise T...
171
172
173
174
175
176
177
178
179
180
  static DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1);
  static DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0);
  static DECLARE_TLV_DB_SCALE(wm8940_pga_vol_tlv, -1200, 75, 0);
  static DECLARE_TLV_DB_SCALE(wm8940_alc_min_tlv, -1200, 600, 0);
  static DECLARE_TLV_DB_SCALE(wm8940_alc_max_tlv, 675, 600, 0);
  static DECLARE_TLV_DB_SCALE(wm8940_alc_tar_tlv, -2250, 50, 0);
  static DECLARE_TLV_DB_SCALE(wm8940_lim_boost_tlv, 0, 100, 0);
  static DECLARE_TLV_DB_SCALE(wm8940_lim_thresh_tlv, -600, 100, 0);
  static DECLARE_TLV_DB_SCALE(wm8940_adc_tlv, -12750, 50, 1);
  static DECLARE_TLV_DB_SCALE(wm8940_capture_boost_vol_tlv, 0, 2000, 0);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
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
249
250
251
252
253
254
  
  static const struct snd_kcontrol_new wm8940_snd_controls[] = {
  	SOC_SINGLE("Digital Loopback Switch", WM8940_COMPANDINGCTL,
  		   6, 1, 0),
  	SOC_ENUM("DAC Companding", wm8940_dac_companding_enum),
  	SOC_ENUM("ADC Companding", wm8940_adc_companding_enum),
  
  	SOC_ENUM("ALC Mode", wm8940_alc_mode_enum),
  	SOC_SINGLE("ALC Switch", WM8940_ALC1, 8, 1, 0),
  	SOC_SINGLE_TLV("ALC Capture Max Gain", WM8940_ALC1,
  		       3, 7, 1, wm8940_alc_max_tlv),
  	SOC_SINGLE_TLV("ALC Capture Min Gain", WM8940_ALC1,
  		       0, 7, 0, wm8940_alc_min_tlv),
  	SOC_SINGLE_TLV("ALC Capture Target", WM8940_ALC2,
  		       0, 14, 0, wm8940_alc_tar_tlv),
  	SOC_SINGLE("ALC Capture Hold", WM8940_ALC2, 4, 10, 0),
  	SOC_SINGLE("ALC Capture Decay", WM8940_ALC3, 4, 10, 0),
  	SOC_SINGLE("ALC Capture Attach", WM8940_ALC3, 0, 10, 0),
  	SOC_SINGLE("ALC ZC Switch", WM8940_ALC4, 1, 1, 0),
  	SOC_SINGLE("ALC Capture Noise Gate Switch", WM8940_NOISEGATE,
  		   3, 1, 0),
  	SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8940_NOISEGATE,
  		   0, 7, 0),
  
  	SOC_SINGLE("DAC Playback Limiter Switch", WM8940_DACLIM1, 8, 1, 0),
  	SOC_SINGLE("DAC Playback Limiter Attack", WM8940_DACLIM1, 0, 9, 0),
  	SOC_SINGLE("DAC Playback Limiter Decay", WM8940_DACLIM1, 4, 11, 0),
  	SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8940_DACLIM2,
  		       4, 9, 1, wm8940_lim_thresh_tlv),
  	SOC_SINGLE_TLV("DAC Playback Limiter Boost", WM8940_DACLIM2,
  		       0, 12, 0, wm8940_lim_boost_tlv),
  
  	SOC_SINGLE("Capture PGA ZC Switch", WM8940_PGAGAIN, 7, 1, 0),
  	SOC_SINGLE_TLV("Capture PGA Volume", WM8940_PGAGAIN,
  		       0, 63, 0, wm8940_pga_vol_tlv),
  	SOC_SINGLE_TLV("Digital Playback Volume", WM8940_DACVOL,
  		       0, 255, 0, wm8940_adc_tlv),
  	SOC_SINGLE_TLV("Digital Capture Volume", WM8940_ADCVOL,
  		       0, 255, 0, wm8940_adc_tlv),
  	SOC_ENUM("Mic Bias Level", wm8940_mic_bias_level_enum),
  	SOC_SINGLE_TLV("Capture Boost Volue", WM8940_ADCBOOST,
  		       8, 1, 0, wm8940_capture_boost_vol_tlv),
  	SOC_SINGLE_TLV("Speaker Playback Volume", WM8940_SPKVOL,
  		       0, 63, 0, wm8940_spk_vol_tlv),
  	SOC_SINGLE("Speaker Playback Switch", WM8940_SPKVOL,  6, 1, 1),
  
  	SOC_SINGLE_TLV("Speaker Mixer Line Bypass Volume", WM8940_SPKVOL,
  		       8, 1, 1, wm8940_att_tlv),
  	SOC_SINGLE("Speaker Playback ZC Switch", WM8940_SPKVOL, 7, 1, 0),
  
  	SOC_SINGLE("Mono Out Switch", WM8940_MONOMIX, 6, 1, 1),
  	SOC_SINGLE_TLV("Mono Mixer Line Bypass Volume", WM8940_MONOMIX,
  		       7, 1, 1, wm8940_att_tlv),
  
  	SOC_SINGLE("High Pass Filter Switch", WM8940_ADC, 8, 1, 0),
  	SOC_ENUM("High Pass Filter Mode", wm8940_filter_mode_enum),
  	SOC_SINGLE("High Pass Filter Cut Off", WM8940_ADC, 4, 7, 0),
  	SOC_SINGLE("ADC Inversion Switch", WM8940_ADC, 0, 1, 0),
  	SOC_SINGLE("DAC Inversion Switch", WM8940_DAC, 0, 1, 0),
  	SOC_SINGLE("DAC Auto Mute Switch", WM8940_DAC, 2, 1, 0),
  	SOC_SINGLE("ZC Timeout Clock Switch", WM8940_ADDCNTRL, 0, 1, 0),
  };
  
  static const struct snd_kcontrol_new wm8940_speaker_mixer_controls[] = {
  	SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_SPKMIX, 1, 1, 0),
  	SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_SPKMIX, 5, 1, 0),
  	SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_SPKMIX, 0, 1, 0),
  };
  
  static const struct snd_kcontrol_new wm8940_mono_mixer_controls[] = {
  	SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_MONOMIX, 1, 1, 0),
  	SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_MONOMIX, 2, 1, 0),
  	SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_MONOMIX, 0, 1, 0),
  };
6be01cfb8   Mark Brown   ASoC: Staticise T...
255
  static DECLARE_TLV_DB_SCALE(wm8940_boost_vol_tlv, -1500, 300, 1);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
  static const struct snd_kcontrol_new wm8940_input_boost_controls[] = {
  	SOC_DAPM_SINGLE("Mic PGA Switch", WM8940_PGAGAIN, 6, 1, 1),
  	SOC_DAPM_SINGLE_TLV("Aux Volume", WM8940_ADCBOOST,
  			    0, 7, 0, wm8940_boost_vol_tlv),
  	SOC_DAPM_SINGLE_TLV("Mic Volume", WM8940_ADCBOOST,
  			    4, 7, 0, wm8940_boost_vol_tlv),
  };
  
  static const struct snd_kcontrol_new wm8940_micpga_controls[] = {
  	SOC_DAPM_SINGLE("AUX Switch", WM8940_INPUTCTL, 2, 1, 0),
  	SOC_DAPM_SINGLE("MICP Switch", WM8940_INPUTCTL, 0, 1, 0),
  	SOC_DAPM_SINGLE("MICN Switch", WM8940_INPUTCTL, 1, 1, 0),
  };
  
  static const struct snd_soc_dapm_widget wm8940_dapm_widgets[] = {
  	SND_SOC_DAPM_MIXER("Speaker Mixer", WM8940_POWER3, 2, 0,
  			   &wm8940_speaker_mixer_controls[0],
  			   ARRAY_SIZE(wm8940_speaker_mixer_controls)),
  	SND_SOC_DAPM_MIXER("Mono Mixer", WM8940_POWER3, 3, 0,
  			   &wm8940_mono_mixer_controls[0],
  			   ARRAY_SIZE(wm8940_mono_mixer_controls)),
  	SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8940_POWER3, 0, 0),
  
  	SND_SOC_DAPM_PGA("SpkN Out", WM8940_POWER3, 5, 0, NULL, 0),
  	SND_SOC_DAPM_PGA("SpkP Out", WM8940_POWER3, 6, 0, NULL, 0),
  	SND_SOC_DAPM_PGA("Mono Out", WM8940_POWER3, 7, 0, NULL, 0),
  	SND_SOC_DAPM_OUTPUT("MONOOUT"),
  	SND_SOC_DAPM_OUTPUT("SPKOUTP"),
  	SND_SOC_DAPM_OUTPUT("SPKOUTN"),
  
  	SND_SOC_DAPM_PGA("Aux Input", WM8940_POWER1, 6, 0, NULL, 0),
  	SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8940_POWER2, 0, 0),
  	SND_SOC_DAPM_MIXER("Mic PGA", WM8940_POWER2, 2, 0,
  			   &wm8940_micpga_controls[0],
  			   ARRAY_SIZE(wm8940_micpga_controls)),
  	SND_SOC_DAPM_MIXER("Boost Mixer", WM8940_POWER2, 4, 0,
  			   &wm8940_input_boost_controls[0],
  			   ARRAY_SIZE(wm8940_input_boost_controls)),
  	SND_SOC_DAPM_MICBIAS("Mic Bias", WM8940_POWER1, 4, 0),
  
  	SND_SOC_DAPM_INPUT("MICN"),
  	SND_SOC_DAPM_INPUT("MICP"),
  	SND_SOC_DAPM_INPUT("AUX"),
  };
6435e5be6   Mark Brown   ASoC: wm8940: Con...
300
  static const struct snd_soc_dapm_route wm8940_dapm_routes[] = {
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
  	/* 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"},
  
  	/*  Microphone PGA */
  	{"Mic PGA", "MICN Switch", "MICN"},
  	{"Mic PGA", "MICP Switch", "MICP"},
  	{"Mic PGA", "AUX Switch", "AUX"},
  
  	/* Boost Mixer */
  	{"Boost Mixer", "Mic PGA Switch", "Mic PGA"},
  	{"Boost Mixer", "Mic Volume",  "MICP"},
  	{"Boost Mixer", "Aux Volume", "Aux Input"},
  
  	{"ADC", NULL, "Boost Mixer"},
  };
8d50e447d   Mark Brown   ASoC: Factor out ...
331
  #define wm8940_reset(c) snd_soc_write(c, WM8940_SOFTRESET, 0);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
332
333
334
335
336
  
  static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
  			      unsigned int fmt)
  {
  	struct snd_soc_codec *codec = codec_dai->codec;
8d50e447d   Mark Brown   ASoC: Factor out ...
337
338
  	u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFE67;
  	u16 clk = snd_soc_read(codec, WM8940_CLOCK) & 0x1fe;
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
339
340
341
342
343
344
345
346
347
348
  
  	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
  	case SND_SOC_DAIFMT_CBM_CFM:
  		clk |= 1;
  		break;
  	case SND_SOC_DAIFMT_CBS_CFS:
  		break;
  	default:
  		return -EINVAL;
  	}
8d50e447d   Mark Brown   ASoC: Factor out ...
349
  	snd_soc_write(codec, WM8940_CLOCK, clk);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
  
  	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
  	case SND_SOC_DAIFMT_I2S:
  		iface |= (2 << 3);
  		break;
  	case SND_SOC_DAIFMT_LEFT_J:
  		iface |= (1 << 3);
  		break;
  	case SND_SOC_DAIFMT_RIGHT_J:
  		break;
  	case SND_SOC_DAIFMT_DSP_A:
  		iface |= (3 << 3);
  		break;
  	case SND_SOC_DAIFMT_DSP_B:
  		iface |= (3 << 3) | (1 << 7);
  		break;
  	}
  
  	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
  	case SND_SOC_DAIFMT_NB_NF:
  		break;
  	case SND_SOC_DAIFMT_NB_IF:
  		iface |= (1 << 7);
  		break;
  	case SND_SOC_DAIFMT_IB_NF:
  		iface |= (1 << 8);
  		break;
  	case SND_SOC_DAIFMT_IB_IF:
  		iface |= (1 << 8) | (1 << 7);
  		break;
  	}
8d50e447d   Mark Brown   ASoC: Factor out ...
381
  	snd_soc_write(codec, WM8940_IFACE, iface);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
382
383
384
385
386
387
388
389
  
  	return 0;
  }
  
  static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
  				struct snd_pcm_hw_params *params,
  				struct snd_soc_dai *dai)
  {
e6968a171   Mark Brown   ASoC: codecs: Rem...
390
  	struct snd_soc_codec *codec = dai->codec;
8d50e447d   Mark Brown   ASoC: Factor out ...
391
392
393
  	u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFD9F;
  	u16 addcntrl = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFF1;
  	u16 companding =  snd_soc_read(codec,
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
394
395
396
397
398
399
400
401
402
  						WM8940_COMPANDINGCTL) & 0xFFDF;
  	int ret;
  
  	/* LoutR control */
  	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
  	    && params_channels(params) == 2)
  		iface |= (1 << 9);
  
  	switch (params_rate(params)) {
b3172f222   Guennadi Liakhovetski   ASoC: fix params_...
403
  	case 8000:
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
404
405
  		addcntrl |= (0x5 << 1);
  		break;
b3172f222   Guennadi Liakhovetski   ASoC: fix params_...
406
  	case 11025:
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
407
408
  		addcntrl |= (0x4 << 1);
  		break;
b3172f222   Guennadi Liakhovetski   ASoC: fix params_...
409
  	case 16000:
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
410
411
  		addcntrl |= (0x3 << 1);
  		break;
b3172f222   Guennadi Liakhovetski   ASoC: fix params_...
412
  	case 22050:
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
413
414
  		addcntrl |= (0x2 << 1);
  		break;
b3172f222   Guennadi Liakhovetski   ASoC: fix params_...
415
  	case 32000:
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
416
417
  		addcntrl |= (0x1 << 1);
  		break;
b3172f222   Guennadi Liakhovetski   ASoC: fix params_...
418
419
  	case 44100:
  	case 48000:
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
420
421
  		break;
  	}
8d50e447d   Mark Brown   ASoC: Factor out ...
422
  	ret = snd_soc_write(codec, WM8940_ADDCNTRL, addcntrl);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
423
424
  	if (ret)
  		goto error_ret;
74b24c386   Mark Brown   ASoC: wm8940: Con...
425
426
  	switch (params_width(params)) {
  	case 8:
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
427
428
  		companding = companding | (1 << 5);
  		break;
74b24c386   Mark Brown   ASoC: wm8940: Con...
429
  	case 16:
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
430
  		break;
74b24c386   Mark Brown   ASoC: wm8940: Con...
431
  	case 20:
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
432
433
  		iface |= (1 << 5);
  		break;
74b24c386   Mark Brown   ASoC: wm8940: Con...
434
  	case 24:
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
435
436
  		iface |= (2 << 5);
  		break;
74b24c386   Mark Brown   ASoC: wm8940: Con...
437
  	case 32:
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
438
439
440
  		iface |= (3 << 5);
  		break;
  	}
8d50e447d   Mark Brown   ASoC: Factor out ...
441
  	ret = snd_soc_write(codec, WM8940_COMPANDINGCTL, companding);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
442
443
  	if (ret)
  		goto error_ret;
8d50e447d   Mark Brown   ASoC: Factor out ...
444
  	ret = snd_soc_write(codec, WM8940_IFACE, iface);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
445
446
447
448
449
450
451
452
  
  error_ret:
  	return ret;
  }
  
  static int wm8940_mute(struct snd_soc_dai *dai, int mute)
  {
  	struct snd_soc_codec *codec = dai->codec;
8d50e447d   Mark Brown   ASoC: Factor out ...
453
  	u16 mute_reg = snd_soc_read(codec, WM8940_DAC) & 0xffbf;
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
454
455
456
  
  	if (mute)
  		mute_reg |= 0x40;
8d50e447d   Mark Brown   ASoC: Factor out ...
457
  	return snd_soc_write(codec, WM8940_DAC, mute_reg);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
458
459
460
461
462
  }
  
  static int wm8940_set_bias_level(struct snd_soc_codec *codec,
  				 enum snd_soc_bias_level level)
  {
fbbf7fea8   Mark Brown   ASoC: wm8940: Con...
463
  	struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
464
  	u16 val;
8d50e447d   Mark Brown   ASoC: Factor out ...
465
  	u16 pwr_reg = snd_soc_read(codec, WM8940_POWER1) & 0x1F0;
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
466
467
468
469
470
471
472
  	int ret = 0;
  
  	switch (level) {
  	case SND_SOC_BIAS_ON:
  		/* ensure bufioen and biasen */
  		pwr_reg |= (1 << 2) | (1 << 3);
  		/* Enable thermal shutdown */
8d50e447d   Mark Brown   ASoC: Factor out ...
473
474
  		val = snd_soc_read(codec, WM8940_OUTPUTCTL);
  		ret = snd_soc_write(codec, WM8940_OUTPUTCTL, val | 0x2);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
475
476
477
  		if (ret)
  			break;
  		/* set vmid to 75k */
8d50e447d   Mark Brown   ASoC: Factor out ...
478
  		ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
479
480
481
482
  		break;
  	case SND_SOC_BIAS_PREPARE:
  		/* ensure bufioen and biasen */
  		pwr_reg |= (1 << 2) | (1 << 3);
8d50e447d   Mark Brown   ASoC: Factor out ...
483
  		ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
484
485
  		break;
  	case SND_SOC_BIAS_STANDBY:
2145554fe   Lars-Peter Clausen   ASoC: wm8940: Rep...
486
  		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
fbbf7fea8   Mark Brown   ASoC: wm8940: Con...
487
  			ret = regcache_sync(wm8940->regmap);
788b6e8ef   Axel Lin   ASoC: wm8940: Con...
488
489
490
491
492
493
  			if (ret < 0) {
  				dev_err(codec->dev, "Failed to sync cache: %d
  ", ret);
  				return ret;
  			}
  		}
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
494
495
496
  		/* ensure bufioen and biasen */
  		pwr_reg |= (1 << 2) | (1 << 3);
  		/* set vmid to 300k for standby */
8d50e447d   Mark Brown   ASoC: Factor out ...
497
  		ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x2);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
498
499
  		break;
  	case SND_SOC_BIAS_OFF:
8d50e447d   Mark Brown   ASoC: Factor out ...
500
  		ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
  		break;
  	}
  
  	return ret;
  }
  
  struct pll_ {
  	unsigned int pre_scale:2;
  	unsigned int n:4;
  	unsigned int k;
  };
  
  static struct pll_ pll_div;
  
  /* The size in bits of the pll divide multiplied by 10
   * to allow rounding later */
  #define FIXED_PLL_SIZE ((1 << 24) * 10)
  static void pll_factors(unsigned int target, unsigned int source)
  {
  	unsigned long long Kpart;
  	unsigned int K, Ndiv, Nmod;
  	/* The left shift ist to avoid accuracy loss when right shifting */
  	Ndiv = target / source;
  
  	if (Ndiv > 12) {
  		source <<= 1;
  		/* Multiply by 2 */
  		pll_div.pre_scale = 0;
  		Ndiv = target / source;
  	} else if (Ndiv < 3) {
  		source >>= 2;
  		/* Divide by 4 */
  		pll_div.pre_scale = 3;
  		Ndiv = target / source;
  	} else if (Ndiv < 6) {
  		source >>= 1;
  		/* divide by 2 */
  		pll_div.pre_scale = 2;
  		Ndiv = target / source;
  	} else
  		pll_div.pre_scale = 1;
  
  	if ((Ndiv < 6) || (Ndiv > 12))
  		printk(KERN_WARNING
  			"WM8940 N value %d outwith recommended range!d
  ",
  			Ndiv);
  
  	pll_div.n = Ndiv;
  	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;
  
  	pll_div.k = K;
  }
  
  /* Untested at the moment */
85488037b   Mark Brown   ASoC: Add source ...
568
569
  static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
  		int source, unsigned int freq_in, unsigned int freq_out)
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
570
571
572
573
574
  {
  	struct snd_soc_codec *codec = codec_dai->codec;
  	u16 reg;
  
  	/* Turn off PLL */
8d50e447d   Mark Brown   ASoC: Factor out ...
575
576
  	reg = snd_soc_read(codec, WM8940_POWER1);
  	snd_soc_write(codec, WM8940_POWER1, reg & 0x1df);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
577
578
579
  
  	if (freq_in == 0 || freq_out == 0) {
  		/* Clock CODEC directly from MCLK */
8d50e447d   Mark Brown   ASoC: Factor out ...
580
581
  		reg = snd_soc_read(codec, WM8940_CLOCK);
  		snd_soc_write(codec, WM8940_CLOCK, reg & 0x0ff);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
582
  		/* Pll power down */
8d50e447d   Mark Brown   ASoC: Factor out ...
583
  		snd_soc_write(codec, WM8940_PLLN, (1 << 7));
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
584
585
586
587
588
589
  		return 0;
  	}
  
  	/* Pll is followed by a frequency divide by 4 */
  	pll_factors(freq_out*4, freq_in);
  	if (pll_div.k)
8d50e447d   Mark Brown   ASoC: Factor out ...
590
  		snd_soc_write(codec, WM8940_PLLN,
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
591
592
  			     (pll_div.pre_scale << 4) | pll_div.n | (1 << 6));
  	else /* No factional component */
8d50e447d   Mark Brown   ASoC: Factor out ...
593
  		snd_soc_write(codec, WM8940_PLLN,
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
594
  			     (pll_div.pre_scale << 4) | pll_div.n);
8d50e447d   Mark Brown   ASoC: Factor out ...
595
596
597
  	snd_soc_write(codec, WM8940_PLLK1, pll_div.k >> 18);
  	snd_soc_write(codec, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff);
  	snd_soc_write(codec, WM8940_PLLK3, pll_div.k & 0x1ff);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
598
  	/* Enable the PLL */
8d50e447d   Mark Brown   ASoC: Factor out ...
599
600
  	reg = snd_soc_read(codec, WM8940_POWER1);
  	snd_soc_write(codec, WM8940_POWER1, reg | 0x020);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
601
602
  
  	/* Run CODEC from PLL instead of MCLK */
8d50e447d   Mark Brown   ASoC: Factor out ...
603
604
  	reg = snd_soc_read(codec, WM8940_CLOCK);
  	snd_soc_write(codec, WM8940_CLOCK, reg | 0x100);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
605
606
607
608
609
610
611
612
  
  	return 0;
  }
  
  static int wm8940_set_dai_sysclk(struct snd_soc_dai *codec_dai,
  				 int clk_id, unsigned int freq, int dir)
  {
  	struct snd_soc_codec *codec = codec_dai->codec;
b2c812e22   Mark Brown   ASoC: Add indirec...
613
  	struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
  
  	switch (freq) {
  	case 11289600:
  	case 12000000:
  	case 12288000:
  	case 16934400:
  	case 18432000:
  		wm8940->sysclk = freq;
  		return 0;
  	}
  	return -EINVAL;
  }
  
  static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
  				 int div_id, int div)
  {
  	struct snd_soc_codec *codec = codec_dai->codec;
  	u16 reg;
  	int ret = 0;
  
  	switch (div_id) {
  	case WM8940_BCLKDIV:
b272cc769   Axel Lin   ASoC: wm8940: Fix...
636
  		reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFFE3;
8d50e447d   Mark Brown   ASoC: Factor out ...
637
  		ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 2));
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
638
639
  		break;
  	case WM8940_MCLKDIV:
8d50e447d   Mark Brown   ASoC: Factor out ...
640
641
  		reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFF1F;
  		ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 5));
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
642
643
  		break;
  	case WM8940_OPCLKDIV:
49fa4d9b5   Axel Lin   ASoC: wm8940: Fix...
644
645
  		reg = snd_soc_read(codec, WM8940_GPIO) & 0xFFCF;
  		ret = snd_soc_write(codec, WM8940_GPIO, reg | (div << 4));
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
646
647
648
649
650
651
652
653
654
655
656
657
  		break;
  	}
  	return ret;
  }
  
  #define WM8940_RATES SNDRV_PCM_RATE_8000_48000
  
  #define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 |				\
  			SNDRV_PCM_FMTBIT_S16_LE |			\
  			SNDRV_PCM_FMTBIT_S20_3LE |			\
  			SNDRV_PCM_FMTBIT_S24_LE |			\
  			SNDRV_PCM_FMTBIT_S32_LE)
85e7652d8   Lars-Peter Clausen   ASoC: Constify sn...
658
  static const struct snd_soc_dai_ops wm8940_dai_ops = {
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
659
660
661
662
663
664
665
  	.hw_params = wm8940_i2s_hw_params,
  	.set_sysclk = wm8940_set_dai_sysclk,
  	.digital_mute = wm8940_mute,
  	.set_fmt = wm8940_set_dai_fmt,
  	.set_clkdiv = wm8940_set_dai_clkdiv,
  	.set_pll = wm8940_set_dai_pll,
  };
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
666
667
  static struct snd_soc_dai_driver wm8940_dai = {
  	.name = "wm8940-hifi",
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
  	.playback = {
  		.stream_name = "Playback",
  		.channels_min = 1,
  		.channels_max = 2,
  		.rates = WM8940_RATES,
  		.formats = WM8940_FORMATS,
  	},
  	.capture = {
  		.stream_name = "Capture",
  		.channels_min = 1,
  		.channels_max = 2,
  		.rates = WM8940_RATES,
  		.formats = WM8940_FORMATS,
  	},
  	.ops = &wm8940_dai_ops,
  	.symmetric_rates = 1,
  };
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
685

f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
686
  static int wm8940_probe(struct snd_soc_codec *codec)
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
687
  {
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
688
  	struct wm8940_setup_data *pdata = codec->dev->platform_data;
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
689
690
  	int ret;
  	u16 reg;
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
691

0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
692
693
694
695
696
697
  	ret = wm8940_reset(codec);
  	if (ret < 0) {
  		dev_err(codec->dev, "Failed to issue reset
  ");
  		return ret;
  	}
bd1204cb5   Lars-Peter Clausen   ASoC: Route all b...
698
  	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
699

8d50e447d   Mark Brown   ASoC: Factor out ...
700
  	ret = snd_soc_write(codec, WM8940_POWER1, 0x180);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
701
702
703
704
705
706
707
  	if (ret < 0)
  		return ret;
  
  	if (!pdata)
  		dev_warn(codec->dev, "No platform data supplied
  ");
  	else {
8d50e447d   Mark Brown   ASoC: Factor out ...
708
709
  		reg = snd_soc_read(codec, WM8940_OUTPUTCTL);
  		ret = snd_soc_write(codec, WM8940_OUTPUTCTL, reg | pdata->vroi);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
710
711
712
  		if (ret < 0)
  			return ret;
  	}
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
713
  	return ret;
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
714
  }
f802d6c02   Julia Lawall   ASoC: constify sn...
715
  static const struct snd_soc_codec_driver soc_codec_dev_wm8940 = {
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
716
  	.probe =	wm8940_probe,
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
717
  	.set_bias_level = wm8940_set_bias_level,
5fdf082b4   Lars-Peter Clausen   ASoC: wm8940: Cle...
718
  	.suspend_bias_off = true,
90a60b06f   Kuninori Morimoto   ASoC: codec dupli...
719
720
721
722
723
724
725
726
  	.component_driver = {
  		.controls		= wm8940_snd_controls,
  		.num_controls		= ARRAY_SIZE(wm8940_snd_controls),
  		.dapm_widgets		= wm8940_dapm_widgets,
  		.num_dapm_widgets	= ARRAY_SIZE(wm8940_dapm_widgets),
  		.dapm_routes		= wm8940_dapm_routes,
  		.num_dapm_routes	= ARRAY_SIZE(wm8940_dapm_routes),
  	},
fbbf7fea8   Mark Brown   ASoC: wm8940: Con...
727
728
729
730
731
732
733
734
735
  };
  
  static const struct regmap_config wm8940_regmap = {
  	.reg_bits = 8,
  	.val_bits = 16,
  
  	.max_register = WM8940_MONOMIX,
  	.reg_defaults = wm8940_reg_defaults,
  	.num_reg_defaults = ARRAY_SIZE(wm8940_reg_defaults),
50c7a0ef2   Geert Uytterhoeven   ASoC: wm8940: Ena...
736
  	.cache_type = REGCACHE_RBTREE,
fbbf7fea8   Mark Brown   ASoC: wm8940: Con...
737
738
739
  
  	.readable_reg = wm8940_readable_register,
  	.volatile_reg = wm8940_volatile_register,
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
740
  };
7a79e94e9   Bill Pemberton   ASoC: codecs: rem...
741
742
  static int wm8940_i2c_probe(struct i2c_client *i2c,
  			    const struct i2c_device_id *id)
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
743
744
  {
  	struct wm8940_priv *wm8940;
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
745
  	int ret;
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
746

42dad0d84   Mark Brown   ASoC: Convert WM8...
747
748
  	wm8940 = devm_kzalloc(&i2c->dev, sizeof(struct wm8940_priv),
  			      GFP_KERNEL);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
749
750
  	if (wm8940 == NULL)
  		return -ENOMEM;
fbbf7fea8   Mark Brown   ASoC: wm8940: Con...
751
752
753
  	wm8940->regmap = devm_regmap_init_i2c(i2c, &wm8940_regmap);
  	if (IS_ERR(wm8940->regmap))
  		return PTR_ERR(wm8940->regmap);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
754
  	i2c_set_clientdata(i2c, wm8940);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
755

f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
756
757
  	ret = snd_soc_register_codec(&i2c->dev,
  			&soc_codec_dev_wm8940, &wm8940_dai, 1);
42dad0d84   Mark Brown   ASoC: Convert WM8...
758

db1e18de9   Axel Lin   ASoC: wm8940: fix...
759
  	return ret;
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
760
  }
7a79e94e9   Bill Pemberton   ASoC: codecs: rem...
761
  static int wm8940_i2c_remove(struct i2c_client *client)
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
762
  {
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
763
  	snd_soc_unregister_codec(&client->dev);
42dad0d84   Mark Brown   ASoC: Convert WM8...
764

0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
765
766
767
768
769
770
771
772
773
774
775
  	return 0;
  }
  
  static const struct i2c_device_id wm8940_i2c_id[] = {
  	{ "wm8940", 0 },
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id);
  
  static struct i2c_driver wm8940_i2c_driver = {
  	.driver = {
091edccf7   Mark Brown   ASoC: Remove unus...
776
  		.name = "wm8940",
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
777
  	},
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
778
  	.probe =    wm8940_i2c_probe,
7a79e94e9   Bill Pemberton   ASoC: codecs: rem...
779
  	.remove =   wm8940_i2c_remove,
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
780
781
  	.id_table = wm8940_i2c_id,
  };
794836b95   Sachin Kamat   ASoC: wm8940: Use...
782
  module_i2c_driver(wm8940_i2c_driver);
0b5e92c5e   Jonathan Cameron   ASoC WM8940 Driver
783
784
785
786
  
  MODULE_DESCRIPTION("ASoC WM8940 driver");
  MODULE_AUTHOR("Jonathan Cameron");
  MODULE_LICENSE("GPL");