• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/sound/soc/codecs/
1/*
2 * ALSA SoC TWL6040 codec driver
3 *
4 * Author:	 Misael Lopez Cruz <x0052729@ti.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA
19 *
20 */
21
22#include <linux/module.h>
23#include <linux/moduleparam.h>
24#include <linux/init.h>
25#include <linux/delay.h>
26#include <linux/pm.h>
27#include <linux/i2c.h>
28#include <linux/gpio.h>
29#include <linux/platform_device.h>
30#include <linux/slab.h>
31#include <linux/i2c/twl.h>
32
33#include <sound/core.h>
34#include <sound/pcm.h>
35#include <sound/pcm_params.h>
36#include <sound/soc.h>
37#include <sound/soc-dapm.h>
38#include <sound/initval.h>
39#include <sound/tlv.h>
40
41#include "twl6040.h"
42
43#define TWL6040_RATES	 (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
44#define TWL6040_FORMATS	 (SNDRV_PCM_FMTBIT_S32_LE)
45
46/* codec private data */
47struct twl6040_data {
48	struct snd_soc_codec codec;
49	int audpwron;
50	int naudint;
51	int codec_powered;
52	int pll;
53	int non_lp;
54	unsigned int sysclk;
55	struct snd_pcm_hw_constraint_list *sysclk_constraints;
56	struct completion ready;
57};
58
59/*
60 * twl6040 register cache & default register settings
61 */
62static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = {
63	0x00, /* not used		0x00	*/
64	0x4B, /* TWL6040_ASICID (ro)	0x01	*/
65	0x00, /* TWL6040_ASICREV (ro)	0x02	*/
66	0x00, /* TWL6040_INTID		0x03	*/
67	0x00, /* TWL6040_INTMR		0x04	*/
68	0x00, /* TWL6040_NCPCTRL	0x05	*/
69	0x00, /* TWL6040_LDOCTL		0x06	*/
70	0x60, /* TWL6040_HPPLLCTL	0x07	*/
71	0x00, /* TWL6040_LPPLLCTL	0x08	*/
72	0x4A, /* TWL6040_LPPLLDIV	0x09	*/
73	0x00, /* TWL6040_AMICBCTL	0x0A	*/
74	0x00, /* TWL6040_DMICBCTL	0x0B	*/
75	0x18, /* TWL6040_MICLCTL	0x0C	- No input selected on Left Mic */
76	0x18, /* TWL6040_MICRCTL	0x0D	- No input selected on Right Mic */
77	0x00, /* TWL6040_MICGAIN	0x0E	*/
78	0x1B, /* TWL6040_LINEGAIN	0x0F	*/
79	0x00, /* TWL6040_HSLCTL		0x10	*/
80	0x00, /* TWL6040_HSRCTL		0x11	*/
81	0x00, /* TWL6040_HSGAIN		0x12	*/
82	0x00, /* TWL6040_EARCTL		0x13	*/
83	0x00, /* TWL6040_HFLCTL		0x14	*/
84	0x00, /* TWL6040_HFLGAIN	0x15	*/
85	0x00, /* TWL6040_HFRCTL		0x16	*/
86	0x00, /* TWL6040_HFRGAIN	0x17	*/
87	0x00, /* TWL6040_VIBCTLL	0x18	*/
88	0x00, /* TWL6040_VIBDATL	0x19	*/
89	0x00, /* TWL6040_VIBCTLR	0x1A	*/
90	0x00, /* TWL6040_VIBDATR	0x1B	*/
91	0x00, /* TWL6040_HKCTL1		0x1C	*/
92	0x00, /* TWL6040_HKCTL2		0x1D	*/
93	0x00, /* TWL6040_GPOCTL		0x1E	*/
94	0x00, /* TWL6040_ALB		0x1F	*/
95	0x00, /* TWL6040_DLB		0x20	*/
96	0x00, /* not used		0x21	*/
97	0x00, /* not used		0x22	*/
98	0x00, /* not used		0x23	*/
99	0x00, /* not used		0x24	*/
100	0x00, /* not used		0x25	*/
101	0x00, /* not used		0x26	*/
102	0x00, /* not used		0x27	*/
103	0x00, /* TWL6040_TRIM1		0x28	*/
104	0x00, /* TWL6040_TRIM2		0x29	*/
105	0x00, /* TWL6040_TRIM3		0x2A	*/
106	0x00, /* TWL6040_HSOTRIM	0x2B	*/
107	0x00, /* TWL6040_HFOTRIM	0x2C	*/
108	0x09, /* TWL6040_ACCCTL		0x2D	*/
109	0x00, /* TWL6040_STATUS (ro)	0x2E	*/
110};
111
112/*
113 * twl6040 vio/gnd registers:
114 * registers under vio/gnd supply can be accessed
115 * before the power-up sequence, after NRESPWRON goes high
116 */
117static const int twl6040_vio_reg[TWL6040_VIOREGNUM] = {
118	TWL6040_REG_ASICID,
119	TWL6040_REG_ASICREV,
120	TWL6040_REG_INTID,
121	TWL6040_REG_INTMR,
122	TWL6040_REG_NCPCTL,
123	TWL6040_REG_LDOCTL,
124	TWL6040_REG_AMICBCTL,
125	TWL6040_REG_DMICBCTL,
126	TWL6040_REG_HKCTL1,
127	TWL6040_REG_HKCTL2,
128	TWL6040_REG_GPOCTL,
129	TWL6040_REG_TRIM1,
130	TWL6040_REG_TRIM2,
131	TWL6040_REG_TRIM3,
132	TWL6040_REG_HSOTRIM,
133	TWL6040_REG_HFOTRIM,
134	TWL6040_REG_ACCCTL,
135	TWL6040_REG_STATUS,
136};
137
138/*
139 * twl6040 vdd/vss registers:
140 * registers under vdd/vss supplies can only be accessed
141 * after the power-up sequence
142 */
143static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = {
144	TWL6040_REG_HPPLLCTL,
145	TWL6040_REG_LPPLLCTL,
146	TWL6040_REG_LPPLLDIV,
147	TWL6040_REG_MICLCTL,
148	TWL6040_REG_MICRCTL,
149	TWL6040_REG_MICGAIN,
150	TWL6040_REG_LINEGAIN,
151	TWL6040_REG_HSLCTL,
152	TWL6040_REG_HSRCTL,
153	TWL6040_REG_HSGAIN,
154	TWL6040_REG_EARCTL,
155	TWL6040_REG_HFLCTL,
156	TWL6040_REG_HFLGAIN,
157	TWL6040_REG_HFRCTL,
158	TWL6040_REG_HFRGAIN,
159	TWL6040_REG_VIBCTLL,
160	TWL6040_REG_VIBDATL,
161	TWL6040_REG_VIBCTLR,
162	TWL6040_REG_VIBDATR,
163	TWL6040_REG_ALB,
164	TWL6040_REG_DLB,
165};
166
167/*
168 * read twl6040 register cache
169 */
170static inline unsigned int twl6040_read_reg_cache(struct snd_soc_codec *codec,
171						unsigned int reg)
172{
173	u8 *cache = codec->reg_cache;
174
175	if (reg >= TWL6040_CACHEREGNUM)
176		return -EIO;
177
178	return cache[reg];
179}
180
181/*
182 * write twl6040 register cache
183 */
184static inline void twl6040_write_reg_cache(struct snd_soc_codec *codec,
185						u8 reg, u8 value)
186{
187	u8 *cache = codec->reg_cache;
188
189	if (reg >= TWL6040_CACHEREGNUM)
190		return;
191	cache[reg] = value;
192}
193
194/*
195 * read from twl6040 hardware register
196 */
197static int twl6040_read_reg_volatile(struct snd_soc_codec *codec,
198			unsigned int reg)
199{
200	u8 value;
201
202	if (reg >= TWL6040_CACHEREGNUM)
203		return -EIO;
204
205	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &value, reg);
206	twl6040_write_reg_cache(codec, reg, value);
207
208	return value;
209}
210
211/*
212 * write to the twl6040 register space
213 */
214static int twl6040_write(struct snd_soc_codec *codec,
215			unsigned int reg, unsigned int value)
216{
217	if (reg >= TWL6040_CACHEREGNUM)
218		return -EIO;
219
220	twl6040_write_reg_cache(codec, reg, value);
221	return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
222}
223
224static void twl6040_init_vio_regs(struct snd_soc_codec *codec)
225{
226	u8 *cache = codec->reg_cache;
227	int reg, i;
228
229	/* allow registers to be accessed by i2c */
230	twl6040_write(codec, TWL6040_REG_ACCCTL, cache[TWL6040_REG_ACCCTL]);
231
232	for (i = 0; i < TWL6040_VIOREGNUM; i++) {
233		reg = twl6040_vio_reg[i];
234		/* skip read-only registers (ASICID, ASICREV, STATUS) */
235		switch (reg) {
236		case TWL6040_REG_ASICID:
237		case TWL6040_REG_ASICREV:
238		case TWL6040_REG_STATUS:
239			continue;
240		default:
241			break;
242		}
243		twl6040_write(codec, reg, cache[reg]);
244	}
245}
246
247static void twl6040_init_vdd_regs(struct snd_soc_codec *codec)
248{
249	u8 *cache = codec->reg_cache;
250	int reg, i;
251
252	for (i = 0; i < TWL6040_VDDREGNUM; i++) {
253		reg = twl6040_vdd_reg[i];
254		twl6040_write(codec, reg, cache[reg]);
255	}
256}
257
258/* twl6040 codec manual power-up sequence */
259static void twl6040_power_up(struct snd_soc_codec *codec)
260{
261	u8 ncpctl, ldoctl, lppllctl, accctl;
262
263	ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL);
264	ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL);
265	lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
266	accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL);
267
268	/* enable reference system */
269	ldoctl |= TWL6040_REFENA;
270	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
271	msleep(10);
272	/* enable internal oscillator */
273	ldoctl |= TWL6040_OSCENA;
274	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
275	udelay(10);
276	/* enable high-side ldo */
277	ldoctl |= TWL6040_HSLDOENA;
278	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
279	udelay(244);
280	/* enable negative charge pump */
281	ncpctl |= TWL6040_NCPENA | TWL6040_NCPOPEN;
282	twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl);
283	udelay(488);
284	/* enable low-side ldo */
285	ldoctl |= TWL6040_LSLDOENA;
286	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
287	udelay(244);
288	/* enable low-power pll */
289	lppllctl |= TWL6040_LPLLENA;
290	twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
291	/* reset state machine */
292	accctl |= TWL6040_RESETSPLIT;
293	twl6040_write(codec, TWL6040_REG_ACCCTL, accctl);
294	mdelay(5);
295	accctl &= ~TWL6040_RESETSPLIT;
296	twl6040_write(codec, TWL6040_REG_ACCCTL, accctl);
297	/* disable internal oscillator */
298	ldoctl &= ~TWL6040_OSCENA;
299	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
300}
301
302/* twl6040 codec manual power-down sequence */
303static void twl6040_power_down(struct snd_soc_codec *codec)
304{
305	u8 ncpctl, ldoctl, lppllctl, accctl;
306
307	ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL);
308	ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL);
309	lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
310	accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL);
311
312	/* enable internal oscillator */
313	ldoctl |= TWL6040_OSCENA;
314	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
315	udelay(10);
316	/* disable low-power pll */
317	lppllctl &= ~TWL6040_LPLLENA;
318	twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
319	/* disable low-side ldo */
320	ldoctl &= ~TWL6040_LSLDOENA;
321	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
322	udelay(244);
323	/* disable negative charge pump */
324	ncpctl &= ~(TWL6040_NCPENA | TWL6040_NCPOPEN);
325	twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl);
326	udelay(488);
327	/* disable high-side ldo */
328	ldoctl &= ~TWL6040_HSLDOENA;
329	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
330	udelay(244);
331	/* disable internal oscillator */
332	ldoctl &= ~TWL6040_OSCENA;
333	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
334	/* disable reference system */
335	ldoctl &= ~TWL6040_REFENA;
336	twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
337	msleep(10);
338}
339
340/* set headset dac and driver power mode */
341static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
342{
343	int hslctl, hsrctl;
344	int mask = TWL6040_HSDRVMODEL | TWL6040_HSDACMODEL;
345
346	hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL);
347	hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL);
348
349	if (high_perf) {
350		hslctl &= ~mask;
351		hsrctl &= ~mask;
352	} else {
353		hslctl |= mask;
354		hsrctl |= mask;
355	}
356
357	twl6040_write(codec, TWL6040_REG_HSLCTL, hslctl);
358	twl6040_write(codec, TWL6040_REG_HSRCTL, hsrctl);
359
360	return 0;
361}
362
363static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w,
364			struct snd_kcontrol *kcontrol, int event)
365{
366	msleep(1);
367	return 0;
368}
369
370static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w,
371			struct snd_kcontrol *kcontrol, int event)
372{
373	struct snd_soc_codec *codec = w->codec;
374	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
375
376	if (SND_SOC_DAPM_EVENT_ON(event))
377		priv->non_lp++;
378	else
379		priv->non_lp--;
380
381	msleep(1);
382
383	return 0;
384}
385
386/* audio interrupt handler */
387static irqreturn_t twl6040_naudint_handler(int irq, void *data)
388{
389	struct snd_soc_codec *codec = data;
390	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
391	u8 intid;
392
393	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID);
394
395	switch (intid) {
396	case TWL6040_THINT:
397		dev_alert(codec->dev, "die temp over-limit detection\n");
398		break;
399	case TWL6040_PLUGINT:
400	case TWL6040_UNPLUGINT:
401	case TWL6040_HOOKINT:
402		break;
403	case TWL6040_HFINT:
404		dev_alert(codec->dev, "hf drivers over current detection\n");
405		break;
406	case TWL6040_VIBINT:
407		dev_alert(codec->dev, "vib drivers over current detection\n");
408		break;
409	case TWL6040_READYINT:
410		complete(&priv->ready);
411		break;
412	default:
413		dev_err(codec->dev, "unknown audio interrupt %d\n", intid);
414		break;
415	}
416
417	return IRQ_HANDLED;
418}
419
420/*
421 * MICATT volume control:
422 * from -6 to 0 dB in 6 dB steps
423 */
424static DECLARE_TLV_DB_SCALE(mic_preamp_tlv, -600, 600, 0);
425
426/*
427 * MICGAIN volume control:
428 * from 6 to 30 dB in 6 dB steps
429 */
430static DECLARE_TLV_DB_SCALE(mic_amp_tlv, 600, 600, 0);
431
432/*
433 * HSGAIN volume control:
434 * from -30 to 0 dB in 2 dB steps
435 */
436static DECLARE_TLV_DB_SCALE(hs_tlv, -3000, 200, 0);
437
438/*
439 * HFGAIN volume control:
440 * from -52 to 6 dB in 2 dB steps
441 */
442static DECLARE_TLV_DB_SCALE(hf_tlv, -5200, 200, 0);
443
444/*
445 * EPGAIN volume control:
446 * from -24 to 6 dB in 2 dB steps
447 */
448static DECLARE_TLV_DB_SCALE(ep_tlv, -2400, 200, 0);
449
450/* Left analog microphone selection */
451static const char *twl6040_amicl_texts[] =
452	{"Headset Mic", "Main Mic", "Aux/FM Left", "Off"};
453
454/* Right analog microphone selection */
455static const char *twl6040_amicr_texts[] =
456	{"Headset Mic", "Sub Mic", "Aux/FM Right", "Off"};
457
458static const struct soc_enum twl6040_enum[] = {
459	SOC_ENUM_SINGLE(TWL6040_REG_MICLCTL, 3, 3, twl6040_amicl_texts),
460	SOC_ENUM_SINGLE(TWL6040_REG_MICRCTL, 3, 3, twl6040_amicr_texts),
461};
462
463static const struct snd_kcontrol_new amicl_control =
464	SOC_DAPM_ENUM("Route", twl6040_enum[0]);
465
466static const struct snd_kcontrol_new amicr_control =
467	SOC_DAPM_ENUM("Route", twl6040_enum[1]);
468
469/* Headset DAC playback switches */
470static const struct snd_kcontrol_new hsdacl_switch_controls =
471	SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSLCTL, 5, 1, 0);
472
473static const struct snd_kcontrol_new hsdacr_switch_controls =
474	SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSRCTL, 5, 1, 0);
475
476/* Handsfree DAC playback switches */
477static const struct snd_kcontrol_new hfdacl_switch_controls =
478	SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 2, 1, 0);
479
480static const struct snd_kcontrol_new hfdacr_switch_controls =
481	SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 2, 1, 0);
482
483static const struct snd_kcontrol_new ep_driver_switch_controls =
484	SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0);
485
486static const struct snd_kcontrol_new twl6040_snd_controls[] = {
487	/* Capture gains */
488	SOC_DOUBLE_TLV("Capture Preamplifier Volume",
489		TWL6040_REG_MICGAIN, 6, 7, 1, 1, mic_preamp_tlv),
490	SOC_DOUBLE_TLV("Capture Volume",
491		TWL6040_REG_MICGAIN, 0, 3, 4, 0, mic_amp_tlv),
492
493	/* Playback gains */
494	SOC_DOUBLE_TLV("Headset Playback Volume",
495		TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, hs_tlv),
496	SOC_DOUBLE_R_TLV("Handsfree Playback Volume",
497		TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv),
498	SOC_SINGLE_TLV("Earphone Playback Volume",
499		TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv),
500};
501
502static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
503	/* Inputs */
504	SND_SOC_DAPM_INPUT("MAINMIC"),
505	SND_SOC_DAPM_INPUT("HSMIC"),
506	SND_SOC_DAPM_INPUT("SUBMIC"),
507	SND_SOC_DAPM_INPUT("AFML"),
508	SND_SOC_DAPM_INPUT("AFMR"),
509
510	/* Outputs */
511	SND_SOC_DAPM_OUTPUT("HSOL"),
512	SND_SOC_DAPM_OUTPUT("HSOR"),
513	SND_SOC_DAPM_OUTPUT("HFL"),
514	SND_SOC_DAPM_OUTPUT("HFR"),
515	SND_SOC_DAPM_OUTPUT("EP"),
516
517	/* Analog input muxes for the capture amplifiers */
518	SND_SOC_DAPM_MUX("Analog Left Capture Route",
519			SND_SOC_NOPM, 0, 0, &amicl_control),
520	SND_SOC_DAPM_MUX("Analog Right Capture Route",
521			SND_SOC_NOPM, 0, 0, &amicr_control),
522
523	/* Analog capture PGAs */
524	SND_SOC_DAPM_PGA("MicAmpL",
525			TWL6040_REG_MICLCTL, 0, 0, NULL, 0),
526	SND_SOC_DAPM_PGA("MicAmpR",
527			TWL6040_REG_MICRCTL, 0, 0, NULL, 0),
528
529	/* ADCs */
530	SND_SOC_DAPM_ADC("ADC Left", "Left Front Capture",
531			TWL6040_REG_MICLCTL, 2, 0),
532	SND_SOC_DAPM_ADC("ADC Right", "Right Front Capture",
533			TWL6040_REG_MICRCTL, 2, 0),
534
535	/* Microphone bias */
536	SND_SOC_DAPM_MICBIAS("Headset Mic Bias",
537			TWL6040_REG_AMICBCTL, 0, 0),
538	SND_SOC_DAPM_MICBIAS("Main Mic Bias",
539			TWL6040_REG_AMICBCTL, 4, 0),
540	SND_SOC_DAPM_MICBIAS("Digital Mic1 Bias",
541			TWL6040_REG_DMICBCTL, 0, 0),
542	SND_SOC_DAPM_MICBIAS("Digital Mic2 Bias",
543			TWL6040_REG_DMICBCTL, 4, 0),
544
545	/* DACs */
546	SND_SOC_DAPM_DAC_E("HSDAC Left", "Headset Playback",
547			TWL6040_REG_HSLCTL, 0, 0,
548			twl6040_hs_dac_event,
549			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
550	SND_SOC_DAPM_DAC_E("HSDAC Right", "Headset Playback",
551			TWL6040_REG_HSRCTL, 0, 0,
552			twl6040_hs_dac_event,
553			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
554	SND_SOC_DAPM_DAC_E("HFDAC Left", "Handsfree Playback",
555			TWL6040_REG_HFLCTL, 0, 0,
556			twl6040_power_mode_event,
557			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
558	SND_SOC_DAPM_DAC_E("HFDAC Right", "Handsfree Playback",
559			TWL6040_REG_HFRCTL, 0, 0,
560			twl6040_power_mode_event,
561			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
562
563	/* Analog playback switches */
564	SND_SOC_DAPM_SWITCH("HSDAC Left Playback",
565			SND_SOC_NOPM, 0, 0, &hsdacl_switch_controls),
566	SND_SOC_DAPM_SWITCH("HSDAC Right Playback",
567			SND_SOC_NOPM, 0, 0, &hsdacr_switch_controls),
568	SND_SOC_DAPM_SWITCH("HFDAC Left Playback",
569			SND_SOC_NOPM, 0, 0, &hfdacl_switch_controls),
570	SND_SOC_DAPM_SWITCH("HFDAC Right Playback",
571			SND_SOC_NOPM, 0, 0, &hfdacr_switch_controls),
572
573	/* Analog playback drivers */
574	SND_SOC_DAPM_PGA_E("Handsfree Left Driver",
575			TWL6040_REG_HFLCTL, 4, 0, NULL, 0,
576			twl6040_power_mode_event,
577			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
578	SND_SOC_DAPM_PGA_E("Handsfree Right Driver",
579			TWL6040_REG_HFRCTL, 4, 0, NULL, 0,
580			twl6040_power_mode_event,
581			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
582	SND_SOC_DAPM_PGA("Headset Left Driver",
583			TWL6040_REG_HSLCTL, 2, 0, NULL, 0),
584	SND_SOC_DAPM_PGA("Headset Right Driver",
585			TWL6040_REG_HSRCTL, 2, 0, NULL, 0),
586	SND_SOC_DAPM_SWITCH_E("Earphone Driver",
587			SND_SOC_NOPM, 0, 0, &ep_driver_switch_controls,
588			twl6040_power_mode_event,
589			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
590
591	/* Analog playback PGAs */
592	SND_SOC_DAPM_PGA("HFDAC Left PGA",
593			TWL6040_REG_HFLCTL, 1, 0, NULL, 0),
594	SND_SOC_DAPM_PGA("HFDAC Right PGA",
595			TWL6040_REG_HFRCTL, 1, 0, NULL, 0),
596
597};
598
599static const struct snd_soc_dapm_route intercon[] = {
600	/* Capture path */
601	{"Analog Left Capture Route", "Headset Mic", "HSMIC"},
602	{"Analog Left Capture Route", "Main Mic", "MAINMIC"},
603	{"Analog Left Capture Route", "Aux/FM Left", "AFML"},
604
605	{"Analog Right Capture Route", "Headset Mic", "HSMIC"},
606	{"Analog Right Capture Route", "Sub Mic", "SUBMIC"},
607	{"Analog Right Capture Route", "Aux/FM Right", "AFMR"},
608
609	{"MicAmpL", NULL, "Analog Left Capture Route"},
610	{"MicAmpR", NULL, "Analog Right Capture Route"},
611
612	{"ADC Left", NULL, "MicAmpL"},
613	{"ADC Right", NULL, "MicAmpR"},
614
615	/* Headset playback path */
616	{"HSDAC Left Playback", "Switch", "HSDAC Left"},
617	{"HSDAC Right Playback", "Switch", "HSDAC Right"},
618
619	{"Headset Left Driver", NULL, "HSDAC Left Playback"},
620	{"Headset Right Driver", NULL, "HSDAC Right Playback"},
621
622	{"HSOL", NULL, "Headset Left Driver"},
623	{"HSOR", NULL, "Headset Right Driver"},
624
625	/* Earphone playback path */
626	{"Earphone Driver", "Switch", "HSDAC Left"},
627	{"EP", NULL, "Earphone Driver"},
628
629	/* Handsfree playback path */
630	{"HFDAC Left Playback", "Switch", "HFDAC Left"},
631	{"HFDAC Right Playback", "Switch", "HFDAC Right"},
632
633	{"HFDAC Left PGA", NULL, "HFDAC Left Playback"},
634	{"HFDAC Right PGA", NULL, "HFDAC Right Playback"},
635
636	{"Handsfree Left Driver", "Switch", "HFDAC Left PGA"},
637	{"Handsfree Right Driver", "Switch", "HFDAC Right PGA"},
638
639	{"HFL", NULL, "Handsfree Left Driver"},
640	{"HFR", NULL, "Handsfree Right Driver"},
641};
642
643static int twl6040_add_widgets(struct snd_soc_codec *codec)
644{
645	snd_soc_dapm_new_controls(codec, twl6040_dapm_widgets,
646				 ARRAY_SIZE(twl6040_dapm_widgets));
647
648	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
649
650	snd_soc_dapm_new_widgets(codec);
651
652	return 0;
653}
654
655static int twl6040_power_up_completion(struct snd_soc_codec *codec,
656					int naudint)
657{
658	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
659	int time_left;
660	u8 intid;
661
662	time_left = wait_for_completion_timeout(&priv->ready,
663				msecs_to_jiffies(48));
664
665	if (!time_left) {
666		twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &intid,
667							TWL6040_REG_INTID);
668		if (!(intid & TWL6040_READYINT)) {
669			dev_err(codec->dev, "timeout waiting for READYINT\n");
670			return -ETIMEDOUT;
671		}
672	}
673
674	priv->codec_powered = 1;
675
676	return 0;
677}
678
679static int twl6040_set_bias_level(struct snd_soc_codec *codec,
680				enum snd_soc_bias_level level)
681{
682	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
683	int audpwron = priv->audpwron;
684	int naudint = priv->naudint;
685	int ret;
686
687	switch (level) {
688	case SND_SOC_BIAS_ON:
689		break;
690	case SND_SOC_BIAS_PREPARE:
691		break;
692	case SND_SOC_BIAS_STANDBY:
693		if (priv->codec_powered)
694			break;
695
696		if (gpio_is_valid(audpwron)) {
697			/* use AUDPWRON line */
698			gpio_set_value(audpwron, 1);
699
700			/* wait for power-up completion */
701			ret = twl6040_power_up_completion(codec, naudint);
702			if (ret)
703				return ret;
704
705			/* sync registers updated during power-up sequence */
706			twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL);
707			twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL);
708			twl6040_read_reg_volatile(codec, TWL6040_REG_LPPLLCTL);
709		} else {
710			/* use manual power-up sequence */
711			twl6040_power_up(codec);
712			priv->codec_powered = 1;
713		}
714
715		/* initialize vdd/vss registers with reg_cache */
716		twl6040_init_vdd_regs(codec);
717		break;
718	case SND_SOC_BIAS_OFF:
719		if (!priv->codec_powered)
720			break;
721
722		if (gpio_is_valid(audpwron)) {
723			/* use AUDPWRON line */
724			gpio_set_value(audpwron, 0);
725
726			/* power-down sequence latency */
727			udelay(500);
728
729			/* sync registers updated during power-down sequence */
730			twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL);
731			twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL);
732			twl6040_write_reg_cache(codec, TWL6040_REG_LPPLLCTL,
733						0x00);
734		} else {
735			/* use manual power-down sequence */
736			twl6040_power_down(codec);
737		}
738
739		priv->codec_powered = 0;
740		break;
741	}
742
743	codec->bias_level = level;
744
745	return 0;
746}
747
748/* set of rates for each pll: low-power and high-performance */
749
750static unsigned int lp_rates[] = {
751	88200,
752	96000,
753};
754
755static struct snd_pcm_hw_constraint_list lp_constraints = {
756	.count	= ARRAY_SIZE(lp_rates),
757	.list	= lp_rates,
758};
759
760static unsigned int hp_rates[] = {
761	96000,
762};
763
764static struct snd_pcm_hw_constraint_list hp_constraints = {
765	.count	= ARRAY_SIZE(hp_rates),
766	.list	= hp_rates,
767};
768
769static int twl6040_startup(struct snd_pcm_substream *substream,
770			struct snd_soc_dai *dai)
771{
772	struct snd_soc_pcm_runtime *rtd = substream->private_data;
773	struct snd_soc_device *socdev = rtd->socdev;
774	struct snd_soc_codec *codec = socdev->card->codec;
775	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
776
777	if (!priv->sysclk) {
778		dev_err(codec->dev,
779			"no mclk configured, call set_sysclk() on init\n");
780		return -EINVAL;
781	}
782
783	/*
784	 * capture is not supported at 17.64 MHz,
785	 * it's reserved for headset low-power playback scenario
786	 */
787	if ((priv->sysclk == 17640000) && substream->stream) {
788		dev_err(codec->dev,
789			"capture mode is not supported at %dHz\n",
790			priv->sysclk);
791		return -EINVAL;
792	}
793
794	snd_pcm_hw_constraint_list(substream->runtime, 0,
795				SNDRV_PCM_HW_PARAM_RATE,
796				priv->sysclk_constraints);
797
798	return 0;
799}
800
801static int twl6040_hw_params(struct snd_pcm_substream *substream,
802			struct snd_pcm_hw_params *params,
803			struct snd_soc_dai *dai)
804{
805	struct snd_soc_pcm_runtime *rtd = substream->private_data;
806	struct snd_soc_device *socdev = rtd->socdev;
807	struct snd_soc_codec *codec = socdev->card->codec;
808	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
809	u8 lppllctl;
810	int rate;
811
812	/* nothing to do for high-perf pll, it supports only 48 kHz */
813	if (priv->pll == TWL6040_HPPLL_ID)
814		return 0;
815
816	lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
817
818	rate = params_rate(params);
819	switch (rate) {
820	case 88200:
821		lppllctl |= TWL6040_LPLLFIN;
822		priv->sysclk = 17640000;
823		break;
824	case 96000:
825		lppllctl &= ~TWL6040_LPLLFIN;
826		priv->sysclk = 19200000;
827		break;
828	default:
829		dev_err(codec->dev, "unsupported rate %d\n", rate);
830		return -EINVAL;
831	}
832
833	twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
834
835	return 0;
836}
837
838static int twl6040_trigger(struct snd_pcm_substream *substream,
839			int cmd, struct snd_soc_dai *dai)
840{
841	struct snd_soc_pcm_runtime *rtd = substream->private_data;
842	struct snd_soc_device *socdev = rtd->socdev;
843	struct snd_soc_codec *codec = socdev->card->codec;
844	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
845
846	switch (cmd) {
847	case SNDRV_PCM_TRIGGER_START:
848	case SNDRV_PCM_TRIGGER_RESUME:
849		/*
850		 * low-power playback mode is restricted
851		 * for headset path only
852		 */
853		if ((priv->sysclk == 17640000) && priv->non_lp) {
854			dev_err(codec->dev,
855				"some enabled paths aren't supported at %dHz\n",
856				priv->sysclk);
857			return -EPERM;
858		}
859		break;
860	default:
861		break;
862	}
863
864	return 0;
865}
866
867static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
868		int clk_id, unsigned int freq, int dir)
869{
870	struct snd_soc_codec *codec = codec_dai->codec;
871	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
872	u8 hppllctl, lppllctl;
873
874	hppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_HPPLLCTL);
875	lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
876
877	switch (clk_id) {
878	case TWL6040_SYSCLK_SEL_LPPLL:
879		switch (freq) {
880		case 32768:
881			/* headset dac and driver must be in low-power mode */
882			headset_power_mode(codec, 0);
883
884			/* clk32k input requires low-power pll */
885			lppllctl |= TWL6040_LPLLENA;
886			twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
887			mdelay(5);
888			lppllctl &= ~TWL6040_HPLLSEL;
889			twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
890			hppllctl &= ~TWL6040_HPLLENA;
891			twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl);
892			break;
893		default:
894			dev_err(codec->dev, "unknown mclk freq %d\n", freq);
895			return -EINVAL;
896		}
897
898		/* lppll divider */
899		switch (priv->sysclk) {
900		case 17640000:
901			lppllctl |= TWL6040_LPLLFIN;
902			break;
903		case 19200000:
904			lppllctl &= ~TWL6040_LPLLFIN;
905			break;
906		default:
907			/* sysclk not yet configured */
908			lppllctl &= ~TWL6040_LPLLFIN;
909			priv->sysclk = 19200000;
910			break;
911		}
912
913		twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
914
915		priv->pll = TWL6040_LPPLL_ID;
916		priv->sysclk_constraints = &lp_constraints;
917		break;
918	case TWL6040_SYSCLK_SEL_HPPLL:
919		hppllctl &= ~TWL6040_MCLK_MSK;
920
921		switch (freq) {
922		case 12000000:
923			/* mclk input, pll enabled */
924			hppllctl |= TWL6040_MCLK_12000KHZ |
925				    TWL6040_HPLLSQRBP |
926				    TWL6040_HPLLENA;
927			break;
928		case 19200000:
929			/* mclk input, pll disabled */
930			hppllctl |= TWL6040_MCLK_19200KHZ |
931				    TWL6040_HPLLSQRENA |
932				    TWL6040_HPLLBP;
933			break;
934		case 26000000:
935			/* mclk input, pll enabled */
936			hppllctl |= TWL6040_MCLK_26000KHZ |
937				    TWL6040_HPLLSQRBP |
938				    TWL6040_HPLLENA;
939			break;
940		case 38400000:
941			/* clk slicer, pll disabled */
942			hppllctl |= TWL6040_MCLK_38400KHZ |
943				    TWL6040_HPLLSQRENA |
944				    TWL6040_HPLLBP;
945			break;
946		default:
947			dev_err(codec->dev, "unknown mclk freq %d\n", freq);
948			return -EINVAL;
949		}
950
951		/* headset dac and driver must be in high-performance mode */
952		headset_power_mode(codec, 1);
953
954		twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl);
955		udelay(500);
956		lppllctl |= TWL6040_HPLLSEL;
957		twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
958		lppllctl &= ~TWL6040_LPLLENA;
959		twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
960
961		/* high-performance pll can provide only 19.2 MHz */
962		priv->pll = TWL6040_HPPLL_ID;
963		priv->sysclk = 19200000;
964		priv->sysclk_constraints = &hp_constraints;
965		break;
966	default:
967		dev_err(codec->dev, "unknown clk_id %d\n", clk_id);
968		return -EINVAL;
969	}
970
971	return 0;
972}
973
974static struct snd_soc_dai_ops twl6040_dai_ops = {
975	.startup	= twl6040_startup,
976	.hw_params	= twl6040_hw_params,
977	.trigger	= twl6040_trigger,
978	.set_sysclk	= twl6040_set_dai_sysclk,
979};
980
981struct snd_soc_dai twl6040_dai = {
982	.name = "twl6040",
983	.playback = {
984		.stream_name = "Playback",
985		.channels_min = 1,
986		.channels_max = 4,
987		.rates = TWL6040_RATES,
988		.formats = TWL6040_FORMATS,
989	},
990	.capture = {
991		.stream_name = "Capture",
992		.channels_min = 1,
993		.channels_max = 2,
994		.rates = TWL6040_RATES,
995		.formats = TWL6040_FORMATS,
996	},
997	.ops = &twl6040_dai_ops,
998};
999EXPORT_SYMBOL_GPL(twl6040_dai);
1000
1001#ifdef CONFIG_PM
1002static int twl6040_suspend(struct platform_device *pdev, pm_message_t state)
1003{
1004	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1005	struct snd_soc_codec *codec = socdev->card->codec;
1006
1007	twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
1008
1009	return 0;
1010}
1011
1012static int twl6040_resume(struct platform_device *pdev)
1013{
1014	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1015	struct snd_soc_codec *codec = socdev->card->codec;
1016
1017	twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
1018
1019	return 0;
1020}
1021#else
1022#define twl6040_suspend NULL
1023#define twl6040_resume NULL
1024#endif
1025
1026static struct snd_soc_codec *twl6040_codec;
1027
1028static int twl6040_probe(struct platform_device *pdev)
1029{
1030	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1031	struct snd_soc_codec *codec;
1032	int ret = 0;
1033
1034	BUG_ON(!twl6040_codec);
1035
1036	codec = twl6040_codec;
1037	socdev->card->codec = codec;
1038
1039	/* register pcms */
1040	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
1041	if (ret < 0) {
1042		dev_err(&pdev->dev, "failed to create pcms\n");
1043		return ret;
1044	}
1045
1046	snd_soc_add_controls(codec, twl6040_snd_controls,
1047				ARRAY_SIZE(twl6040_snd_controls));
1048	twl6040_add_widgets(codec);
1049
1050	if (ret < 0) {
1051		dev_err(&pdev->dev, "failed to register card\n");
1052		goto card_err;
1053	}
1054
1055	return ret;
1056
1057card_err:
1058	snd_soc_free_pcms(socdev);
1059	snd_soc_dapm_free(socdev);
1060	return ret;
1061}
1062
1063static int twl6040_remove(struct platform_device *pdev)
1064{
1065	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1066	struct snd_soc_codec *codec = socdev->card->codec;
1067
1068	twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
1069	snd_soc_free_pcms(socdev);
1070	snd_soc_dapm_free(socdev);
1071	kfree(codec);
1072
1073	return 0;
1074}
1075
1076struct snd_soc_codec_device soc_codec_dev_twl6040 = {
1077	.probe = twl6040_probe,
1078	.remove = twl6040_remove,
1079	.suspend = twl6040_suspend,
1080	.resume = twl6040_resume,
1081};
1082EXPORT_SYMBOL_GPL(soc_codec_dev_twl6040);
1083
1084static int __devinit twl6040_codec_probe(struct platform_device *pdev)
1085{
1086	struct twl4030_codec_data *twl_codec = pdev->dev.platform_data;
1087	struct snd_soc_codec *codec;
1088	struct twl6040_data *priv;
1089	int audpwron, naudint;
1090	int ret = 0;
1091
1092	priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL);
1093	if (priv == NULL)
1094		return -ENOMEM;
1095
1096	if (twl_codec) {
1097		audpwron = twl_codec->audpwron_gpio;
1098		naudint = twl_codec->naudint_irq;
1099	} else {
1100		audpwron = -EINVAL;
1101		naudint = 0;
1102	}
1103
1104	priv->audpwron = audpwron;
1105	priv->naudint = naudint;
1106
1107	codec = &priv->codec;
1108	codec->dev = &pdev->dev;
1109	twl6040_dai.dev = &pdev->dev;
1110
1111	codec->name = "twl6040";
1112	codec->owner = THIS_MODULE;
1113	codec->read = twl6040_read_reg_cache;
1114	codec->write = twl6040_write;
1115	codec->set_bias_level = twl6040_set_bias_level;
1116	snd_soc_codec_set_drvdata(codec, priv);
1117	codec->dai = &twl6040_dai;
1118	codec->num_dai = 1;
1119	codec->reg_cache_size = ARRAY_SIZE(twl6040_reg);
1120	codec->reg_cache = kmemdup(twl6040_reg, sizeof(twl6040_reg),
1121					GFP_KERNEL);
1122	if (codec->reg_cache == NULL) {
1123		ret = -ENOMEM;
1124		goto cache_err;
1125	}
1126
1127	mutex_init(&codec->mutex);
1128	INIT_LIST_HEAD(&codec->dapm_widgets);
1129	INIT_LIST_HEAD(&codec->dapm_paths);
1130	init_completion(&priv->ready);
1131
1132	if (gpio_is_valid(audpwron)) {
1133		ret = gpio_request(audpwron, "audpwron");
1134		if (ret)
1135			goto gpio1_err;
1136
1137		ret = gpio_direction_output(audpwron, 0);
1138		if (ret)
1139			goto gpio2_err;
1140
1141		priv->codec_powered = 0;
1142	}
1143
1144	if (naudint) {
1145		/* audio interrupt */
1146		ret = request_threaded_irq(naudint, NULL,
1147				twl6040_naudint_handler,
1148				IRQF_TRIGGER_LOW | IRQF_ONESHOT,
1149				"twl6040_codec", codec);
1150		if (ret)
1151			goto gpio2_err;
1152	} else {
1153		if (gpio_is_valid(audpwron)) {
1154			/* enable only codec ready interrupt */
1155			twl6040_write_reg_cache(codec, TWL6040_REG_INTMR,
1156					~TWL6040_READYMSK & TWL6040_ALLINT_MSK);
1157		} else {
1158			/* no interrupts at all */
1159			twl6040_write_reg_cache(codec, TWL6040_REG_INTMR,
1160						TWL6040_ALLINT_MSK);
1161		}
1162	}
1163
1164	/* init vio registers */
1165	twl6040_init_vio_regs(codec);
1166
1167	/* power on device */
1168	ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
1169	if (ret)
1170		goto irq_err;
1171
1172	ret = snd_soc_register_codec(codec);
1173	if (ret)
1174		goto reg_err;
1175
1176	twl6040_codec = codec;
1177
1178	ret = snd_soc_register_dai(&twl6040_dai);
1179	if (ret)
1180		goto dai_err;
1181
1182	return 0;
1183
1184dai_err:
1185	snd_soc_unregister_codec(codec);
1186	twl6040_codec = NULL;
1187reg_err:
1188	twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
1189irq_err:
1190	if (naudint)
1191		free_irq(naudint, codec);
1192gpio2_err:
1193	if (gpio_is_valid(audpwron))
1194		gpio_free(audpwron);
1195gpio1_err:
1196	kfree(codec->reg_cache);
1197cache_err:
1198	kfree(priv);
1199	return ret;
1200}
1201
1202static int __devexit twl6040_codec_remove(struct platform_device *pdev)
1203{
1204	struct twl6040_data *priv = snd_soc_codec_get_drvdata(twl6040_codec);
1205	int audpwron = priv->audpwron;
1206	int naudint = priv->naudint;
1207
1208	if (gpio_is_valid(audpwron))
1209		gpio_free(audpwron);
1210
1211	if (naudint)
1212		free_irq(naudint, twl6040_codec);
1213
1214	snd_soc_unregister_dai(&twl6040_dai);
1215	snd_soc_unregister_codec(twl6040_codec);
1216
1217	kfree(twl6040_codec);
1218	twl6040_codec = NULL;
1219
1220	return 0;
1221}
1222
1223static struct platform_driver twl6040_codec_driver = {
1224	.driver = {
1225		.name = "twl6040_codec",
1226		.owner = THIS_MODULE,
1227	},
1228	.probe = twl6040_codec_probe,
1229	.remove = __devexit_p(twl6040_codec_remove),
1230};
1231
1232static int __init twl6040_codec_init(void)
1233{
1234	return platform_driver_register(&twl6040_codec_driver);
1235}
1236module_init(twl6040_codec_init);
1237
1238static void __exit twl6040_codec_exit(void)
1239{
1240	platform_driver_unregister(&twl6040_codec_driver);
1241}
1242module_exit(twl6040_codec_exit);
1243
1244MODULE_DESCRIPTION("ASoC TWL6040 codec driver");
1245MODULE_AUTHOR("Misael Lopez Cruz");
1246MODULE_LICENSE("GPL");
1247