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