[ALSA] ASoC TLV support
[safe/jmp/linux-2.6] / sound / soc / soc-dapm.c
1 /*
2  * soc-dapm.c  --  ALSA SoC Dynamic Audio Power Management
3  *
4  * Copyright 2005 Wolfson Microelectronics PLC.
5  * Author: Liam Girdwood
6  *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
7  *
8  *  This program is free software; you can redistribute  it and/or modify it
9  *  under  the terms of  the GNU General  Public License as published by the
10  *  Free Software Foundation;  either version 2 of the  License, or (at your
11  *  option) any later version.
12  *
13  *  Revision history
14  *    12th Aug 2005   Initial version.
15  *    25th Oct 2005   Implemented path power domain.
16  *    18th Dec 2005   Implemented machine and stream level power domain.
17  *
18  *  Features:
19  *    o Changes power status of internal codec blocks depending on the
20  *      dynamic configuration of codec internal audio paths and active
21  *      DAC's/ADC's.
22  *    o Platform power domain - can support external components i.e. amps and
23  *      mic/meadphone insertion events.
24  *    o Automatic Mic Bias support
25  *    o Jack insertion power event initiation - e.g. hp insertion will enable
26  *      sinks, dacs, etc
27  *    o Delayed powerdown of audio susbsystem to reduce pops between a quick
28  *      device reopen.
29  *
30  *  Todo:
31  *    o DAPM power change sequencing - allow for configurable per
32  *      codec sequences.
33  *    o Support for analogue bias optimisation.
34  *    o Support for reduced codec oversampling rates.
35  *    o Support for reduced codec bias currents.
36  */
37
38 #include <linux/module.h>
39 #include <linux/moduleparam.h>
40 #include <linux/init.h>
41 #include <linux/delay.h>
42 #include <linux/pm.h>
43 #include <linux/bitops.h>
44 #include <linux/platform_device.h>
45 #include <linux/jiffies.h>
46 #include <sound/core.h>
47 #include <sound/pcm.h>
48 #include <sound/pcm_params.h>
49 #include <sound/soc-dapm.h>
50 #include <sound/initval.h>
51
52 /* debug */
53 #define DAPM_DEBUG 0
54 #if DAPM_DEBUG
55 #define dump_dapm(codec, action) dbg_dump_dapm(codec, action)
56 #define dbg(format, arg...) printk(format, ## arg)
57 #else
58 #define dump_dapm(codec, action)
59 #define dbg(format, arg...)
60 #endif
61
62 #define POP_DEBUG 0
63 #if POP_DEBUG
64 #define POP_TIME 500 /* 500 msecs - change if pop debug is too fast */
65 #define pop_wait(time) schedule_timeout_uninterruptible(msecs_to_jiffies(time))
66 #define pop_dbg(format, arg...) printk(format, ## arg); pop_wait(POP_TIME)
67 #else
68 #define pop_dbg(format, arg...)
69 #define pop_wait(time)
70 #endif
71
72 /* dapm power sequences - make this per codec in the future */
73 static int dapm_up_seq[] = {
74         snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic,
75         snd_soc_dapm_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_pga,
76         snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post
77 };
78 static int dapm_down_seq[] = {
79         snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
80         snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic,
81         snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_post
82 };
83
84 static int dapm_status = 1;
85 module_param(dapm_status, int, 0);
86 MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
87
88 /* create a new dapm widget */
89 static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
90         const struct snd_soc_dapm_widget *_widget)
91 {
92         return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
93 }
94
95 /* set up initial codec paths */
96 static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
97         struct snd_soc_dapm_path *p, int i)
98 {
99         switch (w->id) {
100         case snd_soc_dapm_switch:
101         case snd_soc_dapm_mixer: {
102                 int val;
103                 int reg = w->kcontrols[i].private_value & 0xff;
104                 int shift = (w->kcontrols[i].private_value >> 8) & 0x0f;
105                 int mask = (w->kcontrols[i].private_value >> 16) & 0xff;
106                 int invert = (w->kcontrols[i].private_value >> 24) & 0x01;
107
108                 val = snd_soc_read(w->codec, reg);
109                 val = (val >> shift) & mask;
110
111                 if ((invert && !val) || (!invert && val))
112                         p->connect = 1;
113                 else
114                         p->connect = 0;
115         }
116         break;
117         case snd_soc_dapm_mux: {
118                 struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
119                 int val, item, bitmask;
120
121                 for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
122                 ;
123                 val = snd_soc_read(w->codec, e->reg);
124                 item = (val >> e->shift_l) & (bitmask - 1);
125
126                 p->connect = 0;
127                 for (i = 0; i < e->mask; i++) {
128                         if (!(strcmp(p->name, e->texts[i])) && item == i)
129                                 p->connect = 1;
130                 }
131         }
132         break;
133         /* does not effect routing - always connected */
134         case snd_soc_dapm_pga:
135         case snd_soc_dapm_output:
136         case snd_soc_dapm_adc:
137         case snd_soc_dapm_input:
138         case snd_soc_dapm_dac:
139         case snd_soc_dapm_micbias:
140         case snd_soc_dapm_vmid:
141                 p->connect = 1;
142         break;
143         /* does effect routing - dynamically connected */
144         case snd_soc_dapm_hp:
145         case snd_soc_dapm_mic:
146         case snd_soc_dapm_spk:
147         case snd_soc_dapm_line:
148         case snd_soc_dapm_pre:
149         case snd_soc_dapm_post:
150                 p->connect = 0;
151         break;
152         }
153 }
154
155 /* connect mux widget to it's interconnecting audio paths */
156 static int dapm_connect_mux(struct snd_soc_codec *codec,
157         struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
158         struct snd_soc_dapm_path *path, const char *control_name,
159         const struct snd_kcontrol_new *kcontrol)
160 {
161         struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
162         int i;
163
164         for (i = 0; i < e->mask; i++) {
165                 if (!(strcmp(control_name, e->texts[i]))) {
166                         list_add(&path->list, &codec->dapm_paths);
167                         list_add(&path->list_sink, &dest->sources);
168                         list_add(&path->list_source, &src->sinks);
169                         path->name = (char*)e->texts[i];
170                         dapm_set_path_status(dest, path, 0);
171                         return 0;
172                 }
173         }
174
175         return -ENODEV;
176 }
177
178 /* connect mixer widget to it's interconnecting audio paths */
179 static int dapm_connect_mixer(struct snd_soc_codec *codec,
180         struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
181         struct snd_soc_dapm_path *path, const char *control_name)
182 {
183         int i;
184
185         /* search for mixer kcontrol */
186         for (i = 0; i < dest->num_kcontrols; i++) {
187                 if (!strcmp(control_name, dest->kcontrols[i].name)) {
188                         list_add(&path->list, &codec->dapm_paths);
189                         list_add(&path->list_sink, &dest->sources);
190                         list_add(&path->list_source, &src->sinks);
191                         path->name = dest->kcontrols[i].name;
192                         dapm_set_path_status(dest, path, i);
193                         return 0;
194                 }
195         }
196         return -ENODEV;
197 }
198
199 /* update dapm codec register bits */
200 static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
201 {
202         int change, power;
203         unsigned short old, new;
204         struct snd_soc_codec *codec = widget->codec;
205
206         /* check for valid widgets */
207         if (widget->reg < 0 || widget->id == snd_soc_dapm_input ||
208                 widget->id == snd_soc_dapm_output ||
209                 widget->id == snd_soc_dapm_hp ||
210                 widget->id == snd_soc_dapm_mic ||
211                 widget->id == snd_soc_dapm_line ||
212                 widget->id == snd_soc_dapm_spk)
213                 return 0;
214
215         power = widget->power;
216         if (widget->invert)
217                 power = (power ? 0:1);
218
219         old = snd_soc_read(codec, widget->reg);
220         new = (old & ~(0x1 << widget->shift)) | (power << widget->shift);
221
222         change = old != new;
223         if (change) {
224                 pop_dbg("pop test %s : %s in %d ms\n", widget->name,
225                         widget->power ? "on" : "off", POP_TIME);
226                 snd_soc_write(codec, widget->reg, new);
227                 pop_wait(POP_TIME);
228         }
229         dbg("reg old %x new %x change %d\n", old, new, change);
230         return change;
231 }
232
233 /* ramps the volume up or down to minimise pops before or after a
234  * DAPM power event */
235 static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power)
236 {
237         const struct snd_kcontrol_new *k = widget->kcontrols;
238
239         if (widget->muted && !power)
240                 return 0;
241         if (!widget->muted && power)
242                 return 0;
243
244         if (widget->num_kcontrols && k) {
245                 int reg = k->private_value & 0xff;
246                 int shift = (k->private_value >> 8) & 0x0f;
247                 int mask = (k->private_value >> 16) & 0xff;
248                 int invert = (k->private_value >> 24) & 0x01;
249
250                 if (power) {
251                         int i;
252                         /* power up has happended, increase volume to last level */
253                         if (invert) {
254                                 for (i = mask; i > widget->saved_value; i--)
255                                         snd_soc_update_bits(widget->codec, reg, mask, i);
256                         } else {
257                                 for (i = 0; i < widget->saved_value; i++)
258                                         snd_soc_update_bits(widget->codec, reg, mask, i);
259                         }
260                         widget->muted = 0;
261                 } else {
262                         /* power down is about to occur, decrease volume to mute */
263                         int val = snd_soc_read(widget->codec, reg);
264                         int i = widget->saved_value = (val >> shift) & mask;
265                         if (invert) {
266                                 for (; i < mask; i++)
267                                         snd_soc_update_bits(widget->codec, reg, mask, i);
268                         } else {
269                                 for (; i > 0; i--)
270                                         snd_soc_update_bits(widget->codec, reg, mask, i);
271                         }
272                         widget->muted = 1;
273                 }
274         }
275         return 0;
276 }
277
278 /* create new dapm mixer control */
279 static int dapm_new_mixer(struct snd_soc_codec *codec,
280         struct snd_soc_dapm_widget *w)
281 {
282         int i, ret = 0;
283         char name[32];
284         struct snd_soc_dapm_path *path;
285
286         /* add kcontrol */
287         for (i = 0; i < w->num_kcontrols; i++) {
288
289                 /* match name */
290                 list_for_each_entry(path, &w->sources, list_sink) {
291
292                         /* mixer/mux paths name must match control name */
293                         if (path->name != (char*)w->kcontrols[i].name)
294                                 continue;
295
296                         /* add dapm control with long name */
297                         snprintf(name, 32, "%s %s", w->name, w->kcontrols[i].name);
298                         path->long_name = kstrdup (name, GFP_KERNEL);
299                         if (path->long_name == NULL)
300                                 return -ENOMEM;
301
302                         path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
303                                 path->long_name);
304                         ret = snd_ctl_add(codec->card, path->kcontrol);
305                         if (ret < 0) {
306                                 printk(KERN_ERR "asoc: failed to add dapm kcontrol %s\n",
307                                                 path->long_name);
308                                 kfree(path->long_name);
309                                 path->long_name = NULL;
310                                 return ret;
311                         }
312                 }
313         }
314         return ret;
315 }
316
317 /* create new dapm mux control */
318 static int dapm_new_mux(struct snd_soc_codec *codec,
319         struct snd_soc_dapm_widget *w)
320 {
321         struct snd_soc_dapm_path *path = NULL;
322         struct snd_kcontrol *kcontrol;
323         int ret = 0;
324
325         if (!w->num_kcontrols) {
326                 printk(KERN_ERR "asoc: mux %s has no controls\n", w->name);
327                 return -EINVAL;
328         }
329
330         kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
331         ret = snd_ctl_add(codec->card, kcontrol);
332         if (ret < 0)
333                 goto err;
334
335         list_for_each_entry(path, &w->sources, list_sink)
336                 path->kcontrol = kcontrol;
337
338         return ret;
339
340 err:
341         printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
342         return ret;
343 }
344
345 /* create new dapm volume control */
346 static int dapm_new_pga(struct snd_soc_codec *codec,
347         struct snd_soc_dapm_widget *w)
348 {
349         struct snd_kcontrol *kcontrol;
350         int ret = 0;
351
352         if (!w->num_kcontrols)
353                 return -EINVAL;
354
355         kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
356         ret = snd_ctl_add(codec->card, kcontrol);
357         if (ret < 0) {
358                 printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
359                 return ret;
360         }
361
362         return ret;
363 }
364
365 /* reset 'walked' bit for each dapm path */
366 static inline void dapm_clear_walk(struct snd_soc_codec *codec)
367 {
368         struct snd_soc_dapm_path *p;
369
370         list_for_each_entry(p, &codec->dapm_paths, list)
371                 p->walked = 0;
372 }
373
374 /*
375  * Recursively check for a completed path to an active or physically connected
376  * output widget. Returns number of complete paths.
377  */
378 static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
379 {
380         struct snd_soc_dapm_path *path;
381         int con = 0;
382
383         if (widget->id == snd_soc_dapm_adc && widget->active)
384                 return 1;
385
386         if (widget->connected) {
387                 /* connected pin ? */
388                 if (widget->id == snd_soc_dapm_output && !widget->ext)
389                         return 1;
390
391                 /* connected jack or spk ? */
392                 if (widget->id == snd_soc_dapm_hp || widget->id == snd_soc_dapm_spk ||
393                         widget->id == snd_soc_dapm_line)
394                         return 1;
395         }
396
397         list_for_each_entry(path, &widget->sinks, list_source) {
398                 if (path->walked)
399                         continue;
400
401                 if (path->sink && path->connect) {
402                         path->walked = 1;
403                         con += is_connected_output_ep(path->sink);
404                 }
405         }
406
407         return con;
408 }
409
410 /*
411  * Recursively check for a completed path to an active or physically connected
412  * input widget. Returns number of complete paths.
413  */
414 static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
415 {
416         struct snd_soc_dapm_path *path;
417         int con = 0;
418
419         /* active stream ? */
420         if (widget->id == snd_soc_dapm_dac && widget->active)
421                 return 1;
422
423         if (widget->connected) {
424                 /* connected pin ? */
425                 if (widget->id == snd_soc_dapm_input && !widget->ext)
426                         return 1;
427
428                 /* connected VMID/Bias for lower pops */
429                 if (widget->id == snd_soc_dapm_vmid)
430                         return 1;
431
432                 /* connected jack ? */
433                 if (widget->id == snd_soc_dapm_mic || widget->id == snd_soc_dapm_line)
434                         return 1;
435         }
436
437         list_for_each_entry(path, &widget->sources, list_sink) {
438                 if (path->walked)
439                         continue;
440
441                 if (path->source && path->connect) {
442                         path->walked = 1;
443                         con += is_connected_input_ep(path->source);
444                 }
445         }
446
447         return con;
448 }
449
450 /*
451  * Scan each dapm widget for complete audio path.
452  * A complete path is a route that has valid endpoints i.e.:-
453  *
454  *  o DAC to output pin.
455  *  o Input Pin to ADC.
456  *  o Input pin to Output pin (bypass, sidetone)
457  *  o DAC to ADC (loopback).
458  */
459 static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
460 {
461         struct snd_soc_dapm_widget *w;
462         int in, out, i, c = 1, *seq = NULL, ret = 0, power_change, power;
463
464         /* do we have a sequenced stream event */
465         if (event == SND_SOC_DAPM_STREAM_START) {
466                 c = ARRAY_SIZE(dapm_up_seq);
467                 seq = dapm_up_seq;
468         } else if (event == SND_SOC_DAPM_STREAM_STOP) {
469                 c = ARRAY_SIZE(dapm_down_seq);
470                 seq = dapm_down_seq;
471         }
472
473         for(i = 0; i < c; i++) {
474                 list_for_each_entry(w, &codec->dapm_widgets, list) {
475
476                         /* is widget in stream order */
477                         if (seq && seq[i] && w->id != seq[i])
478                                 continue;
479
480                         /* vmid - no action */
481                         if (w->id == snd_soc_dapm_vmid)
482                                 continue;
483
484                         /* active ADC */
485                         if (w->id == snd_soc_dapm_adc && w->active) {
486                                 in = is_connected_input_ep(w);
487                                 dapm_clear_walk(w->codec);
488                                 w->power = (in != 0) ? 1 : 0;
489                                 dapm_update_bits(w);
490                                 continue;
491                         }
492
493                         /* active DAC */
494                         if (w->id == snd_soc_dapm_dac && w->active) {
495                                 out = is_connected_output_ep(w);
496                                 dapm_clear_walk(w->codec);
497                                 w->power = (out != 0) ? 1 : 0;
498                                 dapm_update_bits(w);
499                                 continue;
500                         }
501
502                         /* programmable gain/attenuation */
503                         if (w->id == snd_soc_dapm_pga) {
504                                 int on;
505                                 in = is_connected_input_ep(w);
506                                 dapm_clear_walk(w->codec);
507                                 out = is_connected_output_ep(w);
508                                 dapm_clear_walk(w->codec);
509                                 w->power = on = (out != 0 && in != 0) ? 1 : 0;
510
511                                 if (!on)
512                                         dapm_set_pga(w, on); /* lower volume to reduce pops */
513                                 dapm_update_bits(w);
514                                 if (on)
515                                         dapm_set_pga(w, on); /* restore volume from zero */
516
517                                 continue;
518                         }
519
520                         /* pre and post event widgets */
521                         if (w->id == snd_soc_dapm_pre) {
522                                 if (!w->event)
523                                         continue;
524
525                                 if (event == SND_SOC_DAPM_STREAM_START) {
526                                         ret = w->event(w, SND_SOC_DAPM_PRE_PMU);
527                                         if (ret < 0)
528                                                 return ret;
529                                 } else if (event == SND_SOC_DAPM_STREAM_STOP) {
530                                         ret = w->event(w, SND_SOC_DAPM_PRE_PMD);
531                                         if (ret < 0)
532                                                 return ret;
533                                 }
534                                 continue;
535                         }
536                         if (w->id == snd_soc_dapm_post) {
537                                 if (!w->event)
538                                         continue;
539
540                                 if (event == SND_SOC_DAPM_STREAM_START) {
541                                         ret = w->event(w, SND_SOC_DAPM_POST_PMU);
542                                         if (ret < 0)
543                                                 return ret;
544                                 } else if (event == SND_SOC_DAPM_STREAM_STOP) {
545                                         ret = w->event(w, SND_SOC_DAPM_POST_PMD);
546                                         if (ret < 0)
547                                                 return ret;
548                                 }
549                                 continue;
550                         }
551
552                         /* all other widgets */
553                         in = is_connected_input_ep(w);
554                         dapm_clear_walk(w->codec);
555                         out = is_connected_output_ep(w);
556                         dapm_clear_walk(w->codec);
557                         power = (out != 0 && in != 0) ? 1 : 0;
558                         power_change = (w->power == power) ? 0: 1;
559                         w->power = power;
560
561                         /* call any power change event handlers */
562                         if (power_change) {
563                                 if (w->event) {
564                                         dbg("power %s event for %s flags %x\n",
565                                                 w->power ? "on" : "off", w->name, w->event_flags);
566                                         if (power) {
567                                                 /* power up event */
568                                                 if (w->event_flags & SND_SOC_DAPM_PRE_PMU) {
569                                                         ret = w->event(w, SND_SOC_DAPM_PRE_PMU);
570                                                         if (ret < 0)
571                                                                 return ret;
572                                                 }
573                                                 dapm_update_bits(w);
574                                                 if (w->event_flags & SND_SOC_DAPM_POST_PMU){
575                                                         ret = w->event(w, SND_SOC_DAPM_POST_PMU);
576                                                         if (ret < 0)
577                                                                 return ret;
578                                                 }
579                                         } else {
580                                                 /* power down event */
581                                                 if (w->event_flags & SND_SOC_DAPM_PRE_PMD) {
582                                                         ret = w->event(w, SND_SOC_DAPM_PRE_PMD);
583                                                         if (ret < 0)
584                                                                 return ret;
585                                                 }
586                                                 dapm_update_bits(w);
587                                                 if (w->event_flags & SND_SOC_DAPM_POST_PMD) {
588                                                         ret = w->event(w, SND_SOC_DAPM_POST_PMD);
589                                                         if (ret < 0)
590                                                                 return ret;
591                                                 }
592                                         }
593                                 } else
594                                         /* no event handler */
595                                         dapm_update_bits(w);
596                         }
597                 }
598         }
599
600         return ret;
601 }
602
603 #if DAPM_DEBUG
604 static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
605 {
606         struct snd_soc_dapm_widget *w;
607         struct snd_soc_dapm_path *p = NULL;
608         int in, out;
609
610         printk("DAPM %s %s\n", codec->name, action);
611
612         list_for_each_entry(w, &codec->dapm_widgets, list) {
613
614                 /* only display widgets that effect routing */
615                 switch (w->id) {
616                 case snd_soc_dapm_pre:
617                 case snd_soc_dapm_post:
618                 case snd_soc_dapm_vmid:
619                         continue;
620                 case snd_soc_dapm_mux:
621                 case snd_soc_dapm_output:
622                 case snd_soc_dapm_input:
623                 case snd_soc_dapm_switch:
624                 case snd_soc_dapm_hp:
625                 case snd_soc_dapm_mic:
626                 case snd_soc_dapm_spk:
627                 case snd_soc_dapm_line:
628                 case snd_soc_dapm_micbias:
629                 case snd_soc_dapm_dac:
630                 case snd_soc_dapm_adc:
631                 case snd_soc_dapm_pga:
632                 case snd_soc_dapm_mixer:
633                         if (w->name) {
634                                 in = is_connected_input_ep(w);
635                                 dapm_clear_walk(w->codec);
636                                 out = is_connected_output_ep(w);
637                                 dapm_clear_walk(w->codec);
638                                 printk("%s: %s  in %d out %d\n", w->name,
639                                         w->power ? "On":"Off",in, out);
640
641                                 list_for_each_entry(p, &w->sources, list_sink) {
642                                         if (p->connect)
643                                                 printk(" in  %s %s\n", p->name ? p->name : "static",
644                                                         p->source->name);
645                                 }
646                                 list_for_each_entry(p, &w->sinks, list_source) {
647                                         if (p->connect)
648                                                 printk(" out %s %s\n", p->name ? p->name : "static",
649                                                         p->sink->name);
650                                 }
651                         }
652                 break;
653                 }
654         }
655 }
656 #endif
657
658 /* test and update the power status of a mux widget */
659 static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
660                                  struct snd_kcontrol *kcontrol, int mask,
661                                  int val, struct soc_enum* e)
662 {
663         struct snd_soc_dapm_path *path;
664         int found = 0;
665
666         if (widget->id != snd_soc_dapm_mux)
667                 return -ENODEV;
668
669         if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
670                 return 0;
671
672         /* find dapm widget path assoc with kcontrol */
673         list_for_each_entry(path, &widget->codec->dapm_paths, list) {
674                 if (path->kcontrol != kcontrol)
675                         continue;
676
677                 if (!path->name || ! e->texts[val])
678                         continue;
679
680                 found = 1;
681                 /* we now need to match the string in the enum to the path */
682                 if (!(strcmp(path->name, e->texts[val])))
683                         path->connect = 1; /* new connection */
684                 else
685                         path->connect = 0; /* old connection must be powered down */
686         }
687
688         if (found)
689                 dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
690
691         return 0;
692 }
693
694 /* test and update the power status of a mixer widget */
695 static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
696                                    struct snd_kcontrol *kcontrol, int reg,
697                                    int val_mask, int val, int invert)
698 {
699         struct snd_soc_dapm_path *path;
700         int found = 0;
701
702         if (widget->id != snd_soc_dapm_mixer)
703                 return -ENODEV;
704
705         if (!snd_soc_test_bits(widget->codec, reg, val_mask, val))
706                 return 0;
707
708         /* find dapm widget path assoc with kcontrol */
709         list_for_each_entry(path, &widget->codec->dapm_paths, list) {
710                 if (path->kcontrol != kcontrol)
711                         continue;
712
713                 /* found, now check type */
714                 found = 1;
715                 if (val)
716                         /* new connection */
717                         path->connect = invert ? 0:1;
718                 else
719                         /* old connection must be powered down */
720                         path->connect = invert ? 1:0;
721                 break;
722         }
723
724         if (found)
725                 dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
726
727         return 0;
728 }
729
730 /* show dapm widget status in sys fs */
731 static ssize_t dapm_widget_show(struct device *dev,
732         struct device_attribute *attr, char *buf)
733 {
734         struct snd_soc_device *devdata = dev_get_drvdata(dev);
735         struct snd_soc_codec *codec = devdata->codec;
736         struct snd_soc_dapm_widget *w;
737         int count = 0;
738         char *state = "not set";
739
740         list_for_each_entry(w, &codec->dapm_widgets, list) {
741
742                 /* only display widgets that burnm power */
743                 switch (w->id) {
744                 case snd_soc_dapm_hp:
745                 case snd_soc_dapm_mic:
746                 case snd_soc_dapm_spk:
747                 case snd_soc_dapm_line:
748                 case snd_soc_dapm_micbias:
749                 case snd_soc_dapm_dac:
750                 case snd_soc_dapm_adc:
751                 case snd_soc_dapm_pga:
752                 case snd_soc_dapm_mixer:
753                         if (w->name)
754                                 count += sprintf(buf + count, "%s: %s\n",
755                                         w->name, w->power ? "On":"Off");
756                 break;
757                 default:
758                 break;
759                 }
760         }
761
762         switch(codec->dapm_state){
763         case SNDRV_CTL_POWER_D0:
764                 state = "D0";
765                 break;
766         case SNDRV_CTL_POWER_D1:
767                 state = "D1";
768                 break;
769         case SNDRV_CTL_POWER_D2:
770                 state = "D2";
771                 break;
772         case SNDRV_CTL_POWER_D3hot:
773                 state = "D3hot";
774                 break;
775         case SNDRV_CTL_POWER_D3cold:
776                 state = "D3cold";
777                 break;
778         }
779         count += sprintf(buf + count, "PM State: %s\n", state);
780
781         return count;
782 }
783
784 static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
785
786 int snd_soc_dapm_sys_add(struct device *dev)
787 {
788         int ret = 0;
789
790         if (dapm_status)
791                 ret = device_create_file(dev, &dev_attr_dapm_widget);
792
793         return ret;
794 }
795
796 static void snd_soc_dapm_sys_remove(struct device *dev)
797 {
798         if (dapm_status)
799                 device_remove_file(dev, &dev_attr_dapm_widget);
800 }
801
802 /* free all dapm widgets and resources */
803 static void dapm_free_widgets(struct snd_soc_codec *codec)
804 {
805         struct snd_soc_dapm_widget *w, *next_w;
806         struct snd_soc_dapm_path *p, *next_p;
807
808         list_for_each_entry_safe(w, next_w, &codec->dapm_widgets, list) {
809                 list_del(&w->list);
810                 kfree(w);
811         }
812
813         list_for_each_entry_safe(p, next_p, &codec->dapm_paths, list) {
814                 list_del(&p->list);
815                 kfree(p->long_name);
816                 kfree(p);
817         }
818 }
819
820 /**
821  * snd_soc_dapm_sync_endpoints - scan and power dapm paths
822  * @codec: audio codec
823  *
824  * Walks all dapm audio paths and powers widgets according to their
825  * stream or path usage.
826  *
827  * Returns 0 for success.
828  */
829 int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec)
830 {
831         return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
832 }
833 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints);
834
835 /**
836  * snd_soc_dapm_connect_input - connect dapm widgets
837  * @codec: audio codec
838  * @sink: name of target widget
839  * @control: mixer control name
840  * @source: name of source name
841  *
842  * Connects 2 dapm widgets together via a named audio path. The sink is
843  * the widget receiving the audio signal, whilst the source is the sender
844  * of the audio signal.
845  *
846  * Returns 0 for success else error.
847  */
848 int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
849         const char * control, const char *source)
850 {
851         struct snd_soc_dapm_path *path;
852         struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
853         int ret = 0;
854
855         /* find src and dest widgets */
856         list_for_each_entry(w, &codec->dapm_widgets, list) {
857
858                 if (!wsink && !(strcmp(w->name, sink))) {
859                         wsink = w;
860                         continue;
861                 }
862                 if (!wsource && !(strcmp(w->name, source))) {
863                         wsource = w;
864                 }
865         }
866
867         if (wsource == NULL || wsink == NULL)
868                 return -ENODEV;
869
870         path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
871         if (!path)
872                 return -ENOMEM;
873
874         path->source = wsource;
875         path->sink = wsink;
876         INIT_LIST_HEAD(&path->list);
877         INIT_LIST_HEAD(&path->list_source);
878         INIT_LIST_HEAD(&path->list_sink);
879
880         /* check for external widgets */
881         if (wsink->id == snd_soc_dapm_input) {
882                 if (wsource->id == snd_soc_dapm_micbias ||
883                         wsource->id == snd_soc_dapm_mic ||
884                         wsink->id == snd_soc_dapm_line ||
885                         wsink->id == snd_soc_dapm_output)
886                         wsink->ext = 1;
887         }
888         if (wsource->id == snd_soc_dapm_output) {
889                 if (wsink->id == snd_soc_dapm_spk ||
890                         wsink->id == snd_soc_dapm_hp ||
891                         wsink->id == snd_soc_dapm_line ||
892                         wsink->id == snd_soc_dapm_input)
893                         wsource->ext = 1;
894         }
895
896         /* connect static paths */
897         if (control == NULL) {
898                 list_add(&path->list, &codec->dapm_paths);
899                 list_add(&path->list_sink, &wsink->sources);
900                 list_add(&path->list_source, &wsource->sinks);
901                 path->connect = 1;
902                 return 0;
903         }
904
905         /* connect dynamic paths */
906         switch(wsink->id) {
907         case snd_soc_dapm_adc:
908         case snd_soc_dapm_dac:
909         case snd_soc_dapm_pga:
910         case snd_soc_dapm_input:
911         case snd_soc_dapm_output:
912         case snd_soc_dapm_micbias:
913         case snd_soc_dapm_vmid:
914         case snd_soc_dapm_pre:
915         case snd_soc_dapm_post:
916                 list_add(&path->list, &codec->dapm_paths);
917                 list_add(&path->list_sink, &wsink->sources);
918                 list_add(&path->list_source, &wsource->sinks);
919                 path->connect = 1;
920                 return 0;
921         case snd_soc_dapm_mux:
922                 ret = dapm_connect_mux(codec, wsource, wsink, path, control,
923                         &wsink->kcontrols[0]);
924                 if (ret != 0)
925                         goto err;
926                 break;
927         case snd_soc_dapm_switch:
928         case snd_soc_dapm_mixer:
929                 ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
930                 if (ret != 0)
931                         goto err;
932                 break;
933         case snd_soc_dapm_hp:
934         case snd_soc_dapm_mic:
935         case snd_soc_dapm_line:
936         case snd_soc_dapm_spk:
937                 list_add(&path->list, &codec->dapm_paths);
938                 list_add(&path->list_sink, &wsink->sources);
939                 list_add(&path->list_source, &wsource->sinks);
940                 path->connect = 0;
941                 return 0;
942         }
943         return 0;
944
945 err:
946         printk(KERN_WARNING "asoc: no dapm match for %s --> %s --> %s\n", source,
947                 control, sink);
948         kfree(path);
949         return ret;
950 }
951 EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
952
953 /**
954  * snd_soc_dapm_new_widgets - add new dapm widgets
955  * @codec: audio codec
956  *
957  * Checks the codec for any new dapm widgets and creates them if found.
958  *
959  * Returns 0 for success.
960  */
961 int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
962 {
963         struct snd_soc_dapm_widget *w;
964
965         mutex_lock(&codec->mutex);
966         list_for_each_entry(w, &codec->dapm_widgets, list)
967         {
968                 if (w->new)
969                         continue;
970
971                 switch(w->id) {
972                 case snd_soc_dapm_switch:
973                 case snd_soc_dapm_mixer:
974                         dapm_new_mixer(codec, w);
975                         break;
976                 case snd_soc_dapm_mux:
977                         dapm_new_mux(codec, w);
978                         break;
979                 case snd_soc_dapm_adc:
980                 case snd_soc_dapm_dac:
981                 case snd_soc_dapm_pga:
982                         dapm_new_pga(codec, w);
983                         break;
984                 case snd_soc_dapm_input:
985                 case snd_soc_dapm_output:
986                 case snd_soc_dapm_micbias:
987                 case snd_soc_dapm_spk:
988                 case snd_soc_dapm_hp:
989                 case snd_soc_dapm_mic:
990                 case snd_soc_dapm_line:
991                 case snd_soc_dapm_vmid:
992                 case snd_soc_dapm_pre:
993                 case snd_soc_dapm_post:
994                         break;
995                 }
996                 w->new = 1;
997         }
998
999         dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
1000         mutex_unlock(&codec->mutex);
1001         return 0;
1002 }
1003 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
1004
1005 /**
1006  * snd_soc_dapm_get_volsw - dapm mixer get callback
1007  * @kcontrol: mixer control
1008  * @uinfo: control element information
1009  *
1010  * Callback to get the value of a dapm mixer control.
1011  *
1012  * Returns 0 for success.
1013  */
1014 int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
1015         struct snd_ctl_elem_value *ucontrol)
1016 {
1017         struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1018         int reg = kcontrol->private_value & 0xff;
1019         int shift = (kcontrol->private_value >> 8) & 0x0f;
1020         int rshift = (kcontrol->private_value >> 12) & 0x0f;
1021         int max = (kcontrol->private_value >> 16) & 0xff;
1022         int invert = (kcontrol->private_value >> 24) & 0x01;
1023         int mask = (1 << fls(max)) - 1;
1024
1025         /* return the saved value if we are powered down */
1026         if (widget->id == snd_soc_dapm_pga && !widget->power) {
1027                 ucontrol->value.integer.value[0] = widget->saved_value;
1028                 return 0;
1029         }
1030
1031         ucontrol->value.integer.value[0] =
1032                 (snd_soc_read(widget->codec, reg) >> shift) & mask;
1033         if (shift != rshift)
1034                 ucontrol->value.integer.value[1] =
1035                         (snd_soc_read(widget->codec, reg) >> rshift) & mask;
1036         if (invert) {
1037                 ucontrol->value.integer.value[0] =
1038                         max - ucontrol->value.integer.value[0];
1039                 if (shift != rshift)
1040                         ucontrol->value.integer.value[1] =
1041                                 max - ucontrol->value.integer.value[1];
1042         }
1043
1044         return 0;
1045 }
1046 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
1047
1048 /**
1049  * snd_soc_dapm_put_volsw - dapm mixer set callback
1050  * @kcontrol: mixer control
1051  * @uinfo: control element information
1052  *
1053  * Callback to set the value of a dapm mixer control.
1054  *
1055  * Returns 0 for success.
1056  */
1057 int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
1058         struct snd_ctl_elem_value *ucontrol)
1059 {
1060         struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1061         int reg = kcontrol->private_value & 0xff;
1062         int shift = (kcontrol->private_value >> 8) & 0x0f;
1063         int rshift = (kcontrol->private_value >> 12) & 0x0f;
1064         int max = (kcontrol->private_value >> 16) & 0xff;
1065         int mask = (1 << fls(max)) - 1;
1066         int invert = (kcontrol->private_value >> 24) & 0x01;
1067         unsigned short val, val2, val_mask;
1068         int ret;
1069
1070         val = (ucontrol->value.integer.value[0] & mask);
1071
1072         if (invert)
1073                 val = max - val;
1074         val_mask = mask << shift;
1075         val = val << shift;
1076         if (shift != rshift) {
1077                 val2 = (ucontrol->value.integer.value[1] & mask);
1078                 if (invert)
1079                         val2 = max - val2;
1080                 val_mask |= mask << rshift;
1081                 val |= val2 << rshift;
1082         }
1083
1084         mutex_lock(&widget->codec->mutex);
1085         widget->value = val;
1086
1087         /* save volume value if the widget is powered down */
1088         if (widget->id == snd_soc_dapm_pga && !widget->power) {
1089                 widget->saved_value = val;
1090                 mutex_unlock(&widget->codec->mutex);
1091                 return 1;
1092         }
1093
1094         dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert);
1095         if (widget->event) {
1096                 if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
1097                         ret = widget->event(widget, SND_SOC_DAPM_PRE_REG);
1098                         if (ret < 0)
1099                                 goto out;
1100                 }
1101                 ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
1102                 if (widget->event_flags & SND_SOC_DAPM_POST_REG)
1103                         ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
1104         } else
1105                 ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
1106
1107 out:
1108         mutex_unlock(&widget->codec->mutex);
1109         return ret;
1110 }
1111 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
1112
1113 /**
1114  * snd_soc_dapm_get_enum_double - dapm enumerated double mixer get callback
1115  * @kcontrol: mixer control
1116  * @uinfo: control element information
1117  *
1118  * Callback to get the value of a dapm enumerated double mixer control.
1119  *
1120  * Returns 0 for success.
1121  */
1122 int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
1123         struct snd_ctl_elem_value *ucontrol)
1124 {
1125         struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1126         struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
1127         unsigned short val, bitmask;
1128
1129         for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
1130                 ;
1131         val = snd_soc_read(widget->codec, e->reg);
1132         ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
1133         if (e->shift_l != e->shift_r)
1134                 ucontrol->value.enumerated.item[1] =
1135                         (val >> e->shift_r) & (bitmask - 1);
1136
1137         return 0;
1138 }
1139 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
1140
1141 /**
1142  * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback
1143  * @kcontrol: mixer control
1144  * @uinfo: control element information
1145  *
1146  * Callback to set the value of a dapm enumerated double mixer control.
1147  *
1148  * Returns 0 for success.
1149  */
1150 int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
1151         struct snd_ctl_elem_value *ucontrol)
1152 {
1153         struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
1154         struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
1155         unsigned short val, mux;
1156         unsigned short mask, bitmask;
1157         int ret = 0;
1158
1159         for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
1160                 ;
1161         if (ucontrol->value.enumerated.item[0] > e->mask - 1)
1162                 return -EINVAL;
1163         mux = ucontrol->value.enumerated.item[0];
1164         val = mux << e->shift_l;
1165         mask = (bitmask - 1) << e->shift_l;
1166         if (e->shift_l != e->shift_r) {
1167                 if (ucontrol->value.enumerated.item[1] > e->mask - 1)
1168                         return -EINVAL;
1169                 val |= ucontrol->value.enumerated.item[1] << e->shift_r;
1170                 mask |= (bitmask - 1) << e->shift_r;
1171         }
1172
1173         mutex_lock(&widget->codec->mutex);
1174         widget->value = val;
1175         dapm_mux_update_power(widget, kcontrol, mask, mux, e);
1176         if (widget->event) {
1177                 if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
1178                         ret = widget->event(widget, SND_SOC_DAPM_PRE_REG);
1179                         if (ret < 0)
1180                                 goto out;
1181                 }
1182                 ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
1183                 if (widget->event_flags & SND_SOC_DAPM_POST_REG)
1184                         ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
1185         } else
1186                 ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
1187
1188 out:
1189         mutex_unlock(&widget->codec->mutex);
1190         return ret;
1191 }
1192 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
1193
1194 /**
1195  * snd_soc_dapm_new_control - create new dapm control
1196  * @codec: audio codec
1197  * @widget: widget template
1198  *
1199  * Creates a new dapm control based upon the template.
1200  *
1201  * Returns 0 for success else error.
1202  */
1203 int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
1204         const struct snd_soc_dapm_widget *widget)
1205 {
1206         struct snd_soc_dapm_widget *w;
1207
1208         if ((w = dapm_cnew_widget(widget)) == NULL)
1209                 return -ENOMEM;
1210
1211         w->codec = codec;
1212         INIT_LIST_HEAD(&w->sources);
1213         INIT_LIST_HEAD(&w->sinks);
1214         INIT_LIST_HEAD(&w->list);
1215         list_add(&w->list, &codec->dapm_widgets);
1216
1217         /* machine layer set ups unconnected pins and insertions */
1218         w->connected = 1;
1219         return 0;
1220 }
1221 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
1222
1223 /**
1224  * snd_soc_dapm_stream_event - send a stream event to the dapm core
1225  * @codec: audio codec
1226  * @stream: stream name
1227  * @event: stream event
1228  *
1229  * Sends a stream event to the dapm core. The core then makes any
1230  * necessary widget power changes.
1231  *
1232  * Returns 0 for success else error.
1233  */
1234 int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
1235         char *stream, int event)
1236 {
1237         struct snd_soc_dapm_widget *w;
1238
1239         if (stream == NULL)
1240                 return 0;
1241
1242         mutex_lock(&codec->mutex);
1243         list_for_each_entry(w, &codec->dapm_widgets, list)
1244         {
1245                 if (!w->sname)
1246                         continue;
1247                 dbg("widget %s\n %s stream %s event %d\n", w->name, w->sname,
1248                         stream, event);
1249                 if (strstr(w->sname, stream)) {
1250                         switch(event) {
1251                         case SND_SOC_DAPM_STREAM_START:
1252                                 w->active = 1;
1253                                 break;
1254                         case SND_SOC_DAPM_STREAM_STOP:
1255                                 w->active = 0;
1256                                 break;
1257                         case SND_SOC_DAPM_STREAM_SUSPEND:
1258                                 if (w->active)
1259                                         w->suspend = 1;
1260                                 w->active = 0;
1261                                 break;
1262                         case SND_SOC_DAPM_STREAM_RESUME:
1263                                 if (w->suspend) {
1264                                         w->active = 1;
1265                                         w->suspend = 0;
1266                                 }
1267                                 break;
1268                         case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
1269                                 break;
1270                         case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
1271                                 break;
1272                         }
1273                 }
1274         }
1275         mutex_unlock(&codec->mutex);
1276
1277         dapm_power_widgets(codec, event);
1278         dump_dapm(codec, __FUNCTION__);
1279         return 0;
1280 }
1281 EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
1282
1283 /**
1284  * snd_soc_dapm_device_event - send a device event to the dapm core
1285  * @socdev: audio device
1286  * @event: device event
1287  *
1288  * Sends a device event to the dapm core. The core then makes any
1289  * necessary machine or codec power changes..
1290  *
1291  * Returns 0 for success else error.
1292  */
1293 int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event)
1294 {
1295         struct snd_soc_codec *codec = socdev->codec;
1296         struct snd_soc_machine *machine = socdev->machine;
1297
1298         if (machine->dapm_event)
1299                                 machine->dapm_event(machine, event);
1300         if (codec->dapm_event)
1301                                 codec->dapm_event(codec, event);
1302         return 0;
1303 }
1304 EXPORT_SYMBOL_GPL(snd_soc_dapm_device_event);
1305
1306 /**
1307  * snd_soc_dapm_set_endpoint - set audio endpoint status
1308  * @codec: audio codec
1309  * @endpoint: audio signal endpoint (or start point)
1310  * @status: point status
1311  *
1312  * Set audio endpoint status - connected or disconnected.
1313  *
1314  * Returns 0 for success else error.
1315  */
1316 int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
1317         char *endpoint, int status)
1318 {
1319         struct snd_soc_dapm_widget *w;
1320
1321         list_for_each_entry(w, &codec->dapm_widgets, list) {
1322                 if (!strcmp(w->name, endpoint)) {
1323                         w->connected = status;
1324                 }
1325         }
1326
1327         return 0;
1328 }
1329 EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint);
1330
1331 /**
1332  * snd_soc_dapm_free - free dapm resources
1333  * @socdev: SoC device
1334  *
1335  * Free all dapm widgets and resources.
1336  */
1337 void snd_soc_dapm_free(struct snd_soc_device *socdev)
1338 {
1339         struct snd_soc_codec *codec = socdev->codec;
1340
1341         snd_soc_dapm_sys_remove(socdev->dev);
1342         dapm_free_widgets(codec);
1343 }
1344 EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
1345
1346 /* Module information */
1347 MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
1348 MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
1349 MODULE_LICENSE("GPL");